diff options
author | Dominik Geyer <dominik.geyer@kontron.com> | 2008-05-16 12:55:55 +0000 |
---|---|---|
committer | Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net> | 2008-05-16 12:55:55 +0000 |
commit | fac0afb87d216e51284b934042cfeb6f27e70864 (patch) | |
tree | 1f96fc2a55d9d43fb000f26ab3584102a04aa727 | |
parent | e7b11577641b3f78eda07000583ebdd5e62fed4c (diff) |
Add support for SPI chips on ICH9. This is done by using the generic SPI
interface.
Signed-off-by: Dominik Geyer <dominik.geyer@kontron.com>
Acked-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>
git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3325 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
-rw-r--r-- | util/flashrom/Makefile | 3 | ||||
-rw-r--r-- | util/flashrom/chipset_enable.c | 2 | ||||
-rw-r--r-- | util/flashrom/flash.h | 8 | ||||
-rw-r--r-- | util/flashrom/ichspi.c | 468 | ||||
-rw-r--r-- | util/flashrom/spi.c | 6 |
5 files changed, 484 insertions, 3 deletions
diff --git a/util/flashrom/Makefile b/util/flashrom/Makefile index 2f0b290eae..35b891db1a 100644 --- a/util/flashrom/Makefile +++ b/util/flashrom/Makefile @@ -24,7 +24,8 @@ OBJS = chipset_enable.o board_enable.o udelay.o jedec.o stm50flw0x0x.c \ sst28sf040.o am29f040b.o mx29f002.o sst39sf020.o m29f400bt.o \ w49f002u.o 82802ab.o msys_doc.o pm49fl004.o sst49lf040.o \ sst49lfxxxc.o sst_fwhub.o layout.o cbtable.o flashchips.o \ - flashrom.o w39v080fa.o sharplhf00l04.o w29ee011.o spi.o it87spi.o + flashrom.o w39v080fa.o sharplhf00l04.o w29ee011.o spi.o it87spi.o \ + ichspi.o all: pciutils dep $(PROGRAM) diff --git a/util/flashrom/chipset_enable.c b/util/flashrom/chipset_enable.c index 3dec881ed1..fdcae7be02 100644 --- a/util/flashrom/chipset_enable.c +++ b/util/flashrom/chipset_enable.c @@ -198,7 +198,7 @@ static int enable_flash_ich_dc_spi(struct pci_dev *dev, const char *name, unsign /* Calculate the Root Complex Register Block address */ tmp &= 0xffffc000; printf_debug("Root Complex Register Block address = 0x%x\n", tmp); - rcrb = mmap(0, 0x4000, PROT_READ, MAP_SHARED, fd_mem, (off_t)tmp); + rcrb = mmap(0, 0x4000, PROT_READ | PROT_WRITE, MAP_SHARED, fd_mem, (off_t)tmp); if (rcrb == MAP_FAILED) { perror("Can't mmap memory using " MEM_DEV); exit(1); diff --git a/util/flashrom/flash.h b/util/flashrom/flash.h index 968fba44b3..673643f7e6 100644 --- a/util/flashrom/flash.h +++ b/util/flashrom/flash.h @@ -384,7 +384,6 @@ extern char *lb_part, *lb_vendor; /* spi.c */ int probe_spi_rdid(struct flashchip *flash); int probe_spi_res(struct flashchip *flash); -int it87xx_probe_spi_flash(const char *name); int spi_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr); void spi_write_enable(); void spi_write_disable(); @@ -407,8 +406,14 @@ int probe_29f040b(struct flashchip *flash); int erase_29f040b(struct flashchip *flash); int write_29f040b(struct flashchip *flash, uint8_t *buf); +/* ichspi.c */ +int ich_spi_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr); +int ich_spi_read(struct flashchip *flash, uint8_t * buf); +int ich_spi_write(struct flashchip *flash, uint8_t * buf); + /* it87spi.c */ extern uint16_t it8716f_flashport; +int it87xx_probe_spi_flash(const char *name); int it8716f_spi_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr); int it8716f_spi_chip_read(struct flashchip *flash, uint8_t *buf); int it8716f_spi_chip_write(struct flashchip *flash, uint8_t *buf); @@ -499,4 +504,5 @@ int write_49f002(struct flashchip *flash, uint8_t *buf); int probe_stm50flw0x0x(struct flashchip *flash); int erase_stm50flw0x0x(struct flashchip *flash); int write_stm50flw0x0x(struct flashchip *flash, uint8_t *buf); + #endif /* !__FLASH_H__ */ diff --git a/util/flashrom/ichspi.c b/util/flashrom/ichspi.c new file mode 100644 index 0000000000..79967972b9 --- /dev/null +++ b/util/flashrom/ichspi.c @@ -0,0 +1,468 @@ +/* + * 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 + * + */ + +/* + * This module is designed for supporting the devices + * ST M25P40 + * ST M25P80 + * ST M25P16 + * ST M25P32 already tested + * ST M25P64 + * AT 25DF321 already tested + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <sys/mman.h> +#include <pci/pci.h> +#include "flash.h" +#include "spi.h" + +#define MAXDATABYTES 0x40 + +/*ICH9 controller register definition*/ +#define REG_FADDR 0x08 /* 32 Bits */ +#define REG_FDATA0 0x10 /* 64 Bytes */ +#define REG_SSFS 0x90 /* 08 Bits */ +#define SSFS_SCIP 0x00000001 +#define SSFS_CDS 0x00000004 +#define SSFS_FCERR 0x00000008 +#define SSFS_AEL 0x00000010 +#define REG_SSFC 0x91 /* 24 Bits */ +#define SSFC_SCGO 0x00000200 +#define SSFC_ACS 0x00000400 +#define SSFC_SPOP 0x00000800 +#define SSFC_COP 0x00001000 +#define SSFC_DBC 0x00010000 +#define SSFC_DS 0x00400000 +#define SSFC_SME 0x00800000 +#define SSFC_SCF 0x01000000 +#define SSFC_SCF_20MHZ 0x00000000 +#define SSFC_SCF_33MHZ 0x01000000 +#define REG_PREOP 0x94 /* 16 Bits */ +#define REG_OPTYPE 0x96 /* 16 Bits */ +#define REG_OPMENU 0x98 /* 64 BITS */ + +// ICH9R SPI commands +#define SPI_OPCODE_TYPE_READ_NO_ADDRESS 0 +#define SPI_OPCODE_TYPE_WRITE_NO_ADDRESS 1 +#define SPI_OPCODE_TYPE_READ_WITH_ADDRESS 2 +#define SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS 3 + + +typedef struct _OPCODE { + uint8_t opcode; //This commands spi opcode + uint8_t spi_type; //This commands spi type + uint8_t atomic; //Use preop: (0: none, 1: preop0, 2: preop1 +} OPCODE; + +/* Opcode definition: + * Preop 1: Write Enable + * Preop 2: Write Status register enable + * + * OP 0: Write address + * OP 1: Read Address + * OP 2: ERASE block + * OP 3: Read Status register + * OP 4: Read ID + * OP 5: Write Status register + * OP 6: chip private (read JDEC id) + * OP 7: Chip erase + */ +typedef struct _OPCODES { + uint8_t preop[2]; + OPCODE opcode[8]; +} OPCODES; + + +static OPCODES *curopcodes=NULL; + + +/* HW access functions */ +static inline uint32_t REGREAD32(int X) +{ + volatile uint32_t regval; + regval = *(volatile uint32_t *)((uint8_t *)ich_spibar + X); + return regval; +} + +#define REGWRITE32(X,Y) (*(uint32_t *)((uint8_t *)ich_spibar+X)=Y) +#define REGWRITE16(X,Y) (*(uint16_t *)((uint8_t *)ich_spibar+X)=Y) +#define REGWRITE8(X,Y) (*(uint8_t *)((uint8_t *)ich_spibar+X)=Y) + + +/* Common SPI functions */ +static int program_opcodes(OPCODES * op); +static int run_opcode(uint8_t nr, OPCODE op, uint32_t offset, uint8_t datalength, uint8_t * data); +static int ich_spi_read_page(struct flashchip *flash, uint8_t * buf, int Offset); +static int ich_spi_write_page(struct flashchip *flash, uint8_t * bytes, int Offset); +static int ich_spi_erase_block(struct flashchip *flash, int offset); + + +OPCODES O_ST_M25P = { + { + JEDEC_WREN, + 0 + }, + { + {JEDEC_BYTE_PROGRAM, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS, 1}, // Write Byte + {JEDEC_READ, SPI_OPCODE_TYPE_READ_WITH_ADDRESS, 0}, // Read Data + {JEDEC_BE_D8, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS, 1}, // Erase Sector + {JEDEC_RDSR, SPI_OPCODE_TYPE_READ_NO_ADDRESS, 0}, // Read Device Status Reg + {JEDEC_RES, SPI_OPCODE_TYPE_READ_WITH_ADDRESS, 0}, // Resume Deep Power-Down + {JEDEC_WRSR, SPI_OPCODE_TYPE_WRITE_NO_ADDRESS, 1}, // Write Status Register + {JEDEC_RDID, SPI_OPCODE_TYPE_READ_NO_ADDRESS, 0}, // Read JDEC ID + {JEDEC_CE_C7, SPI_OPCODE_TYPE_WRITE_NO_ADDRESS, 1}, // Bulk erase + } +}; + + +int program_opcodes(OPCODES * op) +{ + uint8_t a; + uint16_t temp16; + uint32_t temp32; + + /* Program Prefix Opcodes */ + temp16 = 0; + /* 0:7 Prefix Opcode 1 */ + temp16 = (op->preop[0]); + /* 8:16 Prefix Opcode 2 */ + temp16 |= ((uint16_t) op->preop[1]) << 8; + REGWRITE16(REG_PREOP, temp16); + + /*Program Opcode Types 0 - 7 */ + temp16 = 0; + for (a = 0; a < 8; a++) { + temp16 |= ((uint16_t) op->opcode[a].spi_type) << (a * 2); + } + REGWRITE16(REG_OPTYPE, temp16); + + /*Program Allowable Opcodes 0 - 3 */ + temp32 = 0; + for (a = 0; a < 4; a++) { + temp32 |= ((uint32_t) op->opcode[a].opcode) << (a * 8); + } + REGWRITE32(REG_OPMENU, temp32); + + /*Program Allowable Opcodes 4 - 7 */ + temp32 = 0; + for (a = 4; a < 8; a++) { + temp32 |= ((uint32_t) op->opcode[a].opcode) << ((a - 4) * 8); + } + REGWRITE32(REG_OPMENU + 4, temp32); + + return 0; +} + +int run_opcode(uint8_t nr, OPCODE op, uint32_t offset, uint8_t datalength, + uint8_t * data) +{ + int write_cmd = 0; + uint32_t temp32; + uint32_t a; + + /* Is it a write command? */ + if ((op.spi_type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS) + || (op.spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS)) { + write_cmd = 1; + } + + /* Programm Offset in Flash into FADDR */ + REGWRITE32(REG_FADDR, (offset & 0x00FFFFFF)); /*SPI addresses are 24 BIT only */ + + /* Program data into FDATA0 to N */ + if (write_cmd && (datalength != 0)) { + temp32 = 0; + for (a = 0; a < datalength; a++) { + if ((a % 4) == 0) { + temp32 = 0; + } + + temp32 |= ((uint32_t) data[a]) << ((a % 4) * 8); + + if ((a % 4) == 3) { + REGWRITE32(REG_FDATA0 + (a - (a % 4)), temp32); + } + } + if (((a - 1) % 4) != 3) { + REGWRITE32(REG_FDATA0 + ((a - 1) - ((a - 1) % 4)), + temp32); + } + + } + + /* Assemble SSFS + SSFC */ + temp32 = 0; + + /* clear error status registers */ + temp32 |= (SSFS_CDS + SSFS_FCERR); + /* USE 20 MhZ */ + temp32 |= SSFC_SCF_20MHZ; + + if (datalength != 0) { + uint32_t datatemp; + temp32 |= SSFC_DS; + datatemp = ((uint32_t) ((datalength - 1) & 0x3f)) << (8 + 8); + temp32 |= datatemp; + } + + /* Select opcode */ + temp32 |= ((uint32_t) (nr & 0x07)) << (8 + 4); + + /* Handle Atomic */ + if (op.atomic != 0) { + /* Select atomic command */ + temp32 |= SSFC_ACS; + /* Selct prefix opcode */ + if ((op.atomic - 1) == 1) { + /*Select prefix opcode 2 */ + temp32 |= SSFC_SPOP; + } + } + + /* Start */ + temp32 |= SSFC_SCGO; + + /* write it */ + REGWRITE32(REG_SSFS, temp32); + + /*wait for cycle complete */ + while ((REGREAD32(REG_SSFS) & SSFS_CDS) == 0) { + /*TODO; Do something that this can't lead into an endless loop. but some + * commands may cause this to be last more than 30 seconds */ + } + + if ((REGREAD32(REG_SSFS) & SSFS_FCERR) != 0) { + printf_debug("Transaction error!\n"); + return 1; + } + + if ((!write_cmd) && (datalength != 0)) { + for (a = 0; a < datalength; a++) { + if ((a % 4) == 0) { + temp32 = REGREAD32(REG_FDATA0 + (a)); + } + + data[a] = + (temp32 & (((uint32_t) 0xff) << ((a % 4) * 8))) >> + ((a % 4) * 8); + } + } + + return 0; +} + + +static int ich_spi_erase_block(struct flashchip *flash, int offset) +{ + printf_debug("Spi_Erase,Offset=%d,sectors=%d\n", offset, 1); + + if (run_opcode(2, curopcodes->opcode[2], offset, 0, NULL) != 0) { + printf_debug("Error erasing sector at 0x%x", offset); + return -1; + } + + printf("DONE BLOCK 0x%x\n", offset); + + return 0; +} + +static int ich_spi_read_page(struct flashchip *flash, uint8_t * buf, int Offset) +{ + int page_size = flash->page_size; + uint32_t remaining = flash->page_size; + int a; + + printf_debug("Spi_Read,Offset=%d,number=%d,buf=%p\n", Offset, page_size, buf); + + for (a = 0; a < page_size; a += MAXDATABYTES) { + if (remaining < MAXDATABYTES) { + + if (run_opcode + (1, curopcodes->opcode[1], + Offset + (page_size - remaining), remaining, + &buf[page_size - remaining]) != 0) { + printf_debug("Error reading"); + return 1; + } + remaining = 0; + } else { + if (run_opcode + (1, curopcodes->opcode[1], + Offset + (page_size - remaining), MAXDATABYTES, + &buf[page_size - remaining]) != 0) { + printf_debug("Error reading"); + return 1; + } + remaining -= MAXDATABYTES; + } + } + + return 0; +} + +static int ich_spi_write_page(struct flashchip *flash, uint8_t * bytes, + int Offset) +{ + int page_size = flash->page_size; + uint32_t remaining = page_size; + int a; + + printf_debug("write_page_ichspi,Offset=%d,number=%d,buf=%p\n", Offset, page_size, + bytes); + + for (a = 0; a < page_size; a += MAXDATABYTES) { + if (remaining < MAXDATABYTES) { + if (run_opcode + (0, curopcodes->opcode[0], + Offset + (page_size - remaining), remaining, + &bytes[page_size - remaining]) != 0) { + printf_debug("Error writing"); + return 1; + } + remaining = 0; + } else { + if (run_opcode + (0, curopcodes->opcode[0], + Offset + (page_size - remaining), MAXDATABYTES, + &bytes[page_size - remaining]) != 0) { + printf_debug("Error writing"); + return 1; + } + remaining -= MAXDATABYTES; + } + } + + return 0; +} + + +int ich_spi_read(struct flashchip *flash, uint8_t * buf) +{ + int i, rc = 0; + int total_size = flash->total_size * 1024; + int page_size = flash->page_size; + + for (i = 0; (i < total_size / page_size) && (rc == 0); i++) { + rc = ich_spi_read_page(flash, (void *)(buf + i * page_size), + i * page_size); + } + + return rc; +} + + +int ich_spi_write(struct flashchip *flash, uint8_t * buf) +{ + int i, j, rc = 0; + int total_size = flash->total_size * 1024; + int page_size = flash->page_size; + int erase_size = 64 * 1024; + + spi_disable_blockprotect(); + + printf("Programming page: \n"); + + for (i = 0; i < total_size / erase_size; i++) { + rc = ich_spi_erase_block(flash, i * erase_size); + if (rc) { + printf("Error erasing block at 0x%x\n", i); + break; + } + + for (j = 0; j < erase_size / page_size; j++) { + ich_spi_write_page(flash, (void *)(buf + (i * erase_size) + (j * page_size)), + (i * erase_size) + (j * page_size)); + } + } + + printf("\n"); + + return rc; +} + +int ich_spi_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr) +{ + int a; + int opcode_index = -1; + const unsigned char cmd = *writearr; + OPCODE *opcode; + uint32_t addr = 0; + uint8_t *data; + int count; + + /* program opcodes if not already done */ + if (curopcodes == NULL) { + printf_debug("Programming OPCODES\n"); + curopcodes=&O_ST_M25P; + program_opcodes(curopcodes); + } + + /* find cmd in opcodes-table */ + for (a = 0; a < 8; a++) { + if ((curopcodes->opcode[a]).opcode == cmd) { + opcode_index = a; + break; + } + } + + /* unknown / not programmed command */ + if (opcode_index == -1) { + printf_debug("Invalid OPCODE 0x%02x\n", cmd); + return 1; + } + + opcode = &(curopcodes->opcode[opcode_index]); + + /* if opcode-type requires an address */ + if (opcode->spi_type == SPI_OPCODE_TYPE_READ_WITH_ADDRESS || + opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) { + addr = (writearr[1]<<16) | + (writearr[2]<<8) | + (writearr[3]<<0); + } + + /* translate read/write array/count */ + if (opcode->spi_type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS) { + data = (uint8_t*)(writearr+1); + count = writecnt-1; + } + else if (opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) { + data = (uint8_t*)(writearr+4); + count = writecnt-4; + } + else { + data = (uint8_t*)readarr; + count = readcnt; + } + + if (run_opcode(opcode_index, *opcode, addr, count, data) != 0) { + printf_debug("run OPCODE 0x%02x failed\n", opcode->opcode); + return 1; + } + + return 0; +} diff --git a/util/flashrom/spi.c b/util/flashrom/spi.c index f50414cbc5..3a25a100b9 100644 --- a/util/flashrom/spi.c +++ b/util/flashrom/spi.c @@ -35,6 +35,8 @@ int spi_command(unsigned int writecnt, unsigned int readcnt, const unsigned char { if (it8716f_flashport) return it8716f_spi_command(writecnt, readcnt, writearr, readarr); + else if (ich9_detected) + return ich_spi_command(writecnt, readcnt, writearr, readarr); printf_debug("%s called, but no SPI chipset detected\n", __FUNCTION__); return 1; } @@ -349,6 +351,8 @@ int spi_chip_read(struct flashchip *flash, uint8_t *buf) { if (it8716f_flashport) return it8716f_spi_chip_read(flash, buf); + else if (ich9_detected) + return ich_spi_read(flash, buf); printf_debug("%s called, but no SPI chipset detected\n", __FUNCTION__); return 1; } @@ -357,6 +361,8 @@ int spi_chip_write(struct flashchip *flash, uint8_t *buf) { if (it8716f_flashport) return it8716f_spi_chip_write(flash, buf); + else if (ich9_detected) + return ich_spi_write(flash, buf); printf_debug("%s called, but no SPI chipset detected\n", __FUNCTION__); return 1; } |