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 <[EMAIL PROTECTED]>
+ *
+ * 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;
+}
+
--
coreboot mailing list: [email protected]
http://www.coreboot.org/mailman/listinfo/coreboot