From a3f04be761d45aed2f6113eb2a6d08679370f546 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 28 Nov 2008 21:36:51 +0000 Subject: Add support for the AMD/ATI SB600 southbridge SPI functionality This has been tested by Uwe Hermann on an RS690/SB600 board. Corresponding to flashrom svn r351 and coreboot v2 svn r3779. Signed-off-by: Jason Wang Reviewed-by: Joe Bao Acked-by: Uwe Hermann --- Makefile | 2 +- chipset_enable.c | 35 +++++++---- flash.h | 9 +++ flashrom.c | 11 ++-- sb600spi.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ spi.c | 27 ++++++++- spi.h | 5 ++ 7 files changed, 245 insertions(+), 19 deletions(-) create mode 100644 sb600spi.c diff --git a/Makefile b/Makefile index 09c68ded..c79d402b 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ OBJS = chipset_enable.o board_enable.o udelay.o jedec.o stm50flw0x0x.o \ w49f002u.o 82802ab.o pm49fl00x.o sst49lf040.o en29f002a.o \ sst49lfxxxc.o sst_fwhub.o layout.o cbtable.o flashchips.o \ flashrom.o w39v080fa.o sharplhf00l04.o w29ee011.o spi.o it87spi.o \ - ichspi.o w39v040c.o + ichspi.o w39v040c.o sb600spi.o all: pciutils dep $(PROGRAM) diff --git a/chipset_enable.c b/chipset_enable.c index 658313d5..22eceb29 100644 --- a/chipset_enable.c +++ b/chipset_enable.c @@ -647,21 +647,36 @@ static int enable_flash_amd8111(struct pci_dev *dev, const char *name) static int enable_flash_sb600(struct pci_dev *dev, const char *name) { - uint32_t old, new; + uint32_t tmp, low_bits, num; uint8_t reg; - /* Clear ROM Protect 0-3 */ + low_bits = tmp = pci_read_long(dev, 0xa0); + low_bits &= ~0xffffc000; /* for mmap aligning requirements */ + low_bits &= 0xfffffff0; /* remove low 4 bits */ + tmp &= 0xffffc000; + printf_debug("SPI base address is at 0x%x\n", tmp + low_bits); + + sb600_spibar = mmap(0, 0x4000, PROT_READ | PROT_WRITE, MAP_SHARED, + fd_mem, (off_t)tmp); + if (sb600_spibar == MAP_FAILED) { + perror("Can't mmap memory using " MEM_DEV); + exit(1); + } + sb600_spibar += low_bits; + + /* Clear ROM protect 0-3. */ for (reg = 0x50; reg < 0x60; reg += 4) { - old = pci_read_long(dev, reg); - new = old & 0xFFFFFFFC; - if (new != old) { - pci_write_byte(dev, reg, new); - if (pci_read_long(dev, reg) != new) { - printf("tried to set 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x50, new, name); - } - } + num = pci_read_long(dev, reg); + num &= 0xfffffffc; + pci_write_byte(dev, reg, num); } + flashbus = BUS_TYPE_SB600_SPI; + + /* Enable SPI ROM in SB600 PM register. */ + OUTB(0x8f, 0xcd6); + OUTB(0x0e, 0xcd7); + return 0; } diff --git a/flash.h b/flash.h index d1e417fb..cbc2e48a 100644 --- a/flash.h +++ b/flash.h @@ -414,6 +414,7 @@ typedef enum { BUS_TYPE_ICH7_SPI, BUS_TYPE_ICH9_SPI, BUS_TYPE_IT87XX_SPI, + BUS_TYPE_SB600_SPI, BUS_TYPE_VIA_SPI } flashbus_t; @@ -497,6 +498,14 @@ int it8716f_spi_command(unsigned int writecnt, unsigned int readcnt, int it8716f_spi_chip_read(struct flashchip *flash, uint8_t *buf); int it8716f_spi_chip_write(struct flashchip *flash, uint8_t *buf); +/* sb600spi.c */ +int sb600_spi_command(unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr); +int sb600_spi_read(struct flashchip *flash, uint8_t *buf); +int sb600_spi_write(struct flashchip *flash, uint8_t *buf); +uint8_t sb600_read_status_register(void); +extern uint8_t volatile *sb600_spibar; + /* jedec.c */ uint8_t oddparity(uint8_t val); void toggle_ready_jedec(volatile uint8_t *dst); diff --git a/flashrom.c b/flashrom.c index 4f1b4880..9357d048 100644 --- a/flashrom.c +++ b/flashrom.c @@ -463,7 +463,7 @@ int main(int argc, char *argv[]) perror(filename); exit(1); } - printf("Force reading flash..."); + printf("Force reading flash... "); if (!flashes[0]->read) memcpy(buf, (const char *)flashes[0]->virtual_memory, size); else @@ -476,7 +476,7 @@ int main(int argc, char *argv[]) fwrite(buf, sizeof(char), size, image); fclose(image); - printf("done\n"); + printf("done.\n"); free(buf); exit(0); } @@ -533,19 +533,20 @@ int main(int argc, char *argv[]) buf = (uint8_t *) calloc(size, sizeof(char)); if (erase_it) { - printf("Erasing flash chip.\n"); + printf("Erasing flash chip... "); if (!flash->erase) { fprintf(stderr, "Error: flashrom has no erase function for this flash chip.\n"); return 1; } flash->erase(flash); + printf("done.\n"); exit(0); } else if (read_it) { if ((image = fopen(filename, "w")) == NULL) { perror(filename); exit(1); } - printf("Reading Flash..."); + printf("Reading flash... "); if (flash->read == NULL) memcpy(buf, (const char *)flash->virtual_memory, size); else @@ -557,7 +558,7 @@ int main(int argc, char *argv[]) fwrite(buf, sizeof(char), size, image); fclose(image); - printf("done\n"); + printf("done.\n"); } else { struct stat image_stat; diff --git a/sb600spi.c b/sb600spi.c new file mode 100644 index 00000000..03504032 --- /dev/null +++ b/sb600spi.c @@ -0,0 +1,175 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2008 Stefan Wildemann + * Copyright (C) 2008 Claus Gindhart + * Copyright (C) 2008 Dominik Geyer + * + * 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 +#include +#include +#include +#include +#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; +} diff --git a/spi.c b/spi.c index 8812eff1..b31a6b87 100644 --- a/spi.c +++ b/spi.c @@ -42,6 +42,8 @@ int spi_command(unsigned int writecnt, unsigned int readcnt, case BUS_TYPE_ICH9_SPI: case BUS_TYPE_VIA_SPI: return ich_spi_command(writecnt, readcnt, writearr, readarr); + case BUS_TYPE_SB600_SPI: + return sb600_spi_command(writecnt, readcnt, writearr, readarr); default: printf_debug ("%s called, but no SPI chipset/strapping detected\n", @@ -157,6 +159,7 @@ int probe_spi_rdid4(struct flashchip *flash) case BUS_TYPE_ICH7_SPI: case BUS_TYPE_ICH9_SPI: case BUS_TYPE_VIA_SPI: + case BUS_TYPE_SB600_SPI: return probe_spi_rdid_generic(flash, 4); default: printf_debug("4b ID not supported on this SPI controller\n"); @@ -229,7 +232,13 @@ uint8_t spi_read_status_register() unsigned char readarr[JEDEC_RDSR_INSIZE]; /* Read Status Register */ - spi_command(sizeof(cmd), sizeof(readarr), cmd, readarr); + if (flashbus == BUS_TYPE_SB600_SPI) { + /* SB600 uses a different way to read status register. */ + return sb600_read_status_register(); + } else { + spi_command(sizeof(cmd), sizeof(readarr), cmd, readarr); + } + return readarr[0]; } @@ -464,6 +473,14 @@ int spi_sector_erase(const struct flashchip *flash, unsigned long addr) return 0; } +int spi_write_status_enable() +{ + const unsigned char cmd[JEDEC_EWSR_OUTSIZE] = { JEDEC_EWSR }; + + /* Send EWSR (Enable Write Status Register). */ + return spi_command(JEDEC_EWSR_OUTSIZE, JEDEC_EWSR_INSIZE, cmd, NULL); +} + /* * This is according the SST25VF016 datasheet, who knows it is more * generic that this... @@ -500,9 +517,9 @@ int spi_disable_blockprotect(void) /* If there is block protection in effect, unprotect it first. */ if ((status & 0x3c) != 0) { printf_debug("Some block protection in effect, disabling\n"); - result = spi_write_enable(); + result = spi_write_status_enable(); if (result) { - printf_debug("spi_write_enable failed\n"); + printf_debug("spi_write_status_enable failed\n"); return result; } result = spi_write_status_register(status & ~0x3c); @@ -532,6 +549,8 @@ int spi_chip_read(struct flashchip *flash, uint8_t *buf) switch (flashbus) { case BUS_TYPE_IT87XX_SPI: return it8716f_spi_chip_read(flash, buf); + case BUS_TYPE_SB600_SPI: + return sb600_spi_read(flash, buf); case BUS_TYPE_ICH7_SPI: case BUS_TYPE_ICH9_SPI: case BUS_TYPE_VIA_SPI: @@ -550,6 +569,8 @@ int spi_chip_write(struct flashchip *flash, uint8_t *buf) switch (flashbus) { case BUS_TYPE_IT87XX_SPI: return it8716f_spi_chip_write(flash, buf); + case BUS_TYPE_SB600_SPI: + return sb600_spi_write(flash, buf); case BUS_TYPE_ICH7_SPI: case BUS_TYPE_ICH9_SPI: case BUS_TYPE_VIA_SPI: diff --git a/spi.h b/spi.h index c096dce5..25ce2977 100644 --- a/spi.h +++ b/spi.h @@ -80,6 +80,11 @@ #define JEDEC_RDSR_INSIZE 0x01 #define JEDEC_RDSR_BIT_WIP (0x01 << 0) +/* Write Status Enable */ +#define JEDEC_EWSR 0x50 +#define JEDEC_EWSR_OUTSIZE 0x01 +#define JEDEC_EWSR_INSIZE 0x00 + /* Write Status Register */ #define JEDEC_WRSR 0x01 #define JEDEC_WRSR_OUTSIZE 0x02 -- cgit v1.2.3