heads/patches/flashrom-1.0/0100-enable-kgpe-d16.patch
Francis Lam bb0e13c24f
Add back flashrom support for KGPE-D16
Also fix up flashrom-x230.sh command only read bios area
2018-05-05 18:59:43 -07:00

2600 lines
84 KiB
Diff

diff --git ./Makefile ./Makefile
index 9cf52b2..3bc0d8c 100644
--- ./Makefile
+++ ./Makefile
@@ -214,6 +214,16 @@ UNSUPPORTED_FEATURES += CONFIG_GFXNVIDIA=yes
else
override CONFIG_GFXNVIDIA = no
endif
+ifeq ($(CONFIG_AST1100), yes)
+UNSUPPORTED_FEATURES += CONFIG_AST1100=yes
+else
+override CONFIG_AST1100 = no
+endif
+ifeq ($(CONFIG_AST2400), yes)
+UNSUPPORTED_FEATURES += CONFIG_AST2400=yes
+else
+override CONFIG_AST2400 = no
+endif
ifeq ($(CONFIG_SATASII), yes)
UNSUPPORTED_FEATURES += CONFIG_SATASII=yes
else
@@ -441,6 +451,16 @@ UNSUPPORTED_FEATURES += CONFIG_GFXNVIDIA=yes
else
override CONFIG_GFXNVIDIA = no
endif
+ifeq ($(CONFIG_AST1100), yes)
+UNSUPPORTED_FEATURES += CONFIG_AST1100=yes
+else
+override CONFIG_AST1100 = no
+endif
+ifeq ($(CONFIG_AST2400), yes)
+UNSUPPORTED_FEATURES += CONFIG_AST2400=yes
+else
+override CONFIG_AST2400 = no
+endif
ifeq ($(CONFIG_SATASII), yes)
UNSUPPORTED_FEATURES += CONFIG_SATASII=yes
else
@@ -514,7 +534,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 \
- opaque.o sfdp.o en29lv640b.o at45db.o
+ spi4ba.o opaque.o sfdp.o en29lv640b.o at45db.o
###############################################################################
# Library code.
@@ -565,6 +585,12 @@ CONFIG_NIC3COM ?= yes
# Enable NVIDIA graphics cards. Note: write and erase do not work properly.
CONFIG_GFXNVIDIA ?= yes
+# Enable AST1100 BMC SoCs.
+CONFIG_AST1100 ?= yes
+
+# Enable AST2400 BMC SoCs.
+CONFIG_AST2400 ?= yes
+
# Always enable SiI SATA controllers for now.
CONFIG_SATASII ?= yes
@@ -664,6 +690,8 @@ ifeq ($(CONFIG_ENABLE_LIBPCI_PROGRAMMERS), no)
override CONFIG_INTERNAL = no
override CONFIG_NIC3COM = no
override CONFIG_GFXNVIDIA = no
+override CONFIG_AST1100 = no
+override CONFIG_AST2400 = no
override CONFIG_SATASII = no
override CONFIG_ATAHPT = no
override CONFIG_ATAVIA = no
@@ -776,6 +804,18 @@ PROGRAMMER_OBJS += gfxnvidia.o
NEED_LIBPCI += CONFIG_GFXNVIDIA
endif
+ifeq ($(CONFIG_AST1100), yes)
+FEATURE_CFLAGS += -D'CONFIG_AST1100=1'
+PROGRAMMER_OBJS += ast1100.o
+NEED_LIBPCI += CONFIG_AST1100
+endif
+
+ifeq ($(CONFIG_AST2400), yes)
+FEATURE_CFLAGS += -D'CONFIG_AST2400=1'
+PROGRAMMER_OBJS += ast2400.o
+NEED_LIBPCI += CONFIG_AST2400
+endif
+
ifeq ($(CONFIG_SATASII), yes)
FEATURE_CFLAGS += -D'CONFIG_SATASII=1'
PROGRAMMER_OBJS += satasii.o
diff --git ./ast1100.c ./ast1100.c
new file mode 100644
index 0000000..cf28422
--- /dev/null
+++ ./ast1100.c
@@ -0,0 +1,421 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2017 Raptor Engineering, LLC
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define PCI_VENDOR_ID_ASPEED 0x1a03
+
+#define ASPEED_MEMMAP_SIZE (128 * 1024)
+#define ASPEED_P2A_OFFSET 0x10000
+
+#define AST1100_SCU_APB_ADDR 0x1e6e2000
+#define AST1100_SCU_APB_BRIDGE_OFFSET (AST1100_SCU_APB_ADDR & 0xffff)
+#define AST1100_SCU_PROT_KEY 0x00
+#define AST1100_SCU_HW_STRAP 0x70
+
+#define AST1100_SCU_PASSWORD 0x1688a8a8
+#define AST1100_SCU_BOOT_SRC_MASK 0x3
+#define AST1100_SCU_BOOT_SPI 0x2
+#define AST1100_SCU_BOOT_NONE 0x3
+
+#define AST1100_SMC_APB_ADDR 0x16000000
+#define AST1100_SMC_SMC00 0x00
+#define AST1100_SMC_CE_CTL(N) (0x4 + (N * 4))
+
+#define AST1100_SMC_SEGMENT_SIZE_MASK 0x3
+#define AST1100_SMC_SEGMENT_SIZE_32M 0x0
+#define AST1100_SMC_SEGMENT_SIZE_16M 0x1
+#define AST1100_SMC_SEGMENT_SIZE_8M 0x2
+#define AST1100_SMC_SEGMENT_SIZE_4M 0x3
+
+#define AST1100_SMC_FLASH_MMIO_ADDR 0x10000000
+
+#define AST1100_SPI_CMD_FAST_R_MODE 0x1
+#define AST1100_SPI_CMD_USER_MODE 0x3
+#define AST1100_SPI_CMD_MASK 0x3
+#define AST1100_SPI_STOP_CE_ACTIVE (0x1 << 2)
+#define AST1100_SPI_SPEED_SHIFT 8
+#define AST1100_SPI_SPEED_MASK (0x7 << AST1100_SPI_SPEED_SHIFT)
+
+#define AST1100_SPI_FLASH_MMIO_ADDR 0x30000000
+
+#define AST1100_WDT_APB_ADDR 0x1e785000
+#define AST1100_WDT_APB_BRIDGE_OFFSET (AST1100_WDT_APB_ADDR & 0xffff)
+
+#define AST1100_WDT1_CTR 0x00
+#define AST1100_WDT1_CTR_RELOAD 0x04
+#define AST1100_WDT1_CTR_RESTART 0x08
+#define AST1100_WDT1_CTL 0x0c
+
+#define AST1100_WDT_SET_CLOCK (0x1 << 4)
+#define AST1100_WDT_RESET_SYSTEM (0x1 << 1)
+#define AST1100_WDT_ENABLE (0x1 << 0)
+
+uint8_t *ast1100_device_bar = 0;
+uint8_t ast1100_device_spi_bus = 0;
+uint8_t ast1100_device_spi_speed = 0;
+uint8_t ast1100_device_halt_cpu = 0;
+uint8_t ast1100_device_reset_cpu = 0;
+uint8_t ast1100_device_resume_cpu = 0;
+uint8_t ast1100_device_tickle_fw = 0;
+uint32_t ast1100_device_flash_mmio_offset = 0;
+uint32_t ast1100_original_wdt_conf = 0;
+
+const struct dev_entry bmc_aspeed_ast1100[] = {
+ {PCI_VENDOR_ID_ASPEED, 0x2000, OK, "ASPEED", "AST1100" },
+
+ {0},
+};
+
+static int ast1100_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr);
+
+static const struct spi_master spi_master_ast1100 = {
+ .type = SPI_CONTROLLER_AST1100,
+ .max_data_read = 256,
+ .max_data_write = 256,
+ .command = ast1100_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+static int ast1100_set_a2b_bridge_scu(void)
+{
+ pci_mmio_writel(0x0, ast1100_device_bar + 0xf000);
+ pci_mmio_writel(AST1100_SCU_APB_ADDR & 0xffff0000, ast1100_device_bar + 0xf004);
+ pci_mmio_writel(0x1, ast1100_device_bar + 0xf000);
+
+ return 0;
+}
+
+static int ast1100_set_a2b_bridge_wdt(void)
+{
+ pci_mmio_writel(0x0, ast1100_device_bar + 0xf000);
+ pci_mmio_writel(AST1100_WDT_APB_ADDR & 0xffff0000, ast1100_device_bar + 0xf004);
+ pci_mmio_writel(0x1, ast1100_device_bar + 0xf000);
+
+ return 0;
+}
+
+static int ast1100_set_a2b_bridge_smc(void)
+{
+ pci_mmio_writel(0x0, ast1100_device_bar + 0xf000);
+ pci_mmio_writel(AST1100_SMC_APB_ADDR, ast1100_device_bar + 0xf004);
+ pci_mmio_writel(0x1, ast1100_device_bar + 0xf000);
+
+ return 0;
+}
+
+static int ast1100_set_a2b_bridge_smc_flash(void)
+{
+ pci_mmio_writel(0x0, ast1100_device_bar + 0xf000);
+ pci_mmio_writel(AST1100_SMC_FLASH_MMIO_ADDR + ast1100_device_flash_mmio_offset, ast1100_device_bar + 0xf004);
+ pci_mmio_writel(0x1, ast1100_device_bar + 0xf000);
+
+ return 0;
+}
+
+static int ast1100_disable_cpu(void) {
+ uint32_t dword;
+
+ if (ast1100_device_halt_cpu) {
+ dword = pci_mmio_readl(ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SCU_APB_BRIDGE_OFFSET + AST1100_SCU_HW_STRAP);
+ if (((dword & AST1100_SCU_BOOT_SRC_MASK) != AST1100_SCU_BOOT_SPI)
+ && ((dword & AST1100_SCU_BOOT_SRC_MASK) != AST1100_SCU_BOOT_NONE)) { /* NONE permitted to allow for BMC recovery after Ctrl+C or crash */
+ msg_perr("CPU halt requested but CPU firmware source is not SPI.\n");
+ pci_mmio_writel(0x0, ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SCU_APB_BRIDGE_OFFSET + AST1100_SCU_PROT_KEY);
+ ast1100_device_halt_cpu = 0;
+ return 1;
+ }
+
+ /* Disable CPU */
+ ast1100_set_a2b_bridge_scu();
+ pci_mmio_writel((dword & ~AST1100_SCU_BOOT_SRC_MASK) | AST1100_SCU_BOOT_NONE, ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SCU_APB_BRIDGE_OFFSET + AST1100_SCU_HW_STRAP);
+ ast1100_original_wdt_conf = pci_mmio_readl(ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_WDT_APB_BRIDGE_OFFSET + AST1100_WDT1_CTL);
+ pci_mmio_writel(ast1100_original_wdt_conf & 0xffff0, ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_WDT_APB_BRIDGE_OFFSET + AST1100_WDT1_CTL);
+ }
+
+ return 0;
+}
+
+static int ast1100_enable_cpu(void) {
+ uint32_t dword;
+
+ if (ast1100_device_halt_cpu && ast1100_device_resume_cpu) {
+ /* Re-enable CPU */
+ ast1100_set_a2b_bridge_scu();
+ dword = pci_mmio_readl(ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SCU_APB_BRIDGE_OFFSET + AST1100_SCU_HW_STRAP);
+ pci_mmio_writel((dword & ~AST1100_SCU_BOOT_SRC_MASK) | AST1100_SCU_BOOT_SPI, ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SCU_APB_BRIDGE_OFFSET + AST1100_SCU_HW_STRAP);
+ }
+
+ return 0;
+}
+
+static int ast1100_reset_cpu(void) {
+ if (ast1100_device_reset_cpu) {
+ /* Disable WDT from issuing full SoC reset
+ * Without this, OpenPOWER systems will crash when the GPIO blocks are reset on WDT timeout
+ */
+ msg_pinfo("Configuring P2A bridge for WDT access\n");
+ ast1100_set_a2b_bridge_wdt();
+ ast1100_original_wdt_conf = pci_mmio_readl(ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_WDT_APB_BRIDGE_OFFSET + AST1100_WDT1_CTL);
+
+ /* Initiate reset */
+ msg_pinfo("Setting WDT to reset CPU immediately\n");
+ pci_mmio_writel(ast1100_original_wdt_conf & 0xffff0, ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_WDT_APB_BRIDGE_OFFSET + AST1100_WDT1_CTL);
+ pci_mmio_writel(0xec08ce00, ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_WDT_APB_BRIDGE_OFFSET + AST1100_WDT1_CTR_RELOAD);
+ pci_mmio_writel(0x4755, ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_WDT_APB_BRIDGE_OFFSET + AST1100_WDT1_CTR_RESTART);
+ pci_mmio_writel(AST1100_WDT_SET_CLOCK, ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_WDT_APB_BRIDGE_OFFSET + AST1100_WDT1_CTL);
+ pci_mmio_writel(AST1100_WDT_RESET_SYSTEM | AST1100_WDT_ENABLE, ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_WDT_APB_BRIDGE_OFFSET + AST1100_WDT1_CTL);
+
+ }
+
+ return 0;
+}
+
+static int ast1100_shutdown(void *data) {
+ /* Reactivate CPU if previously deactivated */
+ ast1100_enable_cpu();
+
+ /* Reset CPU if requested */
+ ast1100_reset_cpu();
+
+ /* Disable backdoor APB access */
+ pci_mmio_writel(0x0, ast1100_device_bar + 0xf000);
+
+ return 0;
+}
+
+int ast1100_init(void)
+{
+ struct pci_dev *dev = NULL;
+ uint32_t dword;
+
+ char *arg;
+
+ ast1100_device_spi_bus = 0;
+ arg = extract_programmer_param("spibus");
+ if (arg)
+ ast1100_device_spi_bus = strtol(arg, NULL, 0);
+ free(arg);
+
+ ast1100_device_spi_speed = 0;
+ arg = extract_programmer_param("spispeed");
+ if (arg)
+ ast1100_device_spi_speed = strtol(arg, NULL, 0);
+ free(arg);
+
+ ast1100_device_halt_cpu = 0;
+ arg = extract_programmer_param("cpu");
+ if (arg && !strcmp(arg,"pause")) {
+ ast1100_device_halt_cpu = 1;
+ ast1100_device_resume_cpu = 1;
+ ast1100_device_reset_cpu = 0;
+ }
+ else if (arg && !strcmp(arg,"halt")) {
+ ast1100_device_halt_cpu = 1;
+ ast1100_device_resume_cpu = 0;
+ ast1100_device_reset_cpu = 0;
+ }
+ else if (arg && !strcmp(arg,"reset")) {
+ ast1100_device_halt_cpu = 1;
+ ast1100_device_resume_cpu = 1;
+ ast1100_device_reset_cpu = 1;
+ }
+ else if (arg) {
+ msg_perr("Invalid CPU option! Valid values are: pause | halt | reset\n");
+ return 1;
+ }
+ arg = extract_programmer_param("tickle");
+ if (arg && !strcmp(arg,"true"))
+ ast1100_device_tickle_fw = 1;
+ free(arg);
+
+ if ((ast1100_device_spi_bus < 0) || (ast1100_device_spi_bus > 2)) {
+ msg_perr("SPI bus number out of range! Valid values are 0 - 2.\n");
+ return 1;
+ }
+
+ if (rget_io_perms())
+ return 1;
+
+ dev = pcidev_init(bmc_aspeed_ast1100, PCI_BASE_ADDRESS_1);
+ if (!dev)
+ return 1;
+
+ uintptr_t io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_1);
+ if (!io_base_addr)
+ return 1;
+
+ msg_pinfo("Detected ASPEED MMIO base address: %p.\n", (void*)io_base_addr);
+
+ ast1100_device_bar = rphysmap("ASPEED", io_base_addr, ASPEED_MEMMAP_SIZE);
+ if (ast1100_device_bar == ERROR_PTR)
+ return 1;
+
+ if (register_shutdown(ast1100_shutdown, dev))
+ return 1;
+
+ io_base_addr += ASPEED_P2A_OFFSET;
+ msg_pinfo("ASPEED P2A base address: %p.\n", (void*)io_base_addr);
+
+ msg_pinfo("Configuring P2A bridge for SCU access\n");
+ ast1100_set_a2b_bridge_scu();
+ pci_mmio_writel(AST1100_SCU_PASSWORD, ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SCU_APB_BRIDGE_OFFSET + AST1100_SCU_PROT_KEY);
+
+ /* Halt CPU if requested */
+ if (ast1100_disable_cpu())
+ return 1;
+
+ msg_pinfo("Configuring P2A bridge for SMC access\n");
+ ast1100_set_a2b_bridge_smc();
+
+ dword = pci_mmio_readl(ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SMC_SMC00);
+ if (((dword >> ((ast1100_device_spi_bus * 2) + 4)) & 0x3) != 0x2) {
+ msg_perr("CE%01x Flash type is not SPI!\n", ast1100_device_spi_bus);
+ return 1;
+ }
+
+ msg_pinfo("Setting CE%01x SPI relative clock speed to %d\n", ast1100_device_spi_bus, ast1100_device_spi_speed);
+ dword = pci_mmio_readl(ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SMC_CE_CTL(ast1100_device_spi_bus));
+ dword &= ~AST1100_SPI_SPEED_MASK;
+ pci_mmio_writel(dword | ((ast1100_device_spi_speed << AST1100_SPI_SPEED_SHIFT) & AST1100_SPI_SPEED_MASK), ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SMC_CE_CTL(ast1100_device_spi_bus));
+
+ msg_pinfo("Enabling CE%01x write\n", ast1100_device_spi_bus);
+ dword = pci_mmio_readl(ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SMC_SMC00);
+ pci_mmio_writel(dword | (0x1 << (10 + ast1100_device_spi_bus)), ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SMC_SMC00);
+
+ dword = pci_mmio_readl(ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SMC_SMC00);
+ dword &= AST1100_SMC_SEGMENT_SIZE_MASK;
+ switch (dword & AST1100_SMC_SEGMENT_SIZE_MASK) {
+ case AST1100_SMC_SEGMENT_SIZE_32M:
+ ast1100_device_flash_mmio_offset = 0x2000000;
+ break;
+ case AST1100_SMC_SEGMENT_SIZE_16M:
+ ast1100_device_flash_mmio_offset = 0x1000000;
+ break;
+ case AST1100_SMC_SEGMENT_SIZE_8M:
+ ast1100_device_flash_mmio_offset = 0x800000;
+ break;
+ case AST1100_SMC_SEGMENT_SIZE_4M:
+ ast1100_device_flash_mmio_offset = 0x400000;
+ break;
+ default:
+ ast1100_device_flash_mmio_offset = 0x2000000;
+ }
+ msg_pinfo("Segment size: 0x%08x\n", ast1100_device_flash_mmio_offset);
+
+ ast1100_device_flash_mmio_offset = ast1100_device_flash_mmio_offset * ast1100_device_spi_bus;
+ msg_pinfo("Using CE%01x offset 0x%08x\n", ast1100_device_spi_bus, ast1100_device_flash_mmio_offset);
+
+ register_spi_master(&spi_master_ast1100);
+
+ return 0;
+}
+
+static void ast1100_spi_xfer_data(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ int i;
+ uint32_t dword;
+
+ for (i = 0; i < writecnt; i++)
+ msg_pspew("[%02x]", writearr[i]);
+ msg_pspew("\n");
+
+ for (i = 0; i < writecnt; i=i+4) {
+ if ((writecnt - i) < 4)
+ break;
+ dword = writearr[i];
+ dword |= writearr[i + 1] << 8;
+ dword |= writearr[i + 2] << 16;
+ dword |= writearr[i + 3] << 24;
+ pci_mmio_writel(dword, ast1100_device_bar + ASPEED_P2A_OFFSET);
+ }
+ for (; i < writecnt; i++)
+ pci_mmio_writeb(writearr[i], ast1100_device_bar + ASPEED_P2A_OFFSET);
+ programmer_delay(1);
+ for (i = 0; i < readcnt;) {
+ dword = pci_mmio_readl(ast1100_device_bar + ASPEED_P2A_OFFSET);
+ if (i < readcnt)
+ readarr[i] = dword & 0xff;
+ i++;
+ if (i < readcnt)
+ readarr[i] = (dword >> 8) & 0xff;
+ i++;
+ if (i < readcnt)
+ readarr[i] = (dword >> 16) & 0xff;
+ i++;
+ if (i < readcnt)
+ readarr[i] = (dword >> 24) & 0xff;
+ i++;
+ }
+
+ for (i = 0; i < readcnt; i++)
+ msg_pspew("[%02x]", readarr[i]);
+ msg_pspew("\n");
+}
+
+/* Returns 0 upon success, a negative number upon errors. */
+static int ast1100_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ uint32_t dword;
+
+ msg_pspew("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, *writearr, writecnt, readcnt);
+
+ /* Set up user command mode */
+ ast1100_set_a2b_bridge_smc();
+ dword = pci_mmio_readl(ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SMC_CE_CTL(ast1100_device_spi_bus));
+ pci_mmio_writel(dword | AST1100_SPI_CMD_USER_MODE, ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SMC_CE_CTL(ast1100_device_spi_bus));
+ dword = pci_mmio_readl(ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SMC_CE_CTL(ast1100_device_spi_bus));
+ pci_mmio_writel(dword & ~AST1100_SPI_STOP_CE_ACTIVE, ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SMC_CE_CTL(ast1100_device_spi_bus));
+
+ /* Transfer data */
+ ast1100_set_a2b_bridge_smc_flash();
+ ast1100_spi_xfer_data(flash, writecnt, readcnt, writearr, readarr);
+
+ /* Tear down user command mode */
+ ast1100_set_a2b_bridge_smc();
+ dword = pci_mmio_readl(ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SMC_CE_CTL(ast1100_device_spi_bus));
+ pci_mmio_writel(dword | AST1100_SPI_STOP_CE_ACTIVE, ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SMC_CE_CTL(ast1100_device_spi_bus));
+ dword = pci_mmio_readl(ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SMC_CE_CTL(ast1100_device_spi_bus));
+ pci_mmio_writel((dword & ~AST1100_SPI_CMD_MASK) | AST1100_SPI_CMD_FAST_R_MODE, ast1100_device_bar + ASPEED_P2A_OFFSET + AST1100_SMC_CE_CTL(ast1100_device_spi_bus));
+
+ if (ast1100_device_tickle_fw) {
+ ast1100_enable_cpu();
+ programmer_delay(100);
+ ast1100_disable_cpu();
+ }
+
+ return 0;
+}
diff --git ./ast2400.c ./ast2400.c
new file mode 100644
index 0000000..01cee76
--- /dev/null
+++ ./ast2400.c
@@ -0,0 +1,426 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2016 - 2017 Raptor Engineering, LLC
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define PCI_VENDOR_ID_ASPEED 0x1a03
+
+#define ASPEED_MEMMAP_SIZE (128 * 1024)
+#define ASPEED_P2A_OFFSET 0x10000
+
+#define AST2400_SCU_APB_ADDR 0x1e6e2000
+#define AST2400_SCU_APB_BRIDGE_OFFSET (AST2400_SCU_APB_ADDR & 0xffff)
+#define AST2400_SCU_PROT_KEY 0x00
+#define AST2400_SCU_MISC_CTL 0x2c
+#define AST2400_SCU_HW_STRAP 0x70
+
+#define AST2400_SCU_PASSWORD 0x1688a8a8
+#define AST2400_SCU_BOOT_SRC_MASK 0x3
+#define AST2400_SCU_BOOT_SPI 0x2
+#define AST2400_SCU_BOOT_NONE 0x3
+
+#define AST2400_SMC_APB_ADDR 0x1e620000
+#define AST2400_SMC_FMC00 0x00
+#define AST2400_SMC_CE_CTL(N) (0x10 + (N * 4))
+#define AST2400_SMC_CE_SEG(N) (0x30 + (N * 4))
+
+#define AST2400_SMC_FLASH_MMIO_ADDR 0x20000000
+
+#define AST2400_SPI_APB_ADDR 0x1e630000
+#define AST2400_SPI_CFG 0x00
+#define AST2400_SPI_CTL 0x04
+
+#define AST2400_SPI_CFG_WRITE_EN 0x1
+#define AST2400_SPI_CMD_FAST_R_MODE 0x1
+#define AST2400_SPI_CMD_USER_MODE 0x3
+#define AST2400_SPI_CMD_MASK 0x3
+#define AST2400_SPI_STOP_CE_ACTIVE (0x1 << 2)
+#define AST2400_SPI_CPOL_1 (0x1 << 4)
+#define AST2400_SPI_LSB_FIRST_CTRL (0x1 << 5)
+#define AST2400_SPI_SPEED_MASK (0xf << 8)
+#define AST2400_SPI_IO_MODE_MASK (0x3 << 28)
+
+#define AST2400_SPI_FLASH_MMIO_ADDR 0x30000000
+
+#define AST2400_WDT_APB_ADDR 0x1e785000
+#define AST2400_WDT_APB_BRIDGE_OFFSET (AST2400_WDT_APB_ADDR & 0xffff)
+
+#define AST2400_WDT1_CTL 0x0c
+
+#define AST2400_WDT_RESET_MODE_MASK (0x3 << 5)
+#define AST2400_WDT_RESET_CPU_ONLY (0x2 << 5)
+
+uint8_t *ast2400_device_bar = 0;
+uint8_t ast2400_device_spi_bus = 0;
+uint8_t ast2400_device_halt_cpu = 0;
+uint8_t ast2400_device_resume_cpu = 0;
+uint8_t ast2400_device_tickle_fw = 0;
+uint32_t ast2400_device_flash_mmio_offset = 0;
+uint32_t ast2400_device_host_mode = 0;
+uint32_t ast2400_original_wdt_conf = 0;
+
+const struct dev_entry bmc_aspeed_ast2400[] = {
+ {PCI_VENDOR_ID_ASPEED, 0x2000, OK, "ASPEED", "AST2400" },
+
+ {0},
+};
+
+static int ast2400_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr);
+
+static const struct spi_master spi_master_ast2400 = {
+ .type = SPI_CONTROLLER_AST2400,
+ .max_data_read = 256,
+ .max_data_write = 256,
+ .command = ast2400_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+static int ast2400_set_a2b_bridge_scu(void)
+{
+ pci_mmio_writel(0x0, ast2400_device_bar + 0xf000);
+ pci_mmio_writel(AST2400_SCU_APB_ADDR & 0xffff0000, ast2400_device_bar + 0xf004);
+ pci_mmio_writel(0x1, ast2400_device_bar + 0xf000);
+
+ return 0;
+}
+
+static int ast2400_set_a2b_bridge_wdt(void)
+{
+ pci_mmio_writel(0x0, ast2400_device_bar + 0xf000);
+ pci_mmio_writel(AST2400_WDT_APB_ADDR & 0xffff0000, ast2400_device_bar + 0xf004);
+ pci_mmio_writel(0x1, ast2400_device_bar + 0xf000);
+
+ return 0;
+}
+
+static int ast2400_set_a2b_bridge_smc(void)
+{
+ pci_mmio_writel(0x0, ast2400_device_bar + 0xf000);
+ pci_mmio_writel(AST2400_SMC_APB_ADDR, ast2400_device_bar + 0xf004);
+ pci_mmio_writel(0x1, ast2400_device_bar + 0xf000);
+
+ return 0;
+}
+
+static int ast2400_set_a2b_bridge_spi(void)
+{
+ pci_mmio_writel(0x0, ast2400_device_bar + 0xf000);
+ pci_mmio_writel(AST2400_SPI_APB_ADDR, ast2400_device_bar + 0xf004);
+ pci_mmio_writel(0x1, ast2400_device_bar + 0xf000);
+
+ return 0;
+}
+
+static int ast2400_set_a2b_bridge_smc_flash(void)
+{
+ pci_mmio_writel(0x0, ast2400_device_bar + 0xf000);
+ pci_mmio_writel(AST2400_SMC_FLASH_MMIO_ADDR + ast2400_device_flash_mmio_offset, ast2400_device_bar + 0xf004);
+ pci_mmio_writel(0x1, ast2400_device_bar + 0xf000);
+
+ return 0;
+}
+
+static int ast2400_set_a2b_bridge_spi_flash(void)
+{
+ pci_mmio_writel(0x0, ast2400_device_bar + 0xf000);
+ pci_mmio_writel(AST2400_SPI_FLASH_MMIO_ADDR, ast2400_device_bar + 0xf004);
+ pci_mmio_writel(0x1, ast2400_device_bar + 0xf000);
+
+ return 0;
+}
+
+static int ast2400_disable_cpu(void) {
+ uint32_t dword;
+
+ if (ast2400_device_halt_cpu) {
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SCU_APB_BRIDGE_OFFSET + AST2400_SCU_HW_STRAP);
+ if (((dword & AST2400_SCU_BOOT_SRC_MASK) != AST2400_SCU_BOOT_SPI)
+ && ((dword & AST2400_SCU_BOOT_SRC_MASK) != AST2400_SCU_BOOT_NONE)) { /* NONE permitted to allow for BMC recovery after Ctrl+C or crash */
+ msg_perr("CPU halt requested but CPU firmware source is not SPI.\n");
+ pci_mmio_writel(0x0, ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SCU_APB_BRIDGE_OFFSET + AST2400_SCU_PROT_KEY);
+ ast2400_device_halt_cpu = 0;
+ return 1;
+ }
+
+ /* Disable WDT from issuing full SoC reset
+ * Without this, OpenPOWER systems will crash when the GPIO blocks are reset on WDT timeout
+ */
+ msg_pinfo("Configuring P2A bridge for WDT access\n");
+ ast2400_set_a2b_bridge_wdt();
+ ast2400_original_wdt_conf = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_WDT_APB_BRIDGE_OFFSET + AST2400_WDT1_CTL);
+ pci_mmio_writel((ast2400_original_wdt_conf & ~AST2400_WDT_RESET_MODE_MASK) | AST2400_WDT_RESET_CPU_ONLY, ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_WDT_APB_BRIDGE_OFFSET + AST2400_WDT1_CTL);
+
+ /* Disable CPU */
+ ast2400_set_a2b_bridge_scu();
+ pci_mmio_writel((dword & ~AST2400_SCU_BOOT_SRC_MASK) | AST2400_SCU_BOOT_NONE, ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SCU_APB_BRIDGE_OFFSET + AST2400_SCU_HW_STRAP);
+ }
+
+ return 0;
+}
+
+static int ast2400_enable_cpu(void) {
+ uint32_t dword;
+
+ if (ast2400_device_halt_cpu && ast2400_device_resume_cpu) {
+ /* Re-enable CPU */
+ ast2400_set_a2b_bridge_scu();
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SCU_APB_BRIDGE_OFFSET + AST2400_SCU_HW_STRAP);
+ pci_mmio_writel((dword & ~AST2400_SCU_BOOT_SRC_MASK) | AST2400_SCU_BOOT_SPI, ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SCU_APB_BRIDGE_OFFSET + AST2400_SCU_HW_STRAP);
+
+ /* Reset WDT configuration */
+ ast2400_set_a2b_bridge_wdt();
+ pci_mmio_writel((ast2400_original_wdt_conf & ~AST2400_WDT_RESET_MODE_MASK) | AST2400_WDT_RESET_CPU_ONLY, ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_WDT_APB_BRIDGE_OFFSET + AST2400_WDT1_CTL);
+ }
+
+ return 0;
+}
+
+static int ast2400_shutdown(void *data) {
+ /* Reactivate CPU if previously deactivated */
+ ast2400_enable_cpu();
+
+ /* Disable backdoor APB access */
+ pci_mmio_writel(0x0, ast2400_device_bar + 0xf000);
+
+ return 0;
+}
+
+int ast2400_init(void)
+{
+ struct pci_dev *dev = NULL;
+ uint32_t dword;
+ uint8_t divisor;
+
+ char *arg;
+
+ ast2400_device_spi_bus = 0;
+ arg = extract_programmer_param("spibus");
+ if (arg) {
+ if (!strcmp(arg,"host"))
+ ast2400_device_host_mode = 1;
+ else
+ ast2400_device_spi_bus = strtol(arg, NULL, 0);
+ }
+ free(arg);
+
+ ast2400_device_halt_cpu = 0;
+ arg = extract_programmer_param("cpu");
+ if (arg && !strcmp(arg,"pause")) {
+ ast2400_device_halt_cpu = 1;
+ ast2400_device_resume_cpu = 1;
+ }
+ if (arg && !strcmp(arg,"halt")) {
+ ast2400_device_halt_cpu = 1;
+ ast2400_device_resume_cpu = 0;
+ }
+ arg = extract_programmer_param("tickle");
+ if (arg && !strcmp(arg,"true"))
+ ast2400_device_tickle_fw = 1;
+ free(arg);
+
+ if ((ast2400_device_host_mode == 0) && ((ast2400_device_spi_bus < 0) || (ast2400_device_spi_bus > 4))) {
+ msg_perr("SPI bus number out of range! Valid values are 0 - 4.\n");
+ return 1;
+ }
+
+ if (rget_io_perms())
+ return 1;
+
+ dev = pcidev_init(bmc_aspeed_ast2400, PCI_BASE_ADDRESS_1);
+ if (!dev)
+ return 1;
+
+ uintptr_t io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_1);
+ if (!io_base_addr)
+ return 1;
+
+ msg_pinfo("Detected ASPEED MMIO base address: %p.\n", (void*)io_base_addr);
+
+ ast2400_device_bar = rphysmap("ASPEED", io_base_addr, ASPEED_MEMMAP_SIZE);
+ if (ast2400_device_bar == ERROR_PTR)
+ return 1;
+
+ if (register_shutdown(ast2400_shutdown, dev))
+ return 1;
+
+ io_base_addr += ASPEED_P2A_OFFSET;
+ msg_pinfo("ASPEED P2A base address: %p.\n", (void*)io_base_addr);
+
+ msg_pinfo("Configuring P2A bridge for SCU access\n");
+ ast2400_set_a2b_bridge_scu();
+ pci_mmio_writel(AST2400_SCU_PASSWORD, ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SCU_APB_BRIDGE_OFFSET + AST2400_SCU_PROT_KEY);
+
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SCU_APB_BRIDGE_OFFSET + AST2400_SCU_MISC_CTL);
+ pci_mmio_writel(dword & ~((0x1 << 24) | (0x2 << 22)), ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SCU_APB_BRIDGE_OFFSET + AST2400_SCU_MISC_CTL);
+
+ /* Halt CPU if requested */
+ if (ast2400_disable_cpu())
+ return 1;
+
+ msg_pinfo("Configuring P2A bridge for SMC access\n");
+ ast2400_set_a2b_bridge_smc();
+
+ if (ast2400_device_host_mode) {
+ msg_pinfo("Configuring P2A bridge for SPI access\n");
+ ast2400_set_a2b_bridge_spi();
+
+ divisor = 0; /* Slowest speed for now */
+
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SPI_CTL);
+ dword &= ~AST2400_SPI_SPEED_MASK;
+ dword |= (divisor << 8);
+ dword &= ~AST2400_SPI_CPOL_1;
+ dword &= ~AST2400_SPI_LSB_FIRST_CTRL; /* MSB first */
+ dword &= ~AST2400_SPI_IO_MODE_MASK; /* Single bit I/O mode */
+ pci_mmio_writel(dword, ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SPI_CTL);
+ }
+ else {
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SMC_FMC00);
+ if (((dword >> (ast2400_device_spi_bus * 2)) & 0x3) != 0x2) {
+ msg_perr("CE%01x Flash type is not SPI!\n", ast2400_device_spi_bus);
+ return 1;
+ }
+
+ msg_pinfo("Enabling CE%01x write\n", ast2400_device_spi_bus);
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SMC_FMC00);
+ pci_mmio_writel(dword | (0x1 << (16 + ast2400_device_spi_bus)), ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SMC_FMC00);
+
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SMC_CE_SEG(ast2400_device_spi_bus));
+ ast2400_device_flash_mmio_offset = ((dword >> 16) & 0x3f) * 0x800000;
+ msg_pinfo("Using CE%01x offset 0x%08x\n", ast2400_device_spi_bus, ast2400_device_flash_mmio_offset);
+ }
+
+ register_spi_master(&spi_master_ast2400);
+
+ return 0;
+}
+
+static void ast2400_spi_xfer_data(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ int i;
+ uint32_t dword;
+
+ for (i = 0; i < writecnt; i++)
+ msg_pspew("[%02x]", writearr[i]);
+ msg_pspew("\n");
+
+ for (i = 0; i < writecnt; i=i+4) {
+ if ((writecnt - i) < 4)
+ break;
+ dword = writearr[i];
+ dword |= writearr[i + 1] << 8;
+ dword |= writearr[i + 2] << 16;
+ dword |= writearr[i + 3] << 24;
+ pci_mmio_writel(dword, ast2400_device_bar + ASPEED_P2A_OFFSET);
+ }
+ for (; i < writecnt; i++)
+ pci_mmio_writeb(writearr[i], ast2400_device_bar + ASPEED_P2A_OFFSET);
+ programmer_delay(1);
+ for (i = 0; i < readcnt;) {
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET);
+ if (i < readcnt)
+ readarr[i] = dword & 0xff;
+ i++;
+ if (i < readcnt)
+ readarr[i] = (dword >> 8) & 0xff;
+ i++;
+ if (i < readcnt)
+ readarr[i] = (dword >> 16) & 0xff;
+ i++;
+ if (i < readcnt)
+ readarr[i] = (dword >> 24) & 0xff;
+ i++;
+ }
+
+ for (i = 0; i < readcnt; i++)
+ msg_pspew("[%02x]", readarr[i]);
+ msg_pspew("\n");
+}
+
+/* Returns 0 upon success, a negative number upon errors. */
+static int ast2400_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ uint32_t dword;
+
+ msg_pspew("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, *writearr, writecnt, readcnt);
+
+ if (ast2400_device_host_mode) {
+ /* Set up user command mode */
+ ast2400_set_a2b_bridge_spi();
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SPI_CFG);
+ pci_mmio_writel(dword | AST2400_SPI_CFG_WRITE_EN, ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SPI_CFG);
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SPI_CTL);
+ pci_mmio_writel(dword | AST2400_SPI_CMD_USER_MODE, ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SPI_CTL);
+
+ /* Transfer data */
+ ast2400_set_a2b_bridge_spi_flash();
+ ast2400_spi_xfer_data(flash, writecnt, readcnt, writearr, readarr);
+
+ /* Tear down user command mode */
+ ast2400_set_a2b_bridge_spi();
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SPI_CTL);
+ pci_mmio_writel((dword & ~AST2400_SPI_CMD_MASK) | AST2400_SPI_CMD_FAST_R_MODE, ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SPI_CTL);
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SPI_CFG);
+ pci_mmio_writel(dword & ~AST2400_SPI_CFG_WRITE_EN, ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SPI_CFG);
+ }
+ else {
+ /* Set up user command mode */
+ ast2400_set_a2b_bridge_smc();
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SMC_CE_CTL(ast2400_device_spi_bus));
+ pci_mmio_writel(dword | AST2400_SPI_CMD_USER_MODE, ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SMC_CE_CTL(ast2400_device_spi_bus));
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SMC_CE_CTL(ast2400_device_spi_bus));
+ pci_mmio_writel(dword & ~AST2400_SPI_STOP_CE_ACTIVE, ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SMC_CE_CTL(ast2400_device_spi_bus));
+
+ /* Transfer data */
+ ast2400_set_a2b_bridge_smc_flash();
+ ast2400_spi_xfer_data(flash, writecnt, readcnt, writearr, readarr);
+
+ /* Tear down user command mode */
+ ast2400_set_a2b_bridge_smc();
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SMC_CE_CTL(ast2400_device_spi_bus));
+ pci_mmio_writel(dword | AST2400_SPI_STOP_CE_ACTIVE, ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SMC_CE_CTL(ast2400_device_spi_bus));
+ dword = pci_mmio_readl(ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SMC_CE_CTL(ast2400_device_spi_bus));
+ pci_mmio_writel((dword & ~AST2400_SPI_CMD_MASK) | AST2400_SPI_CMD_FAST_R_MODE, ast2400_device_bar + ASPEED_P2A_OFFSET + AST2400_SMC_CE_CTL(ast2400_device_spi_bus));
+ }
+
+ if (ast2400_device_tickle_fw) {
+ ast2400_enable_cpu();
+ programmer_delay(100);
+ ast2400_disable_cpu();
+ }
+
+ return 0;
+}
diff --git ./chipdrivers.h ./chipdrivers.h
index c85eac9..20529d5 100644
--- ./chipdrivers.h
+++ ./chipdrivers.h
@@ -195,4 +195,26 @@ 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);
+/* spi4ba.c */
+int spi_enter_4ba_b7(struct flashctx *flash);
+int spi_enter_4ba_b7_we(struct flashctx *flash);
+int spi_byte_program_4ba(struct flashctx *flash, unsigned int addr, uint8_t databyte);
+int spi_nbyte_program_4ba(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len);
+int spi_nbyte_read_4ba(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len);
+int spi_block_erase_20_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_52_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_d8_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_byte_program_4ba_ereg(struct flashctx *flash, unsigned int addr, uint8_t databyte);
+int spi_nbyte_program_4ba_ereg(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len);
+int spi_nbyte_read_4ba_ereg(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len);
+int spi_block_erase_20_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_52_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_d8_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_byte_program_4ba_direct(struct flashctx *flash, unsigned int addr, uint8_t databyte);
+int spi_nbyte_program_4ba_direct(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len);
+int spi_nbyte_read_4ba_direct(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len);
+int spi_block_erase_21_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_5c_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_dc_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+
#endif /* !__CHIPDRIVERS_H__ */
diff --git ./cli_output.c ./cli_output.c
index 61a9af6..4adfd7d 100644
--- ./cli_output.c
+++ ./cli_output.c
@@ -90,7 +90,8 @@ int flashrom_print_cb(enum flashrom_log_level level, const char *fmt, va_list ap
fflush(output_type);
}
#ifndef STANDALONE
- if ((level <= verbose_logfile) && logfile) {
+ /* skip of msgs starting from '\b' added to skip progress percents */
+ if ((level <= verbose_logfile) && logfile && (!fmt || fmt[0] != '\b')) {
ret = vfprintf(logfile, fmt, logfile_args);
if (level != FLASHROM_MSG_SPEW)
fflush(logfile);
diff --git ./flash.h ./flash.h
index 9c3784b..58ad387 100644
--- ./flash.h
+++ ./flash.h
@@ -119,6 +119,14 @@ enum write_granularity {
#define FEATURE_WRSR_EITHER (FEATURE_WRSR_EWSR | FEATURE_WRSR_WREN)
#define FEATURE_OTP (1 << 8)
#define FEATURE_QPI (1 << 9)
+/* Feature bits used for 4-bytes addressing mode */
+#define FEATURE_4BA_SUPPORT (1 << 10)
+#define FEATURE_4BA_ONLY (1 << 11)
+#define FEATURE_4BA_EXTENDED_ADDR_REG (1 << 12)
+#define FEATURE_4BA_DIRECT_READ (1 << 13)
+#define FEATURE_4BA_DIRECT_WRITE (1 << 14)
+#define FEATURE_4BA_ALL_ERASERS_DIRECT (1 << 15)
+#define FEATURE_4BA_ALL_DIRECT (FEATURE_4BA_DIRECT_READ | FEATURE_4BA_DIRECT_WRITE | FEATURE_4BA_ALL_ERASERS_DIRECT)
enum test_state {
OK = 0,
@@ -164,6 +172,14 @@ struct flashchip {
unsigned int page_size;
int feature_bits;
+ /* set of function pointers to use in 4-bytes addressing mode */
+ struct four_bytes_addr_funcs_set {
+ int (*enter_4ba) (struct flashctx *flash);
+ int (*read_nbyte) (struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len);
+ int (*program_byte) (struct flashctx *flash, unsigned int addr, const uint8_t databyte);
+ int (*program_nbyte) (struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len);
+ } four_bytes_addr_funcs;
+
/* Indicate how well flashrom supports different operations of this flash chip. */
struct tested {
enum test_state probe;
@@ -347,6 +363,11 @@ __attribute__((format(printf, 2, 3)));
#define msg_pspew(...) print(FLASHROM_MSG_SPEW, __VA_ARGS__) /* programmer debug spew */
#define msg_cspew(...) print(FLASHROM_MSG_SPEW, __VA_ARGS__) /* chip debug spew */
+/* Read progress will be shown for reads more than 256KB */
+#define MIN_LENGTH_TO_SHOW_READ_PROGRESS 256 * 1024
+/* Read progress will be shown for erases and writes more than 64KB */
+#define MIN_LENGTH_TO_SHOW_ERASE_AND_WRITE_PROGRESS 64 * 1024
+
/* layout.c */
int register_include_arg(char *name);
int read_romlayout(const char *name);
diff --git ./flashchips.c ./flashchips.c
index 7ef361b..c90fd94 100644
--- ./flashchips.c
+++ ./flashchips.c
@@ -8045,6 +8045,100 @@ const struct flashchip flashchips[] = {
{
.vendor = "Macronix",
+ .name = "MX25L25635F/MX25L25645E/MX25L25665E",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L25635F,
+ .total_size = 32768,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0xB1, exit 0xC1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_4BA_SUPPORT,
+ .four_bytes_addr_funcs =
+ {
+ .enter_4ba = spi_enter_4ba_b7, /* enter 4-bytes addressing mode by CMD B7 */
+ .read_nbyte = spi_nbyte_read_4ba, /* read from 4-bytes addressing mode */
+ .program_byte = spi_byte_program_4ba, /* write from 4-bytes addressing mode */
+ .program_nbyte = spi_nbyte_program_4ba /* write from 4-bytes addressing mode */
+ },
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 8192} },
+ .block_erase = spi_block_erase_20_4ba,
+ }, {
+ .eraseblocks = { {32 * 1024, 1024} },
+ .block_erase = spi_block_erase_52_4ba,
+ }, {
+ .eraseblocks = { {64 * 1024, 512} },
+ .block_erase = spi_block_erase_d8_4ba,
+ }, {
+ .eraseblocks = { {32 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {32 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ /* TODO: security register and SBLK/SBULK; MX25L12835F: configuration register */
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX66L51235F",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX66L51235F,
+ .total_size = 65536,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0xB1, exit 0xC1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_4BA_SUPPORT,
+ .four_bytes_addr_funcs =
+ {
+ .enter_4ba = spi_enter_4ba_b7, /* enter 4-bytes addressing mode by CMD B7 */
+ .read_nbyte = spi_nbyte_read_4ba, /* read from 4-bytes addressing mode */
+ .program_byte = spi_byte_program_4ba, /* write from 4-bytes addressing mode */
+ .program_nbyte = spi_nbyte_program_4ba /* write from 4-bytes addressing mode */
+ },
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 16384} },
+ .block_erase = spi_block_erase_20_4ba,
+ }, {
+ .eraseblocks = { {32 * 1024, 2048} },
+ .block_erase = spi_block_erase_52_4ba,
+ }, {
+ .eraseblocks = { {64 * 1024, 1024} },
+ .block_erase = spi_block_erase_d8_4ba,
+ }, {
+ .eraseblocks = { {64 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {64 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ /* TODO: security register and SBLK/SBULK; MX25L12835F: configuration register */
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
.name = "MX25U1635E",
.bustype = BUS_SPI,
.manufacture_id = MACRONIX_ID,
@@ -11747,7 +11841,7 @@ const struct flashchip flashchips[] = {
.total_size = 16384,
.page_size = 256,
.feature_bits = FEATURE_WRSR_WREN,
- .tested = TEST_UNTESTED,
+ .tested = TEST_OK_PREW,
.probe = probe_spi_rdid,
.probe_timing = TIMING_ZERO,
.block_erasers = {
@@ -14588,6 +14682,54 @@ const struct flashchip flashchips[] = {
{
.vendor = "Winbond",
+ .name = "W25Q256.V",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25Q256_V,
+ .total_size = 32768,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
+ /* FOUR_BYTE_ADDR: supports 4-bytes addressing mode */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_4BA_SUPPORT | FEATURE_4BA_DIRECT_READ,
+ .four_bytes_addr_funcs =
+ {
+ .enter_4ba = spi_enter_4ba_b7_we, /* enter 4-bytes addressing mode by CMD B7 + WREN */
+ .read_nbyte = spi_nbyte_read_4ba_direct, /* read directly from any mode, no need to enter 4ba */
+ .program_byte = spi_byte_program_4ba, /* write from 4-bytes addressing mode */
+ .program_nbyte = spi_nbyte_program_4ba /* write from 4-bytes addressing mode */
+ },
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 8192} },
+ .block_erase = spi_block_erase_20_4ba, /* erases 4k from 4-bytes addressing mode */
+ }, {
+ .eraseblocks = { {32 * 1024, 1024} },
+ .block_erase = spi_block_erase_52_4ba, /* erases 32k from 4-bytes addressing mode */
+ }, {
+ .eraseblocks = { {64 * 1024, 512} },
+ .block_erase = spi_block_erase_d8_4ba, /* erases 64k from 4-bytes addressing mode */
+ }, {
+ .eraseblocks = { {32 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {32 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
.name = "W25Q20.W",
.bustype = BUS_SPI,
.manufacture_id = WINBOND_NEX_ID,
diff --git ./flashchips.h ./flashchips.h
index 15574bb..62eb9a5 100644
--- ./flashchips.h
+++ ./flashchips.h
@@ -482,6 +482,7 @@
#define MACRONIX_MX25L25635F 0x2019 /* Same as MX25L25639F, but the latter seems to not support REMS */
#define MACRONIX_MX25L1635D 0x2415
#define MACRONIX_MX25L1635E 0x2515 /* MX25L1635{E} */
+#define MACRONIX_MX66L51235F 0x201a /* MX66L51235F */
#define MACRONIX_MX25U1635E 0x2535
#define MACRONIX_MX25U3235E 0x2536 /* Same as MX25U6435F */
#define MACRONIX_MX25U6435E 0x2537 /* Same as MX25U6435F */
diff --git ./flashrom.c ./flashrom.c
index c600efc..767c2c4 100644
--- ./flashrom.c
+++ ./flashrom.c
@@ -6,6 +6,7 @@
* Copyright (C) 2005-2008 coresystems GmbH
* Copyright (C) 2008,2009 Carl-Daniel Hailfinger
* Copyright (C) 2016 secunet Security Networks AG
+ * Copyright (C) 2016-2017 Raptor Engineering, LLC
* (Written by Nico Huber <nico.huber@secunet.com> for secunet)
*
* This program is free software; you can redistribute it and/or modify
@@ -136,6 +137,30 @@ const struct programmer_entry programmer_table[] = {
},
#endif
+#if CONFIG_AST1100 == 1
+ {
+ .name = "ast1100",
+ .type = PCI,
+ .devs.dev = bmc_aspeed_ast1100,
+ .init = ast1100_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_AST2400 == 1
+ {
+ .name = "ast2400",
+ .type = PCI,
+ .devs.dev = bmc_aspeed_ast2400,
+ .init = ast2400_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
#if CONFIG_DRKAISER == 1
{
.name = "drkaiser",
@@ -1600,6 +1625,18 @@ static int walk_eraseblocks(struct flashctx *const flashctx,
bool first = true;
struct block_eraser *const eraser = &flashctx->chip->block_erasers[erasefunction];
+ int show_progress = 0;
+ unsigned int percent_last, percent_current;
+ unsigned long size = flashctx->chip->total_size * 1024;
+
+ /* progress visualizaion init */
+ if(size >= MIN_LENGTH_TO_SHOW_ERASE_AND_WRITE_PROGRESS) {
+ msg_cinfo(" "); /* only this space will go to logfile but all strings with \b wont. */
+ msg_cinfo("\b 0%%");
+ percent_last = percent_current = 0;
+ show_progress = 1; /* enable progress visualizaion */
+ }
+
info->erase_start = 0;
for (i = 0; i < NUM_ERASEREGIONS; ++i) {
/* count==0 for all automatically initialized array
@@ -1620,6 +1657,14 @@ static int walk_eraseblocks(struct flashctx *const flashctx,
msg_cdbg(", ");
msg_cdbg("0x%06x-0x%06x:", info->erase_start, info->erase_end);
+ if(show_progress) {
+ percent_current = (unsigned int) ((unsigned long long)info->erase_start * 100 / size);
+ if(percent_current != percent_last) {
+ msg_cinfo("\b\b\b%2d%%", percent_current);
+ percent_last = percent_current;
+ }
+ }
+
ret = per_blockfn(flashctx, info, eraser->block_erase);
if (ret)
return ret;
@@ -1627,6 +1672,10 @@ static int walk_eraseblocks(struct flashctx *const flashctx,
if (info->region_end < info->erase_start)
break;
}
+
+ if(show_progress)
+ msg_cinfo("\b\b\b\b"); /* remove progress percents from the screen */
+
msg_cdbg("\n");
return 0;
}
@@ -2223,6 +2272,44 @@ int prepare_flash_access(struct flashctx *const flash,
if (flash->chip->unlock)
flash->chip->unlock(flash);
+ /* Switching to 4-Bytes Addressing mode if flash chip supports it */
+ if(flash->chip->feature_bits & FEATURE_4BA_SUPPORT) {
+ /* Do not switch if chip is already in 4-bytes addressing mode */
+ if (flash->chip->feature_bits & FEATURE_4BA_ONLY) {
+ msg_cdbg("Flash chip is already in 4-bytes addressing mode.\n");
+ }
+ /* Do not switch to 4-Bytes Addressing mode if using Extended Address Register */
+ else if(flash->chip->feature_bits & FEATURE_4BA_EXTENDED_ADDR_REG) {
+ msg_cdbg("Using 4-bytes addressing with extended address register.\n");
+ }
+ /* Go to 4-Bytes Addressing mode if selected
+ operation requires 4-Bytes Addressing mode
+ (no need if functions are direct-4BA) */
+ else if(((read_it || verify_it)
+ && (!(flash->chip->feature_bits & FEATURE_4BA_DIRECT_READ)))
+ || ((erase_it || write_it)
+ && ((flash->chip->feature_bits & FEATURE_4BA_ALL_DIRECT) != FEATURE_4BA_ALL_DIRECT))) {
+
+ if (!flash->chip->four_bytes_addr_funcs.enter_4ba) {
+ msg_cerr("No function for Enter 4-bytes addressing mode for this flash chip.\n"
+ "Please report to flashrom@flashrom.org\n");
+ return 1;
+ }
+
+ if(flash->chip->four_bytes_addr_funcs.enter_4ba(flash)) {
+ msg_cerr("Switching to 4-bytes addressing mode failed!\n");
+ return 1;
+ }
+
+ msg_cdbg("Switched to 4-bytes addressing mode.\n");
+ }
+ /* Do not switch to 4-Bytes Addressing mode if all instructions are direct-4BA
+ or if the flash chip is 4-Bytes Addressing Only and always in 4BA-mode */
+ else {
+ msg_cdbg2("No need to switch to 4-bytes addressing mode.\n");
+ }
+ }
+
return 0;
}
diff --git ./pcidev.c ./pcidev.c
index f4e5542..d36e5c4 100644
--- ./pcidev.c
+++ ./pcidev.c
@@ -37,11 +37,13 @@ enum pci_bartype {
uintptr_t pcidev_readbar(struct pci_dev *dev, int bar)
{
uint64_t addr;
- uint32_t upperaddr;
uint8_t headertype;
uint16_t supported_cycles;
enum pci_bartype bartype = TYPE_UNKNOWN;
+#ifndef __PPC64__
+ uint32_t upperaddr;
+#endif
headertype = pci_read_byte(dev, PCI_HEADER_TYPE) & 0x7f;
msg_pspew("PCI header type 0x%02x\n", headertype);
@@ -97,6 +99,12 @@ uintptr_t pcidev_readbar(struct pci_dev *dev, int bar)
switch (bartype) {
case TYPE_MEMBAR:
msg_pdbg("MEM");
+#ifdef __PPC64__
+ /* PowerPC is able to translate 32-bit BARs into 64-bit host windows.
+ * Use the dev->base_addr[x] mechanism to handle mapping.
+ */
+ addr = dev->base_addr[(bar - 0x10) / 0x4] & PCI_BASE_ADDRESS_MEM_MASK;
+#else
if (!(supported_cycles & PCI_COMMAND_MEMORY)) {
msg_perr("MEM BAR access requested, but device has MEM space accesses disabled.\n");
/* TODO: Abort here? */
@@ -122,6 +130,7 @@ uintptr_t pcidev_readbar(struct pci_dev *dev, int bar)
}
}
addr &= PCI_BASE_ADDRESS_MEM_MASK;
+#endif
break;
case TYPE_IOBAR:
msg_pdbg("I/O\n");
diff --git ./programmer.h ./programmer.h
index a98b713..c9c3a26 100644
--- ./programmer.h
+++ ./programmer.h
@@ -5,6 +5,7 @@
* Copyright (C) 2000 Ronald G. Minnich <rminnich@gmail.com>
* Copyright (C) 2005-2009 coresystems GmbH
* Copyright (C) 2006-2009 Carl-Daniel Hailfinger
+ * Copyright (C) 2016-2017 Raptor Engineering, LLC
*
* 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
@@ -45,6 +46,12 @@ enum programmer {
#if CONFIG_GFXNVIDIA == 1
PROGRAMMER_GFXNVIDIA,
#endif
+#if CONFIG_AST1100 == 1
+ PROGRAMMER_AST1100,
+#endif
+#if CONFIG_AST2400 == 1
+ PROGRAMMER_AST2400,
+#endif
#if CONFIG_DRKAISER == 1
PROGRAMMER_DRKAISER,
#endif
@@ -404,6 +411,18 @@ int gfxnvidia_init(void);
extern const struct dev_entry gfx_nvidia[];
#endif
+/* ast1100.c */
+#if CONFIG_AST1100 == 1
+int ast1100_init(void);
+extern const struct dev_entry bmc_aspeed_ast1100[];
+#endif
+
+/* ast2400.c */
+#if CONFIG_AST2400 == 1
+int ast2400_init(void);
+extern const struct dev_entry bmc_aspeed_ast2400[];
+#endif
+
/* drkaiser.c */
#if CONFIG_DRKAISER == 1
int drkaiser_init(void);
@@ -605,6 +624,14 @@ enum spi_controller {
#if CONFIG_CH341A_SPI == 1
SPI_CONTROLLER_CH341A_SPI,
#endif
+
+#if CONFIG_AST1100 == 1
+ SPI_CONTROLLER_AST1100,
+#endif
+
+#if CONFIG_AST2400 == 1
+ SPI_CONTROLLER_AST2400,
+#endif
};
#define MAX_DATA_UNSPECIFIED 0
diff --git ./spi.c ./spi.c
index c306ac3..817c8de 100644
--- ./spi.c
+++ ./spi.c
@@ -112,7 +126,10 @@ int spi_chip_read(struct flashctx *flash, uint8_t *buf, unsigned int start,
* means 0xffffff, the highest unsigned 24bit number.
*/
addrbase = spi_get_valid_read_addr(flash);
- if (addrbase + flash->chip->total_size * 1024 > (1 << 24)) {
+ /* Show flash chip size warning if flash chip doesn't support
+ 4-Bytes Addressing mode and last address excedes 24 bits */
+ if (!(flash->chip->feature_bits & FEATURE_4BA_SUPPORT) &&
+ addrbase + flash->chip->total_size * 1024 > (1 << 24)) {
msg_perr("Flash chip size exceeds the allowed access window. ");
msg_perr("Read will probably fail.\n");
/* Try to get the best alignment subject to constraints. */
diff --git ./spi25.c ./spi25.c
index 76242be..c324835 100644
--- ./spi25.c
+++ ./spi25.c
@@ -28,6 +28,7 @@
#include "chipdrivers.h"
#include "programmer.h"
#include "spi.h"
+#include "spi4ba.h"
static int spi_rdid(struct flashctx *flash, unsigned char *readarr, int bytes)
{
@@ -967,7 +968,11 @@ int spi_read_chunked(struct flashctx *flash, uint8_t *buf, unsigned int start,
lenhere = min(start + len, (i + 1) * area_size) - starthere;
for (j = 0; j < lenhere; j += chunksize) {
toread = min(chunksize, lenhere - j);
- rc = spi_nbyte_read(flash, starthere + j, buf + starthere - start + j, toread);
+ rc = (flash->chip->feature_bits & FEATURE_4BA_SUPPORT) == 0
+ ? spi_nbyte_read(flash, starthere + j, buf + starthere - start + j, toread)
+ : flash->chip->four_bytes_addr_funcs.read_nbyte(flash, starthere + j,
+ buf + starthere - start + j, toread);
+
if (rc)
break;
}
@@ -994,6 +999,16 @@ int spi_write_chunked(struct flashctx *flash, const uint8_t *buf, unsigned int s
* we're OK for now.
*/
unsigned int page_size = flash->chip->page_size;
+ int show_progress = 0;
+ unsigned int percent_last, percent_current;
+
+ /* progress visualizaion init */
+ if(len >= MIN_LENGTH_TO_SHOW_READ_PROGRESS) {
+ msg_cinfo(" "); /* only this space will go to logfile but all strings with \b wont. */
+ msg_cinfo("\b 0%%");
+ percent_last = percent_current = 0;
+ show_progress = 1; /* enable progress visualizaion */
+ }
/* Warning: This loop has a very unusual condition and body.
* The loop needs to go through each page with at least one affected
@@ -1012,7 +1027,10 @@ int spi_write_chunked(struct flashctx *flash, const uint8_t *buf, unsigned int s
lenhere = min(start + len, (i + 1) * page_size) - starthere;
for (j = 0; j < lenhere; j += chunksize) {
towrite = min(chunksize, lenhere - j);
- rc = spi_nbyte_program(flash, starthere + j, buf + starthere - start + j, towrite);
+ rc = (flash->chip->feature_bits & FEATURE_4BA_SUPPORT) == 0
+ ? spi_nbyte_program(flash, starthere + j, buf + starthere - start + j, towrite)
+ : flash->chip->four_bytes_addr_funcs.program_nbyte(flash, starthere + j,
+ buf + starthere - start + j, towrite);
if (rc)
break;
while (spi_read_status_register(flash) & SPI_SR_WIP)
@@ -1020,8 +1038,20 @@ int spi_write_chunked(struct flashctx *flash, const uint8_t *buf, unsigned int s
}
if (rc)
break;
+
+ if(show_progress) {
+ percent_current = (unsigned int) ((unsigned long long)(starthere +
+ lenhere - start) * 100 / len);
+ if(percent_current != percent_last) {
+ msg_cinfo("\b\b\b%2d%%", percent_current);
+ percent_last = percent_current;
+ }
+ }
}
+ if(show_progress && !rc)
+ msg_cinfo("\b\b\b\b"); /* remove progress percents from the screen */
+
return rc;
}
@@ -1038,7 +1068,9 @@ int spi_chip_write_1(struct flashctx *flash, const uint8_t *buf, unsigned int st
int result = 0;
for (i = start; i < start + len; i++) {
- result = spi_byte_program(flash, i, buf[i - start]);
+ result = (flash->chip->feature_bits & FEATURE_4BA_SUPPORT) == 0
+ ? spi_byte_program(flash, i, buf[i - start])
+ : flash->chip->four_bytes_addr_funcs.program_byte(flash, i, buf[i - start]);
if (result)
return 1;
while (spi_read_status_register(flash) & SPI_SR_WIP)
diff --git ./spi4ba.c ./spi4ba.c
new file mode 100644
index 0000000..6e1cc9b
--- /dev/null
+++ ./spi4ba.c
@@ -0,0 +1,920 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2014 Boris Baykov
+ *
+ * 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; version 2 of the License.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * SPI chip driver functions for 4-bytes addressing
+ */
+
+#include <string.h>
+#include "flash.h"
+#include "chipdrivers.h"
+#include "spi.h"
+#include "programmer.h"
+#include "spi4ba.h"
+
+/* #define MSG_TRACE_4BA_FUNCS 1 */
+
+#ifdef MSG_TRACE_4BA_FUNCS
+#define msg_trace(...) print(MSG_DEBUG, __VA_ARGS__)
+#else
+#define msg_trace(...)
+#endif
+
+/* Enter 4-bytes addressing mode (without sending WREN before) */
+int spi_enter_4ba_b7(struct flashctx *flash)
+{
+ const unsigned char cmd[JEDEC_ENTER_4_BYTE_ADDR_MODE_OUTSIZE] = { JEDEC_ENTER_4_BYTE_ADDR_MODE };
+
+ msg_trace("-> %s\n", __func__);
+
+ /* Switch to 4-bytes addressing mode */
+ return spi_send_command(flash, sizeof(cmd), 0, cmd, NULL);
+}
+
+/* Enter 4-bytes addressing mode with sending WREN before */
+int spi_enter_4ba_b7_we(struct flashctx *flash)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_ENTER_4_BYTE_ADDR_MODE_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_ENTER_4_BYTE_ADDR_MODE },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s\n", __func__);
+
+ /* Switch to 4-bytes addressing mode */
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution\n", __func__);
+ }
+ return result;
+}
+
+/* Program one flash byte from 4-bytes addressing mode */
+int spi_byte_program_4ba(struct flashctx *flash, unsigned int addr,
+ uint8_t databyte)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BYTE_PROGRAM_OUTSIZE + 1,
+ .writearr = (const unsigned char[]){
+ JEDEC_BYTE_PROGRAM,
+ (addr >> 24) & 0xff,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff),
+ databyte
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (0x%08X)\n", __func__, addr);
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ }
+ return result;
+}
+
+/* Program flash bytes from 4-bytes addressing mode */
+int spi_nbyte_program_4ba(struct flashctx *flash, unsigned int addr,
+ const uint8_t *bytes, unsigned int len)
+{
+ int result;
+ unsigned char cmd[(JEDEC_BYTE_PROGRAM_OUTSIZE + 1) - 1 + 256] = {
+ JEDEC_BYTE_PROGRAM,
+ (addr >> 24) & 0xff,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr >> 0) & 0xff
+ };
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = (JEDEC_BYTE_PROGRAM_OUTSIZE + 1) - 1 + len,
+ .writearr = cmd,
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + len - 1);
+
+ if (!len) {
+ msg_cerr("%s called for zero-length write\n", __func__);
+ return 1;
+ }
+ if (len > 256) {
+ msg_cerr("%s called for too long a write\n", __func__);
+ return 1;
+ }
+
+ memcpy(&cmd[(JEDEC_BYTE_PROGRAM_OUTSIZE + 1) - 1], bytes, len);
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ }
+ return result;
+}
+
+/* Read flash bytes from 4-bytes addressing mode */
+int spi_nbyte_read_4ba(struct flashctx *flash, unsigned int addr,
+ uint8_t *bytes, unsigned int len)
+{
+ const unsigned char cmd[JEDEC_READ_OUTSIZE + 1] = {
+ JEDEC_READ,
+ (addr >> 24) & 0xff,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr >> 0) & 0xff
+ };
+
+ msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + len - 1);
+
+ /* Send Read */
+ return spi_send_command(flash, sizeof(cmd), len, cmd, bytes);
+}
+
+/* Erases 4 KB of flash from 4-bytes addressing mode */
+int spi_block_erase_20_4ba(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_SE_OUTSIZE + 1,
+ .writearr = (const unsigned char[]){
+ JEDEC_SE,
+ (addr >> 24) & 0xff,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1);
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 15-800 ms, so wait in 10 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(10 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+/* Erases 32 KB of flash from 4-bytes addressing mode */
+int spi_block_erase_52_4ba(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BE_52_OUTSIZE + 1,
+ .writearr = (const unsigned char[]){
+ JEDEC_BE_52,
+ (addr >> 24) & 0xff,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1);
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 100-4000 ms, so wait in 100 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(100 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+/* Erases 64 KB of flash from 4-bytes addressing mode */
+int spi_block_erase_d8_4ba(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BE_D8_OUTSIZE + 1,
+ .writearr = (const unsigned char[]){
+ JEDEC_BE_D8,
+ (addr >> 24) & 0xff,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1);
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 100-4000 ms, so wait in 100 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(100 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+/* Write Extended Address Register value */
+int spi_write_extended_address_register(struct flashctx *flash, uint8_t regdata)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_WRITE_EXT_ADDR_REG_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_WRITE_EXT_ADDR_REG,
+ regdata
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (%02X)\n", __func__, regdata);
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution\n", __func__);
+ return result;
+ }
+ return 0;
+}
+
+/* Assign required value of Extended Address Register. This function
+ keeps last value of the register and writes the register if the
+ value has to be changed only. */
+int set_extended_address_register(struct flashctx *flash, uint8_t data)
+{
+ static uint8_t ext_addr_reg_state; /* memory for last register state */
+ static int ext_addr_reg_state_valid = 0;
+ int result;
+
+ if (ext_addr_reg_state_valid == 0 || data != ext_addr_reg_state) {
+ result = spi_write_extended_address_register(flash, data);
+ if (result) {
+ ext_addr_reg_state_valid = 0;
+ return result;
+ }
+ ext_addr_reg_state = data;
+ ext_addr_reg_state_valid = 1;
+ }
+ return 0;
+}
+
+/* Program one flash byte using Extended Address Register
+ from 3-bytes addressing mode */
+int spi_byte_program_4ba_ereg(struct flashctx *flash, unsigned int addr,
+ uint8_t databyte)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BYTE_PROGRAM_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_BYTE_PROGRAM,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff),
+ databyte
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (0x%08X)\n", __func__, addr);
+
+ result = set_extended_address_register(flash, (addr >> 24) & 0xff);
+ if (result)
+ return result;
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ }
+ return result;
+}
+
+/* Program flash bytes using Extended Address Register
+ from 3-bytes addressing mode */
+int spi_nbyte_program_4ba_ereg(struct flashctx *flash, unsigned int addr,
+ const uint8_t *bytes, unsigned int len)
+{
+ int result;
+ unsigned char cmd[JEDEC_BYTE_PROGRAM_OUTSIZE - 1 + 256] = {
+ JEDEC_BYTE_PROGRAM,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr >> 0) & 0xff
+ };
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BYTE_PROGRAM_OUTSIZE - 1 + len,
+ .writearr = cmd,
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + len - 1);
+
+ if (!len) {
+ msg_cerr("%s called for zero-length write\n", __func__);
+ return 1;
+ }
+ if (len > 256) {
+ msg_cerr("%s called for too long a write\n", __func__);
+ return 1;
+ }
+
+ memcpy(&cmd[JEDEC_BYTE_PROGRAM_OUTSIZE - 1], bytes, len);
+
+ result = set_extended_address_register(flash, (addr >> 24) & 0xff);
+ if (result)
+ return result;
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ }
+ return result;
+}
+
+/* Read flash bytes using Extended Address Register
+ from 3-bytes addressing mode */
+int spi_nbyte_read_4ba_ereg(struct flashctx *flash, unsigned int addr,
+ uint8_t *bytes, unsigned int len)
+{
+ int result;
+ const unsigned char cmd[JEDEC_READ_OUTSIZE] = {
+ JEDEC_READ,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr >> 0) & 0xff
+ };
+
+ msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + len - 1);
+
+ result = set_extended_address_register(flash, (addr >> 24) & 0xff);
+ if (result)
+ return result;
+
+ /* Send Read */
+ return spi_send_command(flash, sizeof(cmd), len, cmd, bytes);
+}
+
+/* Erases 4 KB of flash using Extended Address Register
+ from 3-bytes addressing mode */
+int spi_block_erase_20_4ba_ereg(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_SE_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_SE,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1);
+
+ result = set_extended_address_register(flash, (addr >> 24) & 0xff);
+ if (result)
+ return result;
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 15-800 ms, so wait in 10 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(10 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+/* Erases 32 KB of flash using Extended Address Register
+ from 3-bytes addressing mode */
+int spi_block_erase_52_4ba_ereg(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BE_52_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_BE_52,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1);
+
+ result = set_extended_address_register(flash, (addr >> 24) & 0xff);
+ if (result)
+ return result;
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 100-4000 ms, so wait in 100 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(100 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+/* Erases 64 KB of flash using Extended Address Register
+ from 3-bytes addressing mode */
+int spi_block_erase_d8_4ba_ereg(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BE_D8_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_BE_D8,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1);
+
+ result = set_extended_address_register(flash, (addr >> 24) & 0xff);
+ if (result)
+ return result;
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 100-4000 ms, so wait in 100 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(100 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+/* Program one flash byte with 4-bytes address from ANY mode (3-bytes or 4-bytes)
+ JEDEC_BYTE_PROGRAM_4BA (12h) instruction is new for 4-bytes addressing flash chips.
+ The presence of this instruction for an exact chip should be checked
+ by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */
+int spi_byte_program_4ba_direct(struct flashctx *flash, unsigned int addr,
+ uint8_t databyte)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BYTE_PROGRAM_4BA_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_BYTE_PROGRAM_4BA,
+ (addr >> 24) & 0xff,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff),
+ databyte
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (0x%08X)\n", __func__, addr);
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ }
+ return result;
+}
+
+/* Program flash bytes with 4-bytes address from ANY mode (3-bytes or 4-bytes)
+ JEDEC_BYTE_PROGRAM_4BA (12h) instruction is new for 4-bytes addressing flash chips.
+ The presence of this instruction for an exact chip should be checked
+ by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */
+int spi_nbyte_program_4ba_direct(struct flashctx *flash, unsigned int addr,
+ const uint8_t *bytes, unsigned int len)
+{
+ int result;
+ unsigned char cmd[JEDEC_BYTE_PROGRAM_4BA_OUTSIZE - 1 + 256] = {
+ JEDEC_BYTE_PROGRAM_4BA,
+ (addr >> 24) & 0xff,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr >> 0) & 0xff
+ };
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BYTE_PROGRAM_4BA_OUTSIZE - 1 + len,
+ .writearr = cmd,
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + len - 1);
+
+ if (!len) {
+ msg_cerr("%s called for zero-length write\n", __func__);
+ return 1;
+ }
+ if (len > 256) {
+ msg_cerr("%s called for too long a write\n", __func__);
+ return 1;
+ }
+
+ memcpy(&cmd[JEDEC_BYTE_PROGRAM_4BA_OUTSIZE - 1], bytes, len);
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ }
+ return result;
+}
+
+/* Read flash bytes with 4-bytes address from ANY mode (3-bytes or 4-bytes)
+ JEDEC_READ_4BA (13h) instruction is new for 4-bytes addressing flash chips.
+ The presence of this instruction for an exact chip should be checked
+ by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */
+int spi_nbyte_read_4ba_direct(struct flashctx *flash, unsigned int addr,
+ uint8_t *bytes, unsigned int len)
+{
+ const unsigned char cmd[JEDEC_READ_4BA_OUTSIZE] = {
+ JEDEC_READ_4BA,
+ (addr >> 24) & 0xff,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr >> 0) & 0xff
+ };
+
+ msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + len - 1);
+
+ /* Send Read */
+ return spi_send_command(flash, sizeof(cmd), len, cmd, bytes);
+}
+
+/* Erase 4 KB of flash with 4-bytes address from ANY mode (3-bytes or 4-bytes)
+ JEDEC_SE_4BA (21h) instruction is new for 4-bytes addressing flash chips.
+ The presence of this instruction for an exact chip should be checked
+ by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */
+int spi_block_erase_21_4ba_direct(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_SE_4BA_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_SE_4BA,
+ (addr >> 24) & 0xff,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1);
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 15-800 ms, so wait in 10 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(10 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+/* Erase 32 KB of flash with 4-bytes address from ANY mode (3-bytes or 4-bytes)
+ JEDEC_BE_5C_4BA (5Ch) instruction is new for 4-bytes addressing flash chips.
+ The presence of this instruction for an exact chip should be checked
+ by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */
+int spi_block_erase_5c_4ba_direct(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BE_5C_4BA_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_BE_5C_4BA,
+ (addr >> 24) & 0xff,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1);
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 100-4000 ms, so wait in 100 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(100 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+/* Erase 64 KB of flash with 4-bytes address from ANY mode (3-bytes or 4-bytes)
+ JEDEC_BE_DC_4BA (DCh) instruction is new for 4-bytes addressing flash chips.
+ The presence of this instruction for an exact chip should be checked
+ by its datasheet or from SFDP 4-Bytes Address Instruction Table (JESD216B). */
+int spi_block_erase_dc_4ba_direct(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BE_DC_4BA_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_BE_DC_4BA,
+ (addr >> 24) & 0xff,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ msg_trace("-> %s (0x%08X-0x%08X)\n", __func__, addr, addr + blocklen - 1);
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 100-4000 ms, so wait in 100 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(100 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
diff --git ./spi4ba.h ./spi4ba.h
new file mode 100644
index 0000000..8e500d1
--- /dev/null
+++ ./spi4ba.h
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2014 Boris Baykov
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/*
+ * JEDEC flash chips instructions for 4-bytes addressing
+ * SPI chip driver functions for 4-bytes addressing
+ */
+
+#ifndef __SPI_4BA_H__
+#define __SPI_4BA_H__ 1
+
+/* Enter 4-byte Address Mode */
+#define JEDEC_ENTER_4_BYTE_ADDR_MODE 0xB7
+#define JEDEC_ENTER_4_BYTE_ADDR_MODE_OUTSIZE 0x01
+#define JEDEC_ENTER_4_BYTE_ADDR_MODE_INSIZE 0x00
+
+/* Exit 4-byte Address Mode */
+#define JEDEC_EXIT_4_BYTE_ADDR_MODE 0xE9
+#define JEDEC_EXIT_4_BYTE_ADDR_MODE_OUTSIZE 0x01
+#define JEDEC_EXIT_4_BYTE_ADDR_MODE_INSIZE 0x00
+
+/* Write Extended Address Register */
+#define JEDEC_WRITE_EXT_ADDR_REG 0xC5
+#define JEDEC_WRITE_EXT_ADDR_REG_OUTSIZE 0x02
+#define JEDEC_WRITE_EXT_ADDR_REG_INSIZE 0x00
+
+/* Read Extended Address Register */
+#define JEDEC_READ_EXT_ADDR_REG 0xC8
+#define JEDEC_READ_EXT_ADDR_REG_OUTSIZE 0x01
+#define JEDEC_READ_EXT_ADDR_REG_INSIZE 0x01
+
+/* Read the memory with 4-byte address
+ From ANY mode (3-bytes or 4-bytes) it works with 4-byte address */
+#define JEDEC_READ_4BA 0x13
+#define JEDEC_READ_4BA_OUTSIZE 0x05
+/* JEDEC_READ_4BA_INSIZE : any length */
+
+/* Write memory byte with 4-byte address
+ From ANY mode (3-bytes or 4-bytes) it works with 4-byte address */
+#define JEDEC_BYTE_PROGRAM_4BA 0x12
+#define JEDEC_BYTE_PROGRAM_4BA_OUTSIZE 0x06
+#define JEDEC_BYTE_PROGRAM_4BA_INSIZE 0x00
+
+/* Sector Erase 0x21 (with 4-byte address), usually 4k size.
+ From ANY mode (3-bytes or 4-bytes) it works with 4-byte address */
+#define JEDEC_SE_4BA 0x21
+#define JEDEC_SE_4BA_OUTSIZE 0x05
+#define JEDEC_SE_4BA_INSIZE 0x00
+
+/* Block Erase 0x5C (with 4-byte address), usually 32k size.
+ From ANY mode (3-bytes or 4-bytes) it works with 4-byte address */
+#define JEDEC_BE_5C_4BA 0x5C
+#define JEDEC_BE_5C_4BA_OUTSIZE 0x05
+#define JEDEC_BE_5C_4BA_INSIZE 0x00
+
+/* Block Erase 0xDC (with 4-byte address), usually 64k size.
+ From ANY mode (3-bytes or 4-bytes) it works with 4-byte address */
+#define JEDEC_BE_DC_4BA 0xdc
+#define JEDEC_BE_DC_4BA_OUTSIZE 0x05
+#define JEDEC_BE_DC_4BA_INSIZE 0x00
+
+/* enter 4-bytes addressing mode */
+int spi_enter_4ba_b7(struct flashctx *flash);
+int spi_enter_4ba_b7_we(struct flashctx *flash);
+
+/* read/write flash bytes in 4-bytes addressing mode */
+int spi_byte_program_4ba(struct flashctx *flash, unsigned int addr, uint8_t databyte);
+int spi_nbyte_program_4ba(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len);
+int spi_nbyte_read_4ba(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len);
+
+/* erase flash bytes in 4-bytes addressing mode */
+int spi_block_erase_20_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_52_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_d8_4ba(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+
+/* read/write flash bytes from 3-bytes addressing mode using extended address register */
+int spi_byte_program_4ba_ereg(struct flashctx *flash, unsigned int addr, uint8_t databyte);
+int spi_nbyte_program_4ba_ereg(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len);
+int spi_nbyte_read_4ba_ereg(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len);
+
+/* erase flash bytes from 3-bytes addressing mode using extended address register */
+int spi_block_erase_20_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_52_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_d8_4ba_ereg(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+
+/* read/write flash bytes with 4-bytes address from any mode (3-byte or 4-byte) */
+int spi_byte_program_4ba_direct(struct flashctx *flash, unsigned int addr, uint8_t databyte);
+int spi_nbyte_program_4ba_direct(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len);
+int spi_nbyte_read_4ba_direct(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len);
+
+/* erase flash bytes with 4-bytes address from any mode (3-byte or 4-byte) */
+int spi_block_erase_21_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_5c_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_dc_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+
+
+#endif /* __SPI_4BA_H__ */