📄 spi_flash.c
字号:
* The malloc engine is ready and we can move our buffers to * normal RAM * return: --- */void spi_init_r(void){#if defined(CONFIG_POST) && (CONFIG_POST & CFG_POST_SPI) /* Our testing strategy here is pretty basic: * - fill src memory with an 8-bit pattern * - write the src memory to the SPI flash * - read the SPI flash into the dst memory * - compare src and dst memory regions * - repeat a few times * The variations we test for: * - change the 8-bit pattern a bit * - change the read/write block size so we know: * - writes smaller/equal/larger than the buffer work * - writes smaller/equal/larger than the sector work * - change the SPI offsets so we know: * - writing partial sectors works */ uint8_t *mem_src, *mem_dst; size_t i, c, l, o; size_t test_count, errors; uint8_t pattern; SPI_INIT(); if (spi_detect_part()) goto out; eeprom_info(); ulong lengths[] = { flash.write_length, flash.write_length * 2, flash.write_length / 2, flash.sector_size, flash.sector_size * 2, flash.sector_size / 2 }; ulong offsets[] = { 0, flash.write_length, flash.write_length * 2, flash.write_length / 2, flash.write_length / 4, flash.sector_size, flash.sector_size * 2, flash.sector_size / 2, flash.sector_size / 4, }; /* the exact addresses are arbitrary ... they just need to not overlap */ mem_src = (void *)(0); mem_dst = (void *)(max(flash.write_length, flash.sector_size) * 2); test_count = 0; errors = 0; pattern = 0x00; for (i = 0; i < 16; ++i) { /* 16 = 8 bits * 2 iterations */ for (l = 0; l < ARRAY_SIZE(lengths); ++l) { for (o = 0; o < ARRAY_SIZE(offsets); ++o) { ulong len = lengths[l]; ulong off = offsets[o]; printf("Testing pattern 0x%02X of length %5lu and offset %5lu: ", pattern, len, off); /* setup the source memory region */ memset(mem_src, pattern, len); test_count += 4; for (c = 0; c < 4; ++c) { /* 4 is just a random repeat count */ if (ctrlc()) { puts("\nAbort\n"); goto out; } /* make sure background fill pattern != pattern */ memset(mem_dst, pattern ^ 0xFF, len); /* write out the source memory and then read it back and compare */ eeprom_write(0, off, mem_src, len); eeprom_read(0, off, mem_dst, len); if (memcmp(mem_src, mem_dst, len)) { for (c = 0; c < len; ++c) if (mem_src[c] != mem_dst[c]) break; printf(" FAIL @ offset %u, skipping repeats ", c); ++errors; break; } /* XXX: should shrink write region here to test with * leading/trailing canaries so we know surrounding * bytes don't get screwed. */ } puts("\n"); } } /* invert the pattern every other run and shift out bits slowly */ pattern ^= 0xFF; if (i % 2) pattern = (pattern | 0x01) << 1; } if (errors) printf("SPI FAIL: Out of %i tests, there were %i errors ;(\n", test_count, errors); else printf("SPI PASS: %i tests worked!\n", test_count); out: SPI_DEINIT();#endif}static void transmit_address(uint32_t addr){ /* Send the highest byte of the 24 bit address at first */ spi_write_read_byte(addr >> 16); /* Send the middle byte of the 24 bit address at second */ spi_write_read_byte(addr >> 8); /* Send the lowest byte of the 24 bit address finally */ spi_write_read_byte(addr);}/* * Read a value from flash for verify purpose * Inputs: unsigned long ulStart - holds the SPI start address * int pnData - pointer to store value read from flash * long lCount - number of elements to read */static int read_flash(unsigned long address, long count, uchar *buffer){ size_t i; /* Send the read command to SPI device */ SPI_ON(); spi_write_read_byte(flash.ops->read); transmit_address(address);#ifndef CONFIG_SPI_FLASH_SLOW_READ /* Send dummy byte when doing SPI fast reads */ spi_write_read_byte(0);#endif /* After the SPI device address has been placed on the MOSI pin the data can be */ /* received on the MISO pin. */ for (i = 1; i <= count; ++i) { *buffer++ = spi_write_read_byte(0); if (i % flash.sector_size == 0) puts("."); } SPI_OFF(); return 0;}static int enable_writing(void){ ulong start; if (flash.manufacturer_id == JED_MANU_ATMEL) return 0; /* A write enable instruction must previously have been executed */ SPI_ON(); spi_write_read_byte(0x06); SPI_OFF(); /* The status register will be polled to check the write enable latch "WREN" */ start = get_timer(0); while (get_timer(0) - start < TIMEOUT) { if (read_status_register() & 0x02) return 0; if (ctrlc()) { puts("\nAbort\n"); return -1; } } puts("Timeout\n"); return -1;}static long address_to_sector(unsigned long address){ if (address > (flash.num_sectors * flash.sector_size) - 1) return -1; return address / flash.sector_size;}static int erase_sector(int address){ /* sector gets checked in higher function, so assume it's valid * here and figure out the offset of the sector in flash */ if (enable_writing()) return -1; /* * Send the erase block command to the flash followed by the 24 address * to point to the start of a sector */ SPI_ON(); spi_write_read_byte(flash.ops->erase); transmit_address(address); SPI_OFF(); return wait_for_ready_status();}/* Write [count] bytes out of [buffer] into the given SPI [address] */static long write_flash(unsigned long address, long count, uchar *buffer){ long i, write_buffer_size; if (enable_writing()) return -1; /* Send write command followed by the 24 bit address */ SPI_ON(); spi_write_read_byte(flash.ops->write); transmit_address(address); /* Shoot out a single write buffer */ write_buffer_size = min(count, flash.write_length); for (i = 0; i < write_buffer_size; ++i) spi_write_read_byte(buffer[i]); SPI_OFF(); /* Wait for the flash to do its thing */ if (wait_for_ready_status()) { puts("SPI Program Time out! "); return -1; } return i;}/* Write [count] bytes out of [buffer] into the given SPI [address] */static int write_sector(unsigned long address, long count, uchar *buffer){ long write_cnt; while (count != 0) { write_cnt = write_flash(address, count, buffer); if (write_cnt == -1) return -1; /* Now that we've sent some bytes out to the flash, update * our counters a bit */ count -= write_cnt; address += write_cnt; buffer += write_cnt; } /* return the appropriate error code */ return 0;}/* * Function: spi_write */ssize_t spi_write(uchar *addr, int alen, uchar *buffer, int len){ unsigned long offset; int start_sector, end_sector; int start_byte, end_byte; uchar *temp = NULL; int num, ret = 0; SPI_INIT(); if (spi_detect_part()) goto out; offset = addr[0] << 16 | addr[1] << 8 | addr[2]; /* Get the start block number */ start_sector = address_to_sector(offset); if (start_sector == -1) { puts("Invalid sector! "); goto out; } end_sector = address_to_sector(offset + len - 1); if (end_sector == -1) { puts("Invalid sector! "); goto out; } /* Since flashes operate in sector units but the eeprom command * operates as a continuous stream of bytes, we need to emulate * the eeprom behavior. So here we read in the sector, overlay * any bytes we're actually modifying, erase the sector, and * then write back out the new sector. */ temp = malloc(flash.sector_size); if (!temp) { puts("Malloc for sector failed! "); goto out; } for (num = start_sector; num <= end_sector; num++) { unsigned long address = num * flash.sector_size; /* XXX: should add an optimization when spanning sectors: * No point in reading in a sector if we're going to be * clobbering the whole thing. Need to also add a test * case to make sure the optimization is correct. */ if (read_flash(address, flash.sector_size, temp)) { puts("Read sector failed! "); len = 0; break; } start_byte = max(address, offset); end_byte = address + flash.sector_size - 1; if (end_byte > (offset + len)) end_byte = (offset + len - 1); memcpy(temp + start_byte - address, buffer + start_byte - offset, end_byte - start_byte + 1); if (erase_sector(address)) { puts("Erase sector failed! "); goto out; } if (write_sector(address, flash.sector_size, temp)) { puts("Write sector failed! "); goto out; } puts("."); } ret = len; out: free(temp); SPI_DEINIT(); return ret;}/* * Function: spi_read */ssize_t spi_read(uchar *addr, int alen, uchar *buffer, int len){ unsigned long offset; SPI_INIT(); if (spi_detect_part()) len = 0; else { offset = addr[0] << 16 | addr[1] << 8 | addr[2]; read_flash(offset, len, buffer); } SPI_DEINIT(); return len;}/* * Spit out some useful information about the SPI eeprom */int eeprom_info(void){ int ret = 0; SPI_INIT(); if (spi_detect_part()) ret = 1; else printf("SPI Device: %s 0x%02X (%s) 0x%02X 0x%02X\n" "Parameters: num sectors = %i, sector size = %i, write size = %i\n" "Flash Size: %i mbit (%i mbyte)\n" "Status: 0x%02X\n", flash.flash->name, flash.manufacturer_id, flash.manufacturer->name, flash.device_id1, flash.device_id2, flash.num_sectors, flash.sector_size, flash.write_length, (flash.num_sectors * flash.sector_size) >> 17, (flash.num_sectors * flash.sector_size) >> 20, read_status_register()); SPI_DEINIT(); return ret;}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -