Edward O'Callaghan submitted this change.

View Change

Approvals: build bot (Jenkins): Verified Angel Pons: Looks good to me, approved Edward O'Callaghan: Looks good to me, approved
Add ENE LPC programmer

Initial support of ENE LPC interface keyboard controller.

BUG=b:156140422
BRANCH=none

Signed-off-by: Victor Ding <victording@google.com>
Change-Id: I970afd8c1bd92c159c60e09f22e2f18c0433729d
Reviewed-on: https://review.coreboot.org/c/flashrom/+/44580
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Reviewed-by: Edward O'Callaghan <quasisec@chromium.org>
---
M Makefile
A ene_lpc.c
M flashrom.c
M meson.build
M meson_options.txt
M programmer.h
6 files changed, 645 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile
index 803529f..85b4850 100644
--- a/Makefile
+++ b/Makefile
@@ -170,6 +170,11 @@
else
override CONFIG_DEVELOPERBOX_SPI = no
endif
+ifeq ($(CONFIG_ENE_LPC), yes)
+UNSUPPORTED_FEATURES += CONFIG_ENE_LPC=yes
+else
+override CONFIG_ENE_LPC = no
+endif
ifeq ($(CONFIG_FT2232_SPI), yes)
UNSUPPORTED_FEATURES += CONFIG_FT2232_SPI=yes
else
@@ -271,6 +276,11 @@
else
override CONFIG_ATAPROMISE = no
endif
+ifeq ($(CONFIG_ENE_LPC), yes)
+UNSUPPORTED_FEATURES += CONFIG_ENE_LPC=yes
+else
+override CONFIG_ENE_LPC = no
+endif
ifeq ($(CONFIG_IT8212), yes)
UNSUPPORTED_FEATURES += CONFIG_IT8212=yes
else
@@ -381,6 +391,11 @@
else
override CONFIG_DEVELOPERBOX_SPI = no
endif
+ifeq ($(CONFIG_ENE_LPC), yes)
+UNSUPPORTED_FEATURES += CONFIG_ENE_LPC=yes
+else
+override CONFIG_ENE_LPC = no
+endif
ifeq ($(CONFIG_FT2232_SPI), yes)
UNSUPPORTED_FEATURES += CONFIG_FT2232_SPI=yes
else
@@ -671,6 +686,9 @@
# Promise ATA controller support.
CONFIG_ATAPROMISE ?= no

+# ENE LPC interface keyboard controller
+CONFIG_ENE_LPC ?= yes
+
# Always enable FT2232 SPI dongles for now.
CONFIG_FT2232_SPI ?= yes

@@ -855,6 +873,11 @@
NEED_LIBPCI += CONFIG_INTERNAL
endif

+ifeq ($(CONFIG_ENE_LPC), yes)
+FEATURE_CFLAGS += -D'CONFIG_ENE_LPC=1'
+PROGRAMMER_OBJS += ene_lpc.o
+endif
+
ifeq ($(CONFIG_SERPROG), yes)
FEATURE_CFLAGS += -D'CONFIG_SERPROG=1'
PROGRAMMER_OBJS += serprog.o
diff --git a/ene_lpc.c b/ene_lpc.c
new file mode 100644
index 0000000..56d6580
--- /dev/null
+++ b/ene_lpc.c
@@ -0,0 +1,596 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2012-2020, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include "chipdrivers.h"
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+#include "spi.h"
+
+/* MCU registers */
+#define REG_EC_HWVER 0xff00
+#define REG_EC_FWVER 0xff01
+#define REG_EC_EDIID 0xff24
+#define REG_8051_CTRL 0xff14
+#define REG_EC_EXTCMD 0xff10
+
+#define CPU_RESET 1
+
+/* MCU SPI peripheral registers */
+#define REG_SPI_DATA 0xfeab
+#define REG_SPI_COMMAND 0xfeac
+#define REG_SPI_CONFIG 0xfead
+
+#define CFG_CSn_FORCE_LOW (1 << 4)
+#define CFG_COMMAND_WRITE_ENABLE (1 << 3)
+#define CFG_STATUS (1 << 1)
+#define CFG_ENABLE_BUSY_STATUS_CHECK (1 << 0)
+
+/* Timeout */
+#define EC_COMMAND_TIMEOUT 4
+#define EC_RESTART_TIMEOUT 10
+#define ENE_SPI_DELAY_CYCLE 4
+#define EC_PAUSE_TIMEOUT 12
+#define EC_RESET_TRIES 3
+
+#define ENE_KB94X_PAUSE_WAKEUP_PORT 0x64
+
+#define MASK_INPUT_BUFFER_FULL 2
+#define MASK_OUTPUT_BUFFER_FULL 1
+
+const int port_ene_bank = 1;
+const int port_ene_offset = 2;
+const int port_ene_data = 3;
+
+/* Supported ENE ECs, ENE_LAST should always be LAST member */
+enum ene_chip_id {
+ ENE_KB932 = 0,
+ ENE_KB94X,
+ ENE_LAST
+};
+
+/* EC state */
+enum ene_ec_state {
+ EC_STATE_NORMAL,
+ EC_STATE_IDLE,
+ EC_STATE_RESET,
+ EC_STATE_UNKNOWN
+};
+
+/* chip-specific parameters */
+typedef struct {
+ enum ene_chip_id chip_id;
+ uint8_t hwver;
+ uint8_t ediid;
+ uint32_t port_bios;
+ uint32_t port_ec_command;
+ uint32_t port_ec_data;
+ uint8_t ec_reset_cmd;
+ uint8_t ec_reset_data;
+ uint8_t ec_restart_cmd;
+ uint8_t ec_restart_data;
+ uint8_t ec_pause_cmd;
+ uint8_t ec_pause_data;
+ uint16_t ec_status_buf;
+ uint8_t ec_is_stopping;
+ uint8_t ec_is_running;
+ uint8_t ec_is_pausing;
+ uint32_t port_io_base;
+} ene_chip_t;
+
+typedef struct
+{
+ /* pointer to table entry of identified chip */
+ ene_chip_t *chip;
+ /* current ec state */
+ enum ene_ec_state ec_state;
+ struct timeval pause_begin;
+} ene_lpc_data_t;
+
+/* table of supported chips + parameters */
+static ene_chip_t ene_chips[] = {
+ {
+ ENE_KB932, /* chip_id */
+ 0xa2, 0x02, /* hwver + ediid */
+ 0x66, /* port_bios */
+ 0x6c, 0x68, /* port_ec_{command,data} */
+ 0x59, 0xf2, /* ec_reset_{cmd,data} */
+ 0x59, 0xf9, /* ec_restart_{cmd,data} */
+ 0x59, 0xf1, /* ec_pause_{cmd,data} */
+ 0xf554, /* ec_status_buf */
+ 0xa5, 0x00, /* ec_is_{stopping,running} masks */
+ 0x33, /* ec_is_pausing mask */
+ 0xfd60 /* port_io_base */
+ },
+ {
+ ENE_KB94X, /* chip_id */
+ 0xa3, 0x05, /* hwver + ediid */
+ 0x66, /* port_bios */
+ 0x66, 0x68, /* port_ec_{command,data} */
+ 0x7d, 0x10, /* ec_reset_{cmd,data} */
+ 0x7f, 0x10, /* ec_restart_{cmd,data} */
+ 0x7e, 0x10, /* ec_pause_{cmd,data} */
+ 0xf710, /* ec_status_buf */
+ 0x02, 0x00, /* ec_is_{stopping,running} masks */
+ 0x01, /* ec_is_pausing mask */
+ 0x0380 /* port_io_base */
+ }
+};
+
+static void ec_command(const ene_chip_t *chip, uint8_t cmd, uint8_t data)
+{
+ struct timeval begin, now;
+
+ /* Spin wait for EC input buffer empty */
+ gettimeofday(&begin, NULL);
+ while (INB(chip->port_ec_command) & MASK_INPUT_BUFFER_FULL) {
+ gettimeofday(&now, NULL);
+ if ((now.tv_sec - begin.tv_sec) >= EC_COMMAND_TIMEOUT) {
+ msg_pdbg("%s: buf not empty\n", __func__);
+ return;
+ }
+ }
+
+ /* Write command */
+ OUTB(cmd, chip->port_ec_command);
+
+ if (chip->chip_id == ENE_KB932) {
+ /* Spin wait for EC input buffer empty */
+ gettimeofday(&begin, NULL);
+ while (INB(chip->port_ec_command) &
+ MASK_INPUT_BUFFER_FULL) {
+ gettimeofday(&now, NULL);
+ if ((now.tv_sec - begin.tv_sec) >=
+ EC_COMMAND_TIMEOUT) {
+ msg_pdbg("%s: buf not empty\n", __func__);
+ return;
+ }
+ }
+ /* Write data */
+ OUTB(data, chip->port_ec_data);
+ }
+}
+
+static uint8_t ene_read(const ene_chip_t *chip, uint16_t addr)
+{
+ uint8_t bank;
+ uint8_t offset;
+ uint8_t data;
+ uint32_t port_io_base;
+
+ bank = addr >> 8;
+ offset = addr & 0xff;
+ port_io_base = chip->port_io_base;
+
+ OUTB(bank, port_io_base + port_ene_bank);
+ OUTB(offset, port_io_base + port_ene_offset);
+ data = INB(port_io_base + port_ene_data);
+
+ return data;
+}
+
+static void ene_write(const ene_chip_t *chip, uint16_t addr, uint8_t data)
+{
+ uint8_t bank;
+ uint8_t offset;
+ uint32_t port_io_base;
+
+ bank = addr >> 8;
+ offset = addr & 0xff;
+ port_io_base = chip->port_io_base;
+
+ OUTB(bank, port_io_base + port_ene_bank);
+ OUTB(offset, port_io_base + port_ene_offset);
+
+ OUTB(data, port_io_base + port_ene_data);
+}
+
+/**
+ * wait_cycles, wait for n LPC bus clock cycles
+ *
+ * @param n: number of LPC cycles to wait
+ * @return void
+ */
+static void wait_cycles(const ene_chip_t *chip,int n)
+{
+ while (n--)
+ INB(chip->port_io_base + port_ene_bank);
+}
+
+static int is_spicmd_write(uint8_t cmd)
+{
+ switch (cmd) {
+ case JEDEC_WREN:
+ /* Chip Write Enable */
+ case JEDEC_EWSR:
+ /* Write Status Enable */
+ case JEDEC_CE_60:
+ /* Chip Erase 0x60 */
+ case JEDEC_CE_C7:
+ /* Chip Erase 0xc7 */
+ case JEDEC_BE_52:
+ /* Block Erase 0x52 */
+ case JEDEC_BE_D8:
+ /* Block Erase 0xd8 */
+ case JEDEC_BE_D7:
+ /* Block Erase 0xd7 */
+ case JEDEC_SE:
+ /* Sector Erase */
+ case JEDEC_BYTE_PROGRAM:
+ /* Write memory byte */
+ case JEDEC_AAI_WORD_PROGRAM:
+ /* Write AAI word */
+ return 1;
+ }
+ return 0;
+}
+
+static void ene_spi_start(const ene_chip_t *chip)
+{
+ int cfg;
+
+ cfg = ene_read(chip, REG_SPI_CONFIG);
+ cfg |= CFG_CSn_FORCE_LOW;
+ cfg |= CFG_COMMAND_WRITE_ENABLE;
+ ene_write(chip, REG_SPI_CONFIG, cfg);
+
+ wait_cycles(chip, ENE_SPI_DELAY_CYCLE);
+}
+
+static void ene_spi_end(const ene_chip_t *chip)
+{
+ int cfg;
+
+ cfg = ene_read(chip, REG_SPI_CONFIG);
+ cfg &= ~CFG_CSn_FORCE_LOW;
+ cfg |= CFG_COMMAND_WRITE_ENABLE;
+ ene_write(chip, REG_SPI_CONFIG, cfg);
+
+ wait_cycles(chip, ENE_SPI_DELAY_CYCLE);
+}
+
+static int ene_spi_wait(const ene_chip_t *chip)
+{
+ struct timeval begin, now;
+
+ gettimeofday(&begin, NULL);
+ while(ene_read(chip, REG_SPI_CONFIG) & CFG_STATUS) {
+ gettimeofday(&now, NULL);
+ if ((now.tv_sec - begin.tv_sec) >= EC_COMMAND_TIMEOUT) {
+ msg_pdbg("%s: spi busy\n", __func__);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int ene_pause_ec(ene_lpc_data_t *ctx_data)
+{
+ struct timeval begin, now;
+ const ene_chip_t *chip = ctx_data->chip;
+
+ if (!chip->ec_pause_cmd)
+ return -1;
+
+ /* EC prepare pause */
+ ec_command(chip, chip->ec_pause_cmd, chip->ec_pause_data);
+
+ gettimeofday(&begin, NULL);
+ /* Spin wait for EC ready */
+ while (ene_read(chip, chip->ec_status_buf) !=
+ chip->ec_is_pausing) {
+ gettimeofday(&now, NULL);
+ if ((now.tv_sec - begin.tv_sec) >=
+ EC_COMMAND_TIMEOUT) {
+ msg_pdbg("%s: unable to pause ec\n", __func__);
+ return -1;
+ }
+ }
+
+
+ gettimeofday(&ctx_data->pause_begin, NULL);
+ ctx_data->ec_state = EC_STATE_IDLE;
+ return 0;
+}
+
+static int ene_resume_ec(ene_lpc_data_t *ctx_data)
+{
+ struct timeval begin, now;
+ const ene_chip_t *chip = ctx_data->chip;
+
+ if (chip->chip_id == ENE_KB94X)
+ OUTB(0xff, ENE_KB94X_PAUSE_WAKEUP_PORT);
+ else
+ /* Trigger 8051 interrupt to resume */
+ ene_write(chip, REG_EC_EXTCMD, 0xff);
+
+ gettimeofday(&begin, NULL);
+ while (ene_read(chip, chip->ec_status_buf) !=
+ chip->ec_is_running) {
+ gettimeofday(&now, NULL);
+ if ((now.tv_sec - begin.tv_sec) >=
+ EC_COMMAND_TIMEOUT) {
+ msg_pdbg("%s: unable to resume ec\n", __func__);
+ return -1;
+ }
+ }
+
+ ctx_data->ec_state = EC_STATE_NORMAL;
+ return 0;
+}
+
+static int ene_pause_timeout_check(ene_lpc_data_t *ctx_data)
+{
+ struct timeval pause_now;
+ gettimeofday(&pause_now, NULL);
+ if ((pause_now.tv_sec - ctx_data->pause_begin.tv_sec) >=
+ EC_PAUSE_TIMEOUT) {
+ if(ene_resume_ec(ctx_data) == 0)
+ ene_pause_ec(ctx_data);
+
+ }
+ return 0;
+}
+
+static int ene_reset_ec(ene_lpc_data_t *ctx_data)
+{
+ uint8_t reg;
+ struct timeval begin, now;
+ const ene_chip_t *chip = ctx_data->chip;
+
+ gettimeofday(&begin, NULL);
+
+ /* EC prepare reset */
+ ec_command(chip, chip->ec_reset_cmd, chip->ec_reset_data);
+
+ /* Spin wait for EC ready */
+ while (ene_read(chip, chip->ec_status_buf) !=
+ chip->ec_is_stopping) {
+ gettimeofday(&now, NULL);
+ if ((now.tv_sec - begin.tv_sec) >=
+ EC_COMMAND_TIMEOUT) {
+ msg_pdbg("%s: unable to reset ec\n", __func__);
+ return -1;
+ }
+ }
+
+ /* Wait 1 second */
+ sleep(1);
+
+ /* Reset 8051 */
+ reg = ene_read(chip, REG_8051_CTRL);
+ reg |= CPU_RESET;
+ ene_write(chip, REG_8051_CTRL, reg);
+
+ ctx_data->ec_state = EC_STATE_RESET;
+ return 0;
+}
+
+static int ene_enter_flash_mode(ene_lpc_data_t *ctx_data)
+{
+ if (ene_pause_ec(ctx_data))
+ return ene_reset_ec(ctx_data);
+ return 0;
+}
+
+static int ene_spi_send_command(const struct flashctx *flash,
+ unsigned int writecnt,
+ unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ unsigned int i;
+ int tries = EC_RESET_TRIES;
+ ene_lpc_data_t *ctx_data = (ene_lpc_data_t *)flash->mst->spi.data;
+ const ene_chip_t *chip = ctx_data->chip;
+
+ if (ctx_data->ec_state == EC_STATE_IDLE && is_spicmd_write(writearr[0])) {
+ do {
+ /* Enter reset mode if we need to write/erase */
+ if (ene_resume_ec(ctx_data))
+ continue;
+
+ if (!ene_reset_ec(ctx_data))
+ break;
+ } while (--tries > 0);
+
+ if (!tries) {
+ msg_perr("%s: EC failed reset, skipping write\n", __func__);
+ ctx_data->ec_state = EC_STATE_IDLE;
+ return 1;
+ }
+ }
+ else if(chip->chip_id == ENE_KB94X && ctx_data->ec_state == EC_STATE_IDLE)
+ ene_pause_timeout_check(ctx_data);
+
+ ene_spi_start(chip);
+
+ for (i = 0; i < writecnt; i++) {
+ ene_write(chip, REG_SPI_COMMAND, writearr[i]);
+ if (ene_spi_wait(chip)) {
+ msg_pdbg("%s: write count %d\n", __func__, i);
+ return 1;
+ }
+ }
+
+ for (i = 0; i < readcnt; i++) {
+ /* Push data by clock the serial bus */
+ ene_write(chip, REG_SPI_COMMAND, 0);
+ if (ene_spi_wait(chip)) {
+ msg_pdbg("%s: read count %d\n", __func__, i);
+ return 1;
+ }
+ readarr[i] = ene_read(chip, REG_SPI_DATA);
+ if (ene_spi_wait(chip)) {
+ msg_pdbg("%s: read count %d\n", __func__, i);
+ return 1;
+ }
+ }
+
+ ene_spi_end(chip);
+ return 0;
+}
+
+static int ene_leave_flash_mode(void *data)
+{
+ ene_lpc_data_t *ctx_data = (ene_lpc_data_t *)data;
+ const ene_chip_t *chip = ctx_data->chip;
+ int rv = 0;
+ uint8_t reg;
+ struct timeval begin, now;
+
+ if (ctx_data->ec_state == EC_STATE_RESET) {
+ reg = ene_read(chip, REG_8051_CTRL);
+ reg &= ~CPU_RESET;
+ ene_write(chip, REG_8051_CTRL, reg);
+
+ gettimeofday(&begin, NULL);
+ /* EC restart */
+ while (ene_read(chip, chip->ec_status_buf) !=
+ chip->ec_is_running) {
+ gettimeofday(&now, NULL);
+ if ((now.tv_sec - begin.tv_sec) >=
+ EC_RESTART_TIMEOUT) {
+ msg_pdbg("%s: ec restart busy\n", __func__);
+ rv = 1;
+ goto exit;
+ }
+ }
+ msg_pdbg("%s: send ec restart\n", __func__);
+ ec_command(chip, chip->ec_restart_cmd,
+ chip->ec_restart_data);
+
+ ctx_data->ec_state = EC_STATE_NORMAL;
+ rv = 0;
+ goto exit;
+ }
+
+ rv = ene_resume_ec(ctx_data);
+
+exit:
+ /*
+ * Trigger ec interrupt after pause/reset by sending 0x80
+ * to bios command port.
+ */
+ OUTB(0x80, chip->port_bios);
+ free(data);
+ return rv;
+}
+
+static struct spi_master spi_master_ene = {
+ .max_data_read = 256,
+ .max_data_write = 256,
+ .command = ene_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+};
+
+int ene_lpc_init()
+{
+ uint8_t hwver, ediid, i;
+ int ret = 0;
+ char *p = NULL;
+ ene_lpc_data_t *ctx_data = NULL;
+
+ msg_pdbg("%s\n", __func__);
+
+ ctx_data = calloc(1, sizeof(ene_lpc_data_t));
+ if (!ctx_data) {
+ msg_perr("Unable to allocate space for extra context data.\n");
+ return 1;
+ }
+ ctx_data->ec_state = EC_STATE_NORMAL;
+
+ p = extract_programmer_param("type");
+ if (p && strcmp(p, "ec")) {
+ msg_pdbg("ene_lpc only supports \"ec\" type devices\n");
+ ret = 1;
+ goto ene_probe_spi_flash_exit;
+ }
+
+ for (i = 0; i < ENE_LAST; ++i) {
+ ctx_data->chip = &ene_chips[i];
+
+ hwver = ene_read(ctx_data->chip, REG_EC_HWVER);
+ ediid = ene_read(ctx_data->chip, REG_EC_EDIID);
+
+ if(hwver == ene_chips[i].hwver &&
+ ediid == ene_chips[i].ediid) {
+ break;
+ }
+ }
+
+ if (i == ENE_LAST) {
+ msg_pdbg("ENE EC not found (probe failed)\n");
+ ret = 1;
+ goto ene_probe_spi_flash_exit;
+ }
+
+ /* TODO: probe the EC stop protocol
+ *
+ * Compal - ec_command(0x41, 0xa1) returns 43 4f 4d 50 41 4c 9c
+ */
+
+
+ if (register_shutdown(ene_leave_flash_mode, ctx_data)) {
+ ret = 1;
+ goto ene_probe_spi_flash_exit;
+ }
+
+ ene_enter_flash_mode(ctx_data);
+
+ internal_buses_supported |= BUS_LPC;
+ spi_master_ene.data = ctx_data;
+ register_spi_master(&spi_master_ene);
+ msg_pdbg("%s: successfully initialized ene\n", __func__);
+
+ene_probe_spi_flash_exit:
+ free(p);
+ if (ret)
+ free(ctx_data);
+ return ret;
+}
+
+#endif /* __i386__ || __x86_64__ */
diff --git a/flashrom.c b/flashrom.c
index 4a30986..35ac6f5 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -279,6 +279,18 @@
},
#endif

+#if CONFIG_ENE_LPC == 1
+ {
+ .name = "ene_lpc",
+ .type = OTHER,
+ .devs.note = "ENE LPC interface keyboard controller\n",
+ .init = ene_lpc_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
#if CONFIG_RAYER_SPI == 1
{
.name = "rayer_spi",
diff --git a/meson.build b/meson.build
index 04176f3..180b5dd 100644
--- a/meson.build
+++ b/meson.build
@@ -40,6 +40,7 @@
config_digilent_spi = get_option('config_digilent_spi')
config_drkaiser = get_option('config_drkaiser')
config_dummy = get_option('config_dummy')
+config_ene_lpc = get_option('config_ene_lpc')
config_ft2232_spi = get_option('config_ft2232_spi')
config_gfxnvidia = get_option('config_gfxnvidia')
config_raiden = get_option('config_raiden')
@@ -202,6 +203,10 @@
cargs += '-DCONFIG_INTERNAL_DMI=1'
endif
endif
+if config_ene_lpc
+ srcs += 'ene_lpc.c'
+ cargs += '-DCONFIG_ENE_LPC=1'
+endif
if config_it8212
srcs += 'it8212.c'
cargs += '-DCONFIG_IT8212=1'
diff --git a/meson_options.txt b/meson_options.txt
index ac48e4e..ba5130b 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -11,6 +11,7 @@
option('config_digilent_spi', type : 'boolean', value : true, description : 'Digilent Development board JTAG')
option('config_drkaiser', type : 'boolean', value : true, description : 'Dr. Kaiser')
option('config_dummy', type : 'boolean', value : true, description : 'dummy tracing')
+option('config_ene_lpc', type : 'boolean', value : true, description : 'ENE LPC interface keyboard controller')
option('config_ft2232_spi', type : 'boolean', value : true, description : 'FT2232 SPI dongles')
option('config_gfxnvidia', type : 'boolean', value : true, description : 'NVIDIA graphics cards')
option('config_raiden', type : 'boolean', value : true, description : 'ChromiumOS Servo DUT debug board')
diff --git a/programmer.h b/programmer.h
index c5cab18..b3bc700 100644
--- a/programmer.h
+++ b/programmer.h
@@ -61,6 +61,9 @@
#if CONFIG_ATAPROMISE == 1
PROGRAMMER_ATAPROMISE,
#endif
+#if CONFIG_ENE_LPC == 1
+ PROGRAMMER_ENE_LPC,
+#endif
#if CONFIG_IT8212 == 1
PROGRAMMER_IT8212,
#endif
@@ -578,6 +581,11 @@
extern const struct dev_entry devs_digilent_spi[];
#endif

+/* ene_lpc.c */
+#if CONFIG_ENE_LPC == 1
+int ene_lpc_init(void);
+#endif
+
/* jlink_spi.c */
#if CONFIG_JLINK_SPI == 1
int jlink_spi_init(void);

To view, visit change 44580. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: I970afd8c1bd92c159c60e09f22e2f18c0433729d
Gerrit-Change-Number: 44580
Gerrit-PatchSet: 3
Gerrit-Owner: Victor Ding <victording@google.com>
Gerrit-Reviewer: Angel Pons <th3fanbus@gmail.com>
Gerrit-Reviewer: Edward O'Callaghan <quasisec@chromium.org>
Gerrit-Reviewer: Nico Huber <nico.h@gmx.de>
Gerrit-Reviewer: build bot (Jenkins) <no-reply@coreboot.org>
Gerrit-MessageType: merged