diff --git a/src/ec/lenovo/mec1653/Kconfig b/src/ec/lenovo/mec1653/Kconfig new file mode 100644 index 0000000000..463b00559a --- /dev/null +++ b/src/ec/lenovo/mec1653/Kconfig @@ -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 diff --git a/src/ec/lenovo/mec1653/Makefile.mk b/src/ec/lenovo/mec1653/Makefile.mk new file mode 100644 index 0000000000..2188069c7c --- /dev/null +++ b/src/ec/lenovo/mec1653/Makefile.mk @@ -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 diff --git a/src/ec/lenovo/mec1653/debug.c b/src/ec/lenovo/mec1653/debug.c new file mode 100644 index 0000000000..5f7000b320 --- /dev/null +++ b/src/ec/lenovo/mec1653/debug.c @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#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); +} diff --git a/src/ec/lenovo/mec1653/debug.h b/src/ec/lenovo/mec1653/debug.h new file mode 100644 index 0000000000..063b4b58e7 --- /dev/null +++ b/src/ec/lenovo/mec1653/debug.h @@ -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 */ diff --git a/src/ec/lenovo/mec1653/mec1653.c b/src/ec/lenovo/mec1653/mec1653.c new file mode 100644 index 0000000000..08682c0e38 --- /dev/null +++ b/src/ec/lenovo/mec1653/mec1653.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#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(); +} diff --git a/src/ec/lenovo/mec1653/mec1653.h b/src/ec/lenovo/mec1653/mec1653.h new file mode 100644 index 0000000000..c3d6074465 --- /dev/null +++ b/src/ec/lenovo/mec1653/mec1653.h @@ -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 */ diff --git a/src/ec/lenovo/mec1653/uart.c b/src/ec/lenovo/mec1653/uart.c new file mode 100644 index 0000000000..c6a6186406 --- /dev/null +++ b/src/ec/lenovo/mec1653/uart.c @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#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); +} diff --git a/src/ec/lenovo/mec1653/uart.h b/src/ec/lenovo/mec1653/uart.h new file mode 100644 index 0000000000..46fe4d10ff --- /dev/null +++ b/src/ec/lenovo/mec1653/uart.h @@ -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 */