diff options
Diffstat (limited to 'util/flashrom/sb600spi.c')
-rw-r--r-- | util/flashrom/sb600spi.c | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/util/flashrom/sb600spi.c b/util/flashrom/sb600spi.c new file mode 100644 index 0000000000..03504032e0 --- /dev/null +++ b/util/flashrom/sb600spi.c @@ -0,0 +1,175 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2008 Stefan Wildemann <stefan.wildemann@kontron.com> + * Copyright (C) 2008 Claus Gindhart <claus.gindhart@kontron.com> + * Copyright (C) 2008 Dominik Geyer <dominik.geyer@kontron.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <sys/mman.h> +#include <pci/pci.h> +#include "flash.h" +#include "spi.h" + +typedef struct _spi_controller { + unsigned int spi_cntrl0; /* 00h */ + unsigned int restrictedcmd1; /* 04h */ + unsigned int restrictedcmd2; /* 08h */ + unsigned int spi_cntrl1; /* 0ch */ + unsigned int spi_cmdvalue0; /* 10h */ + unsigned int spi_cmdvalue1; /* 14h */ + unsigned int spi_cmdvalue2; /* 18h */ + unsigned int spi_fakeid; /* 1Ch */ +} sb600_spi_controller; + +sb600_spi_controller *spi_bar = NULL; +uint8_t volatile *sb600_spibar; + +int sb600_spi_read(struct flashchip *flash, uint8_t *buf) +{ + int rc = 0, i; + int total_size = flash->total_size * 1024; + int page_size = 8; + + for (i = 0; i < total_size / page_size; i++) + spi_nbyte_read(i * page_size, (void *)(buf + i * page_size), + page_size); + return rc; +} + +uint8_t sb600_read_status_register(void) +{ + const unsigned char cmd[0x02] = { JEDEC_RDSR, 0x00 }; + unsigned char readarr[JEDEC_RDSR_INSIZE]; + + /* Read Status Register */ + spi_command(sizeof(cmd), sizeof(readarr), cmd, readarr); + return readarr[0]; +} + +int sb600_spi_write(struct flashchip *flash, uint8_t *buf) +{ + int rc = 0, i; + int total_size = flash->total_size * 1024; + + /* Erase first */ + printf("Erasing flash before programming... "); + flash->erase(flash); + printf("done.\n"); + + printf("Programming flash"); + for (i = 0; i < total_size; i++, buf++) { + spi_disable_blockprotect(); + spi_write_enable(); + spi_byte_program(i, *buf); + /* wait program complete. */ + if (i % 0x8000 == 0) + printf("."); + while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP) + ; + } + printf(" done.\n"); + return rc; +} + +void reset_internal_fifo_pointer(void) +{ + sb600_spibar[2] |= 0x10; + + while (sb600_spibar[0xD] & 0x7) + printf("reset\n"); +} + +void execute_command(void) +{ + sb600_spibar[2] |= 1; + + while (sb600_spibar[2] & 1) + ; +} + +int sb600_spi_command(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++; + + writecnt--; + + spi_bar = (sb600_spi_controller *) sb600_spibar; + + printf_debug("%s, cmd=%x, writecnt=%x, readcnt=%x\n", + __func__, cmd, writecnt, readcnt); + + if (readcnt > 8) { + printf("%s, SB600 SPI controller can not receive %d bytes, " + "which is limited with 8 bytes\n", __func__, readcnt); + return 1; + } + + if (writecnt > 8) { + printf("%s, SB600 SPI controller can not sent %d bytes, " + "which is limited with 8 bytes\n", __func__, writecnt); + return 1; + } + + sb600_spibar[0] = cmd; + sb600_spibar[1] = readcnt << 4 | (writecnt); + + /* Before we use the FIFO, reset it first. */ + reset_internal_fifo_pointer(); + + /* Send the write byte to FIFO. */ + for (count = 0; count < writecnt; count++, writearr++) { + printf_debug(" [%x]", *writearr); + sb600_spibar[0xC] = *writearr; + } + printf_debug("\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. + */ + reset_internal_fifo_pointer(); + + execute_command(); + + /* + * After the command executed, we should find out the index of the + * received byte. Here we just reset the FIFO pointer, skip the + * writecnt, is there anyone who have anther method to replace it? + */ + reset_internal_fifo_pointer(); + + for (count = 0; count < writecnt; count++) { + cmd = sb600_spibar[0xC]; /* Skip the byte we send. */ + printf_debug("[ %2x]", cmd); + } + + printf_debug("The FIFO pointer 6 is %d.\n", sb600_spibar[0xd] & 0x07); + for (count = 0; count < readcnt; count++, readarr++) { + *readarr = sb600_spibar[0xC]; + printf_debug("[%02x]", *readarr); + } + printf_debug("\n"); + + return 0; +} |