static void execute_command(void) { msg_pspew("Executing... "); mmio_writeb(mmio_readb(sb600_spibar + 2) | 1, sb600_spibar + 2); while (mmio_readb(sb600_spibar + 2) & 1) ; msg_pspew("done\n"); }
static void print_hex(const void *buf, size_t len) { size_t i; for (i = 0; i < len; i++) { msg_pspew(" %02x", ((uint8_t *)buf)[i]); if (i % CH341_PACKET_LENGTH == CH341_PACKET_LENGTH - 1) msg_pspew("\n"); } }
static void *physmap_common(const char *descr, uintptr_t phys_addr, size_t len, bool readonly, bool autocleanup, bool round) { void *virt_addr; uintptr_t offset = 0; if (len == 0) { msg_pspew("Not mapping %s, zero size at 0x%0*" PRIxPTR ".\n", descr, PRIxPTR_WIDTH, phys_addr); return ERROR_PTR; } if (round) offset = round_to_page_boundaries(&phys_addr, &len); if (readonly) virt_addr = sys_physmap_ro_cached(phys_addr, len); else virt_addr = sys_physmap_rw_uncached(phys_addr, len); if (ERROR_PTR == virt_addr) { if (NULL == descr) descr = "memory"; msg_perr("Error accessing %s, 0x%zx bytes at 0x%0*" PRIxPTR "\n", descr, len, PRIxPTR_WIDTH, phys_addr); msg_perr(MEM_DEV " mmap failed: %s\n", strerror(errno)); #ifdef __linux__ if (EINVAL == errno) { msg_perr("In Linux this error can be caused by the CONFIG_NONPROMISC_DEVMEM (<2.6.27),\n"); msg_perr("CONFIG_STRICT_DEVMEM (>=2.6.27) and CONFIG_X86_PAT kernel options.\n"); msg_perr("Please check if either is enabled in your kernel before reporting a failure.\n"); msg_perr("You can override CONFIG_X86_PAT at boot with the nopat kernel parameter but\n"); msg_perr("disabling the other option unfortunately requires a kernel recompile. Sorry!\n"); } #elif defined (__OpenBSD__) msg_perr("Please set securelevel=-1 in /etc/rc.securelevel " "and reboot, or reboot into\n" "single user mode.\n"); #endif return ERROR_PTR; } if (autocleanup) { struct undo_physmap_data *d = malloc(sizeof(struct undo_physmap_data)); if (d == NULL) { msg_perr("%s: Out of memory!\n", __func__); physunmap(virt_addr, len); return ERROR_PTR; } d->virt_addr = virt_addr; d->len = len; if (register_shutdown(undo_physmap, d) != 0) { msg_perr("%s: Could not register shutdown function!\n", __func__); physunmap(virt_addr, len); return ERROR_PTR; } } return virt_addr + offset; }
static void reset_internal_fifo_pointer(void) { mmio_writeb(mmio_readb(sb600_spibar + 2) | 0x10, sb600_spibar + 2); /* FIXME: This loop needs a timeout and a clearer message. */ while (mmio_readb(sb600_spibar + 0xD) & 0x7) msg_pspew("reset\n"); }
void physunmap(void *virt_addr, size_t len) { if (len == 0) { msg_pspew("Not unmapping zero size at %p\n", virt_addr); return; } munmap(virt_addr, len); }
static void atavia_prettyprint_access(uint8_t access) { uint8_t bmask = access & BROM_BYTE_ENABLE_MASK; uint8_t size = access & BROM_SIZE_MASK; msg_pspew("Accessing byte(s):%s%s%s%s\n", ((bmask & (1<<3)) == 0) ? " 3" : "", ((bmask & (1<<2)) == 0) ? " 2" : "", ((bmask & (1<<1)) == 0) ? " 1" : "", ((bmask & (1<<0)) == 0) ? " 0" : ""); if (size == BROM_SIZE_0K) { msg_pspew("No ROM device found.\n"); } else msg_pspew("ROM device with %s kB attached.\n", (size == BROM_SIZE_64K) ? ">=64" : (size == BROM_SIZE_32K) ? "32" : "16"); msg_pspew("Access is a %s.\n", (access & BROM_WRITE) ? "write" : "read"); msg_pspew("Device is %s.\n", (access & BROM_TRIGGER) ? "busy" : "ready"); }
static void *physmap_common(const char *descr, unsigned long phys_addr, size_t len, int mayfail, int readonly) { void *virt_addr; if (len == 0) { msg_pspew("Not mapping %s, zero size at 0x%08lx.\n", descr, phys_addr); return ERROR_PTR; } if ((getpagesize() - 1) & len) { msg_perr("Mapping %s at 0x%08lx, unaligned size 0x%lx.\n", descr, phys_addr, (unsigned long)len); } if ((getpagesize() - 1) & phys_addr) { msg_perr("Mapping %s, 0x%lx bytes at unaligned 0x%08lx.\n", descr, (unsigned long)len, phys_addr); } if (readonly) { virt_addr = sys_physmap_ro_cached(phys_addr, len); } else { virt_addr = sys_physmap_rw_uncached(phys_addr, len); } if (ERROR_PTR == virt_addr) { if (NULL == descr) descr = "memory"; msg_perr("Error accessing %s, 0x%lx bytes at 0x%08lx\n", descr, (unsigned long)len, phys_addr); perror(MEM_DEV " mmap failed"); #ifdef __linux__ if (EINVAL == errno) { msg_perr("In Linux this error can be caused by the CONFIG_NONPROMISC_DEVMEM (<2.6.27),\n"); msg_perr("CONFIG_STRICT_DEVMEM (>=2.6.27) and CONFIG_X86_PAT kernel options.\n"); msg_perr("Please check if either is enabled in your kernel before reporting a failure.\n"); msg_perr("You can override CONFIG_X86_PAT at boot with the nopat kernel parameter but\n"); msg_perr("disabling the other option unfortunately requires a kernel recompile. Sorry!\n"); } #elif defined (__OpenBSD__) msg_perr("Please set securelevel=-1 in /etc/rc.securelevel " "and reboot, or reboot into \n"); msg_perr("single user mode.\n"); #endif if (!mayfail) exit(3); } return virt_addr; }
static int compare_internal_fifo_pointer(uint8_t want) { uint8_t have = mmio_readb(sb600_spibar + 0xd) & 0x07; want %= FIFO_SIZE_OLD; if (have != want) { msg_perr("AMD SPI FIFO pointer corruption! Pointer is %d, wanted %d\n", have, want); msg_perr("Something else is accessing the flash chip and causes random corruption.\n" "Please stop all applications and drivers and IPMI which access the flash chip.\n"); return 1; } else { msg_pspew("AMD SPI FIFO pointer is %d, wanted %d\n", have, want); return 0; } }
int wbsio_check_for_spi(void) { if (0 == (wbsio_spibase = wbsio_get_spibase(WBSIO_PORT1))) if (0 == (wbsio_spibase = wbsio_get_spibase(WBSIO_PORT2))) return 1; msg_pspew("\nwbsio_spibase = 0x%x\n", wbsio_spibase); msg_pdbg("%s: Winbond saved on 4 register bits so max chip size is " "1024 kB!\n", __func__); max_rom_decode.spi = 1024 * 1024; register_spi_master(&spi_master_wbsio); return 0; }
static int compare_internal_fifo_pointer(uint8_t want) { uint8_t tmp; tmp = mmio_readb(sb600_spibar + 0xd) & 0x07; want &= 0x7; if (want != tmp) { msg_perr("FIFO pointer corruption! Pointer is %d, wanted %d\n", tmp, want); msg_perr("Something else is accessing the flash chip and " "causes random corruption.\nPlease stop all " "applications and drivers and IPMI which access the " "flash chip.\n"); return 1; } else { msg_pspew("SB600 FIFO pointer is %d, wanted %d\n", tmp, want); return 0; } }
static int spi100_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr) { /* First byte is cmd which can not be sent through the buffer. */ unsigned char cmd = *writearr++; writecnt--; msg_pspew("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, cmd, writecnt, readcnt); mmio_writeb(cmd, sb600_spibar + 0); int ret = check_readwritecnt(flash, writecnt, readcnt); if (ret != 0) return ret; /* Use the extended TxByteCount and RxByteCount registers. */ mmio_writeb(writecnt, sb600_spibar + 0x48); mmio_writeb(readcnt, sb600_spibar + 0x4b); msg_pspew("Filling buffer: "); int count; for (count = 0; count < writecnt; count++) { msg_pspew("[%02x]", writearr[count]); mmio_writeb(writearr[count], sb600_spibar + 0x80 + count); } msg_pspew("\n"); execute_command(); msg_pspew("Reading buffer: "); for (count = 0; count < readcnt; count++) { readarr[count] = mmio_readb(sb600_spibar + 0x80 + (writecnt + count) % FIFO_SIZE_YANGTZE); msg_pspew("[%02x]", readarr[count]); } msg_pspew("\n"); return 0; }
/* W83627DHG has 11 command modes: * 1=1 command only * 2=1 command+1 data write * 3=1 command+2 data read * 4=1 command+3 address * 5=1 command+3 address+1 data write * 6=1 command+3 address+4 data write * 7=1 command+3 address+1 dummy address inserted by wbsio+4 data read * 8=1 command+3 address+1 data read * 9=1 command+3 address+2 data read * a=1 command+3 address+3 data read * b=1 command+3 address+4 data read * * mode[7:4] holds the command mode * mode[3:0] holds SPI address bits [19:16] * * The Winbond SPI master only supports 20 bit addresses on the SPI bus. :\ * Would one more byte of RAM in the chip (to get all 24 bits) really make * such a big difference? */ static int wbsio_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr) { int i; uint8_t mode = 0; msg_pspew("%s:", __func__); if (1 == writecnt && 0 == readcnt) { mode = 0x10; } else if (2 == writecnt && 0 == readcnt) { OUTB(writearr[1], wbsio_spibase + 4); msg_pspew(" data=0x%02x", writearr[1]); mode = 0x20; } else if (1 == writecnt && 2 == readcnt) { mode = 0x30; } else if (4 == writecnt && 0 == readcnt) { msg_pspew(" addr=0x%02x", (writearr[1] & 0x0f)); for (i = 2; i < writecnt; i++) { OUTB(writearr[i], wbsio_spibase + i); msg_pspew("%02x", writearr[i]); } mode = 0x40 | (writearr[1] & 0x0f); } else if (5 == writecnt && 0 == readcnt) { msg_pspew(" addr=0x%02x", (writearr[1] & 0x0f)); for (i = 2; i < 4; i++) { OUTB(writearr[i], wbsio_spibase + i); msg_pspew("%02x", writearr[i]); } OUTB(writearr[i], wbsio_spibase + i); msg_pspew(" data=0x%02x", writearr[i]); mode = 0x50 | (writearr[1] & 0x0f); } else if (8 == writecnt && 0 == readcnt) { msg_pspew(" addr=0x%02x", (writearr[1] & 0x0f)); for (i = 2; i < 4; i++) { OUTB(writearr[i], wbsio_spibase + i); msg_pspew("%02x", writearr[i]); } msg_pspew(" data=0x"); for (; i < writecnt; i++) { OUTB(writearr[i], wbsio_spibase + i); msg_pspew("%02x", writearr[i]); } mode = 0x60 | (writearr[1] & 0x0f); } else if (5 == writecnt && 4 == readcnt) { /* XXX: TODO not supported by flashrom infrastructure! * This mode, 7, discards the fifth byte in writecnt, * but since we can not express that in flashrom, fail * the operation for now. */ ; } else if (4 == writecnt && readcnt >= 1 && readcnt <= 4) { msg_pspew(" addr=0x%02x", (writearr[1] & 0x0f)); for (i = 2; i < writecnt; i++) { OUTB(writearr[i], wbsio_spibase + i); msg_pspew("%02x", writearr[i]); } mode = ((7 + readcnt) << 4) | (writearr[1] & 0x0f); } msg_pspew(" cmd=%02x mode=%02x\n", writearr[0], mode); if (!mode) { msg_perr("%s: unsupported command type wr=%d rd=%d\n", __func__, writecnt, readcnt); /* Command type refers to the number of bytes read/written. */ return SPI_INVALID_LENGTH; } OUTB(writearr[0], wbsio_spibase); OUTB(mode, wbsio_spibase + 1); programmer_delay(10); if (!readcnt) return 0; msg_pspew("%s: returning data =", __func__); for (i = 0; i < readcnt; i++) { readarr[i] = INB(wbsio_spibase + 4 + i); msg_pspew(" 0x%02x", readarr[i]); } msg_pspew("\n"); return 0; }
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; headertype = pci_read_byte(dev, PCI_HEADER_TYPE) & 0x7f; msg_pspew("PCI header type 0x%02x\n", headertype); /* Don't use dev->base_addr[x] (as value for 'bar'), won't work on older libpci. */ addr = pci_read_long(dev, bar); /* Sanity checks. */ switch (headertype) { case PCI_HEADER_TYPE_NORMAL: switch (bar) { case PCI_BASE_ADDRESS_0: case PCI_BASE_ADDRESS_1: case PCI_BASE_ADDRESS_2: case PCI_BASE_ADDRESS_3: case PCI_BASE_ADDRESS_4: case PCI_BASE_ADDRESS_5: if ((addr & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) bartype = TYPE_IOBAR; else bartype = TYPE_MEMBAR; break; case PCI_ROM_ADDRESS: bartype = TYPE_ROMBAR; break; } break; case PCI_HEADER_TYPE_BRIDGE: switch (bar) { case PCI_BASE_ADDRESS_0: case PCI_BASE_ADDRESS_1: if ((addr & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) bartype = TYPE_IOBAR; else bartype = TYPE_MEMBAR; break; case PCI_ROM_ADDRESS1: bartype = TYPE_ROMBAR; break; } break; case PCI_HEADER_TYPE_CARDBUS: break; default: msg_perr("Unknown PCI header type 0x%02x, BAR type cannot be determined reliably.\n", headertype); break; } supported_cycles = pci_read_word(dev, PCI_COMMAND); msg_pdbg("Requested BAR is of type "); switch (bartype) { case TYPE_MEMBAR: msg_pdbg("MEM"); if (!(supported_cycles & PCI_COMMAND_MEMORY)) { msg_perr("MEM BAR access requested, but device has MEM space accesses disabled.\n"); /* TODO: Abort here? */ } msg_pdbg(", %sbit, %sprefetchable\n", ((addr & 0x6) == 0x0) ? "32" : (((addr & 0x6) == 0x4) ? "64" : "reserved"), (addr & 0x8) ? "" : "not "); if ((addr & 0x6) == 0x4) { /* The spec says that a 64-bit register consumes * two subsequent dword locations. */ upperaddr = pci_read_long(dev, bar + 4); if (upperaddr != 0x00000000) { /* Fun! A real 64-bit resource. */ if (sizeof(uintptr_t) != sizeof(uint64_t)) { msg_perr("BAR unreachable!"); /* TODO: Really abort here? If multiple PCI devices match, * we might never tell the user about the other devices. */ return 0; } addr |= (uint64_t)upperaddr << 32; } } addr &= PCI_BASE_ADDRESS_MEM_MASK; break; case TYPE_IOBAR: msg_pdbg("I/O\n"); #if __FLASHROM_HAVE_OUTB__ if (!(supported_cycles & PCI_COMMAND_IO)) { msg_perr("I/O BAR access requested, but device has I/O space accesses disabled.\n"); /* TODO: Abort here? */ } #else msg_perr("I/O BAR access requested, but flashrom does not support I/O BAR access on this " "platform (yet).\n"); #endif addr &= PCI_BASE_ADDRESS_IO_MASK; break; case TYPE_ROMBAR: msg_pdbg("ROM\n"); /* Not sure if this check is needed. */ if (!(supported_cycles & PCI_COMMAND_MEMORY)) { msg_perr("MEM BAR access requested, but device has MEM space accesses disabled.\n"); /* TODO: Abort here? */ } addr &= PCI_ROM_ADDRESS_MASK; break; case TYPE_UNKNOWN: msg_perr("BAR type unknown, please report a bug at [email protected]\n"); } return (uintptr_t)addr; }
/* Tries up to timeout ms to read readcnt characters and places them into the array starting at c. Returns * 0 on success, positive values on temporary errors (e.g. timeouts) and negative ones on permanent errors. * If really_read is not NULL, this function sets its contents to the number of bytes read successfully. */ int serialport_read_nonblock(unsigned char *c, unsigned int readcnt, unsigned int timeout, unsigned int *really_read) { int ret = 1; /* disable blocked i/o and declare platform-specific variables */ #if IS_WINDOWS DWORD rv; COMMTIMEOUTS oldTimeout; COMMTIMEOUTS newTimeout = { .ReadIntervalTimeout = MAXDWORD, .ReadTotalTimeoutMultiplier = 0, .ReadTotalTimeoutConstant = 0, .WriteTotalTimeoutMultiplier = 0, .WriteTotalTimeoutConstant = 0 }; if(!GetCommTimeouts(sp_fd, &oldTimeout)) { msg_perr_strerror("Could not get serial port timeout settings: "); return -1; } if(!SetCommTimeouts(sp_fd, &newTimeout)) { msg_perr_strerror("Could not set serial port timeout settings: "); return -1; } #else ssize_t rv; const int flags = fcntl(sp_fd, F_GETFL); if (flags == -1) { msg_perr_strerror("Could not get serial port mode: "); return -1; } if (fcntl(sp_fd, F_SETFL, flags | O_NONBLOCK) != 0) { msg_perr_strerror("Could not set serial port mode to non-blocking: "); return -1; } #endif int i; int rd_bytes = 0; for (i = 0; i < timeout; i++) { msg_pspew("readcnt %d rd_bytes %d\n", readcnt, rd_bytes); #if IS_WINDOWS ReadFile(sp_fd, c + rd_bytes, readcnt - rd_bytes, &rv, NULL); msg_pspew("read %lu bytes\n", rv); #else rv = read(sp_fd, c + rd_bytes, readcnt - rd_bytes); msg_pspew("read %zd bytes\n", rv); #endif if ((rv == -1) && (errno != EAGAIN)) { msg_perr_strerror("Serial port read error: "); ret = -1; break; } if (rv > 0) rd_bytes += rv; if (rd_bytes == readcnt) { ret = 0; break; } internal_delay(1000); /* 1ms units */ } if (really_read != NULL) *really_read = rd_bytes; /* restore original blocking behavior */ #if IS_WINDOWS if (!SetCommTimeouts(sp_fd, &oldTimeout)) { msg_perr_strerror("Could not restore serial port timeout settings: "); ret = -1; } #else if (fcntl(sp_fd, F_SETFL, flags) != 0) { msg_perr_strerror("Could not restore serial port mode to blocking: "); ret = -1; } #endif return ret; } /* Tries up to timeout ms to write writecnt characters from the array starting at buf. Returns * 0 on success, positive values on temporary errors (e.g. timeouts) and negative ones on permanent errors. * If really_wrote is not NULL, this function sets its contents to the number of bytes written successfully. */ int serialport_write_nonblock(const unsigned char *buf, unsigned int writecnt, unsigned int timeout, unsigned int *really_wrote) { int ret = 1; /* disable blocked i/o and declare platform-specific variables */ #if IS_WINDOWS DWORD rv; COMMTIMEOUTS oldTimeout; COMMTIMEOUTS newTimeout = { .ReadIntervalTimeout = MAXDWORD, .ReadTotalTimeoutMultiplier = 0, .ReadTotalTimeoutConstant = 0, .WriteTotalTimeoutMultiplier = 0, .WriteTotalTimeoutConstant = 0 }; if(!GetCommTimeouts(sp_fd, &oldTimeout)) { msg_perr_strerror("Could not get serial port timeout settings: "); return -1; } if(!SetCommTimeouts(sp_fd, &newTimeout)) { msg_perr_strerror("Could not set serial port timeout settings: "); return -1; } #else ssize_t rv; const int flags = fcntl(sp_fd, F_GETFL); if (flags == -1) { msg_perr_strerror("Could not get serial port mode: "); return -1; } if (fcntl(sp_fd, F_SETFL, flags | O_NONBLOCK) != 0) { msg_perr_strerror("Could not set serial port mode to non-blocking: "); return -1; } #endif int i; int wr_bytes = 0; for (i = 0; i < timeout; i++) { msg_pspew("writecnt %d wr_bytes %d\n", writecnt, wr_bytes); #if IS_WINDOWS WriteFile(sp_fd, buf + wr_bytes, writecnt - wr_bytes, &rv, NULL); msg_pspew("wrote %lu bytes\n", rv); #else rv = write(sp_fd, buf + wr_bytes, writecnt - wr_bytes); msg_pspew("wrote %zd bytes\n", rv); #endif if ((rv == -1) && (errno != EAGAIN)) { msg_perr_strerror("Serial port write error: "); ret = -1; break; } if (rv > 0) { wr_bytes += rv; if (wr_bytes == writecnt) { msg_pspew("write successful\n"); ret = 0; break; } } internal_delay(1000); /* 1ms units */ } if (really_wrote != NULL) *really_wrote = wr_bytes; /* restore original blocking behavior */ #if IS_WINDOWS if (!SetCommTimeouts(sp_fd, &oldTimeout)) { msg_perr_strerror("Could not restore serial port timeout settings: "); return -1; } #else if (fcntl(sp_fd, F_SETFL, flags) != 0) { msg_perr_strerror("Could not restore serial port blocking behavior: "); return -1; } #endif return ret; }
static int sb600_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr) { int count; /* First byte is cmd which can not being sent through FIFO. */ unsigned char cmd = *writearr++; unsigned int readoffby1; unsigned char readwrite; writecnt--; msg_pspew("%s, cmd=%x, writecnt=%x, readcnt=%x\n", __func__, cmd, writecnt, readcnt); if (readcnt > 8) { msg_pinfo("%s, SB600 SPI controller can not receive %d bytes, " "it is limited to 8 bytes\n", __func__, readcnt); return SPI_INVALID_LENGTH; } if (writecnt > 8) { msg_pinfo("%s, SB600 SPI controller can not send %d bytes, " "it is limited to 8 bytes\n", __func__, writecnt); return SPI_INVALID_LENGTH; } /* This is a workaround for a bug in SB600 and SB700. If we only send * an opcode and no additional data/address, the SPI controller will * read one byte too few from the chip. Basically, the last byte of * the chip response is discarded and will not end up in the FIFO. * It is unclear if the CS# line is set high too early as well. */ readoffby1 = (writecnt) ? 0 : 1; readwrite = (readcnt + readoffby1) << 4 | (writecnt); mmio_writeb(readwrite, sb600_spibar + 1); mmio_writeb(cmd, sb600_spibar + 0); /* Before we use the FIFO, reset it first. */ reset_internal_fifo_pointer(); /* Send the write byte to FIFO. */ msg_pspew("Writing: "); for (count = 0; count < writecnt; count++, writearr++) { msg_pspew("[%02x]", *writearr); mmio_writeb(*writearr, sb600_spibar + 0xC); } msg_pspew("\n"); /* * We should send the data by sequence, which means we need to reset * the FIFO pointer to the first byte we want to send. */ if (reset_compare_internal_fifo_pointer(writecnt)) return SPI_PROGRAMMER_ERROR; msg_pspew("Executing: \n"); execute_command(); /* * After the command executed, we should find out the index of the * received byte. Here we just reset the FIFO pointer and skip the * writecnt. * It would be possible to increase the FIFO pointer by one instead * of reading and discarding one byte from the FIFO. * The FIFO is implemented on top of an 8 byte ring buffer and the * buffer is never cleared. For every byte that is shifted out after * the opcode, the FIFO already stores the response from the chip. * Usually, the chip will respond with 0x00 or 0xff. */ if (reset_compare_internal_fifo_pointer(writecnt + readcnt)) return SPI_PROGRAMMER_ERROR; /* Skip the bytes we sent. */ msg_pspew("Skipping: "); for (count = 0; count < writecnt; count++) { cmd = mmio_readb(sb600_spibar + 0xC); msg_pspew("[%02x]", cmd); } msg_pspew("\n"); if (compare_internal_fifo_pointer(writecnt)) return SPI_PROGRAMMER_ERROR; msg_pspew("Reading: "); for (count = 0; count < readcnt; count++, readarr++) { *readarr = mmio_readb(sb600_spibar + 0xC); msg_pspew("[%02x]", *readarr); } msg_pspew("\n"); if (reset_compare_internal_fifo_pointer(readcnt + writecnt)) return SPI_PROGRAMMER_ERROR; if (mmio_readb(sb600_spibar + 1) != readwrite) { msg_perr("Unexpected change in SB600 read/write count!\n"); msg_perr("Something else is accessing the flash chip and " "causes random corruption.\nPlease stop all " "applications and drivers and IPMI which access the " "flash chip.\n"); return SPI_PROGRAMMER_ERROR; } return 0; }
static int sb600_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr) { /* First byte is cmd which can not be sent through the FIFO. */ unsigned char cmd = *writearr++; writecnt--; msg_pspew("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, cmd, writecnt, readcnt); mmio_writeb(cmd, sb600_spibar + 0); int ret = check_readwritecnt(flash, writecnt, readcnt); if (ret != 0) return ret; /* This is a workaround for a bug in SPI controller. If we only send * an opcode and no additional data/address, the SPI controller will * read one byte too few from the chip. Basically, the last byte of * the chip response is discarded and will not end up in the FIFO. * It is unclear if the CS# line is set high too early as well. */ unsigned int readoffby1 = (writecnt > 0) ? 0 : 1; uint8_t readwrite = (readcnt + readoffby1) << 4 | (writecnt); mmio_writeb(readwrite, sb600_spibar + 1); reset_internal_fifo_pointer(); msg_pspew("Filling FIFO: "); int count; for (count = 0; count < writecnt; count++) { msg_pspew("[%02x]", writearr[count]); mmio_writeb(writearr[count], sb600_spibar + 0xC); } msg_pspew("\n"); if (compare_internal_fifo_pointer(writecnt)) return SPI_PROGRAMMER_ERROR; /* * We should send the data in sequence, which means we need to reset * the FIFO pointer to the first byte we want to send. */ reset_internal_fifo_pointer(); execute_command(); if (compare_internal_fifo_pointer(writecnt + readcnt)) return SPI_PROGRAMMER_ERROR; /* * After the command executed, we should find out the index of the * received byte. Here we just reset the FIFO pointer and skip the * writecnt. * It would be possible to increase the FIFO pointer by one instead * of reading and discarding one byte from the FIFO. * The FIFO is implemented on top of an 8 byte ring buffer and the * buffer is never cleared. For every byte that is shifted out after * the opcode, the FIFO already stores the response from the chip. * Usually, the chip will respond with 0x00 or 0xff. */ reset_internal_fifo_pointer(); /* Skip the bytes we sent. */ msg_pspew("Skipping: "); for (count = 0; count < writecnt; count++) { msg_pspew("[%02x]", mmio_readb(sb600_spibar + 0xC)); } msg_pspew("\n"); if (compare_internal_fifo_pointer(writecnt)) return SPI_PROGRAMMER_ERROR; msg_pspew("Reading FIFO: "); for (count = 0; count < readcnt; count++) { readarr[count] = mmio_readb(sb600_spibar + 0xC); msg_pspew("[%02x]", readarr[count]); } msg_pspew("\n"); if (compare_internal_fifo_pointer(writecnt+readcnt)) return SPI_PROGRAMMER_ERROR; if (mmio_readb(sb600_spibar + 1) != readwrite) { msg_perr("Unexpected change in AMD SPI read/write count!\n"); msg_perr("Something else is accessing the flash chip and causes random corruption.\n" "Please stop all applications and drivers and IPMI which access the flash chip.\n"); return SPI_PROGRAMMER_ERROR; } return 0; }