static void execute_command(void) { mmio_writeb(mmio_readb(sb600_spibar + 2) | 1, sb600_spibar + 2); while (mmio_readb(sb600_spibar + 2) & 1) ; }
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 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"); }
static void mcp6x_request_spibus(void) { mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530); mcp_gpiostate |= 1 << MCP6X_SPI_REQUEST; mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530); /* Wait until we are allowed to use the SPI bus. */ while (!(mmio_readw(mcp6x_spibar + 0x530) & (1 << MCP6X_SPI_GRANT))) ; /* Update the cache. */ mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530); }
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; } }
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; } }
/* According to ITE 8502 document, the procedure to follow mode is following: * 1. write 0x00 to LPC/FWH address 0xffff_fexxh (drive CE# high) * 2. write data to LPC/FWH address 0xffff_fdxxh (drive CE# low and MOSI * with data) * 3. read date from LPC/FWH address 0xffff_fdxxh (drive CE# low and get * data from MISO) */ static int it85xx_spi_send_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr) { int i; it85xx_enter_scratch_rom(); /* exit scratch rom ONLY when programmer shuts down. Otherwise, the * temporary flash state may halt EC. */ #ifdef LPC_IO INDIRECT_A1(shm_io_base, (((unsigned long int)ce_high) >> 8) & 0xff); INDIRECT_WRITE(shm_io_base, 0xFF); /* Write anything to this address.*/ INDIRECT_A1(shm_io_base, (((unsigned long int)ce_low) >> 8) & 0xff); #endif #ifdef LPC_MEMORY mmio_writeb(0, ce_high); #endif for (i = 0; i < writecnt; ++i) { #ifdef LPC_IO INDIRECT_WRITE(shm_io_base, writearr[i]); #endif #ifdef LPC_MEMORY mmio_writeb(writearr[i], ce_low); #endif } for (i = 0; i < readcnt; ++i) { #ifdef LPC_IO readarr[i] = INDIRECT_READ(shm_io_base); #endif #ifdef LPC_MEMORY readarr[i] = mmio_readb(ce_low); #endif } #ifdef LPC_IO INDIRECT_A1(shm_io_base, (((unsigned long int)ce_high) >> 8) & 0xff); INDIRECT_WRITE(shm_io_base, 0xFF); /* Write anything to this address.*/ #endif #ifdef LPC_MEMORY mmio_writeb(0, ce_high); #endif 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; }
uint8_t mmio_le_readb(void *addr) { return le_to_cpu8(mmio_readb(addr)); }
static int mcp6x_bitbang_get_miso(void) { mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530); return (mcp_gpiostate >> MCP6X_SPI_MISO) & 0x1; }
static void determine_generation(struct pci_dev *dev) { amd_gen = CHIPSET_AMD_UNKNOWN; msg_pdbg2("Trying to determine the generation of the SPI interface... "); if (dev->device_id == 0x438d) { amd_gen = CHIPSET_SB6XX; msg_pdbg("SB6xx detected.\n"); } else if (dev->device_id == 0x439d) { struct pci_dev *smbus_dev = pci_dev_find(0x1002, 0x4385); if (smbus_dev == NULL) return; uint8_t rev = pci_read_byte(smbus_dev, PCI_REVISION_ID); if (rev >= 0x39 && rev <= 0x3D) { amd_gen = CHIPSET_SB7XX; msg_pdbg("SB7xx/SP5100 detected.\n"); } else if (rev >= 0x40 && rev <= 0x42) { amd_gen = CHIPSET_SB89XX; msg_pdbg("SB8xx/SB9xx/Hudson-1 detected.\n"); } else { msg_pwarn("SB device found but SMBus revision 0x%02x does not match known values.\n" "Assuming SB8xx/SB9xx/Hudson-1. Please send a log to [email protected]\n", rev); amd_gen = CHIPSET_SB89XX; } } else if (dev->device_id == 0x780e) { /* The PCI ID of the LPC bridge doesn't change between Hudson-2/3/4 and Yangtze (Kabini/Temash) * although they use different SPI interfaces. */ #ifdef USE_YANGTZE_HEURISTICS /* This heuristic accesses the SPI interface MMIO BAR at locations beyond those supported by * Hudson in the hope of getting 0xff readback on older chipsets and non-0xff readback on * Yangtze (and newer, compatible chipsets). */ int i; msg_pdbg("Checking for AMD Yangtze (Kabini/Temash) or later... "); for (i = 0x20; i <= 0x4f; i++) { if (mmio_readb(sb600_spibar + i) != 0xff) { amd_gen = CHIPSET_YANGTZE; msg_pdbg("found.\n"); return; } } msg_pdbg("not found. Assuming Hudson.\n"); amd_gen = CHIPSET_HUDSON234; #else struct pci_dev *smbus_dev = pci_dev_find(0x1022, 0x780B); if (smbus_dev == NULL) { msg_pdbg("No SMBus device with ID 1022:780B found.\n"); return; } uint8_t rev = pci_read_byte(smbus_dev, PCI_REVISION_ID); if (rev >= 0x11 && rev <= 0x15) { amd_gen = CHIPSET_HUDSON234; msg_pdbg("Hudson-2/3/4 detected.\n"); } else if (rev >= 0x39 && rev <= 0x3A) { amd_gen = CHIPSET_YANGTZE; msg_pdbg("Yangtze detected.\n"); } else { msg_pwarn("FCH device found but SMBus revision 0x%02x does not match known values.\n" "Please report this to [email protected] and include this log and\n" "the output of lspci -nnvx, thanks!.\n", rev); } #endif } else msg_pwarn("%s: Unknown LPC device %" PRIx16 ":%" PRIx16 ".\n" "Please report this to [email protected] and include this log and\n" "the output of lspci -nnvx, thanks!\n", __func__, dev->vendor_id, dev->device_id); }
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; }
static uint8_t internal_chip_readb(const struct flashctx *flash, const chipaddr addr) { return mmio_readb((void *) addr); }