summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--chipdrivers.h6
-rw-r--r--edi.c497
-rw-r--r--edi.h30
-rw-r--r--ene.h51
-rw-r--r--flash.h3
-rw-r--r--flashchips.c24
7 files changed, 611 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index 33702478f..e26b41e21 100644
--- a/Makefile
+++ b/Makefile
@@ -518,7 +518,7 @@ endif
CHIP_OBJS = jedec.o stm50.o w39.o w29ee011.o \
sst28sf040.o 82802ab.o \
- sst49lfxxxc.o sst_fwhub.o flashchips.o spi.o spi25.o spi25_statusreg.o \
+ sst49lfxxxc.o sst_fwhub.o edi.o flashchips.o spi.o spi25.o spi25_statusreg.o \
opaque.o sfdp.o en29lv640b.o at45db.o
###############################################################################
diff --git a/chipdrivers.h b/chipdrivers.h
index 8405220d7..af59c30f7 100644
--- a/chipdrivers.h
+++ b/chipdrivers.h
@@ -199,4 +199,10 @@ int erase_sector_stm50(struct flashctx *flash, unsigned int block, unsigned int
int probe_en29lv640b(struct flashctx *flash);
int write_en29lv640b(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+/* edi.c */
+int edi_chip_block_erase(struct flashctx *flash, unsigned int page, unsigned int size);
+int edi_chip_write(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+int edi_chip_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
+int edi_probe_kb9012(struct flashctx *flash);
+
#endif /* !__CHIPDRIVERS_H__ */
diff --git a/edi.c b/edi.c
new file mode 100644
index 000000000..0ca170428
--- /dev/null
+++ b/edi.c
@@ -0,0 +1,497 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2015 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <string.h>
+#include "flash.h"
+#include "ene.h"
+#include "edi.h"
+
+static unsigned int edi_read_buffer_length = EDI_READ_BUFFER_LENGTH_DEFAULT;
+
+static const struct ene_chip ene_kb9012 = {
+ .hwversion = ENE_KB9012_HWVERSION,
+ .ediid = ENE_KB9012_EDIID,
+};
+
+static void edi_write_cmd(unsigned char *cmd, unsigned short address, unsigned char data)
+{
+ cmd[0] = EDI_WRITE; /* EDI write command. */
+ cmd[1] = 0x00; /* Address is only 2 bytes. */
+ cmd[2] = (address >> 8) & 0xff; /* Address higher byte. */
+ cmd[3] = (address >> 0) & 0xff; /* Address lower byte. */
+ cmd[4] = data; /* Write data. */
+}
+
+static void edi_read_cmd(unsigned char *cmd, unsigned short address)
+{
+ cmd[0] = EDI_READ; /* EDI read command. */
+ cmd[1] = 0x00; /* Address is only 2 bytes. */
+ cmd[2] = (address >> 8) & 0xff; /* Address higher byte. */
+ cmd[3] = (address >> 0) & 0xff; /* Address lower byte. */
+}
+
+static int edi_write(struct flashctx *flash, unsigned short address, unsigned char data)
+{
+ unsigned char cmd[5];
+ int rc;
+
+ edi_write_cmd(cmd, address, data);
+
+ rc = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL);
+ if (rc)
+ return -1;
+
+ return 0;
+}
+
+static int edi_read_byte(struct flashctx *flash, unsigned short address, unsigned char *data)
+{
+ unsigned char cmd[4];
+ unsigned char buffer[edi_read_buffer_length];
+ unsigned int index;
+ unsigned int i;
+ int rc;
+
+ edi_read_cmd(cmd, address);
+
+ rc = spi_send_command(flash, sizeof(cmd), sizeof(buffer), cmd, buffer);
+ if (rc)
+ return -1;
+
+ index = 0;
+
+ for (i = 0; i < sizeof(buffer); i++) {
+ index = i;
+
+ if (buffer[i] == EDI_NOT_READY)
+ continue;
+
+ if (buffer[i] == EDI_READY) {
+ if (i == (sizeof(buffer) - 1)) {
+ /*
+ * Buffer size was too small for receiving the value.
+ * This is as good as getting only EDI_NOT_READY.
+ */
+
+ buffer[i] = EDI_NOT_READY;
+ break;
+ }
+
+ *data = buffer[i + 1];
+ return 0;
+ }
+ }
+
+ if (buffer[index] == EDI_NOT_READY)
+ return -EDI_NOT_READY;
+
+ return -1;
+}
+
+static int edi_read(struct flashctx *flash, unsigned short address, unsigned char *data)
+{
+ int rc;
+
+ do {
+ rc = edi_read_byte(flash, address, data);
+ if (rc == -EDI_NOT_READY) {
+ /*
+ * Buffer size is increased, one step at a time,
+ * to hold more data if we only catch EDI_NOT_READY.
+ * Once CS is deasserted, no more data will be sent by the EC,
+ * so we cannot keep reading afterwards and have to start a new
+ * transaction with a longer buffer, to be safe.
+ */
+
+ if (edi_read_buffer_length < EDI_READ_BUFFER_LENGTH_MAX) {
+ msg_pwarn("%s: Retrying read with greater buffer length!\n", __func__);
+ edi_read_buffer_length++;
+ } else {
+ msg_perr("%s: Maximum buffer length reached and data still not ready!\n", __func__);
+ return -1;
+ }
+ } else if (rc < 0) {
+ return -1;
+ }
+ } while (rc == -EDI_NOT_READY);
+
+ return 0;
+}
+
+static int edi_disable(struct flashctx *flash)
+{
+ unsigned char cmd = EDI_DISABLE;
+ int rc;
+
+ rc = spi_send_command(flash, sizeof(cmd), 0, &cmd, NULL);
+ if (rc)
+ return -1;
+
+ return 0;
+}
+
+static int edi_chip_probe(struct flashctx *flash, const struct ene_chip *chip)
+{
+ unsigned char hwversion;
+ unsigned char ediid;
+ int rc;
+
+ rc = edi_read(flash, ENE_EC_HWVERSION, &hwversion);
+ if (rc < 0)
+ return 0;
+
+ rc = edi_read(flash, ENE_EC_EDIID, &ediid);
+ if (rc < 0)
+ return 0;
+
+ if (chip->hwversion == hwversion && chip->ediid == ediid)
+ return 1;
+
+ return 0;
+}
+
+static int edi_spi_enable(struct flashctx *flash)
+{
+ unsigned char buffer;
+ int rc;
+
+ rc = edi_read(flash, ENE_XBI_EFCFG, &buffer);
+ if (rc < 0)
+ return -1;
+
+ buffer |= ENE_XBI_EFCFG_CMD_WE;
+
+ rc = edi_write(flash, ENE_XBI_EFCFG, buffer);
+ if (rc < 0)
+ return -1;
+
+ return 0;
+}
+
+static int edi_spi_disable(struct flashctx *flash)
+{
+ unsigned char buffer;
+ int rc;
+
+ rc = edi_read(flash, ENE_XBI_EFCFG, &buffer);
+ if (rc < 0)
+ return -1;
+
+ buffer &= ~ENE_XBI_EFCFG_CMD_WE;
+
+ rc = edi_write(flash, ENE_XBI_EFCFG, buffer);
+ if (rc < 0)
+ return -1;
+
+ return 0;
+}
+
+static int edi_spi_busy(struct flashctx *flash)
+{
+ unsigned char buffer;
+ int rc;
+
+ rc = edi_read(flash, ENE_XBI_EFCFG, &buffer);
+ if (rc < 0)
+ return -1;
+
+ return !!(buffer & ENE_XBI_EFCFG_BUSY);
+}
+
+static int edi_spi_address(struct flashctx *flash, unsigned int start, unsigned int address)
+{
+ int rc;
+
+ if ((address == start) || (((address - 1) & 0xff) != (address & 0xff))) {
+ rc = edi_write(flash, ENE_XBI_EFA0, ((address & 0xff) >> 0));
+ if (rc < 0)
+ return -1;
+ }
+
+ if ((address == start) || (((address - 1) & 0xff00) != (address & 0xff00))) {
+ rc = edi_write(flash, ENE_XBI_EFA1, ((address & 0xff00) >> 8));
+ if (rc < 0)
+ return -1;
+ }
+
+ if ((address == start) || (((address - 1) & 0xff0000) != (address & 0xff0000))) {
+ rc = edi_write(flash, ENE_XBI_EFA2, ((address & 0xff0000) >> 16));
+ if (rc < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int edi_8051_reset(struct flashctx *flash)
+{
+ unsigned char buffer;
+ int rc;
+
+ rc = edi_read(flash, ENE_EC_PXCFG, &buffer);
+ if (rc < 0)
+ return -1;
+
+ buffer |= ENE_EC_PXCFG_8051_RESET;
+
+ rc = edi_write(flash, ENE_EC_PXCFG, buffer);
+ if (rc < 0)
+ return -1;
+
+ return 0;
+}
+
+static int edi_8051_execute(struct flashctx *flash)
+{
+ unsigned char buffer;
+ int rc;
+
+ rc = edi_read(flash, ENE_EC_PXCFG, &buffer);
+ if (rc < 0)
+ return -1;
+
+ buffer &= ~ENE_EC_PXCFG_8051_RESET;
+
+ rc = edi_write(flash, ENE_EC_PXCFG, buffer);
+ if (rc < 0)
+ return -1;
+
+ return 0;
+}
+
+int edi_chip_block_erase(struct flashctx *flash, unsigned int page, unsigned int size)
+{
+ unsigned int timeout = 64;
+ int rc;
+
+ if (size != flash->chip->page_size) {
+ msg_perr("%s: Block erase size is not page size!\n", __func__);
+ return -1;
+ }
+
+ rc = edi_spi_enable(flash);
+ if (rc < 0) {
+ msg_perr("%s: Unable to enable SPI!\n", __func__);
+ return -1;
+ }
+
+ rc = edi_spi_address(flash, page, page);
+ if (rc < 0)
+ return -1;
+
+ rc = edi_write(flash, ENE_XBI_EFCMD, ENE_XBI_EFCMD_ERASE);
+ if (rc < 0)
+ return -1;
+
+ while (edi_spi_busy(flash) == 1 && timeout) {
+ programmer_delay(10);
+ timeout--;
+ }
+
+ if (!timeout) {
+ msg_perr("%s: Timed out waiting for SPI not busy!\n", __func__);
+ return -1;
+ }
+
+ rc = edi_spi_disable(flash);
+ if (rc < 0) {
+ msg_perr("%s: Unable to disable SPI!\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int edi_chip_write(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
+{
+ unsigned int address = start;
+ unsigned int pages;
+ unsigned int timeout;
+ unsigned int i, j;
+ int rc;
+
+ if ((start % flash->chip->page_size) != 0) {
+ msg_perr("%s: Start address is not page-aligned!\n", __func__);
+ return -1;
+ }
+
+ if ((len % flash->chip->page_size) != 0) {
+ msg_perr("%s: Length is not page-aligned!\n", __func__);
+ return -1;
+ }
+
+ pages = len / flash->chip->page_size;
+
+ rc = edi_spi_enable(flash);
+ if (rc < 0) {
+ msg_perr("%s: Unable to enable SPI!\n", __func__);
+ return -1;
+ }
+
+ for (i = 0; i < pages; i++) {
+ timeout = 64;
+
+ /* Clear page buffer. */
+ rc = edi_write(flash, ENE_XBI_EFCMD, ENE_XBI_EFCMD_HVPL_CLEAR);
+ if (rc < 0)
+ return -1;
+
+ for (j = 0; j < flash->chip->page_size; j++) {
+ rc = edi_spi_address(flash, start, address);
+ if (rc < 0)
+ return -1;
+
+ rc = edi_write(flash, ENE_XBI_EFDAT, *buf);
+ if (rc < 0)
+ return -1;
+
+ rc = edi_write(flash, ENE_XBI_EFCMD, ENE_XBI_EFCMD_HVPL_LATCH);
+ if (rc < 0)
+ return -1;
+
+ buf++;
+ address++;
+ }
+
+ /* Program page buffer to flash. */
+ rc = edi_write(flash, ENE_XBI_EFCMD, ENE_XBI_EFCMD_PROGRAM);
+ if (rc < 0)
+ return -1;
+
+ while (edi_spi_busy(flash) == 1 && timeout) {
+ programmer_delay(10);
+ timeout--;
+ }
+
+ if (!timeout) {
+ msg_perr("%s: Timed out waiting for SPI not busy!\n", __func__);
+ return -1;
+ }
+ }
+
+ rc = edi_spi_disable(flash);
+ if (rc < 0) {
+ msg_perr("%s: Unable to disable SPI!\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int edi_chip_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len)
+{
+ unsigned int address = start;
+ unsigned int i;
+ unsigned int timeout;
+ int rc;
+
+ rc = edi_spi_enable(flash);
+ if (rc < 0) {
+ msg_perr("%s: Unable to enable SPI!\n", __func__);
+ return -1;
+ }
+
+ /*
+ * EDI brings such a drastic overhead that there is about no need to
+ * have any delay in between calls. The EDI protocol will handle wait
+ * I/O times on its own anyway.
+ */
+
+ for (i = 0; i < len; i++) {
+ timeout = 64;
+
+ rc = edi_spi_address(flash, start, address);
+ if (rc < 0)
+ return -1;
+
+ rc = edi_write(flash, ENE_XBI_EFCMD, ENE_XBI_EFCMD_READ);
+ if (rc < 0)
+ return -1;
+
+ do {
+ rc = edi_read(flash, ENE_XBI_EFDAT, buf);
+ if (rc == 0)
+ break;
+
+ /* Just in case. */
+ while (edi_spi_busy(flash) == 1 && timeout) {
+ programmer_delay(10);
+ timeout--;
+ }
+
+ if (!timeout) {
+ msg_perr("%s: Timed out waiting for SPI not busy!\n", __func__);
+ return -1;
+ }
+ } while (1);
+
+ buf++;
+ address++;
+ }
+
+ rc = edi_spi_disable(flash);
+ if (rc < 0) {
+ msg_perr("%s: Unable to disable SPI!\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int edi_shutdown(void *data)
+{
+ struct flashctx *flash;
+ int rc;
+
+ if (data == NULL)
+ return -1;
+
+ flash = (struct flashctx *)data;
+
+ rc = edi_8051_execute(flash);
+ if (rc < 0) {
+ msg_perr("%s: Unable to execute 8051!\n", __func__);
+ return -1;
+ }
+
+ rc = edi_disable(flash);
+ if (rc < 0) {
+ msg_perr("%s: Unable to disable EDI!\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int edi_probe_kb9012(struct flashctx *flash)
+{
+ int probe;
+ int rc;
+
+ probe = edi_chip_probe(flash, &ene_kb9012);
+ if (!probe)
+ return 0;
+
+ rc = edi_8051_reset(flash);
+ if (rc < 0) {
+ msg_perr("%s: Unable to reset 8051!\n", __func__);
+ return 0;
+ }
+
+ register_shutdown(edi_shutdown, (void *)flash);
+
+ return 1;
+}
diff --git a/edi.h b/edi.h
new file mode 100644
index 000000000..542bf26ec
--- /dev/null
+++ b/edi.h
@@ -0,0 +1,30 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2015 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __EDI_H__
+#define __EDI_H__ 1
+
+#define EDI_READ 0x30
+#define EDI_WRITE 0x40
+#define EDI_DISABLE 0xf3
+
+#define EDI_NOT_READY 0x5f
+#define EDI_READY 0x50
+
+#define EDI_READ_BUFFER_LENGTH_DEFAULT 3
+#define EDI_READ_BUFFER_LENGTH_MAX 32
+
+#endif
diff --git a/ene.h b/ene.h
new file mode 100644
index 000000000..e03e49b2e
--- /dev/null
+++ b/ene.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2015 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ENE_H__
+#define __ENE_H__ 1
+
+#define ENE_XBI_EFA0 0xfea8
+#define ENE_XBI_EFA1 0xfea9
+#define ENE_XBI_EFA2 0xfeaa
+#define ENE_XBI_EFDAT 0xfeab
+#define ENE_XBI_EFCMD 0xfeac
+#define ENE_XBI_EFCFG 0xfead
+
+#define ENE_XBI_EFCFG_CMD_WE (1 << 3)
+#define ENE_XBI_EFCFG_BUSY (1 << 1)
+
+#define ENE_XBI_EFCMD_HVPL_LATCH 0x02
+#define ENE_XBI_EFCMD_READ 0x03
+#define ENE_XBI_EFCMD_ERASE 0x20
+#define ENE_XBI_EFCMD_PROGRAM 0x70
+#define ENE_XBI_EFCMD_HVPL_CLEAR 0x80
+
+#define ENE_EC_PXCFG 0xff14
+
+#define ENE_EC_PXCFG_8051_RESET 0x01
+
+#define ENE_EC_HWVERSION 0xff00
+#define ENE_EC_EDIID 0xff24
+
+#define ENE_KB9012_HWVERSION 0xc3
+#define ENE_KB9012_EDIID 0x04
+
+struct ene_chip {
+ unsigned char hwversion;
+ unsigned char ediid;
+};
+
+#endif
diff --git a/flash.h b/flash.h
index f2c6cbef0..7bf3cc714 100644
--- a/flash.h
+++ b/flash.h
@@ -199,6 +199,7 @@ struct flashchip {
/* SPI25 is very common. Keep it at zero so we don't have
to specify it for each and every chip in the database.*/
SPI25 = 0,
+ SPI_EDI = 1,
} spi_cmd_set;
int (*probe) (struct flashctx *flash);
@@ -266,7 +267,7 @@ struct flashrom_flashctx {
/* Timing used in probe routines. ZERO is -2 to differentiate between an unset
* field and zero delay.
- *
+ *
* SPI devices will always have zero delay and ignore this field.
*/
#define TIMING_FIXME -1
diff --git a/flashchips.c b/flashchips.c
index e389af667..54c494390 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -3282,6 +3282,30 @@ const struct flashchip flashchips[] = {
},
{
+ .vendor = "ENE",
+ .name = "KB9012 (EDI)",
+ .bustype = BUS_SPI,
+ .spi_cmd_set = SPI_EDI,
+ .total_size = 128,
+ .page_size = 128,
+ .feature_bits = FEATURE_ERASED_ZERO,
+ .tested = TEST_OK_PREW,
+ .probe = edi_probe_kb9012,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {128, 1024} },
+ .block_erase = edi_chip_block_erase,
+ },
+ },
+ .gran = write_gran_128bytes,
+ .write = edi_chip_write,
+ .read = edi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
.vendor = "ESMT",
.name = "F49B002UA",
.bustype = BUS_PARALLEL,