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

Reply via email to