Fintek SPI Driver file for Flashrom
Index: fintek_spi.c =================================================================== --- fintek_spi.c (revision 0) +++ fintek_spi.c (revision 0) @@ -0,0 +1,384 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2008 Sean Nelson snelson@nmt.edu + * + * 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; version 2 of the License. + * + * 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 + */ + +/* + * Contains the Fintek F7188x SPI specific routines + * + * The Fintek SPI is different from the it87xxx SIO in that there's no + * base address to help program through SPI, but in the F7188x SIOs the + * SPI Interface is accessed through LDN 0x8 and configuration registers + * 0xf0 - 0xf8 and 0xfa - 0xff. + */ + +#include <stdio.h> +#include <pci/pci.h> +#include <stdint.h> +#include <string.h> +#include "flash.h" +#include "spi.h" + +#define FINTEK_SUPERIO_PORT1 0x4e +#define FINTEK_SUPERIO_PORT2 0x2e + +/* Logical Device Number for the SPI Device in Fintek SIO */ +#define SPI_LDN 0x08 + +/* the Configuration Register for ROM address masking + * Bit 7 is the ROM Write Enable. + * Bit 0 which enables address range 0xfff00000 - 0xfff7ffff + * The rest of the bits are 'power on trapped' + * via pull-up/down sio pin connections. + */ +#define FINTEK_SIO_ROM_ADDR_SELECT 0x27 + +/* Configuration Registers of the SPI Device */ +#define SPI_CTRL 0xf0 +#define SPI_TIMEOUT 0xf1 +#define SPI_BAUD 0xf2 +#define SPI_STATUS 0xf3 +#define SPI_H_DATA 0xf4 +#define SPI_CMD_DATA 0xf5 +#define SPI_CS 0xf6 +#define SPI_MMAP 0xf7 +#define SPI_OP 0xf8 +#define SPI_L_DATA 0xfa +#define SPI_H_ADDR 0xfb +#define SPI_M_ADDR 0xfc +#define SPI_L_ADDR 0xfd +#define SPI_PROG 0xfe +#define SPI_WR_DATA 0xff + +uint16_t fintek_flashport = 0; + +/* + * use fast 33MHz SPI (<>0) or slow 16MHz (0) + */ +int fast_spi = 1; + +/* + * for now assume SST Flash style continuous program mode + * set to 0 to use AMD style continuous + */ +int sst_continuous = 1; + +/* Generic Super I/O helper functions */ +uint8_t regval(uint16_t port, uint8_t reg) +{ + OUTB(reg, port); + return INB(port + 1); +} + +void regwrite(uint16_t port, uint8_t reg, uint8_t val) +{ + OUTB(reg, port); + OUTB(val, port + 1); +} + +/* Helper functions for most recent ITE IT87xx Super I/O chips */ +#define CHIP_ID_BYTE1_REG 0x20 +#define CHIP_ID_BYTE2_REG 0x21 +static void enter_conf_mode_fintek(uint16_t port) +{ + OUTB(0x87, port); + OUTB(0x87, port); +} + +static void exit_conf_mode_fintek(uint16_t port) +{ + OUTB(0xaa, port); +} + +static uint16_t find_fintek_spi_flash_port(uint16_t port) +{ + uint8_t tmp = 0; + uint16_t id, flashport = 0; + + enter_conf_mode_fintek(port); + + id = regval(port, CHIP_ID_BYTE1_REG) << 8; + id |= regval(port, CHIP_ID_BYTE2_REG); + + /* id 0x0541 is good for F71883 and F71882 according to datasheets */ + if (id == 0x0541) { + /* + * ROM Address Select Register (readonly): NOLDN, reg 0x27 + * bit 7 - ROM Write Enable + * bit 6 - SPI Enable + * bit 5 - use SPI as Backup BIOS; 0b = use SPI as Primary BIOS + * bit 4 - port 0x4e as config reg port; 0b = port is 2e + * bit 3 - enable addr 0x000e0000 to 0x000effff decoding + * + * bit 2 - enable addr 0xfff80000 to 0xffffffff decoding + * and addr 0x000f0000 to 0x000fffff decoding + * + * bit 1 - enable addr 0xffee000 to 0xffeeffff decoding + * + * bit 0 - enable addr 0xfff0000 to 0xfff7ffff decoding + * default: disable + */ + tmp = regval(port, 0x27); + printf("Serial flash segment 0x%08x-0x%08x %sabled\n", + 0xFFF00000, 0xFFF7FFFF, (tmp & 1 << 0) ? "en" : "dis"); + printf("Serial flash segment 0x%08x-0x%08x %sabled\n", + 0xFFEE0000, 0xFFEEFFFF, (tmp & 1 << 1) ? "en" : "dis"); + printf("Serial flash segment 0x%08x-0x%08x %sabled\n", + 0xFFF80000, 0xFFFFFFFF, (tmp & 1 << 2) ? "en" : "dis"); + printf("Serial flash segment 0x%08x-0x%08x %sabled\n", + 0x000F0000, 0x000FFFFF, (tmp & 1 << 2) ? "en" : "dis"); + printf("Serial flash segment 0x%08x-0x%08x %sabled\n", + 0x000E0000, 0x000EFFFF, (tmp & 1 << 3) ? "en" : "dis"); + printf("LPC write to serial flash %sabled\n", + (tmp & 1 << 7) ? "en" : "dis"); + + /* If any serial flash segment is enabled, enable writing. */ + if ((tmp & 0xf) && (!(tmp & 1 << 7))) { + printf("Enabling LPC write to serial flash\n"); + tmp |= 1 << 7; + regwrite(port, 0x27, tmp); + } + + /* + * Since we have to use configuration registers of the SIO, we use + * LDN 0x8, reg 0x27 to figure out the SIO port we use for SPI + * programming and use LDN 0x8's config registers in programming + * the flash via SPI. If bit 4 is 1 then the port is 0x4e, + * otherwise we use SIO port 0x2e. + */ + if (tmp & 1 << 4) + { + flashport = FINTEK_SUPERIO_PORT1; + } else + { + flashport = FINTEK_SUPERIO_PORT2; + } + } + exit_conf_mode_fintek(port); + return flashport; +} + +int fintek_probe_spi_flash(const char *name) +{ + fintek_flashport = find_ite_spi_flash_port(FINTEK_SUPERIO_PORT1); + + if (!fintek_flashport) + fintek_flashport = find_ite_spi_flash_port(FINTEK_SUPERIO_PORT2); + + if (fintek_flashport) + flashbus = BUS_TYPE_FINTEK_SPI; + + return (!fintek_flashport); +} + +/* + * spi commands use various SPI configuration registers: + * Baud Rate Divistor Register (0xf2) -- bits 2-0 chooses between + * 33MHz or 16.7MHz SCK frequency. + * Status Register (0xf3) -- Interrupt status and operation status. + * High Byte Data Register (0xf4) -- received upper 8 bits in a operation. + * Command Data Register (0xf5) -- command value for flash command. + * Operate Register (0xf8) -- setting a bit will start the IO of SPI + * Low Byte Data Register (0xfa) -- received lower 8 bits in a operation. + * Address High Byte Register (0xfb) -- high byte address for sector erase, + * program, read. + * Address Middle Byte Register (0xfc) -- similar to previous. + * Address Low Byte Register (0xfd) -- similar to previous. + * Program Byte Register (0xfe) -- byte for programming in continuous mode. + * Write Data Register (0xff) -- data to write flash for program, write status + */ + +/* NOTE: + * this has return paths that do not clean up; can be fixed by a little + * reordering of code and possibly a new variable + */ +int fintek_spi_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr) +{ + uint8_t busy, op_busy, operate; + int i; + + /* can't read back more than 2 bytes, fail otherwise */ + if (readcnt > 2) { + printf("%s called with unsupported readcnt %i.\n", + __FUNCTION__, readcnt); + return 1; + } + + enter_conf_mode_fintek(fintek_flashport); + regwrite(fintek_flashport, 0x07, 0x8); + + /* 33 or 16 MHz, SST continuous mode. + */ + regwrite(fintek_flashport, 0xf2, (fast_spi ? 0 : 1)); + operate = regval(fintek_flashport, 0xf8) & 0x80; + regwrite(fintek_flashport, 0xf8, operate|((sst_continuous ? 1 : 0) << 7)); + + /* + * wait to make sure the SPI isn't doing anything, + * before we do our thing + */ + do { + busy = regval(fintek_flashport, 0xf3) & 0x04; + } while (busy); + + /* + * depending on the write mode and count fill registers with cmd and data + * + * the datasheet says the F7188x SIOs have a 3 byte addressing, + * 1 byte for command, 2 bytes for return data, and 1 byte writing + */ + switch (writecnt) { + case 1: /* when we only need to send a command byte */ + regwrite(fintek_flashport, 0xf5, writearr[0]); + break; + case 2: /* when we need to send a cmd byte and a write_data byte */ + regwrite(fintek_flashport, 0xf5, writearr[0]); + regwrite(fintek_flashport, 0xff, writearr[1]); + break; + case 4: /* when we need to send a cmd byte and an address */ + regwrite(fintek_flashport, 0xf5, writearr[0]); + regwrite(fintek_flashport, 0xfb, writearr[1]); + regwrite(fintek_flashport, 0xfc, writearr[2]); + regwrite(fintek_flashport, 0xfd, writearr[3]); + break; + case 5: + /* when we need to send a cmd byte, write_data byte, and address */ + regwrite(fintek_flashport, 0xf5, writearr[0]); + regwrite(fintek_flashport, 0xfb, writearr[1]); + regwrite(fintek_flashport, 0xfc, writearr[2]); + regwrite(fintek_flashport, 0xfd, writearr[3]); + regwrite(fintek_flashport, 0xff, writearr[4]); + break; + /* + * Just in case, we somehow get a unknown byte count for a command + */ + default: + printf("%s called with unsupported writecnt %i.\n", + __FUNCTION__, writecnt); + return 1; + } + + /* + * The Fintek F7188x SuperIOs use a SPI operate Register at Index 0xf8 to + * set off the execution of various SPI functions + */ + operate = regval(fintek_flashport, 0xf8); + switch(writearr[0]) + { + case JEDEC_RDSR: + regwrite(fintek_flashport, 0xf8, operate|(1 << 5)); + break; + case JEDEC_WRSR: + regwrite(fintek_flashport, 0xf8, operate|(1 << 4)); + break; + case JEDEC_SE: + regwrite(fintek_flashport, 0xf8, operate|(1 << 3)); + break; + case JEDEC_RDID: + regwrite(fintek_flashport, 0xf8, operate|(1 << 2)); + break; + case JEDEC_BYTE_PROGRAM: + regwrite(fintek_flashport, 0xf8, operate|(1 << 1)); + break; + case JEDEC_READ: + regwrite(fintek_flashport, 0xf8, operate|(1 << 0)); + break; + default: /* IO_SPI */ + regwrite(fintek_flashport, 0xf8, operate|(1 << 6)); + } + + do { + op_busy = regval(fintek_flashport, 0xf8) & 0x7f + } while (op_busy);/* wait for op to finish */ + + if (readcnt > 0) { + /* + * wait to make sure that the SPI isn't working, and to + * insure that we can actually save the return data + * from SPI slave device + */ + do { + busy = regval(fintek_flashport, 0xf3) & 0x08; + } while (busy); + + /* save the low byte from SPI first */ + readarr[0] = regval(fintek_flashport, 0xfa); + /* next save the high byte from SPI */ + readarr[1] = regval(fintek_flashport, 0xf4); + } + exit_conf_mode_fintek(fintek_flashport); + + return 0; +} + +/* NOTE: use flash->page_size or somesuch that should be used + * am i sure that the scheme will work? + */ +static void fintek_spi_page_program(struct flashchip *flash, int block, uint8_t *buf, uint8_t *bios) { + int i; + uint8_t oper, op_busy; + + spi_write_enable(); /* tell the SPI slave device to enable write */ + + enter_conf_mode_fintek(fintek_flashport); + regwrite(fintek_flashport, 0x07, 0x8); /* SPI device select */ + + regwrite(fintek_flashport, 0xf5, 0x06); /* CMD_DATA */ + regwrite(fintek_flashport, 0xf2, (fast_spi ? 0 : 1 )); /* set SPI data */ + + + for (i = 0; i < flash->page_size; i++) { + bios[flash->page_size * block + i] = buf[flash->page_size * block + i]; + + /* we have to start off the SPI during a memory cycle */ + oper = regval(fintek_flashport, 0xf8); + regwrite(fintek_flashport, 0xf8, oper|(1 << 1)); + do { + op_busy = regval(fintek_flashport, 0xf8) & 0x7f + } while (op_busy);/* wait for op to finish */ + } + + regwrite(fintek_flashport, 0xf0, 0); + exit_conf_mode_fintek(fintek_flashport); + /* Wait until the Write-In-Progress bit is cleared. + * This usually takes 1-10 ms, so wait in 1 ms steps. + */ + while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP) + usleep(1000); +} + +int fintek_spi_chip_read(struct flashchip *flash, uint8_t *buf) +{ + int total_size = 1024 * flash->total_size; + int i; + fast_spi = 0; + /* NOTE: it is said that the fintek can be set up for a large + * decode window; so we can just use a memcpy + */ + memcpy(buf, (const char *)flash->virtual_memory, total_size); + return 0; +} + +int fintek_spi_chip_write(struct flashchip *flash, uint8_t *buf) { + int total_size = 1024 * flash->total_size; + int i; + for (i = 0; i < total_size / flash->page_size; i++) { + fintek_spi_page_program(flash, i, buf, (uint8_t *)flash->virtual_memory); + } + return 0; +} +
Sean Nelson wrote:
Fintek SPI Driver file for Flashrom
Thanks to Peter Stuge for help and pushing me to write this SPI code. The SPI interface is for Fintek F71882 and F71883 SuperIOs. There may be other Fintek SuperIOs that can use this but I havn't checked the available Fintek datasheets.
Signed-off-by: Sean Nelson snelson@nmt.edu --- This code was developed using the datasheets and has not been tested. The Fintek SPI interface is weird compared to the SB600 and the other SPI interfaces already found in flashrom.
Sean Nelson wrote:
Fintek SPI Driver file for Flashrom
Thanks to Peter Stuge for help and pushing me to write this SPI code. The SPI interface is for Fintek F71882 and F71883 SuperIOs. There may be other Fintek SuperIOs that can use this but I havn't checked the available Fintek datasheets.
Signed-off-by: Sean Nelson snelson@nmt.edu --- This code was developed using the datasheets and has not been tested. The Fintek SPI interface is weird compared to the SB600 and the other SPI interfaces already found in flashrom.
Hi,
Sean Nelson wrote:
Fintek SPI Driver file for Flashrom
We need access to hardware to test this code because the data sheet is unclear.
If anyone has one of these superios let us know. We promise to not brick your board. :)
//Peter