int spi_xfer(struct spi_slave *slave, const void *dout, unsigned int bytesout, void *din, unsigned int bytesin) { /* First byte is cmd which can not being sent through FIFO. */ u8 cmd = *(u8 *)dout++; u8 readoffby1; u8 count; bytesout--; /* * Check if this is a write command attempting to transfer more bytes * than the controller can handle. Iterations for writes are not * supported here because each SPI write command needs to be preceded * and followed by other SPI commands, and this sequence is controlled * by the SPI chip driver. */ if (bytesout > AMD_SB_SPI_TX_LEN) { printk(BIOS_DEBUG, "FCH SPI: Too much to write. Does your SPI chip driver use" " spi_crop_chunk()?\n"); return -1; } readoffby1 = bytesout ? 0 : 1; #if CONFIG_SOUTHBRIDGE_AMD_AGESA_YANGTZE spi_write(0x1E, 5); spi_write(0x1F, bytesout); /* SpiExtRegIndx [5] - TxByteCount */ spi_write(0x1E, 6); spi_write(0x1F, bytesin); /* SpiExtRegIndx [6] - RxByteCount */ #else u8 readwrite = (bytesin + readoffby1) << 4 | bytesout; spi_write(SPI_REG_CNTRL01, readwrite); #endif spi_write(SPI_REG_OPCODE, cmd); reset_internal_fifo_pointer(); for (count = 0; count < bytesout; count++, dout++) { spi_write(SPI_REG_FIFO, *(uint8_t *)dout); } reset_internal_fifo_pointer(); execute_command(); reset_internal_fifo_pointer(); /* Skip the bytes we sent. */ for (count = 0; count < bytesout; count++) { cmd = spi_read(SPI_REG_FIFO); } reset_internal_fifo_pointer(); for (count = 0; count < bytesin; count++, din++) { *(uint8_t *)din = spi_read(SPI_REG_FIFO); } return 0; }
int spi_xfer(struct spi_slave *slave, const void *dout, unsigned int bytesout, void *din, unsigned int bytesin) { /* First byte is cmd which cannot be sent through the FIFO. */ u8 cmd = *(u8 *)dout++; u8 readoffby1; u8 readwrite; u8 count; uint32_t spibar = get_spi_bar(); bytesout--; /* * Check if this is a write command attempting to transfer more bytes * than the controller can handle. Iterations for writes are not * supported here because each SPI write command needs to be preceded * and followed by other SPI commands, and this sequence is controlled * by the SPI chip driver. */ if (bytesout > AMD_SB_SPI_TX_LEN) { printk(BIOS_DEBUG, "FCH SPI: Too much to write. Does your SPI chip driver use" " spi_crop_chunk()?\n"); return -1; } readoffby1 = bytesout ? 0 : 1; readwrite = (bytesin + readoffby1) << 4 | bytesout; write8((void *)(spibar + 1), readwrite); write8((void *)(spibar + 0), cmd); reset_internal_fifo_pointer(); for (count = 0; count < bytesout; count++, dout++) { write8((void *)(spibar + 0x0C), *(u8 *)dout); } reset_internal_fifo_pointer(); execute_command(); reset_internal_fifo_pointer(); /* Skip the bytes we sent. */ for (count = 0; count < bytesout; count++) { cmd = read8((void *)(spibar + 0x0C)); } /* read response bytes */ for (count = 0; count < bytesin; count++, din++) { *(u8 *)din = read8((void *)(spibar + 0x0C)); } return 0; }
static int reset_compare_internal_fifo_pointer(uint8_t want) { int ret; ret = compare_internal_fifo_pointer(want); reset_internal_fifo_pointer(); return ret; }
int spi_xfer(struct spi_slave *slave, const void *dout, unsigned int bitsout, void *din, unsigned int bitsin) { /* First byte is cmd which can not being sent through FIFO. */ u8 cmd = *(u8 *)dout++; u8 readoffby1; u8 readwrite; u8 bytesout, bytesin; u8 count; bitsout -= 8; bytesout = bitsout / 8; bytesin = bitsin / 8; readoffby1 = bytesout ? 0 : 1; readwrite = (bytesin + readoffby1) << 4 | bytesout; writeb(readwrite, spibar + 1); writeb(cmd, spibar + 0); reset_internal_fifo_pointer(); for (count = 0; count < bytesout; count++, dout++) { writeb(*(u8 *)dout, spibar + 0x0C); } reset_internal_fifo_pointer(); execute_command(); reset_internal_fifo_pointer(); /* Skip the bytes we sent. */ for (count = 0; count < bytesout; count++) { readb(spibar + 0x0C); } //reset_internal_fifo_pointer(); for (count = 0; count < bytesin; count++, din++) { *(u8 *)din = readb(spibar + 0x0C); } return 0; }
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; }