SPI: Add early romstage SPI driver using hardware sequencing

This is a basic romstage driver that can be used for the
MRC cache code on systems where we do not have the MRC cache
stored in a flash region that is memory mapped.

It uses the hardware sequencing interface to avoid having
to know anything about the flash chip itself.

BUG=chrome-os-partner:15031
BRANCH=stout
TEST=manual: this was tested with debug code added to romstage
that attempted to read the MRC cache at offset 0x3e0000.

SPI READ offset=003e0000 size=64 buffer=ff7fba00
SPI ADDR 0x003e0000
SPI HSFC 0x3f00
SPI READ: 0=4443524d
SPI READ: 1=00000bb0
SPI READ: 2=00008e24
SPI READ: 3=00000000
SPI READ: 4=001c8bbb
SPI READ: 5=0c206466
SPI READ: 6=0a043220
SPI READ: 7=000058b4
SPI READ: 8=00000000
SPI READ: 9=00000000
SPI READ: 10=00100000
SPI READ: 11=00100005
SPI READ: 12=20202025
SPI READ: 13=000e0001
SPI READ: 14=00000000
SPI READ: 15=00000000

Change-Id: I5f78f53111f912ff5dda52bbf90fdc1824b82681
Signed-off-by: Duncan Laurie <dlaurie@chromium.org>
Reviewed-on: http://review.coreboot.org/1777
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
This commit is contained in:
Duncan Laurie 2012-10-11 13:04:14 -07:00 committed by Ronald G. Minnich
parent 215f278563
commit d4bc067954
3 changed files with 131 additions and 0 deletions

View File

@ -44,4 +44,5 @@ romstage-$(CONFIG_USBDEBUG) += usb_debug.c
ramstage-$(CONFIG_USBDEBUG) += usb_debug.c ramstage-$(CONFIG_USBDEBUG) += usb_debug.c
smm-$(CONFIG_USBDEBUG) += usb_debug.c smm-$(CONFIG_USBDEBUG) += usb_debug.c
romstage-y += reset.c romstage-y += reset.c
romstage-y += early_spi.c

View File

@ -0,0 +1,115 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* 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 <arch/romcc_io.h>
#include <console/console.h>
#include <device/pci_ids.h>
#include <device/pci_def.h>
#include <delay.h>
#include "pch.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 */
RCBA16(SPIBAR_HSFS) |= SPIBAR_HSFS_AEL | SPIBAR_HSFS_FCERR |
SPIBAR_HSFS_FDONE;
if (RCBA16(SPIBAR_HSFS) & SPIBAR_HSFS_SCIP) {
printk(BIOS_ERR, "SPI ERROR: transaction in progress\n");
return -1;
}
/* Set flash address */
RCBA32(SPIBAR_FADDR) = offset;
/* Setup read transaction */
RCBA16(SPIBAR_HSFC) = SPIBAR_HSFC_BYTE_COUNT(size) |
SPIBAR_HSFC_CYCLE_READ;
/* Start transactinon */
RCBA16(SPIBAR_HSFC) |= SPIBAR_HSFC_GO;
/* Wait for completion */
for (i = 0; i < SPI_RETRY; i++) {
if (RCBA16(SPIBAR_HSFS) & SPIBAR_HSFS_SCIP) {
/* Cycle in progress, wait 1ms */
udelay(SPI_DELAY);
continue;
}
if (RCBA16(SPIBAR_HSFS) & SPIBAR_HSFS_AEL) {
printk(BIOS_ERR, "SPI ERROR: Access Error\n");
return -1;
}
if (RCBA16(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++ = RCBA32(SPIBAR_FDATA(i/sizeof(u32)));
} else {
/* reading < dword */
u8 j, *ptr8 = (u8*)ptr32;
u32 temp = RCBA32(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;
}

View File

@ -71,6 +71,7 @@ void pch_log_state(void);
void enable_smbus(void); void enable_smbus(void);
void enable_usb_bar(void); void enable_usb_bar(void);
int smbus_read_byte(unsigned device, unsigned address); int smbus_read_byte(unsigned device, unsigned address);
int early_spi_read(u32 offset, u32 size, u8 *buffer);
#endif #endif
#endif #endif
@ -539,5 +540,19 @@ int smbus_read_byte(unsigned device, unsigned address);
#define SPI_OPPREFIX ((0x50 << 8) | 0x06) /* EWSR and WREN */ #define SPI_OPPREFIX ((0x50 << 8) | 0x06) /* EWSR and WREN */
#define SPIBAR_HSFS 0x3804 /* SPI hardware sequence status */
#define SPIBAR_HSFS_SCIP (1 << 5) /* SPI Cycle In Progress */
#define SPIBAR_HSFS_AEL (1 << 2) /* SPI Access Error Log */
#define SPIBAR_HSFS_FCERR (1 << 1) /* SPI Flash Cycle Error */
#define SPIBAR_HSFS_FDONE (1 << 0) /* SPI Flash Cycle Done */
#define SPIBAR_HSFC 0x3806 /* SPI hardware sequence control */
#define SPIBAR_HSFC_BYTE_COUNT(c) (((c - 1) & 0x3f) << 8)
#define SPIBAR_HSFC_CYCLE_READ (0 << 1) /* Read cycle */
#define SPIBAR_HSFC_CYCLE_WRITE (2 << 1) /* Write cycle */
#define SPIBAR_HSFC_CYCLE_ERASE (3 << 1) /* Erase cycle */
#define SPIBAR_HSFC_GO (1 << 0) /* GO: start SPI transaction */
#define SPIBAR_FADDR 0x3808 /* SPI flash address */
#define SPIBAR_FDATA(n) (0x3810 + (4 * n)) /* SPI flash data */
#endif /* __ACPI__ */ #endif /* __ACPI__ */
#endif /* SOUTHBRIDGE_INTEL_BD82X6X_PCH_H */ #endif /* SOUTHBRIDGE_INTEL_BD82X6X_PCH_H */