summaryrefslogtreecommitdiff
path: root/src/soc/intel/skylake/romstage/spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc/intel/skylake/romstage/spi.c')
-rw-r--r--src/soc/intel/skylake/romstage/spi.c149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/soc/intel/skylake/romstage/spi.c b/src/soc/intel/skylake/romstage/spi.c
new file mode 100644
index 0000000000..a2c5d33a75
--- /dev/null
+++ b/src/soc/intel/skylake/romstage/spi.c
@@ -0,0 +1,149 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Google Inc.
+ *
+ * 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
+ */
+
+#include <arch/io.h>
+#include <console/console.h>
+#include <device/pci_ids.h>
+#include <device/pci_def.h>
+#include <delay.h>
+#include <soc/spi.h>
+#include <soc/rcba.h>
+#include <soc/romstage.h>
+
+#define SPI_DELAY 10 /* 10us */
+#define SPI_RETRY 200000 /* 2s */
+
+static int early_spi_read_block(u32 offset, u8 size, u8 *buffer)
+{
+ u32 *ptr32 = (u32*)buffer;
+ u32 i;
+
+ /* Clear status bits */
+ SPIBAR16(SPIBAR_HSFS) |= SPIBAR_HSFS_AEL | SPIBAR_HSFS_FCERR |
+ SPIBAR_HSFS_FDONE;
+
+ if (SPIBAR16(SPIBAR_HSFS) & SPIBAR_HSFS_SCIP) {
+ printk(BIOS_ERR, "SPI ERROR: transaction in progress\n");
+ return -1;
+ }
+
+ /* Set flash address */
+ SPIBAR32(SPIBAR_FADDR) = offset;
+
+ /* Setup read transaction */
+ SPIBAR16(SPIBAR_HSFC) = SPIBAR_HSFC_BYTE_COUNT(size) |
+ SPIBAR_HSFC_CYCLE_READ;
+
+ /* Start transaction */
+ SPIBAR16(SPIBAR_HSFC) |= SPIBAR_HSFC_GO;
+
+ /* Wait for completion */
+ for (i = 0; i < SPI_RETRY; i++) {
+ if (SPIBAR16(SPIBAR_HSFS) & SPIBAR_HSFS_SCIP) {
+ /* Cycle in progress, wait 1ms */
+ udelay(SPI_DELAY);
+ continue;
+ }
+
+ if (SPIBAR16(SPIBAR_HSFS) & SPIBAR_HSFS_AEL) {
+ printk(BIOS_ERR, "SPI ERROR: Access Error\n");
+ return -1;
+
+ }
+
+ if (SPIBAR16(SPIBAR_HSFS) & SPIBAR_HSFS_FCERR) {
+ printk(BIOS_ERR, "SPI ERROR: Flash Cycle Error\n");
+ return -1;
+ }
+ break;
+ }
+
+ if (i >= SPI_RETRY) {
+ printk(BIOS_ERR, "SPI ERROR: Timeout\n");
+ return -1;
+ }
+
+ /* Read the data */
+ for (i = 0; i < size; i+=sizeof(u32)) {
+ if (size-i >= 4) {
+ /* reading >= dword */
+ *ptr32++ = SPIBAR32(SPIBAR_FDATA(i/sizeof(u32)));
+ } else {
+ /* reading < dword */
+ u8 j, *ptr8 = (u8*)ptr32;
+ u32 temp = SPIBAR32(SPIBAR_FDATA(i/sizeof(u32)));
+ for (j = 0; j < (size-i); j++) {
+ *ptr8++ = temp & 0xff;
+ temp >>= 8;
+ }
+ }
+ }
+
+ return size;
+}
+
+int early_spi_read(u32 offset, u32 size, u8 *buffer)
+{
+ u32 current = 0;
+
+ while (size > 0) {
+ u8 count = (size < 64) ? size : 64;
+ if (early_spi_read_block(offset + current, count,
+ buffer + current) < 0)
+ return -1;
+ size -= count;
+ current += count;
+ }
+
+ return 0;
+}
+
+/*
+ * Minimal set of commands to read WPSR from SPI.
+ * Don't use this code outside romstage -- it trashes the opmenu table.
+ * Returns 0 on success, < 0 on failure.
+ */
+int early_spi_read_wpsr(u8 *sr)
+{
+ int retry;
+
+ /* No address associated with rdsr */
+ SPIBAR8(SPIBAR_OPTYPE) = 0x0;
+ /* Setup opcode[0] = read wpsr */
+ SPIBAR8(SPIBAR_OPMENU_LOWER) = 0x5;
+
+ /* Start transaction */
+ SPIBAR16(SPIBAR_SSFC) = SPIBAR_SSFC_DATA | SPIBAR_SSFC_GO;
+
+ /* Wait for error / complete status */
+ for (retry = SPI_RETRY; retry; retry--) {
+ u16 status = SPIBAR16(SPIBAR_SSFS);
+ if (status & SPIBAR_SSFS_ERROR) {
+ printk(BIOS_ERR, "SPI rdsr failed\n");
+ return -1;
+ } else if (status & SPIBAR_SSFS_DONE) {
+ break;
+ }
+
+ udelay(SPI_DELAY);
+ }
+
+ *sr = SPIBAR32(SPIBAR_FDATA(0)) & 0xff;
+ return 0;
+}