int spi_xfer(struct spi_slave *slave, const void *dout, unsigned int bitsout, void *din, unsigned int bitsin) { unsigned int writeCnt = bitsout/8; unsigned int readCnt = bitsin/8; unsigned char* writeBuff = (unsigned char*)dout; unsigned char* readBuff = (unsigned char*)din; // // First byte is cmd opcode // and should not be sent through the buffer. // unsigned char cmd = *writeBuff++; writeCnt--; SPI_TRACE("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, cmd, writeCnt, readCnt); writeb(cmd, spibar + 0); int ret = check_readwritecnt(writeCnt, readCnt); if (ret != 0) return ret; // // Set tx/rx count using extended TxByteCount and RxByteCount registers // // SPIx1E SpiExtRegIndx // SPIx1F SpiExtRegData // SPIx1F_x05 TxByteCount // SPIx1F_x06 RxByteCoun // writeb(SPI_TX_BYTE_COUNT_IDX, spibar + SPI_EXT_REG_INDX); writeb(writeCnt, spibar + SPI_EXT_REG_DATA); writeb(SPI_RX_BYTE_COUNT_IDX, spibar + SPI_EXT_REG_INDX); writeb(readCnt, spibar + SPI_EXT_REG_DATA); SPI_TRACE("Filling buffer: "); int count; for (count = 0; count < writeCnt; count++) { SPI_TRACE("[%02x]", writeBuff[count]); writeb(writeBuff[count], spibar + SPI_FIFO_BASE + count); } SPI_TRACE("\n"); execute_command(); SPI_TRACE("Reading buffer: "); for (count = 0; count < readCnt; count++) { readBuff[count] = readb(spibar + SPI_FIFO_BASE + (writeCnt + count) % FIFO_SIZE_YANGTZE); SPI_TRACE("[%02x]", readBuff[count]); } SPI_TRACE("\n"); 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; }
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; }