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 +#include +#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 +#include +#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 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 * 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 +#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__ */