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; +} +