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> Reviewed-on: https://review.coreboot.org/c/coreboot/+/88395 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: David Hendricks <david.hendricks@gmail.com> Reviewed-by: Filip Lewiński <filip.lewinski@3mdeb.com> Reviewed-by: Máté Kukri <km@mkukri.xyz>
This commit is contained in:
parent
2181b02765
commit
96e381766e
8 changed files with 270 additions and 0 deletions
24
src/ec/lenovo/mec1653/Kconfig
Normal file
24
src/ec/lenovo/mec1653/Kconfig
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config EC_LENOVO_MEC1653
|
||||
bool
|
||||
|
||||
if EC_LENOVO_MEC1653
|
||||
|
||||
config MEC1653_HAS_DEBUG_UNLOCK
|
||||
bool
|
||||
|
||||
config MEC1653_DEBUG_UNLOCK_KEY
|
||||
string
|
||||
depends on MEC1653_HAS_DEBUG_UNLOCK
|
||||
help
|
||||
Debug unlock key necessary to enable the UART.
|
||||
|
||||
config MEC1653_ENABLE_UART
|
||||
bool "Enable EC UART"
|
||||
depends on MEC1653_HAS_DEBUG_UNLOCK
|
||||
help
|
||||
The EC UART physical interface is board-specific and requires
|
||||
a board-specific debug unlock key.
|
||||
|
||||
endif
|
||||
9
src/ec/lenovo/mec1653/Makefile.mk
Normal file
9
src/ec/lenovo/mec1653/Makefile.mk
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
ifeq ($(CONFIG_EC_LENOVO_MEC1653),y)
|
||||
|
||||
bootblock-y += mec1653.c
|
||||
bootblock-$(CONFIG_MEC1653_ENABLE_UART) += debug.c
|
||||
bootblock-$(CONFIG_MEC1653_ENABLE_UART) += uart.c
|
||||
|
||||
endif
|
||||
81
src/ec/lenovo/mec1653/debug.c
Normal file
81
src/ec/lenovo/mec1653/debug.c
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <arch/io.h>
|
||||
#include <ec/acpi/ec.h>
|
||||
#include "debug.h"
|
||||
#include "mec1653.h"
|
||||
|
||||
|
||||
static void debug_cmd(uint8_t cmd)
|
||||
{
|
||||
ec_set_ports(EC_SC, EC_DATA);
|
||||
ec_write(EC_DEBUG_CMD, cmd);
|
||||
while (ec_read(EC_DEBUG_CMD) & 0x80)
|
||||
;
|
||||
}
|
||||
|
||||
uint32_t debug_read_dword(uint32_t addr)
|
||||
{
|
||||
ec_set_ports(EC3_CMD, EC3_DATA);
|
||||
ec_clear_out_queue();
|
||||
ec_ready_send(EC_SEND_TIMEOUT_US);
|
||||
outl(addr << 8 | 0xE2, EC3_DATA);
|
||||
ec_ready_recv(EC_SEND_TIMEOUT_US);
|
||||
return inl(EC3_DATA);
|
||||
}
|
||||
|
||||
void debug_write_dword(uint32_t addr, uint32_t val)
|
||||
{
|
||||
ec_set_ports(EC3_CMD, EC3_DATA);
|
||||
ec_clear_out_queue();
|
||||
ec_ready_send(EC_SEND_TIMEOUT_US);
|
||||
outl(addr << 8 | 0xEA, EC3_DATA);
|
||||
ec_ready_send(EC_SEND_TIMEOUT_US);
|
||||
outl(val, EC3_DATA);
|
||||
}
|
||||
|
||||
// Helper function to convert hex character to byte
|
||||
static uint8_t hex_char_to_byte(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
return 0; // Invalid character
|
||||
}
|
||||
|
||||
// Helper function to convert 16-char hex string to 8 bytes
|
||||
static void hex_string_to_bytes(const char *hex_str, uint8_t *bytes)
|
||||
{
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
bytes[i] = (hex_char_to_byte(hex_str[i * 2]) << 4) |
|
||||
hex_char_to_byte(hex_str[i * 2 + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
void debug_write_key(uint8_t i, const char *hex_key)
|
||||
{
|
||||
uint8_t key_bytes[8];
|
||||
|
||||
ec_set_ports(EC_SC, EC_DATA);
|
||||
hex_string_to_bytes(hex_key, key_bytes);
|
||||
|
||||
for (int j = 0; j < 8; ++j)
|
||||
ec_write(0x3e + j, key_bytes[j]);
|
||||
debug_cmd(0xc0 | (i & 0xf));
|
||||
}
|
||||
|
||||
void debug_read_key(uint8_t i, uint8_t *key)
|
||||
{
|
||||
debug_cmd(0x80 | (i & 0xf));
|
||||
for (int j = 0; j < 8; ++j)
|
||||
key[j] = ec_read(0x3e + j);
|
||||
}
|
||||
|
||||
uint16_t debug_loaded_keys(void)
|
||||
{
|
||||
ec_set_ports(EC_SC, EC_DATA);
|
||||
return (uint16_t) ec_read(0x87) << 8 | (uint16_t) ec_read(0x86);
|
||||
}
|
||||
36
src/ec/lenovo/mec1653/debug.h
Normal file
36
src/ec/lenovo/mec1653/debug.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef EC_LENOVO_MEC1653_DEBUG_H
|
||||
#define EC_LENOVO_MEC1653_DEBUG_H
|
||||
|
||||
// The following location (via either EC0 or EC1) can be used to interact with the debug interface
|
||||
#define EC_DEBUG_CMD 0x3d
|
||||
|
||||
// RW unlock key index
|
||||
#define DEBUG_RW_KEY_IDX 1
|
||||
|
||||
#define EC_SEND_TIMEOUT_US 20000 // 20ms
|
||||
#define EC_RECV_TIMEOUT_US 320000 // 320ms
|
||||
|
||||
#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
|
||||
|
||||
|
||||
// Read loaded debug key mask
|
||||
uint16_t debug_loaded_keys(void);
|
||||
|
||||
void debug_read_key(uint8_t i, uint8_t *key);
|
||||
|
||||
void debug_write_key(uint8_t i, const char *hex_key);
|
||||
|
||||
uint32_t debug_read_dword(uint32_t addr);
|
||||
|
||||
void debug_write_dword(uint32_t addr, uint32_t val);
|
||||
|
||||
#endif /* EC_LENOVO_MEC1653_DEBUG_H */
|
||||
30
src/ec/lenovo/mec1653/mec1653.c
Normal file
30
src/ec/lenovo/mec1653/mec1653.c
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <ec/acpi/ec.h>
|
||||
#include <arch/io.h>
|
||||
#include "mec1653.h"
|
||||
#include "uart.h"
|
||||
|
||||
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
|
||||
ec_set_ports(EC_SC, EC_DATA);
|
||||
ec_clear_out_queue();
|
||||
ec_write(ec_read(0), 0x20);
|
||||
|
||||
// Reset LEDs to power on state
|
||||
// (Without this warm reboot leaves LEDs off)
|
||||
ec_write(0x0c, 0x80);
|
||||
ec_write(0x0c, 0x07);
|
||||
ec_write(0x0c, 0x8a);
|
||||
|
||||
// Setup debug UART
|
||||
if (CONFIG(MEC1653_ENABLE_UART))
|
||||
mec1653_configure_uart();
|
||||
}
|
||||
8
src/ec/lenovo/mec1653/mec1653.h
Normal file
8
src/ec/lenovo/mec1653/mec1653.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef EC_LENOVO_MEC1653_H
|
||||
#define EC_LENOVO_MEC1653_H
|
||||
|
||||
void bootblock_ec_init(void);
|
||||
|
||||
#endif /* EC_LENOVO_MEC1653_H */
|
||||
47
src/ec/lenovo/mec1653/uart.c
Normal file
47
src/ec/lenovo/mec1653/uart.c
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <device/pnp_ops.h>
|
||||
#include <ec/lenovo/mec1653/mec1653.h>
|
||||
#include "debug.h"
|
||||
#include "uart.h"
|
||||
|
||||
static void pnp_write_config32(pnp_devfn_t dev, uint8_t offset, uint32_t value)
|
||||
{
|
||||
pnp_write_config(dev, offset, value & 0xff);
|
||||
pnp_write_config(dev, offset + 1, (value >> 8) & 0xff);
|
||||
pnp_write_config(dev, offset + 2, (value >> 16) & 0xff);
|
||||
pnp_write_config(dev, offset + 3, (value >> 24) & 0xff);
|
||||
}
|
||||
|
||||
void mec1653_configure_uart(void)
|
||||
{
|
||||
pnp_devfn_t lpc_dev = PNP_DEV(EC_CFG_PORT, LDN_LPCIF);
|
||||
pnp_devfn_t uart_dev = PNP_DEV(EC_CFG_PORT, LDN_UART);
|
||||
|
||||
// Enter PNP conf state
|
||||
outb(MEC_CFG_ENTRY_KEY, EC_CFG_PORT);
|
||||
|
||||
// Select LPC I/F LDN
|
||||
pnp_set_logical_device(lpc_dev);
|
||||
|
||||
// Write UART BAR
|
||||
pnp_write_config32(lpc_dev, LPCIF_BAR_UART, (uint32_t) UART_PORT << 16 | 0x8707);
|
||||
|
||||
// Set SIRQ4 to UART
|
||||
pnp_write_config(lpc_dev, LPCIF_SIRQ(UART_IRQ), LDN_UART);
|
||||
|
||||
// Enable and configure UART LDN
|
||||
pnp_set_logical_device(uart_dev);
|
||||
pnp_set_enable(uart_dev, 1);
|
||||
pnp_write_config(uart_dev, UART_CONFIG_SELECT, 0);
|
||||
|
||||
// Exit PNP conf state
|
||||
outb(MEC_CFG_EXIT_KEY, EC_CFG_PORT);
|
||||
|
||||
// Supply debug unlock key
|
||||
debug_write_key(DEBUG_RW_KEY_IDX, CONFIG_MEC1653_DEBUG_UNLOCK_KEY);
|
||||
|
||||
// Use debug writes to set UART_TX and UART_RX GPIOs
|
||||
debug_write_dword(MEC1653_CFG_REG + MEC1653_CFG_TX_GPIO_OFFSET, 0x1000);
|
||||
debug_write_dword(MEC1653_CFG_REG + MEC1653_CFG_RX_GPIO_OFFSET, 0x1000);
|
||||
}
|
||||
35
src/ec/lenovo/mec1653/uart.h
Normal file
35
src/ec/lenovo/mec1653/uart.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef EC_LENOVO_MEC1653_UART_H
|
||||
#define EC_LENOVO_MEC1653_UART_H
|
||||
|
||||
#define MEC_CFG_ENTRY_KEY 0x55
|
||||
#define MEC_CFG_EXIT_KEY 0xaa
|
||||
|
||||
#define UART_PORT 0x3f8
|
||||
#define UART_IRQ 4
|
||||
|
||||
#define MEC1653_CFG_REG 0xf0c400
|
||||
#define MEC1653_CFG_TX_GPIO_OFFSET 0x110
|
||||
#define MEC1653_CFG_RX_GPIO_OFFSET 0x114
|
||||
|
||||
// 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
|
||||
|
||||
// LPC I/F registers
|
||||
#define LPCIF_SIRQ(i) (0x40 + (i))
|
||||
|
||||
#define LPCIF_BAR_UART 0x80
|
||||
|
||||
// UART registers
|
||||
#define UART_ACTIVATE 0x30
|
||||
#define UART_CONFIG_SELECT 0xf0
|
||||
|
||||
void mec1653_configure_uart(void);
|
||||
|
||||
#endif /* EC_LENOVO_MEC1653_UART_H */
|
||||
Loading…
Add table
Add a link
Reference in a new issue