Signed-off-by: Amit Uttamchandani amit.uttam@gmail.com --- programmer.h | 1 + satamv.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+)
diff --git a/programmer.h b/programmer.h index d4d1f90..0259022 100644 --- a/programmer.h +++ b/programmer.h @@ -527,6 +527,7 @@ enum spi_controller { #if CONFIG_USBBLASTER_SPI == 1 SPI_CONTROLLER_USBBLASTER, #endif + SPI_CONTROLLER_SATAMV, };
#define MAX_DATA_UNSPECIFIED 0 diff --git a/satamv.c b/satamv.c index 3065f0c..16787b8 100644 --- a/satamv.c +++ b/satamv.c @@ -22,9 +22,15 @@ #if defined(__i386__) || defined(__x86_64__)
#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> #include "flash.h" #include "programmer.h" +#include "chipdrivers.h" #include "hwaccess.h" +#include "spi.h" +#include <unistd.h>
uint8_t *mv_bar; uint16_t mv_iobar; @@ -32,20 +38,46 @@ uint16_t mv_iobar; const struct dev_entry satas_mv[] = { /* 88SX6041 and 88SX6042 are the same according to the datasheet. */ {0x11ab, 0x7042, OK, "Marvell", "88SX7042 PCI-e 4-port SATA-II"}, + {0x1b4b, 0x9445, OK, "Marvell", "88SE9445 PCI-e 4-port SAS/SATA 6 Gbps"},
{0}, };
#define NVRAM_PARAM 0x1045c #define FLASH_PARAM 0x1046c +#define EXPANSION_ROM_WINDOW0_REMAP 0x004c8 +#define SPI_FLASH_CONTROL 0x0c800 +#define SPI_FLASH_ADDRESS 0x0c804 +#define SPI_FLASH_WRITE_DATA 0x0c808 +#define SPI_FLASH_READ_DATA 0x0c80c #define EXPANSION_ROM_BAR_CONTROL 0x00d2c #define PCI_BAR2_CONTROL 0x00c08 #define GPIO_PORT_CONTROL 0x104f0
+static unsigned int spi_write_256_chunksize = 256; + +static int satamv_spi_send_command(struct flashctx *flash, unsigned int writecnt, + unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr); +static int satamv_spi_write_256(struct flashctx *flash, uint8_t *buf, + unsigned int start, unsigned int len); static void satamv_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr); static uint8_t satamv_chip_readb(const struct flashctx *flash, const chipaddr addr); + +static const struct spi_programmer spi_programmer_satamv = { + .type = SPI_CONTROLLER_SATAMV, + .max_data_read = MAX_DATA_READ_UNLIMITED, + .max_data_write = MAX_DATA_UNSPECIFIED, + .command = satamv_spi_send_command, + .multicommand = default_spi_send_multicommand, + .read = default_spi_read, + .write_256 = satamv_spi_write_256, + .write_aai = default_spi_write_aai, +}; + static const struct par_programmer par_programmer_satamv = { .chip_readb = satamv_chip_readb, .chip_readw = fallback_chip_readw, @@ -57,6 +89,44 @@ static const struct par_programmer par_programmer_satamv = { .chip_writen = fallback_chip_writen, };
+static int init_94xx(struct pci_dev *dev) +{ + uintptr_t addr; + uint32_t tmp; + + msg_pspew("Initializing 94xx\n"); + + addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_2); + if (!addr) + return 1; + msg_pspew("addr=0x%08" PRIXPTR "\n", addr); + + mv_bar = rphysmap("Marvell 88SE94xx registers", addr, 0x40000); + if (mv_bar == ERROR_PTR) + return 1; + msg_pspew("mv_bar=%p\n", mv_bar); + + /* Read some registers for fun. + * TODO: Remove once everything is working + */ + tmp = pci_mmio_readl(mv_bar + EXPANSION_ROM_WINDOW0_REMAP); + msg_pspew("PCIe Expansion ROM Window Remap: 0x%04x\n", tmp); + + tmp = pci_mmio_readl(mv_bar + SPI_FLASH_CONTROL); + msg_pspew("SPI Flash Control: 0x%08x\n", tmp); + + tmp = pci_mmio_readl(mv_bar + SPI_FLASH_ADDRESS); + msg_pspew("SPI Flash Address: 0x%08x\n", tmp); + + tmp = pci_mmio_readl(mv_bar + SPI_FLASH_READ_DATA); + msg_pspew("SPI Flash Read Data: 0x%08x\n", tmp); + + /* Register SPI programmer */ + register_spi_programmer(&spi_programmer_satamv); + + return 0; +} + /* * Random notes: * FCE# Flash Chip Enable @@ -78,6 +148,7 @@ int satamv_init(void) struct pci_dev *dev = NULL; uintptr_t addr; uint32_t tmp; + uint16_t id;
if (rget_io_perms()) return 1; @@ -87,6 +158,12 @@ int satamv_init(void) if (!dev) return 1;
+ id = dev->device_id; + + /* If 9445, do separate init */ + if (id == 0x9445) + return init_94xx(dev); + addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0); if (!addr) return 1; @@ -192,6 +269,80 @@ static uint8_t satamv_chip_readb(const struct flashctx *flash, return satamv_indirect_chip_readb(addr); }
+static int satamv_spi_send_command(struct flashctx *flash, unsigned int writecnt, + unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr) +{ + uint8_t busy; + int i; + + msg_pspew("%s:", __func__); + msg_pspew(" SPI Flash Control Start: 0x%04x", pci_mmio_readl(mv_bar + SPI_FLASH_CONTROL)); + msg_pspew(" writecnt=%d, readcnt=%d\n", writecnt, readcnt); + + for (i = 0; i < writecnt; i++) + msg_pspew("writearr[%d]=0x%x\n", i, writearr[i]); + + do { + busy = pci_mmio_readl(mv_bar + SPI_FLASH_CONTROL); + msg_pspew("SPI Flash Control during busy wait (while loop 1): 0x%04x\n", busy); + busy &= 0x1; + } while (busy); + + if (readcnt > 4) { + msg_pinfo("%s called with unsupported readcnt %i.\n", + __func__, readcnt); + return SPI_INVALID_LENGTH; + } + + /* Write FFs to WRITE_DATA and READ_DATA */ + pci_mmio_writel(0xffffffff, mv_bar + SPI_FLASH_WRITE_DATA); + pci_mmio_writel(0xffffffff, mv_bar + SPI_FLASH_READ_DATA); + + switch (writecnt) { + case 1: + pci_mmio_writel((0x00000005 | (writearr[0]<<8) | (readcnt<<4)), mv_bar + SPI_FLASH_CONTROL); + break; + case 2: + break; + case 4: + break; + case 5: + break; + default: + msg_pinfo("%s called with unsupported writecnt %i.\n", + __func__, writecnt); + return SPI_INVALID_LENGTH; + } + + if (readcnt > 0) { + do { + busy = pci_mmio_readl(mv_bar + SPI_FLASH_CONTROL); + msg_pspew("SPI Flash Control during busy wait (while loop 2): 0x%04x\n", busy); + busy &= 0x1; + } while (busy); + + /* Read from SPI Flash */ + pci_mmio_writel((0x00000005 | (readcnt<<4)), mv_bar + SPI_FLASH_CONTROL); + msg_pspew("SPI Return: 0x%04x\n", pci_mmio_readl(mv_bar + SPI_FLASH_READ_DATA)); + + for (i = 0; i < readcnt; i++) + readarr[i] = pci_mmio_readl(mv_bar + SPI_FLASH_READ_DATA); + } + + msg_pspew("SPI Flash Control End: 0x%04x\n", pci_mmio_readl(mv_bar + SPI_FLASH_CONTROL)); + + return 0; +} + +static int satamv_spi_write_256(struct flashctx *flash, uint8_t *buf, + unsigned int start, unsigned int len) +{ + return spi_write_chunked(flash, buf, start, len, + spi_write_256_chunksize); +} + #else #error PCI port I/O access is not supported on this architecture yet. #endif
Hi Amit,
thanks for your patch! I have rewritten the spi_send_command part almost completely to handle all the quirks of the chipset. Rumor has it (well, the Linux kernel source implies it) that addresses are limited to a certain size. That needs to be checked and (if it's true) addressed in the flashrom source as well. spi_write_256_chunksize has been adjusted to 4 (maximum supported by the hardware) instead of 256.
Am 26.04.2014 06:54 schrieb Amit Uttamchandani:
Signed-off-by: Amit Uttamchandani amit.uttam@gmail.com
Add SPI support for Marvell 88SE9445 The same code should work for all Marvell 88S?94?? chips.
Untested, might even work. Do not test writing yet! I'd be happy if it survives probe and read.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-satamv_spi_mv94xx/satamv.c =================================================================== --- flashrom-satamv_spi_mv94xx/satamv.c (Revision 1780) +++ flashrom-satamv_spi_mv94xx/satamv.c (Arbeitskopie) @@ -1,8 +1,8 @@ /* * This file is part of the flashrom project. * - * Copyright (C) 2010,2011 Carl-Daniel Hailfinger - * Written by Carl-Daniel Hailfinger for Angelbird Ltd. + * Copyright (C) 2010,2011,2014 Carl-Daniel Hailfinger + * Parts written by Carl-Daniel Hailfinger 2010, 2011 for Angelbird Ltd. * * 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 @@ -22,9 +22,15 @@ #if defined(__i386__) || defined(__x86_64__)
#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> #include "flash.h" #include "programmer.h" +#include "chipdrivers.h" #include "hwaccess.h" +#include "spi.h" +#include <unistd.h>
uint8_t *mv_bar; uint16_t mv_iobar; @@ -32,20 +38,47 @@ const struct dev_entry satas_mv[] = { /* 88SX6041 and 88SX6042 are the same according to the datasheet. */ {0x11ab, 0x7042, OK, "Marvell", "88SX7042 PCI-e 4-port SATA-II"}, + {0x1b4b, 0x9445, OK, "Marvell", "88SE9445 PCI-e 4-port SAS/SATA 6 Gbps"},
{0}, };
#define NVRAM_PARAM 0x1045c #define FLASH_PARAM 0x1046c +#define EXPANSION_ROM_WINDOW0_REMAP 0x004c8 +#define MV94XX_SPI_CONTROL 0x0c800 +#define MV94XX_SPI_ADDRESS 0x0c804 +#define MV94XX_SPI_WRITE_DATA 0x0c808 +#define MV94XX_SPI_READ_DATA 0x0c80c #define EXPANSION_ROM_BAR_CONTROL 0x00d2c #define PCI_BAR2_CONTROL 0x00c08 #define GPIO_PORT_CONTROL 0x104f0
+#define MV94XX_SPI_DATA_DIR_READ (1 << 2) +#define MV94XX_SPI_ADDR_PHASE (1 << 1) +#define MV94XX_SPI_START (1 << 0) + +static unsigned int spi_write_256_chunksize = 4; + +static int satamv_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr); +static int satamv_spi_write_256(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len); static void satamv_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr); static uint8_t satamv_chip_readb(const struct flashctx *flash, const chipaddr addr); + +static const struct spi_programmer spi_programmer_satamv = { + .type = SPI_CONTROLLER_SATAMV, + .max_data_read = MAX_DATA_READ_UNLIMITED, + .max_data_write = MAX_DATA_UNSPECIFIED, + .command = satamv_spi_send_command, + .multicommand = default_spi_send_multicommand, + .read = default_spi_read, + .write_256 = satamv_spi_write_256, + .write_aai = default_spi_write_aai, +}; + static const struct par_programmer par_programmer_satamv = { .chip_readb = satamv_chip_readb, .chip_readw = fallback_chip_readw, @@ -57,6 +90,44 @@ .chip_writen = fallback_chip_writen, };
+static int init_94xx(struct pci_dev *dev) +{ + uintptr_t addr; + uint32_t tmp; + + msg_pspew("Initializing 94xx\n"); + + addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_2); + if (!addr) + return 1; + msg_pspew("addr=0x%08" PRIXPTR "\n", addr); + + mv_bar = rphysmap("Marvell 88SE94xx registers", addr, 0x40000); + if (mv_bar == ERROR_PTR) + return 1; + msg_pspew("mv_bar=%p\n", mv_bar); + + /* Read some registers for fun. + * TODO: Remove once everything is working + */ + tmp = pci_mmio_readl(mv_bar + EXPANSION_ROM_WINDOW0_REMAP); + msg_pspew("PCIe Expansion ROM Window Remap: 0x%04x\n", tmp); + + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_CONTROL); + msg_pspew("SPI Flash Control: 0x%08x\n", tmp); + + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_ADDRESS); + msg_pspew("SPI Flash Address: 0x%08x\n", tmp); + + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_READ_DATA); + msg_pspew("SPI Flash Read Data: 0x%08x\n", tmp); + + /* Register SPI programmer */ + register_spi_programmer(&spi_programmer_satamv); + + return 0; +} + /* * Random notes: * FCE# Flash Chip Enable @@ -78,6 +149,7 @@ struct pci_dev *dev = NULL; uintptr_t addr; uint32_t tmp; + uint16_t id;
if (rget_io_perms()) return 1; @@ -87,6 +159,12 @@ if (!dev) return 1;
+ id = dev->device_id; + + /* If 9445, do separate init */ + if (id == 0x9445) + return init_94xx(dev); + addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0); if (!addr) return 1; @@ -192,6 +270,125 @@ return satamv_indirect_chip_readb(addr); }
+static int satamv_spi_send_command(struct flashctx *flash, unsigned int writecnt, + unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr) +{ + uint32_t tmp; + uint32_t spi_cntl = 0x0; + int i; + + if (writecnt == 0) { + msg_perr("%s: Tried to send an empty SPI command!\n" + "Please report a bug at flashrom@flashrom.org\n", __func__); + return ERROR_FLASHROM_BUG; + } + if (!(((readcnt == 0) && (writecnt <= 8)) || (((writecnt == 1) || (writecnt == 4)) && (readcnt <= 4)))) { + msg_pinfo("%s called with unsupported readcnt(%i)/writecnt(%i) combination.\n", __func__, + readcnt, writecnt); + return SPI_INVALID_LENGTH; + } + + msg_pspew("%s:", __func__); + msg_pspew(" SPI Flash Control Start: 0x%08x", pci_mmio_readl(mv_bar + MV94XX_SPI_CONTROL)); + msg_pspew(" writecnt=%d, readcnt=%d\n", writecnt, readcnt); + + for (i = 0; i < writecnt; i++) + msg_pspew("writearr[%d]=0x%x\n", i, writearr[i]); + + do { + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_CONTROL); + msg_pspew("SPI Flash Control during busy wait (while loop 1): 0x%08x\n", tmp); + tmp &= 0x1; + } while (tmp); + + + /* Clear WRITE_DATA and READ_DATA. Unneeded, but a great help for debugging. */ + pci_mmio_writel(0xffffffff, mv_bar + MV94XX_SPI_WRITE_DATA); + pci_mmio_writel(0xffffffff, mv_bar + MV94XX_SPI_READ_DATA); + + if (readcnt > 0) { + switch (writecnt) { + case 4: + spi_cntl |= MV94XX_SPI_ADDR_PHASE; + pci_mmio_writel((writearr[1] << 16) | (writearr[2] << 8) | writearr[3], + mv_bar + MV94XX_SPI_ADDRESS); + case 1: + break; + } + spi_cntl |= MV94XX_SPI_DATA_DIR_READ; + spi_cntl |= readcnt << 4; + spi_cntl |= writearr[0] << 8; + } else { + uint32_t wr_data = 0xffffffff; + switch (writecnt) { + case 8: + wr_data = (writearr[7] << 24) | (writearr[6] << 16) | (writearr[5] << 8) | writearr[4]; + break; + case 7: + wr_data = (writearr[6] << 16) | (writearr[5] << 8) | writearr[4]; + break; + case 6: + wr_data = (writearr[5] << 8) | writearr[4]; + break; + case 5: + wr_data = (writearr[4] << 24) | (writearr[3] << 16) | (writearr[2] << 8) | writearr[1]; + break; + case 4: + wr_data = (writearr[3] << 16) | (writearr[2] << 8) | writearr[1]; + break; + case 3: + wr_data = (writearr[2] << 8) | writearr[1]; + break; + case 2: + wr_data = writearr[1]; + break; + case 1: + /* Only a command, no data. */ + break; + } + if (writecnt >= 6) { + /* Have to use the address field. */ + spi_cntl |= MV94XX_SPI_ADDR_PHASE; + pci_mmio_writel((writearr[1] << 16) | (writearr[2] << 8) | writearr[3], + mv_bar + MV94XX_SPI_ADDRESS); + spi_cntl |= (writecnt - 3) << 4; + } else { + spi_cntl |= writecnt << 4; + } + pci_mmio_writel(wr_data, mv_bar + MV94XX_SPI_WRITE_DATA); + spi_cntl |= writearr[0] << 8; + } + + pci_mmio_writel(spi_cntl, mv_bar + MV94XX_SPI_CONTROL); + pci_mmio_writel(spi_cntl | MV94XX_SPI_START, mv_bar + MV94XX_SPI_CONTROL); + + do { + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_CONTROL); + msg_pspew("SPI Flash Control during busy wait (while loop 1): 0x%08x\n", tmp); + tmp &= 0x1; + } while (tmp); + + + if (readcnt > 0) { + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_READ_DATA); + for (i = 0; i < readcnt; i++) + readarr[i] = (tmp >> (i * 8)) & 0xff; + } + + msg_pspew("SPI Flash Control End: 0x%08x\n", pci_mmio_readl(mv_bar + MV94XX_SPI_CONTROL)); + + return 0; +} + +static int satamv_spi_write_256(struct flashctx *flash, uint8_t *buf, + unsigned int start, unsigned int len) +{ + return spi_write_chunked(flash, buf, start, len, + spi_write_256_chunksize); +} + #else #error PCI port I/O access is not supported on this architecture yet. #endif Index: flashrom-satamv_spi_mv94xx/programmer.h =================================================================== --- flashrom-satamv_spi_mv94xx/programmer.h (Revision 1780) +++ flashrom-satamv_spi_mv94xx/programmer.h (Arbeitskopie) @@ -527,6 +527,7 @@ #if CONFIG_USBBLASTER_SPI == 1 SPI_CONTROLLER_USBBLASTER, #endif + SPI_CONTROLLER_SATAMV, };
#define MAX_DATA_UNSPECIFIED 0
Regards, Carl-Daniel
On Sun, 27 Apr 2014 02:14:24 +0200 Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net wrote:
Index: flashrom-satamv_spi_mv94xx/programmer.h
--- flashrom-satamv_spi_mv94xx/programmer.h (Revision 1780) +++ flashrom-satamv_spi_mv94xx/programmer.h (Arbeitskopie) @@ -527,6 +527,7 @@ #if CONFIG_USBBLASTER_SPI == 1 SPI_CONTROLLER_USBBLASTER, #endif
- SPI_CONTROLLER_SATAMV,
};
Needs to be guarded too IMHO
Am 27.04.2014 05:22 schrieb Stefan Tauner:
On Sun, 27 Apr 2014 02:14:24 +0200 Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net wrote:
Index: flashrom-satamv_spi_mv94xx/programmer.h
--- flashrom-satamv_spi_mv94xx/programmer.h (Revision 1780) +++ flashrom-satamv_spi_mv94xx/programmer.h (Arbeitskopie) @@ -527,6 +527,7 @@ #if CONFIG_USBBLASTER_SPI == 1 SPI_CONTROLLER_USBBLASTER, #endif
- SPI_CONTROLLER_SATAMV,
};
Needs to be guarded too IMHO
Definitely, thanks for catching this. The arch dependencies on i386/x86_64 are broken as well, but I don't have a quick fix for this because one half of the driver has the dependency, the other half doesn't.
Amit confirmed that probe works: "Found Winbond flash chip "W25X40" (512 kB, SPI) on satamv." However, reading failed due to incorrect limits in max_data_read and max_data_write.
New version, read should work fine. Write might work, but I'd rather wait a bit for that unless we know the image is correct and the flash chip can be accessed easily with another external programmer.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-satamv_spi_mv94xx/satamv.c =================================================================== --- flashrom-satamv_spi_mv94xx/satamv.c (Revision 1780) +++ flashrom-satamv_spi_mv94xx/satamv.c (Arbeitskopie) @@ -1,8 +1,8 @@ /* * This file is part of the flashrom project. * - * Copyright (C) 2010,2011 Carl-Daniel Hailfinger - * Written by Carl-Daniel Hailfinger for Angelbird Ltd. + * Copyright (C) 2010,2011,2014 Carl-Daniel Hailfinger + * Parts written by Carl-Daniel Hailfinger 2010, 2011 for Angelbird Ltd. * * 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 @@ -19,12 +19,20 @@ */
/* Datasheets are not public (yet?) */ + +// FIXME: This arch limitation is only valid for the 88SX60 series due to I/O port access #if defined(__i386__) || defined(__x86_64__)
#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> #include "flash.h" #include "programmer.h" +#include "chipdrivers.h" #include "hwaccess.h" +#include "spi.h" +#include <unistd.h>
uint8_t *mv_bar; uint16_t mv_iobar; @@ -32,20 +40,44 @@ const struct dev_entry satas_mv[] = { /* 88SX6041 and 88SX6042 are the same according to the datasheet. */ {0x11ab, 0x7042, OK, "Marvell", "88SX7042 PCI-e 4-port SATA-II"}, + {0x1b4b, 0x9445, OK, "Marvell", "88SE9445 PCI-e 4-port SAS/SATA 6 Gbps"},
{0}, };
#define NVRAM_PARAM 0x1045c #define FLASH_PARAM 0x1046c +#define EXPANSION_ROM_WINDOW0_REMAP 0x004c8 +#define MV94XX_SPI_CONTROL 0x0c800 +#define MV94XX_SPI_ADDRESS 0x0c804 +#define MV94XX_SPI_WRITE_DATA 0x0c808 +#define MV94XX_SPI_READ_DATA 0x0c80c #define EXPANSION_ROM_BAR_CONTROL 0x00d2c #define PCI_BAR2_CONTROL 0x00c08 #define GPIO_PORT_CONTROL 0x104f0
+#define MV94XX_SPI_DATA_DIR_READ (1 << 2) +#define MV94XX_SPI_ADDR_PHASE (1 << 1) +#define MV94XX_SPI_START (1 << 0) + +static int satamv_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr); static void satamv_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr); static uint8_t satamv_chip_readb(const struct flashctx *flash, const chipaddr addr); + +static const struct spi_programmer spi_programmer_satamv = { + .type = SPI_CONTROLLER_SATAMV, + .max_data_read = 4, + .max_data_write = 4, + .command = satamv_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 const struct par_programmer par_programmer_satamv = { .chip_readb = satamv_chip_readb, .chip_readw = fallback_chip_readw, @@ -57,6 +89,44 @@ .chip_writen = fallback_chip_writen, };
+static int init_94xx(struct pci_dev *dev) +{ + uintptr_t addr; + uint32_t tmp; + + msg_pspew("Initializing 94xx\n"); + + addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_2); + if (!addr) + return 1; + msg_pspew("addr=0x%08" PRIXPTR "\n", addr); + + mv_bar = rphysmap("Marvell 88SE94xx registers", addr, 0x40000); + if (mv_bar == ERROR_PTR) + return 1; + msg_pspew("mv_bar=%p\n", mv_bar); + + /* Read some registers for fun. + * TODO: Remove once everything is working + */ + tmp = pci_mmio_readl(mv_bar + EXPANSION_ROM_WINDOW0_REMAP); + msg_pspew("PCIe Expansion ROM Window Remap: 0x%04x\n", tmp); + + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_CONTROL); + msg_pspew("SPI Flash Control: 0x%08x\n", tmp); + + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_ADDRESS); + msg_pspew("SPI Flash Address: 0x%08x\n", tmp); + + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_READ_DATA); + msg_pspew("SPI Flash Read Data: 0x%08x\n", tmp); + + /* Register SPI programmer */ + register_spi_programmer(&spi_programmer_satamv); + + return 0; +} + /* * Random notes: * FCE# Flash Chip Enable @@ -78,6 +148,7 @@ struct pci_dev *dev = NULL; uintptr_t addr; uint32_t tmp; + uint16_t id;
if (rget_io_perms()) return 1; @@ -87,6 +158,12 @@ if (!dev) return 1;
+ id = dev->device_id; + + /* If 9445, do separate init */ + if (id == 0x9445) + return init_94xx(dev); + addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0); if (!addr) return 1; @@ -192,6 +269,118 @@ return satamv_indirect_chip_readb(addr); }
+static int satamv_spi_send_command(struct flashctx *flash, unsigned int writecnt, + unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr) +{ + uint32_t tmp; + uint32_t spi_cntl = 0x0; + int i; + + if (writecnt == 0) { + msg_perr("%s: Tried to send an empty SPI command!\n" + "Please report a bug at flashrom@flashrom.org\n", __func__); + return ERROR_FLASHROM_BUG; + } + if (!(((readcnt == 0) && (writecnt <= 8)) || (((writecnt == 1) || (writecnt == 4)) && (readcnt <= 4)))) { + msg_pinfo("%s called with unsupported readcnt(%i)/writecnt(%i) combination.\n", __func__, + readcnt, writecnt); + return SPI_INVALID_LENGTH; + } + + msg_pspew("%s:", __func__); + msg_pspew(" SPI Flash Control Start: 0x%08x", pci_mmio_readl(mv_bar + MV94XX_SPI_CONTROL)); + msg_pspew(" writecnt=%d, readcnt=%d\n", writecnt, readcnt); + + for (i = 0; i < writecnt; i++) + msg_pspew("writearr[%d]=0x%x\n", i, writearr[i]); + + do { + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_CONTROL); + msg_pspew("SPI Flash Control during busy wait (while loop 1): 0x%08x\n", tmp); + tmp &= 0x1; + } while (tmp); + + + /* Clear WRITE_DATA and READ_DATA. Unneeded, but a great help for debugging. */ + pci_mmio_writel(0xffffffff, mv_bar + MV94XX_SPI_WRITE_DATA); + pci_mmio_writel(0xffffffff, mv_bar + MV94XX_SPI_READ_DATA); + + if (readcnt > 0) { + switch (writecnt) { + case 4: + spi_cntl |= MV94XX_SPI_ADDR_PHASE; + pci_mmio_writel((writearr[1] << 16) | (writearr[2] << 8) | writearr[3], + mv_bar + MV94XX_SPI_ADDRESS); + case 1: + break; + } + spi_cntl |= MV94XX_SPI_DATA_DIR_READ; + spi_cntl |= readcnt << 4; + spi_cntl |= writearr[0] << 8; + } else { + uint32_t wr_data = 0xffffffff; + switch (writecnt) { + case 8: + wr_data = (writearr[7] << 24) | (writearr[6] << 16) | (writearr[5] << 8) | writearr[4]; + break; + case 7: + wr_data = (writearr[6] << 16) | (writearr[5] << 8) | writearr[4]; + break; + case 6: + wr_data = (writearr[5] << 8) | writearr[4]; + break; + case 5: + wr_data = (writearr[4] << 24) | (writearr[3] << 16) | (writearr[2] << 8) | writearr[1]; + break; + case 4: + wr_data = (writearr[3] << 16) | (writearr[2] << 8) | writearr[1]; + break; + case 3: + wr_data = (writearr[2] << 8) | writearr[1]; + break; + case 2: + wr_data = writearr[1]; + break; + case 1: + /* Only a command, no data. */ + break; + } + if (writecnt >= 6) { + /* Have to use the address field. */ + spi_cntl |= MV94XX_SPI_ADDR_PHASE; + pci_mmio_writel((writearr[1] << 16) | (writearr[2] << 8) | writearr[3], + mv_bar + MV94XX_SPI_ADDRESS); + spi_cntl |= (writecnt - 3) << 4; + } else { + spi_cntl |= writecnt << 4; + } + pci_mmio_writel(wr_data, mv_bar + MV94XX_SPI_WRITE_DATA); + spi_cntl |= writearr[0] << 8; + } + + pci_mmio_writel(spi_cntl, mv_bar + MV94XX_SPI_CONTROL); + pci_mmio_writel(spi_cntl | MV94XX_SPI_START, mv_bar + MV94XX_SPI_CONTROL); + + do { + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_CONTROL); + msg_pspew("SPI Flash Control during busy wait (while loop 1): 0x%08x\n", tmp); + tmp &= 0x1; + } while (tmp); + + + if (readcnt > 0) { + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_READ_DATA); + for (i = 0; i < readcnt; i++) + readarr[i] = (tmp >> (i * 8)) & 0xff; + } + + msg_pspew("SPI Flash Control End: 0x%08x\n", pci_mmio_readl(mv_bar + MV94XX_SPI_CONTROL)); + + return 0; +} + #else #error PCI port I/O access is not supported on this architecture yet. #endif Index: flashrom-satamv_spi_mv94xx/programmer.h =================================================================== --- flashrom-satamv_spi_mv94xx/programmer.h (Revision 1780) +++ flashrom-satamv_spi_mv94xx/programmer.h (Arbeitskopie) @@ -527,6 +527,9 @@ #if CONFIG_USBBLASTER_SPI == 1 SPI_CONTROLLER_USBBLASTER, #endif +#if CONFIG_SATAMV == 1 + SPI_CONTROLLER_SATAMV, +#endif };
#define MAX_DATA_UNSPECIFIED 0
On Mon, Apr 28, 2014 at 08:45:14AM +0200, Carl-Daniel Hailfinger wrote:
Amit confirmed that probe works: "Found Winbond flash chip "W25X40" (512 kB, SPI) on satamv." However, reading failed due to incorrect limits in max_data_read and max_data_write.
New version, read should work fine. Write might work, but I'd rather wait a bit for that unless we know the image is correct and the flash chip can be accessed easily with another external programmer.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Thanks for the latest patch! Read works as expected, the file I read looks good.
Write does not work as of yet. Here is the output:
$ sudo ./flashrom -p satamv -w 9445_rev5.bin flashrom v0.9.7-r1769 on Linux 3.2.48-logicube-ng.16-bfq (x86_64) flashrom is free software, get the source code at http://www.flashrom.org
Calibrating delay loop... OK. Found Winbond flash chip "W25X40" (512 kB, SPI) on satamv. Reading old flash chip contents... done. Erasing and writing flash chip... FAILED at 0x00000000! Expected=0xff, Found=0x55, failed byte count from 0x00000000-0x00000fff: 0xf37 ERASE FAILED! Reading current flash chip contents... done. FAILED at 0x00000000! Expected=0xff, Found=0x55, failed byte count from 0x00000000-0x0000ffff: 0xfcd0 ERASE FAILED! Reading current flash chip contents... done. FAILED at 0x00000000! Expected=0xff, Found=0x55, failed byte count from 0x00000000-0x0007ffff: 0x25de9 ERASE FAILED! FAILED! Uh oh. Erase/write failed. Checking if anything changed. Good. It seems nothing was changed. Writing to the flash chip apparently didn't do anything. Please check the connections (especially those to write protection pins) between the programmer and the flash chip. If you think the error is caused by flashrom please report this on IRC at chat.freenode.net (channel #flashrom) or mail flashrom@flashrom.org, thanks!
Am 28.04.2014 20:00 schrieb Amit Uttamchandani:
On Mon, Apr 28, 2014 at 08:45:14AM +0200, Carl-Daniel Hailfinger wrote:
Amit confirmed that probe works: "Found Winbond flash chip "W25X40" (512 kB, SPI) on satamv." However, reading failed due to incorrect limits in max_data_read and max_data_write.
New version, read should work fine. Write might work, but I'd rather wait a bit for that unless we know the image is correct and the flash chip can be accessed easily with another external programmer.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Thanks for the latest patch! Read works as expected, the file I read looks good.
Write does not work as of yet. Here is the output:
$ sudo ./flashrom -p satamv -w 9445_rev5.bin flashrom v0.9.7-r1769 on Linux 3.2.48-logicube-ng.16-bfq (x86_64) flashrom is free software, get the source code at http://www.flashrom.org
Calibrating delay loop... OK. Found Winbond flash chip "W25X40" (512 kB, SPI) on satamv. Reading old flash chip contents... done. Erasing and writing flash chip... FAILED at 0x00000000! Expected=0xff, Found=0x55, failed byte count from 0x00000000-0x00000fff: 0xf37 ERASE FAILED! Reading current flash chip contents... done. FAILED at 0x00000000! Expected=0xff, Found=0x55, failed byte count from 0x00000000-0x0000ffff: 0xfcd0 ERASE FAILED! Reading current flash chip contents... done. FAILED at 0x00000000! Expected=0xff, Found=0x55, failed byte count from 0x00000000-0x0007ffff: 0x25de9 ERASE FAILED! FAILED! Uh oh. Erase/write failed. Checking if anything changed. Good. It seems nothing was changed. Writing to the flash chip apparently didn't do anything.
Found the bug. TheMarvell 88SE94xx encoding of the number of bytes to send via SPI differs from the encoding of the number of bytes to receive via SPI. You actually have to subtract one from the number of bytes to send, but use the unmodified number of bytes to receive. This was an off-by-one for commands, especially affecting erase and write commands.
New version. Should even work for write+erase. WARNING! The controller may not like getting its flash erased, and may hang. Not sure. Just speculation on my part. The datasheet is silent about concurrent accesses to the flash chip.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-satamv_spi_mv94xx/satamv.c =================================================================== --- flashrom-satamv_spi_mv94xx/satamv.c (Revision 1780) +++ flashrom-satamv_spi_mv94xx/satamv.c (Arbeitskopie) @@ -1,8 +1,8 @@ /* * This file is part of the flashrom project. * - * Copyright (C) 2010,2011 Carl-Daniel Hailfinger - * Written by Carl-Daniel Hailfinger for Angelbird Ltd. + * Copyright (C) 2010,2011,2014 Carl-Daniel Hailfinger + * Parts written by Carl-Daniel Hailfinger 2010, 2011 for Angelbird Ltd. * * 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 @@ -19,12 +19,20 @@ */
/* Datasheets are not public (yet?) */ + +// FIXME: This arch limitation is only valid for the 88SX60 series due to I/O port access #if defined(__i386__) || defined(__x86_64__)
#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> #include "flash.h" #include "programmer.h" +#include "chipdrivers.h" #include "hwaccess.h" +#include "spi.h" +#include <unistd.h>
uint8_t *mv_bar; uint16_t mv_iobar; @@ -32,20 +40,44 @@ const struct dev_entry satas_mv[] = { /* 88SX6041 and 88SX6042 are the same according to the datasheet. */ {0x11ab, 0x7042, OK, "Marvell", "88SX7042 PCI-e 4-port SATA-II"}, + {0x1b4b, 0x9445, OK, "Marvell", "88SE9445 PCI-e 4-port SAS/SATA 6 Gbps"},
{0}, };
#define NVRAM_PARAM 0x1045c #define FLASH_PARAM 0x1046c +#define EXPANSION_ROM_WINDOW0_REMAP 0x004c8 +#define MV94XX_SPI_CONTROL 0x0c800 +#define MV94XX_SPI_ADDRESS 0x0c804 +#define MV94XX_SPI_WRITE_DATA 0x0c808 +#define MV94XX_SPI_READ_DATA 0x0c80c #define EXPANSION_ROM_BAR_CONTROL 0x00d2c #define PCI_BAR2_CONTROL 0x00c08 #define GPIO_PORT_CONTROL 0x104f0
+#define MV94XX_SPI_DATA_DIR_READ (1 << 2) +#define MV94XX_SPI_ADDR_PHASE (1 << 1) +#define MV94XX_SPI_START (1 << 0) + +static int satamv_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr); static void satamv_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr); static uint8_t satamv_chip_readb(const struct flashctx *flash, const chipaddr addr); + +static const struct spi_programmer spi_programmer_satamv = { + .type = SPI_CONTROLLER_SATAMV, + .max_data_read = 4, + .max_data_write = 4, + .command = satamv_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 const struct par_programmer par_programmer_satamv = { .chip_readb = satamv_chip_readb, .chip_readw = fallback_chip_readw, @@ -57,6 +89,44 @@ .chip_writen = fallback_chip_writen, };
+static int init_94xx(struct pci_dev *dev) +{ + uintptr_t addr; + uint32_t tmp; + + msg_pspew("Initializing 94xx\n"); + + addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_2); + if (!addr) + return 1; + msg_pspew("addr=0x%08" PRIXPTR "\n", addr); + + mv_bar = rphysmap("Marvell 88SE94xx registers", addr, 0x40000); + if (mv_bar == ERROR_PTR) + return 1; + msg_pspew("mv_bar=%p\n", mv_bar); + + /* Read some registers for fun. + * TODO: Remove once everything is working + */ + tmp = pci_mmio_readl(mv_bar + EXPANSION_ROM_WINDOW0_REMAP); + msg_pspew("PCIe Expansion ROM Window Remap: 0x%04x\n", tmp); + + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_CONTROL); + msg_pspew("SPI Flash Control: 0x%08x\n", tmp); + + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_ADDRESS); + msg_pspew("SPI Flash Address: 0x%08x\n", tmp); + + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_READ_DATA); + msg_pspew("SPI Flash Read Data: 0x%08x\n", tmp); + + /* Register SPI programmer */ + register_spi_programmer(&spi_programmer_satamv); + + return 0; +} + /* * Random notes: * FCE# Flash Chip Enable @@ -78,6 +148,7 @@ struct pci_dev *dev = NULL; uintptr_t addr; uint32_t tmp; + uint16_t id;
if (rget_io_perms()) return 1; @@ -87,6 +158,12 @@ if (!dev) return 1;
+ id = dev->device_id; + + /* If 9445, do separate init */ + if (id == 0x9445) + return init_94xx(dev); + addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0); if (!addr) return 1; @@ -192,6 +269,120 @@ return satamv_indirect_chip_readb(addr); }
+static int satamv_spi_send_command(struct flashctx *flash, unsigned int writecnt, + unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr) +{ + uint32_t tmp; + uint32_t spi_cntl = 0x0; + int i; + + if (writecnt == 0) { + msg_perr("%s: Tried to send an empty SPI command!\n" + "Please report a bug at flashrom@flashrom.org\n", __func__); + return ERROR_FLASHROM_BUG; + } + if (!(((readcnt == 0) && (writecnt <= 8)) || (((writecnt == 1) || (writecnt == 4)) && (readcnt <= 4)))) { + msg_pinfo("%s called with unsupported readcnt(%i)/writecnt(%i) combination.\n", __func__, + readcnt, writecnt); + return SPI_INVALID_LENGTH; + } + + msg_pspew("%s:", __func__); + msg_pspew(" SPI Flash Control Start: 0x%08x", pci_mmio_readl(mv_bar + MV94XX_SPI_CONTROL)); + msg_pspew(" writecnt=%d, readcnt=%d\n", writecnt, readcnt); + + for (i = 0; i < writecnt; i++) + msg_pspew("writearr[%d]=0x%x\n", i, writearr[i]); + + do { + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_CONTROL); + msg_pspew("SPI Flash Control during busy wait (while loop 1): 0x%08x\n", tmp); + tmp &= 0x1; + } while (tmp); + + + /* Clear WRITE_DATA and READ_DATA. Unneeded, but a great help for debugging. */ + pci_mmio_writel(0xffffffff, mv_bar + MV94XX_SPI_WRITE_DATA); + pci_mmio_writel(0xffffffff, mv_bar + MV94XX_SPI_READ_DATA); + + if (readcnt > 0) { + switch (writecnt) { + case 4: + spi_cntl |= MV94XX_SPI_ADDR_PHASE; + pci_mmio_writel((writearr[1] << 16) | (writearr[2] << 8) | writearr[3], + mv_bar + MV94XX_SPI_ADDRESS); + case 1: + break; + } + spi_cntl |= MV94XX_SPI_DATA_DIR_READ; + spi_cntl |= readcnt << 4; + spi_cntl |= writearr[0] << 8; + } else { + uint32_t wr_data = 0xffffffff; + switch (writecnt) { + case 8: + wr_data = (writearr[7] << 24) | (writearr[6] << 16) | (writearr[5] << 8) | writearr[4]; + break; + case 7: + wr_data = (writearr[6] << 16) | (writearr[5] << 8) | writearr[4]; + break; + case 6: + wr_data = (writearr[5] << 8) | writearr[4]; + break; + case 5: + wr_data = (writearr[4] << 24) | (writearr[3] << 16) | (writearr[2] << 8) | writearr[1]; + break; + case 4: + wr_data = (writearr[3] << 16) | (writearr[2] << 8) | writearr[1]; + break; + case 3: + wr_data = (writearr[2] << 8) | writearr[1]; + break; + case 2: + wr_data = writearr[1]; + break; + case 1: + /* Only a command, no data. */ + break; + } + if (writecnt >= 6) { + /* Have to use the address field. */ + spi_cntl |= MV94XX_SPI_ADDR_PHASE; + pci_mmio_writel((writearr[1] << 16) | (writearr[2] << 8) | writearr[3], + mv_bar + MV94XX_SPI_ADDRESS); + /* Not counted: 1 byte command, 3 bytes address. */ + spi_cntl |= (writecnt - 4) << 4; + } else { + /* Not counted: 1 byte command. */ + spi_cntl |= (writecnt - 1) << 4; + } + pci_mmio_writel(wr_data, mv_bar + MV94XX_SPI_WRITE_DATA); + spi_cntl |= writearr[0] << 8; + } + + pci_mmio_writel(spi_cntl, mv_bar + MV94XX_SPI_CONTROL); + pci_mmio_writel(spi_cntl | MV94XX_SPI_START, mv_bar + MV94XX_SPI_CONTROL); + + do { + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_CONTROL); + msg_pspew("SPI Flash Control during busy wait (while loop 1): 0x%08x\n", tmp); + tmp &= 0x1; + } while (tmp); + + + if (readcnt > 0) { + tmp = pci_mmio_readl(mv_bar + MV94XX_SPI_READ_DATA); + for (i = 0; i < readcnt; i++) + readarr[i] = (tmp >> (i * 8)) & 0xff; + } + + msg_pspew("SPI Flash Control End: 0x%08x\n", pci_mmio_readl(mv_bar + MV94XX_SPI_CONTROL)); + + return 0; +} + #else #error PCI port I/O access is not supported on this architecture yet. #endif Index: flashrom-satamv_spi_mv94xx/programmer.h =================================================================== --- flashrom-satamv_spi_mv94xx/programmer.h (Revision 1780) +++ flashrom-satamv_spi_mv94xx/programmer.h (Arbeitskopie) @@ -527,6 +527,9 @@ #if CONFIG_USBBLASTER_SPI == 1 SPI_CONTROLLER_USBBLASTER, #endif +#if CONFIG_SATAMV == 1 + SPI_CONTROLLER_SATAMV, +#endif };
#define MAX_DATA_UNSPECIFIED 0
On Wed, Apr 30, 2014 at 12:10:39AM +0200, Carl-Daniel Hailfinger wrote:
[snip]
Found the bug. TheMarvell 88SE94xx encoding of the number of bytes to send via SPI differs from the encoding of the number of bytes to receive via SPI. You actually have to subtract one from the number of bytes to send, but use the unmodified number of bytes to receive. This was an off-by-one for commands, especially affecting erase and write commands.
New version. Should even work for write+erase. WARNING! The controller may not like getting its flash erased, and may hang. Not sure. Just speculation on my part. The datasheet is silent about concurrent accesses to the flash chip.
This patch works. Tested erase and then tested a write with a new version of the firmware. All works as expected.
Thank you so much.