[flashrom] [PATCH] Fintek SPI driver, based off it87spi
Carl-Daniel Hailfinger
c-d.hailfinger.devel.2006 at gmx.net
Thu Mar 10 01:49:33 CET 2011
Auf 19.09.2010 06:14, Sean Nelson schrieb:
> Fintek SPI Flashrom Driver:
> * based off of it87spi.c
> * untested
> * should be good for F71882 and F71889
>
> Signed-off-by: Sean Nelson <audiohacked at gmail.com>
Updated against latest svn, still untested. Testers welcome!
This needs to be cleaned up before being merged, but if anyone has that
hardware, he/she now has a chance to test with current svn.
Regards,
Carl-Daniel
Index: flashrom-snelson_fintek_spi/Makefile
===================================================================
--- flashrom-snelson_fintek_spi/Makefile (Revision 1280)
+++ flashrom-snelson_fintek_spi/Makefile (Arbeitskopie)
@@ -219,7 +219,7 @@
FEATURE_CFLAGS += -D'CONFIG_INTERNAL=1'
PROGRAMMER_OBJS += processor_enable.o chipset_enable.o board_enable.o cbtable.o dmi.o internal.o
# FIXME: The PROGRAMMER_OBJS below should only be included on x86.
-PROGRAMMER_OBJS += it87spi.o it85spi.o ichspi.o sb600spi.o wbsio_spi.o mcp6x_spi.o
+PROGRAMMER_OBJS += it87spi.o it85spi.o ichspi.o sb600spi.o wbsio_spi.o mcp6x_spi.o fintek_spi.o
NEED_PCI := yes
endif
Index: flashrom-snelson_fintek_spi/spi.c
===================================================================
--- flashrom-snelson_fintek_spi/spi.c (Revision 1280)
+++ flashrom-snelson_fintek_spi/spi.c (Arbeitskopie)
@@ -42,6 +42,13 @@
#if CONFIG_INTERNAL == 1
#if defined(__i386__) || defined(__x86_64__)
+ { /* SPI_CONTROLLER_FINTEK */
+ .command = fintek_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = fintek_spi_chip_read,
+ .write_256 = fintek_spi_chip_write_256,
+ },
+
{ /* SPI_CONTROLLER_ICH7 */
.command = ich_spi_send_command,
.multicommand = ich_spi_send_multicommand,
Index: flashrom-snelson_fintek_spi/programmer.h
===================================================================
--- flashrom-snelson_fintek_spi/programmer.h (Revision 1280)
+++ flashrom-snelson_fintek_spi/programmer.h (Arbeitskopie)
@@ -276,6 +276,7 @@
extern struct superio superio;
#define SUPERIO_VENDOR_NONE 0x0
#define SUPERIO_VENDOR_ITE 0x1
+#define SUPERIO_VENDOR_FINTEK 0x2
struct pci_dev *pci_dev_find_filter(struct pci_filter filter);
struct pci_dev *pci_dev_find_vendorclass(uint16_t vendor, uint16_t class);
struct pci_dev *pci_dev_find(uint16_t vendor, uint16_t device);
@@ -518,6 +519,7 @@
SPI_CONTROLLER_NONE,
#if CONFIG_INTERNAL == 1
#if defined(__i386__) || defined(__x86_64__)
+ SPI_CONTROLLER_FINTEK,
SPI_CONTROLLER_ICH7,
SPI_CONTROLLER_ICH9,
SPI_CONTROLLER_IT85XX,
@@ -568,6 +570,19 @@
const unsigned char *writearr, unsigned char *readarr);
int default_spi_send_multicommand(struct spi_command *cmds);
+/* fintek_spi.c */
+#if CONFIG_INTERNAL == 1
+void enter_conf_mode_fintek(uint16_t port);
+void exit_conf_mode_fintek(uint16_t port);
+struct superio probe_superio_fintek(void);
+int init_superio_fintek(void);
+int fintek_spi_init(void);
+int fintek_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr, unsigned char *readarr);
+int fintek_spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len);
+int fintek_spi_chip_write_256(struct flashchip *flash, uint8_t *buf, int start, int len);
+#endif
+
/* ichspi.c */
#if CONFIG_INTERNAL == 1
extern uint32_t ichspi_bbar;
Index: flashrom-snelson_fintek_spi/fintek_spi.c
===================================================================
--- flashrom-snelson_fintek_spi/fintek_spi.c (Revision 0)
+++ flashrom-snelson_fintek_spi/fintek_spi.c (Revision 0)
@@ -0,0 +1,438 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Sean Nelson <audiohacked at gmail.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; 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.
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <string.h>
+#include <stdlib.h>
+#include "flash.h"
+#include "chipdrivers.h"
+#include "programmer.h"
+#include "spi.h"
+
+#define FINTEK_SUPERIO_PORT1 0x2e
+#define FINTEK_SUPERIO_PORT2 0x4e
+
+/* 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_CTRL_SPTIE (1 << 5)
+#define SPI_CTRL_CPOL (1 << 3)
+#define SPI_CTRL_CPHA (1 << 2)
+#define SPI_CTRL_LSBFE (1 << 0)
+
+#define SPI_TIMEOUT 0xf1
+#define SPI_BAUD 0xf2
+#define SPI_BAUD_33 (0 << 0)
+#define SPI_BAUD_16 (1 << 0)
+
+#define SPI_STATUS 0xf3
+#define SPI_STATUS_SPIE (1 << 7)
+#define SPI_STATUS_SPE (1 << 5)
+#define SPI_STATUS_SPTEF (1 << 3)
+
+#define SPI_H_DATA 0xf4
+#define SPI_CMD_DATA 0xf5
+#define SPI_CS 0xf6
+#define SPI_CS_CS0 (1 << 0)
+#define SPI_CS_CS1 (1 << 1)
+#define SPI_CS_CS2 (1 << 2)
+#define SPI_CS_CS3 (1 << 3)
+
+#define SPI_MMAP 0xf7
+#define SPI_MMAP_512 0x00
+#define SPI_MMAP_1024 0x01
+#define SPI_MMAP_2048 0x02
+#define SPI_MMAP_4096 0x03
+#define SPI_MMAP_8092 0x04
+
+#define SPI_OP 0xf8
+#define SPI_OP_TYPE (1 << 7)
+#define SPI_OP_IO_SPI (1 << 6)
+#define SPI_OP_RDSR (1 << 5)
+#define SPI_OP_WRSR (1 << 4)
+#define SPI_OP_ERASE (1 << 3)
+#define SPI_OP_RDID (1 << 2)
+#define SPI_OP_PROG (1 << 1)
+#define SPI_OP_READ (1 << 0)
+
+#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) */
+static int fast_spi = 1;
+
+/* Helper functions for Fintek F7188x Super I/O chips */
+#define CHIP_ID_BYTE1_REG 0x20
+#define CHIP_ID_BYTE2_REG 0x21
+void enter_conf_mode_fintek(uint16_t port)
+{
+ OUTB(0x87, port);
+ OUTB(0x87, port);
+}
+
+void exit_conf_mode_fintek(uint16_t port)
+{
+ OUTB(0xaa, port);
+}
+
+uint16_t probe_id_fintek(uint16_t port)
+{
+ uint16_t id;
+
+ enter_conf_mode_fintek(port);
+ id = sio_read(port, CHIP_ID_BYTE1_REG) << 8;
+ id |= sio_read(port, CHIP_ID_BYTE2_REG);
+ exit_conf_mode_fintek(port);
+
+ return id;
+}
+
+struct superio probe_superio_fintek(void)
+{
+ struct superio ret = {};
+ uint16_t fintek_ports[] = {FINTEK_SUPERIO_PORT1, FINTEK_SUPERIO_PORT2, 0};
+ uint16_t *i = fintek_ports;
+
+ ret.vendor = SUPERIO_VENDOR_FINTEK;
+ for (; *i; i++) {
+ ret.port = *i;
+ ret.model = probe_id_fintek(ret.port);
+ switch (ret.model >> 8) {
+ case 0x0541: /* f71882 */
+ case 0x0723: /* f71889 */
+ msg_pinfo("Found Fintek Super I/O, ID 0x%04hx.\n",
+ ret.model);
+ return ret;
+ }
+ }
+
+ /* No good ID found. */
+ ret.vendor = SUPERIO_VENDOR_NONE;
+ ret.port = 0;
+ ret.model = 0;
+ return ret;
+}
+
+static uint16_t fintek_spi_probe(struct superio fintek)
+{
+ uint8_t tmp = 0;
+
+ enter_conf_mode_fintek(fintek.port);
+ /* NOLDN, reg 0x27, mask out lowest bit (suspend) */
+ tmp = sio_read(fintek.port, 0x27);
+ /* If Fintek was not explicitly selected, we want to check
+ * quickly if LPC->SPI translation is active.
+ */
+
+ if (fintek.model == 0x0541) {
+ msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
+ 0xFFFE0000, 0xFFFFFFFF, (tmp & 1 << 1) ? "en" : "dis");
+ msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
+ 0x000E0000, 0x000FFFFF, (tmp & 1 << 1) ? "en" : "dis");
+ msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
+ 0xFFEE0000, 0xFFEFFFFF, (tmp & 1 << 2) ? "en" : "dis");
+ msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
+ 0xFFF80000, 0xFFFEFFFF, (tmp & 1 << 3) ? "en" : "dis");
+ msg_pdbg("LPC write to serial flash %sabled\n",
+ (tmp & 1 << 4) ? "en" : "dis");
+ }
+ /* The LPC->SPI force write enable below only makes sense for
+ * non-programmer mode.
+ */
+ /* If any serial flash segment is enabled, enable writing. */
+ if ((tmp & 0xe) && (!(tmp & 1 << 4))) {
+ msg_pdbg("Enabling LPC write to serial flash\n");
+ tmp |= 1 << 4;
+ sio_write(fintek.port, 0x27, tmp);
+ }
+ msg_pdbg("Serial flash pin %i\n", (tmp & 1 << 5) ? 87 : 29);
+
+ sio_write(fintek.port, 0x07, 0x8);
+ if (fintek.model == 0x0541) {
+ /* disable FWH */
+ tmp = sio_read(fintek.port, SPI_STATUS);
+ tmp |= (1<<6);
+ sio_write(fintek.port, SPI_STATUS, tmp);
+
+ /* set max decode size: 8092k bytes */
+ sio_write(fintek.port, SPI_MMAP, 0x4);
+ }
+
+ exit_conf_mode_fintek(fintek.port);
+ fintek_flashport = fintek.port;
+ if (buses_supported & CHIP_BUSTYPE_SPI)
+ msg_pdbg("Overriding chipset SPI with Fintek SPI.\n");
+ spi_controller = SPI_CONTROLLER_FINTEK;
+ buses_supported |= CHIP_BUSTYPE_SPI;
+ return 0;
+}
+
+int init_superio_fintek(void)
+{
+ if (superio.vendor != SUPERIO_VENDOR_FINTEK)
+ return 1;
+
+ switch (superio.model) {
+ case 0x8718:
+ case 0x8720:
+ return fintek_spi_probe(superio);
+ break;
+ default:
+ msg_pdbg("Super I/O ID 0x%04hx is not on the list of flash "
+ "capable controllers.\n", superio.model);
+ }
+ return 1;
+}
+
+
+int fintek_spi_init(void)
+{
+ int ret;
+
+ get_io_perms();
+ /* Probe for the Super I/O chip and fill global struct superio. */
+ probe_superio();
+ ret = init_superio_fintek();
+ if (!ret) {
+ buses_supported = CHIP_BUSTYPE_SPI;
+ } else {
+ buses_supported = CHIP_BUSTYPE_NONE;
+ }
+ return ret;
+}
+
+/*
+ * The IT8716F only supports commands with length 1,2,4,5 bytes including
+ * command byte and can not read more than 3 bytes from the device.
+ *
+ * This function expects writearr[0] to be the first byte sent to the device,
+ * whereas the IT8716F splits commands internally into address and non-address
+ * commands with the address in inverse wire order. That's why the register
+ * ordering in case 4 and 5 may seem strange.
+ */
+int fintek_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr, unsigned char *readarr)
+{
+ uint8_t busy, operate;
+
+ enter_conf_mode_fintek(fintek_flashport);
+ sio_write(fintek_flashport, 0x07, 0x8);
+ /*
+ * wait to make sure the SPI isn't doing anything,
+ * before we do our thing
+ */
+ do {
+ busy = sio_read(fintek_flashport, SPI_STATUS) & 0x03;
+ } while (busy);
+
+ if (readcnt > 2) {
+ msg_pinfo("%s called with unsupported readcnt %i.\n",
+ __func__, readcnt);
+ return SPI_INVALID_LENGTH;
+ }
+
+ switch (writecnt) {
+ case 1:
+ sio_write(fintek_flashport, SPI_CMD_DATA, writearr[0]);
+ break;
+ case 2:
+ sio_write(fintek_flashport, SPI_CMD_DATA, writearr[0]);
+ sio_write(fintek_flashport, SPI_WR_DATA, writearr[1]);
+ break;
+ case 4:
+ sio_write(fintek_flashport, SPI_CMD_DATA, writearr[0]);
+ sio_write(fintek_flashport, SPI_H_ADDR, writearr[1]);
+ sio_write(fintek_flashport, SPI_M_ADDR, writearr[2]);
+ sio_write(fintek_flashport, SPI_L_ADDR, writearr[3]);
+ break;
+ case 5:
+ sio_write(fintek_flashport, SPI_CMD_DATA, writearr[0]);
+ sio_write(fintek_flashport, SPI_H_ADDR, writearr[1]);
+ sio_write(fintek_flashport, SPI_M_ADDR, writearr[2]);
+ sio_write(fintek_flashport, SPI_L_ADDR, writearr[3]);
+ sio_write(fintek_flashport, SPI_WR_DATA, writearr[4]);
+ break;
+ default:
+ msg_pinfo("%s called with unsupported writecnt %i.\n",
+ __func__, writecnt);
+ return SPI_INVALID_LENGTH;
+ }
+ /*
+ * Start IO, 33 or 16 MHz, readcnt input bytes, writecnt output bytes.
+ * Note:
+ * We can't use writecnt directly, but have to use a strange encoding.
+ */
+ operate = sio_read(fintek_flashport, SPI_BAUD);
+ operate |= ((fast_spi ? 0 : 1) << 0);
+ sio_write(fintek_flashport, SPI_BAUD, operate);
+
+ /*
+ * The Fintek F7188x SuperIOs use a SPI operate Register at Index 0xf8 to
+ * set off the execution of various SPI functions
+ */
+ operate = sio_read(fintek_flashport, SPI_OP);
+ sio_write(fintek_flashport, SPI_OP, operate|(1 << 7));
+
+ switch(writearr[0])
+ {
+ case JEDEC_RDSR:
+ sio_write(fintek_flashport, SPI_OP, operate|(1 << 5));
+ break;
+ case JEDEC_WRSR:
+ sio_write(fintek_flashport, SPI_OP, operate|(1 << 4));
+ break;
+ case JEDEC_SE:
+ sio_write(fintek_flashport, SPI_OP, operate|(1 << 3));
+ break;
+ case JEDEC_RDID:
+ sio_write(fintek_flashport, SPI_OP, operate|(1 << 2));
+ break;
+ case JEDEC_BYTE_PROGRAM:
+ sio_write(fintek_flashport, SPI_OP, operate|(1 << 1));
+ break;
+ case JEDEC_READ:
+ sio_write(fintek_flashport, SPI_OP, operate|(1 << 0));
+ break;
+ default: /* IO_SPI */
+ sio_write(fintek_flashport, SPI_OP, operate|(1 << 6));
+ }
+
+ if (readcnt > 0) {
+ do {
+ busy = sio_read(fintek_flashport, SPI_STATUS) & 0x03;
+ } while (busy);
+
+ /* save the low byte from SPI first */
+ readarr[0] = sio_read(fintek_flashport, SPI_L_DATA);
+ /* next save the high byte from SPI */
+ readarr[1] = sio_read(fintek_flashport, SPI_H_DATA);
+ }
+ sio_write(fintek_flashport, SPI_OP, 0);
+ exit_conf_mode_fintek(fintek_flashport);
+
+ return 0;
+}
+
+/* Page size is usually 256 bytes */
+static int fintek_spi_page_program(struct flashchip *flash, uint8_t *buf, int start)
+{
+ int i;
+ int result;
+ //uint8_t tmp;
+ chipaddr bios = flash->virtual_memory;
+
+ enter_conf_mode_fintek(fintek_flashport);
+ sio_write(fintek_flashport, 0x07, 0x8);
+
+ result = spi_write_enable();
+ if (result)
+ return result;
+ /* FIXME: The command below seems to be redundant or wrong. */
+ sio_write(fintek_flashport, SPI_CMD_DATA, 0x06);
+
+ sio_write(fintek_flashport, SPI_BAUD, ((fast_spi ? 0 : 1) << 0) );
+ for (i = 0; i < 256; i++) {
+ chip_writeb(buf[i], bios + start + i);
+ }
+ sio_write(fintek_flashport, SPI_OP, 0);
+ /* 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)
+ programmer_delay(1000);
+
+ exit_conf_mode_fintek(fintek_flashport);
+ return 0;
+}
+
+int fintek_spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len)
+{
+ int total_size = 1024 * flash->total_size;
+ fast_spi = 0;
+
+ /* FIXME: Allow force-activating Fintek SPI even if flash translation is not active? */
+ if ((total_size > 8 * 1024 * 1024)) {
+ spi_read_chunked(flash, buf, start, len, 2);
+ } else {
+ read_memmapped(flash, buf, start, len);
+ }
+
+ return 0;
+}
+
+int fintek_spi_chip_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
+{
+ /* FIXME: Allow force-activating Fintek SPI even if flash translation is not active? */
+ if ((flash->total_size * 1024 > 8 * 1024 * 1024)) {
+ spi_chip_write_1(flash, buf, start, len);
+ } else {
+ int lenhere;
+
+ if (start % 256) {
+ /* start to the end of the page or start + len,
+ * whichever is smaller. Page length is hardcoded to
+ * 256 bytes (IT87 SPI hardware limitation).
+ */
+ lenhere = min(len, (start | 0xff) - start + 1);
+ spi_chip_write_1(flash, buf, start, lenhere);
+ start += lenhere;
+ len -= lenhere;
+ buf += lenhere;
+ }
+
+ /* FIXME: Handle chips which have max writechunk size >1 and <256. */
+ while (len >= 256) {
+ fintek_spi_page_program(flash, buf, start);
+ start += 256;
+ len -= 256;
+ buf += 256;
+ }
+ if (len)
+ spi_chip_write_1(flash, buf, start, len);
+ }
+
+ return 0;
+}
+
+#endif
--
http://www.hailfinger.org/
More information about the flashrom
mailing list