277 changes: 24 additions & 253 deletions src/drivers/pc80/rtc/mc146818rtc.c
Expand Up @@ -17,6 +17,7 @@
#include <fallback.h>
#include <version.h>
#include <console/console.h>
#include <option.h>
#include <pc80/mc146818rtc.h>
#include <rtc.h>
#include <string.h>
Expand All @@ -25,18 +26,6 @@
#include <security/vboot/vbnv_layout.h>
#include <types.h>

/* There's no way around this include guard. option_table.h is autogenerated */
#if CONFIG(USE_OPTION_TABLE)
#include "option_table.h"
#else
#define LB_CKS_RANGE_START 0
#define LB_CKS_RANGE_END 0
#define LB_CKS_LOC 0
#endif

/* Don't warn for checking >= LB_CKS_RANGE_START even though it may be 0. */
#pragma GCC diagnostic ignored "-Wtype-limits"

static void cmos_reset_date(void)
{
/* Now setup a default date equals to the build date */
Expand All @@ -53,7 +42,7 @@ static void cmos_reset_date(void)
rtc_set(&time);
}

static int cmos_checksum_valid(int range_start, int range_end, int cks_loc)
int cmos_checksum_valid(int range_start, int range_end, int cks_loc)
{
int i;
u16 sum, old_sum;
Expand All @@ -69,7 +58,7 @@ static int cmos_checksum_valid(int range_start, int range_end, int cks_loc)
return sum == old_sum;
}

static void cmos_set_checksum(int range_start, int range_end, int cks_loc)
void cmos_set_checksum(int range_start, int range_end, int cks_loc)
{
int i;
u16 sum;
Expand All @@ -81,16 +70,21 @@ static void cmos_set_checksum(int range_start, int range_end, int cks_loc)
cmos_write(((sum >> 0) & 0x0ff), cks_loc + 1);
}

/* See if the CMOS error condition has been flagged */
int cmos_error(void)
{
return (cmos_read(RTC_VALID) & RTC_VRT) == 0;
}

#define RTC_CONTROL_DEFAULT (RTC_24H)
#define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ)

static bool __cmos_init(bool invalid)
{
bool cmos_invalid;
bool checksum_invalid = false;
bool clear_cmos;
bool cleared_cmos = false;
size_t i;
uint8_t x;

/*
* Avoid clearing pending interrupts and resetting the RTC control
Expand All @@ -104,29 +98,25 @@ static bool __cmos_init(bool invalid)
printk(BIOS_DEBUG, "RTC Init\n");

/* See if there has been a CMOS power problem. */
x = cmos_read(RTC_VALID);
cmos_invalid = !(x & RTC_VRT);
cmos_invalid = cmos_error();

if (CONFIG(USE_OPTION_TABLE)) {
/* See if there is a CMOS checksum error */
checksum_invalid = !cmos_checksum_valid(PC_CKS_RANGE_START,
PC_CKS_RANGE_END, PC_CKS_LOC);

clear_cmos = false;
} else {
clear_cmos = true;
}

if (cmos_invalid || invalid)
cmos_write(cmos_read(RTC_CONTROL) | RTC_SET, RTC_CONTROL);
cmos_disable_rtc();

if (invalid || cmos_invalid || checksum_invalid) {
if (clear_cmos) {
if (!CONFIG(USE_OPTION_TABLE)) {
cmos_write(0, 0x01);
cmos_write(0, 0x03);
cmos_write(0, 0x05);
for (i = 10; i < 128; i++)
cmos_write(0, i);
cleared_cmos = true;
}

if (cmos_invalid || invalid)
Expand All @@ -136,9 +126,8 @@ static bool __cmos_init(bool invalid)
invalid ? " Clear requested":"",
cmos_invalid ? " Power Problem":"",
checksum_invalid ? " Checksum invalid":"",
clear_cmos ? " zeroing cmos":"");
} else
clear_cmos = false;
cleared_cmos ? " zeroing cmos":"");
}

/* Setup the real time clock */
cmos_write(RTC_CONTROL_DEFAULT, RTC_CONTROL);
Expand All @@ -149,8 +138,7 @@ static bool __cmos_init(bool invalid)

if (CONFIG(USE_OPTION_TABLE)) {
/* See if there is a LB CMOS checksum error */
checksum_invalid = !cmos_checksum_valid(LB_CKS_RANGE_START,
LB_CKS_RANGE_END, LB_CKS_LOC);
checksum_invalid = !cmos_lb_cks_valid();
if (checksum_invalid)
printk(BIOS_DEBUG, "RTC: coreboot checksum invalid\n");

Expand All @@ -161,7 +149,7 @@ static bool __cmos_init(bool invalid)
/* Clear any pending interrupts */
cmos_read(RTC_INTR_FLAGS);

return clear_cmos;
return cleared_cmos;
}

static void cmos_init_vbnv(bool invalid)
Expand Down Expand Up @@ -191,231 +179,14 @@ void cmos_init(bool invalid)
}

/*
* This routine returns the value of the requested bits.
* input bit = bit count from the beginning of the cmos image
* length = number of bits to include in the value
* ret = a character pointer to where the value is to be returned
* returns CB_SUCCESS = successful, cb_err code if an error occurred
* Upon return the caller is guaranteed 244 microseconds to complete any
* RTC operations. wait_uip may be called a single time prior to multiple
* accesses, but sequences requiring more time should call wait_uip again.
*/
static enum cb_err get_cmos_value(unsigned long bit, unsigned long length,
void *vret)
{
unsigned char *ret;
unsigned long byte, byte_bit;
unsigned long i;
unsigned char uchar;

/*
* The table is checked when it is built to ensure all
* values are valid.
*/
ret = vret;
byte = bit / 8; /* find the byte where the data starts */
byte_bit = bit % 8; /* find the bit in the byte where the data starts */
if (length < 9) { /* one byte or less */
uchar = cmos_read(byte); /* load the byte */
uchar >>= byte_bit; /* shift the bits to byte align */
/* clear unspecified bits */
ret[0] = uchar & ((1 << length) - 1);
} else { /* more than one byte so transfer the whole bytes */
for (i = 0; length; i++, length -= 8, byte++) {
/* load the byte */
ret[i] = cmos_read(byte);
}
}
return CB_SUCCESS;
}

static enum cb_err locate_cmos_layout(struct region_device *rdev)
{
uint32_t cbfs_type = CBFS_COMPONENT_CMOS_LAYOUT;
MAYBE_STATIC_BSS struct cbfsf fh = {};

/*
* In case VBOOT is enabled and this function is called from SMM,
* we have multiple CMOS layout files and to locate them we'd need to
* include VBOOT into SMM...
*
* Support only one CMOS layout in the 'COREBOOT' region for now.
*/
if (!region_device_sz(&(fh.data))) {
if (cbfs_locate_file_in_region(&fh, "COREBOOT", "cmos_layout.bin",
&cbfs_type)) {
printk(BIOS_ERR, "RTC: cmos_layout.bin could not be found. "
"Options are disabled\n");
return CB_CMOS_LAYOUT_NOT_FOUND;
}
}

cbfs_file_data(rdev, &fh);

return CB_SUCCESS;
}

enum cb_err get_option(void *dest, const char *name)
{
struct cmos_option_table *ct;
struct region_device rdev;
struct cmos_entries *ce;
size_t namelen;
int found = 0;

if (!CONFIG(USE_OPTION_TABLE))
return CB_CMOS_OTABLE_DISABLED;

/* Figure out how long name is */
namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);

if (locate_cmos_layout(&rdev) != CB_SUCCESS) {
return CB_CMOS_LAYOUT_NOT_FOUND;
}
ct = rdev_mmap_full(&rdev);
if (!ct) {
printk(BIOS_ERR, "RTC: cmos_layout.bin could not be mapped. "
"Options are disabled\n");

return CB_CMOS_LAYOUT_NOT_FOUND;
}

/* find the requested entry record */
ce = (struct cmos_entries *)((unsigned char *)ct + ct->header_length);
for (; ce->tag == LB_TAG_OPTION;
ce = (struct cmos_entries *)((unsigned char *)ce + ce->size)) {
if (memcmp(ce->name, name, namelen) == 0) {
found = 1;
break;
}
}
if (!found) {
printk(BIOS_DEBUG, "No CMOS option '%s'.\n", name);
rdev_munmap(&rdev, ct);
return CB_CMOS_OPTION_NOT_FOUND;
}

if (!cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC)) {
rdev_munmap(&rdev, ct);
return CB_CMOS_CHECKSUM_INVALID;
}
if (get_cmos_value(ce->bit, ce->length, dest) != CB_SUCCESS) {
rdev_munmap(&rdev, ct);
return CB_CMOS_ACCESS_ERROR;
}
rdev_munmap(&rdev, ct);
return CB_SUCCESS;
}

static enum cb_err set_cmos_value(unsigned long bit, unsigned long length,
void *vret)
{
unsigned char *ret;
unsigned long byte, byte_bit;
unsigned long i;
unsigned char uchar, mask;
unsigned int chksum_update_needed = 0;

ret = vret;
byte = bit / 8; /* find the byte where the data starts */
byte_bit = bit % 8; /* find the bit where the data starts */
if (length <= 8) { /* one byte or less */
mask = (1 << length) - 1;
mask <<= byte_bit;

uchar = cmos_read(byte);
uchar &= ~mask;
uchar |= (ret[0] << byte_bit);
cmos_write(uchar, byte);
if (byte >= LB_CKS_RANGE_START && byte <= LB_CKS_RANGE_END)
chksum_update_needed = 1;
} else { /* more that one byte so transfer the whole bytes */
if (byte_bit || length % 8)
return CB_ERR_ARG;

for (i = 0; length; i++, length -= 8, byte++) {
cmos_write(ret[i], byte);
if (byte >= LB_CKS_RANGE_START &&
byte <= LB_CKS_RANGE_END)
chksum_update_needed = 1;
}
}

if (chksum_update_needed) {
cmos_set_checksum(LB_CKS_RANGE_START, LB_CKS_RANGE_END,
LB_CKS_LOC);
}
return CB_SUCCESS;
}

unsigned int read_option_lowlevel(unsigned int start, unsigned int size,
unsigned int def)
static void wait_uip(void)
{
printk(BIOS_NOTICE, "NOTICE: read_option() used to access CMOS "
"from non-ROMCC code, please use get_option() instead.\n");
if (CONFIG(USE_OPTION_TABLE)) {
const unsigned char byte = cmos_read(start / 8);
return (byte >> (start & 7U)) & ((1U << size) - 1U);
}
return def;
}

enum cb_err set_option(const char *name, void *value)
{
struct cmos_option_table *ct;
struct region_device rdev;
struct cmos_entries *ce;
unsigned long length;
size_t namelen;
int found = 0;

if (!CONFIG(USE_OPTION_TABLE))
return CB_CMOS_OTABLE_DISABLED;

/* Figure out how long name is */
namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);

if (locate_cmos_layout(&rdev) != CB_SUCCESS) {
return CB_CMOS_LAYOUT_NOT_FOUND;
}
ct = rdev_mmap_full(&rdev);
if (!ct) {
printk(BIOS_ERR, "RTC: cmos_layout.bin could not be mapped. "
"Options are disabled\n");

return CB_CMOS_LAYOUT_NOT_FOUND;
}

/* find the requested entry record */
ce = (struct cmos_entries *)((unsigned char *)ct + ct->header_length);
for (; ce->tag == LB_TAG_OPTION;
ce = (struct cmos_entries *)((unsigned char *)ce + ce->size)) {
if (memcmp(ce->name, name, namelen) == 0) {
found = 1;
break;
}
}
if (!found) {
printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name);
rdev_munmap(&rdev, ct);
return CB_CMOS_OPTION_NOT_FOUND;
}

length = ce->length;
if (ce->config == 's') {
length = MAX(strlen((const char *)value) * 8, ce->length - 8);
/* make sure the string is null terminated */
if (set_cmos_value(ce->bit + ce->length - 8, 8, &(u8[]){0})
!= CB_SUCCESS) {
rdev_munmap(&rdev, ct);
return CB_CMOS_ACCESS_ERROR;
}
}

if (set_cmos_value(ce->bit, length, value) != CB_SUCCESS) {
rdev_munmap(&rdev, ct);
return CB_CMOS_ACCESS_ERROR;
}

rdev_munmap(&rdev, ct);
return CB_SUCCESS;
while (cmos_read(RTC_REG_A) & RTC_UIP)
;
}

/*
Expand Down
64 changes: 6 additions & 58 deletions src/drivers/pc80/rtc/mc146818rtc_boot.c
Expand Up @@ -12,82 +12,30 @@
*/

#include <stdint.h>
#include <cbfs.h>
#include <option.h>
#include <pc80/mc146818rtc.h>
#include <fallback.h>
#if CONFIG(USE_OPTION_TABLE)
#include <option_table.h>
#endif

int cmos_error(void)
{
unsigned char reg_d;
/* See if the cmos error condition has been flagged */
reg_d = cmos_read(RTC_REG_D);
return (reg_d & RTC_VRT) == 0;
}

int cmos_chksum_valid(void)
{
#if CONFIG(USE_OPTION_TABLE)
unsigned char addr;
u16 sum, old_sum;

sum = 0;
/* Compute the cmos checksum */
for (addr = LB_CKS_RANGE_START; addr <= LB_CKS_RANGE_END; addr++)
sum += cmos_read(addr);

/* Read the stored checksum */
old_sum = cmos_read(LB_CKS_LOC) << 8;
old_sum |= cmos_read(LB_CKS_LOC + 1);

return sum == old_sum;
#else
return 0;
#endif
}

#if CONFIG(USE_OPTION_TABLE)
void sanitize_cmos(void)
{
if (cmos_error() || !cmos_chksum_valid() ||
CONFIG(STATIC_OPTION_TABLE)) {
size_t length = 128;
const unsigned char *cmos_default =
cbfs_boot_map_with_leak("cmos.default",
CBFS_COMPONENT_CMOS_DEFAULT, &length);
if (cmos_default) {
size_t i;
cmos_disable_rtc();
for (i = 14; i < MIN(128, length); i++)
cmos_write_inner(cmos_default[i], i);
cmos_enable_rtc();
}
}
}
#endif

#if CONFIG_MAX_REBOOT_CNT > 15
#error "CONFIG_MAX_REBOOT_CNT too high"
#endif

static inline int boot_count(uint8_t rtc_byte)
static int boot_count(uint8_t rtc_byte)
{
return rtc_byte >> 4;
}

static inline uint8_t increment_boot_count(uint8_t rtc_byte)
static uint8_t increment_boot_count(uint8_t rtc_byte)
{
return rtc_byte + (1 << 4);
}

static inline uint8_t boot_set_fallback(uint8_t rtc_byte)
static uint8_t boot_set_fallback(uint8_t rtc_byte)
{
return rtc_byte & ~RTC_BOOT_NORMAL;
}

static inline int boot_use_normal(uint8_t rtc_byte)
static int boot_use_normal(uint8_t rtc_byte)
{
return rtc_byte & RTC_BOOT_NORMAL;
}
Expand All @@ -96,7 +44,7 @@ int do_normal_boot(void)
{
unsigned char byte;

if (cmos_error() || !cmos_chksum_valid()) {
if (cmos_error() || (CONFIG(USE_OPTION_TABLE) && !cmos_lb_cks_valid())) {
/* Invalid CMOS checksum detected!
* Force fallback boot...
*/
Expand Down
263 changes: 263 additions & 0 deletions src/drivers/pc80/rtc/option.c
@@ -0,0 +1,263 @@
/*
* This file is part of the coreboot project.
*
* 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.
*/

#include <console/console.h>
#include <string.h>
#include <cbfs.h>
#include <option.h>
#include <pc80/mc146818rtc.h>
#include <types.h>

/* option_table.h is autogenerated */
#include "option_table.h"

/* Don't warn for checking >= LB_CKS_RANGE_START even though it may be 0. */
#pragma GCC diagnostic ignored "-Wtype-limits"

/*
* This routine returns the value of the requested bits.
* input bit = bit count from the beginning of the cmos image
* length = number of bits to include in the value
* ret = a character pointer to where the value is to be returned
* returns CB_SUCCESS = successful, cb_err code if an error occurred
*/
static enum cb_err get_cmos_value(unsigned long bit, unsigned long length,
void *vret)
{
unsigned char *ret;
unsigned long byte, byte_bit;
unsigned long i;
unsigned char uchar;

/*
* The table is checked when it is built to ensure all
* values are valid.
*/
ret = vret;
byte = bit / 8; /* find the byte where the data starts */
byte_bit = bit % 8; /* find the bit in the byte where the data starts */
if (length < 9) { /* one byte or less */
uchar = cmos_read(byte); /* load the byte */
uchar >>= byte_bit; /* shift the bits to byte align */
/* clear unspecified bits */
ret[0] = uchar & ((1 << length) - 1);
} else { /* more than one byte so transfer the whole bytes */
for (i = 0; length; i++, length -= 8, byte++) {
/* load the byte */
ret[i] = cmos_read(byte);
}
}
return CB_SUCCESS;
}

static enum cb_err locate_cmos_layout(struct region_device *rdev)
{
uint32_t cbfs_type = CBFS_COMPONENT_CMOS_LAYOUT;
MAYBE_STATIC_BSS struct cbfsf fh = {};

/*
* In case VBOOT is enabled and this function is called from SMM,
* we have multiple CMOS layout files and to locate them we'd need to
* include VBOOT into SMM...
*
* Support only one CMOS layout in the 'COREBOOT' region for now.
*/
if (!region_device_sz(&(fh.data))) {
if (cbfs_locate_file_in_region(&fh, "COREBOOT", "cmos_layout.bin",
&cbfs_type)) {
printk(BIOS_ERR, "RTC: cmos_layout.bin could not be found. "
"Options are disabled\n");
return CB_CMOS_LAYOUT_NOT_FOUND;
}
}

cbfs_file_data(rdev, &fh);

return CB_SUCCESS;
}

enum cb_err cmos_get_option(void *dest, const char *name)
{
struct cmos_option_table *ct;
struct region_device rdev;
struct cmos_entries *ce;
size_t namelen;
int found = 0;

/* Figure out how long name is */
namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);

if (locate_cmos_layout(&rdev) != CB_SUCCESS) {
return CB_CMOS_LAYOUT_NOT_FOUND;
}
ct = rdev_mmap_full(&rdev);
if (!ct) {
printk(BIOS_ERR, "RTC: cmos_layout.bin could not be mapped. "
"Options are disabled\n");

return CB_CMOS_LAYOUT_NOT_FOUND;
}

/* find the requested entry record */
ce = (struct cmos_entries *)((unsigned char *)ct + ct->header_length);
for (; ce->tag == LB_TAG_OPTION;
ce = (struct cmos_entries *)((unsigned char *)ce + ce->size)) {
if (memcmp(ce->name, name, namelen) == 0) {
found = 1;
break;
}
}
if (!found) {
printk(BIOS_DEBUG, "No CMOS option '%s'.\n", name);
rdev_munmap(&rdev, ct);
return CB_CMOS_OPTION_NOT_FOUND;
}

if (!cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC)) {
rdev_munmap(&rdev, ct);
return CB_CMOS_CHECKSUM_INVALID;
}
if (get_cmos_value(ce->bit, ce->length, dest) != CB_SUCCESS) {
rdev_munmap(&rdev, ct);
return CB_CMOS_ACCESS_ERROR;
}
rdev_munmap(&rdev, ct);
return CB_SUCCESS;
}

static enum cb_err set_cmos_value(unsigned long bit, unsigned long length,
void *vret)
{
unsigned char *ret;
unsigned long byte, byte_bit;
unsigned long i;
unsigned char uchar, mask;
unsigned int chksum_update_needed = 0;

ret = vret;
byte = bit / 8; /* find the byte where the data starts */
byte_bit = bit % 8; /* find the bit where the data starts */
if (length <= 8) { /* one byte or less */
mask = (1 << length) - 1;
mask <<= byte_bit;

uchar = cmos_read(byte);
uchar &= ~mask;
uchar |= (ret[0] << byte_bit);
cmos_write(uchar, byte);
if (byte >= LB_CKS_RANGE_START && byte <= LB_CKS_RANGE_END)
chksum_update_needed = 1;
} else { /* more that one byte so transfer the whole bytes */
if (byte_bit || length % 8)
return CB_ERR_ARG;

for (i = 0; length; i++, length -= 8, byte++) {
cmos_write(ret[i], byte);
if (byte >= LB_CKS_RANGE_START &&
byte <= LB_CKS_RANGE_END)
chksum_update_needed = 1;
}
}

if (chksum_update_needed) {
cmos_set_checksum(LB_CKS_RANGE_START, LB_CKS_RANGE_END,
LB_CKS_LOC);
}
return CB_SUCCESS;
}

enum cb_err cmos_set_option(const char *name, void *value)
{
struct cmos_option_table *ct;
struct region_device rdev;
struct cmos_entries *ce;
unsigned long length;
size_t namelen;
int found = 0;

/* Figure out how long name is */
namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);

if (locate_cmos_layout(&rdev) != CB_SUCCESS) {
return CB_CMOS_LAYOUT_NOT_FOUND;
}
ct = rdev_mmap_full(&rdev);
if (!ct) {
printk(BIOS_ERR, "RTC: cmos_layout.bin could not be mapped. "
"Options are disabled\n");

return CB_CMOS_LAYOUT_NOT_FOUND;
}

/* find the requested entry record */
ce = (struct cmos_entries *)((unsigned char *)ct + ct->header_length);
for (; ce->tag == LB_TAG_OPTION;
ce = (struct cmos_entries *)((unsigned char *)ce + ce->size)) {
if (memcmp(ce->name, name, namelen) == 0) {
found = 1;
break;
}
}
if (!found) {
printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name);
rdev_munmap(&rdev, ct);
return CB_CMOS_OPTION_NOT_FOUND;
}

length = ce->length;
if (ce->config == 's') {
length = MAX(strlen((const char *)value) * 8, ce->length - 8);
/* make sure the string is null terminated */
if (set_cmos_value(ce->bit + ce->length - 8, 8, &(u8[]){0})
!= CB_SUCCESS) {
rdev_munmap(&rdev, ct);
return CB_CMOS_ACCESS_ERROR;
}
}

if (set_cmos_value(ce->bit, length, value) != CB_SUCCESS) {
rdev_munmap(&rdev, ct);
return CB_CMOS_ACCESS_ERROR;
}

rdev_munmap(&rdev, ct);
return CB_SUCCESS;
}

int cmos_lb_cks_valid(void)
{
return cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC);
}

static void cmos_load_defaults(void)
{
size_t length = 128;
size_t i;

const unsigned char *cmos_default =
cbfs_boot_map_with_leak("cmos.default",
CBFS_COMPONENT_CMOS_DEFAULT, &length);
if (!cmos_default)
return;

u8 control_state = cmos_disable_rtc();
for (i = 14; i < MIN(128, length); i++)
cmos_write_inner(cmos_default[i], i);
cmos_restore_rtc(control_state);
}

void sanitize_cmos(void)
{
if (cmos_error() || !cmos_lb_cks_valid() || CONFIG(STATIC_OPTION_TABLE))
cmos_load_defaults();
}
137 changes: 137 additions & 0 deletions src/drivers/pc80/rtc/post.c
@@ -0,0 +1,137 @@
/*
* This file is part of the coreboot project.
*
* 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.
*/

#include <stdint.h>
#include <elog.h>
#include <console/console.h>
#include <device/device.h>
#include <pc80/mc146818rtc.h>
#include <smp/spinlock.h>

DECLARE_SPIN_LOCK(cmos_post_lock)

void cmos_post_log(void)
{
u8 code = 0;
u32 extra = 0;

spin_lock(&cmos_post_lock);

/* Get post code from other bank */
switch (cmos_read(CMOS_POST_BANK_OFFSET)) {
case CMOS_POST_BANK_0_MAGIC:
code = cmos_read(CMOS_POST_BANK_1_OFFSET);
if (CONFIG(CMOS_POST_EXTRA))
extra = cmos_read32(CMOS_POST_BANK_1_EXTRA);
break;
case CMOS_POST_BANK_1_MAGIC:
code = cmos_read(CMOS_POST_BANK_0_OFFSET);
if (CONFIG(CMOS_POST_EXTRA))
extra = cmos_read32(CMOS_POST_BANK_0_EXTRA);
break;
}

spin_unlock(&cmos_post_lock);

/* Check last post code in previous boot against normal list */
switch (code) {
case POST_OS_BOOT:
case POST_OS_RESUME:
case POST_ENTER_ELF_BOOT:
case 0:
break;
default:
printk(BIOS_WARNING, "POST: Unexpected post code "
"in previous boot: 0x%02x\n", code);
#if CONFIG(ELOG) && (ENV_RAMSTAGE || CONFIG(ELOG_PRERAM))
elog_add_event_word(ELOG_TYPE_LAST_POST_CODE, code);
if (CONFIG(CMOS_POST_EXTRA) && extra)
elog_add_event_dword(ELOG_TYPE_POST_EXTRA, extra);
#endif
}
}

void cmos_post_init(void)
{
u8 magic = CMOS_POST_BANK_0_MAGIC;

/* Switch to the other bank */
switch (cmos_read(CMOS_POST_BANK_OFFSET)) {
case CMOS_POST_BANK_1_MAGIC:
break;
case CMOS_POST_BANK_0_MAGIC:
magic = CMOS_POST_BANK_1_MAGIC;
break;
default:
/* Initialize to zero */
cmos_write(0, CMOS_POST_BANK_0_OFFSET);
cmos_write(0, CMOS_POST_BANK_1_OFFSET);
if (CONFIG(CMOS_POST_EXTRA)) {
cmos_write32(0, CMOS_POST_BANK_0_EXTRA);
cmos_write32(0, CMOS_POST_BANK_1_EXTRA);
}
}

cmos_write(magic, CMOS_POST_BANK_OFFSET);
}

void cmos_post_code(u8 value)
{
spin_lock(&cmos_post_lock);

switch (cmos_read(CMOS_POST_BANK_OFFSET)) {
case CMOS_POST_BANK_0_MAGIC:
cmos_write(value, CMOS_POST_BANK_0_OFFSET);
break;
case CMOS_POST_BANK_1_MAGIC:
cmos_write(value, CMOS_POST_BANK_1_OFFSET);
break;
}

spin_unlock(&cmos_post_lock);
}

static void __unused cmos_post_extra(u32 value)
{
spin_lock(&cmos_post_lock);

switch (cmos_read(CMOS_POST_BANK_OFFSET)) {
case CMOS_POST_BANK_0_MAGIC:
cmos_write32(value, CMOS_POST_BANK_0_EXTRA);
break;
case CMOS_POST_BANK_1_MAGIC:
cmos_write32(value, CMOS_POST_BANK_1_EXTRA);
break;
}

spin_unlock(&cmos_post_lock);
}

#if CONFIG(CMOS_POST_EXTRA)
void post_log_path(const struct device *dev)
{
if (dev) {
/* Encode path into lower 3 bytes */
u32 path = dev_path_encode(dev);
/* Upper byte contains the log type */
path |= CMOS_POST_EXTRA_DEV_PATH << 24;
cmos_post_extra(path);
}
}

void post_log_clear(void)
{
cmos_post_extra(0);
}
#endif /* CONFIG_CMOS_POST_EXTRA */
2 changes: 1 addition & 1 deletion src/drivers/pc80/tpm/tis.c
Expand Up @@ -902,7 +902,7 @@ static void lpc_tpm_fill_ssdt(struct device *dev)
acpigen_write_name("_CID");
acpigen_emit_eisaid("PNP0C31");

acpigen_write_name_integer("_UID", 1);
acpi_device_write_uid(dev);

u32 did_vid = tpm_read_did_vid(0);
if (did_vid > 0 && did_vid < 0xffffffff)
Expand Down
2 changes: 1 addition & 1 deletion src/drivers/smmstore/Kconfig
Expand Up @@ -21,7 +21,7 @@ config SMMSTORE_IN_CBFS
bool
default n
help
Select this if you want to the SMMSTORE region in a
Select this if you want to add an SMMSTORE region to a
cbfsfile in a cbfs FMAP region

if SMMSTORE
Expand Down
16 changes: 6 additions & 10 deletions src/drivers/spi/Kconfig
Expand Up @@ -61,9 +61,14 @@ config BOOT_DEVICE_SPI_FLASH_RW_NOMMAP_EARLY
Include the common implementation in all stages, including the
early ones.

config SPI_FLASH_DONT_INCLUDE_ALL_DRIVERS
bool
default y if COMMON_CBFS_SPI_WRAPPER
default n

config SPI_FLASH_INCLUDE_ALL_DRIVERS
bool
default n if COMMON_CBFS_SPI_WRAPPER
default n if SPI_FLASH_DONT_INCLUDE_ALL_DRIVERS
default y

config SPI_FLASH_SMM
Expand Down Expand Up @@ -149,15 +154,6 @@ config SPI_FLASH_WINBOND
Select this option if your chipset driver needs to store certain
data in the SPI flash and your SPI flash is made by Winbond.

config SPI_FLASH_FAST_READ_DUAL_OUTPUT_3B
bool
default n
depends on SPI_FLASH
help
Select this option if your SPI flash supports the fast read dual-
output command (opcode 0x3b) where the opcode and address are sent
to the chip on MOSI and data is received on both MOSI and MISO.

config SPI_FLASH_HAS_VOLATILE_GROUP
bool
default n
Expand Down
173 changes: 21 additions & 152 deletions src/drivers/spi/adesto.c
Expand Up @@ -39,206 +39,75 @@
#define CMD_AT25DF_DP 0xb9 /* Deep Power-down */
#define CMD_AT25DF_RES 0xab /* Release from DP, and Read Signature */

struct adesto_spi_flash_params {
uint16_t id;
/* Log2 of page size in power-of-two mode */
uint8_t l2_page_size;
uint16_t pages_per_sector;
uint16_t sectors_per_block;
uint16_t nr_blocks;
const char *name;
};

static const struct adesto_spi_flash_params adesto_spi_flash_table[] = {
static const struct spi_flash_part_id flash_table[] = {
{
.id = 0x4218,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 256,
.name = "AT25SL128A",
.nr_sectors_shift = 12,
},
{
.id = 0x4501,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 16,
.name = "AT25DF081A", /* Yes, 81A id < 81 */
.nr_sectors_shift = 8,
},
{
.id = 0x4502,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 16,
.name = "AT25DF081",
.nr_sectors_shift = 8,
},
{
.id = 0x4602,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 32,
.name = "AT25DF161",
.nr_sectors_shift = 9,
},
{
.id = 0x4603,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 32,
.name = "AT25DL161",
.nr_sectors_shift = 9,
},
{
.id = 0x4700,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 64,
.name = "AT25DF321",
.nr_sectors_shift = 10,
},
{
.id = 0x4701,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 64,
.name = "AT25DF321A",
.nr_sectors_shift = 10,
},
{
.id = 0x4800,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 128,
.name = "AT25DF641",
.nr_sectors_shift = 11,
},
{
.id = 0x8501,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 16,
.name = "AT25SF081",
.nr_sectors_shift = 8,
},
{
.id = 0x8600,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 32,
.name = "AT25DQ161",
.nr_sectors_shift = 9,
},
{
.id = 0x8601,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 32,
.name = "AT25SF161",
.nr_sectors_shift = 9,
},
{
.id = 0x8700,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 64,
.name = "AT25DQ321",
.nr_sectors_shift = 10,
},
};

static int adesto_write(const struct spi_flash *flash, u32 offset, size_t len,
const void *buf)
{
unsigned long byte_addr;
unsigned long page_size;
size_t chunk_len;
size_t actual;
int ret;
u8 cmd[4];

page_size = flash->page_size;

for (actual = 0; actual < len; actual += chunk_len) {
byte_addr = offset % page_size;
chunk_len = MIN(len - actual, page_size - byte_addr);
chunk_len = spi_crop_chunk(&flash->spi, sizeof(cmd), chunk_len);

cmd[0] = CMD_AT25DF_PP;
cmd[1] = (offset >> 16) & 0xff;
cmd[2] = (offset >> 8) & 0xff;
cmd[3] = offset & 0xff;
#if CONFIG(DEBUG_SPI_FLASH)
printk(BIOS_SPEW, "PP: %p => cmd = { 0x%02x 0x%02x%02x%02x }"
" chunk_len = %zu\n", buf + actual,
cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
#endif

ret = spi_flash_cmd(&flash->spi, CMD_AT25DF_WREN, NULL, 0);
if (ret < 0) {
printk(BIOS_WARNING, "SF: Enabling Write failed\n");
goto out;
}

ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd),
buf + actual, chunk_len);
if (ret < 0) {
printk(BIOS_WARNING, "SF: adesto Page Program failed\n");
goto out;
}

ret = spi_flash_cmd_wait_ready(flash,
SPI_FLASH_PROG_TIMEOUT_MS);
if (ret)
goto out;

offset += chunk_len;
}

#if CONFIG(DEBUG_SPI_FLASH)
printk(BIOS_SPEW, "SF: adesto: Successfully programmed %zu bytes @"
" 0x%lx\n", len, (unsigned long)(offset - len));
#endif
ret = 0;

out:
return ret;
}

static const struct spi_flash_ops spi_flash_ops = {
.write = adesto_write,
.erase = spi_flash_cmd_erase,
const struct spi_flash_vendor_info spi_flash_adesto_vi = {
.id = VENDOR_ID_ADESTO,
.page_size_shift = 8,
.sector_size_kib_shift = 2,
.match_id_mask = 0xffff,
.ids = flash_table,
.nr_part_ids = ARRAY_SIZE(flash_table),
.desc = &spi_flash_pp_0x20_sector_desc,
};

int spi_flash_probe_adesto(const struct spi_slave *spi, u8 *idcode,
struct spi_flash *flash)
{
const struct adesto_spi_flash_params *params;
unsigned int i;

for (i = 0; i < ARRAY_SIZE(adesto_spi_flash_table); i++) {
params = &adesto_spi_flash_table[i];
if (params->id == ((idcode[1] << 8) | idcode[2]))
break;
}

if (i == ARRAY_SIZE(adesto_spi_flash_table)) {
printk(BIOS_WARNING, "SF: Unsupported adesto ID %02x%02x\n",
idcode[1], idcode[2]);
return -1;
}

memcpy(&flash->spi, spi, sizeof(*spi));
flash->name = params->name;
/* Assuming power-of-two page size initially. */
flash->page_size = 1 << params->l2_page_size;
flash->sector_size = flash->page_size * params->pages_per_sector;
flash->size = flash->sector_size *params->sectors_per_block *
params->nr_blocks;
flash->erase_cmd = CMD_AT25DF_SE;

flash->ops = &spi_flash_ops;

return 0;
}
160 changes: 18 additions & 142 deletions src/drivers/spi/amic.c
Expand Up @@ -34,184 +34,60 @@
#define CMD_A25_DP 0xb9 /* Deep Power-down */
#define CMD_A25_RES 0xab /* Release from DP, and Read Signature */

struct amic_spi_flash_params {
uint16_t id;
/* Log2 of page size in power-of-two mode */
uint8_t l2_page_size;
uint16_t pages_per_sector;
uint16_t sectors_per_block;
uint16_t nr_blocks;
const char *name;
};

static const struct amic_spi_flash_params amic_spi_flash_table[] = {
static const struct spi_flash_part_id flash_table[] = {
{
.id = 0x2015,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 32,
.name = "A25L16PU",
.nr_sectors_shift = 9,
},
{
.id = 0x2025,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 32,
.name = "A25L16PT",
.nr_sectors_shift = 9,
},
{
.id = 0x3014,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 16,
.name = "A25L080",
.nr_sectors_shift = 8,
},
{
.id = 0x3015,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 32,
.name = "A25L016",
.nr_sectors_shift = 9,
},
{
.id = 0x3016,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 64,
.name = "A25L032",
.nr_sectors_shift = 10,
},
{
.id = 0x4014,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 16,
.name = "A25LQ080",
.nr_sectors_shift = 8,
},
{
.id = 0x4015,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 32,
.name = "A25LQ16",
.nr_sectors_shift = 9,
},
{
.id = 0x4016,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 64,
.name = "A25LQ032",
.nr_sectors_shift = 10,
},
{
.id = 0x4017,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 128,
.name = "A25LQ64",
.nr_sectors_shift = 11,
},
};

static int amic_write(const struct spi_flash *flash, u32 offset, size_t len,
const void *buf)
{
unsigned long byte_addr;
unsigned long page_size;
size_t chunk_len;
size_t actual;
int ret;
u8 cmd[4];

page_size = flash->page_size;
byte_addr = offset % page_size;

for (actual = 0; actual < len; actual += chunk_len) {
chunk_len = MIN(len - actual, page_size - byte_addr);
chunk_len = spi_crop_chunk(&flash->spi, sizeof(cmd), chunk_len);

cmd[0] = CMD_A25_PP;
cmd[1] = (offset >> 16) & 0xff;
cmd[2] = (offset >> 8) & 0xff;
cmd[3] = offset & 0xff;
#if CONFIG(DEBUG_SPI_FLASH)
printk(BIOS_SPEW, "PP: %p => cmd = { 0x%02x 0x%02x%02x%02x }"
" chunk_len = %zu\n", buf + actual,
cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
#endif

ret = spi_flash_cmd(&flash->spi, CMD_A25_WREN, NULL, 0);
if (ret < 0) {
printk(BIOS_WARNING, "SF: Enabling Write failed\n");
goto out;
}

ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd),
buf + actual, chunk_len);
if (ret < 0) {
printk(BIOS_WARNING, "SF: AMIC Page Program failed\n");
goto out;
}

ret = spi_flash_cmd_wait_ready(flash,
SPI_FLASH_PROG_TIMEOUT_MS);
if (ret)
goto out;

offset += chunk_len;
byte_addr = 0;
}

#if CONFIG(DEBUG_SPI_FLASH)
printk(BIOS_SPEW, "SF: AMIC: Successfully programmed %zu bytes @"
" 0x%lx\n", len, (unsigned long)(offset - len));
#endif
ret = 0;

out:
return ret;
}

static const struct spi_flash_ops spi_flash_ops = {
.write = amic_write,
.erase = spi_flash_cmd_erase,
const struct spi_flash_vendor_info spi_flash_amic_vi = {
.id = VENDOR_ID_AMIC,
.page_size_shift = 8,
.sector_size_kib_shift = 2,
.match_id_mask = 0xffff,
.ids = flash_table,
.nr_part_ids = ARRAY_SIZE(flash_table),
.desc = &spi_flash_pp_0x20_sector_desc,
};

int spi_flash_probe_amic(const struct spi_slave *spi, u8 *idcode,
struct spi_flash *flash)
{
const struct amic_spi_flash_params *params;
unsigned int i;

for (i = 0; i < ARRAY_SIZE(amic_spi_flash_table); i++) {
params = &amic_spi_flash_table[i];
if (params->id == ((idcode[1] << 8) | idcode[2]))
break;
}

if (i == ARRAY_SIZE(amic_spi_flash_table)) {
printk(BIOS_WARNING, "SF: Unsupported AMIC ID %02x%02x\n",
idcode[1], idcode[2]);
return -1;
}

memcpy(&flash->spi, spi, sizeof(*spi));
flash->name = params->name;

/* Assuming power-of-two page size initially. */
flash->page_size = 1 << params->l2_page_size;
flash->sector_size = flash->page_size * params->pages_per_sector;
flash->size = flash->sector_size * params->sectors_per_block *
params->nr_blocks;
flash->erase_cmd = CMD_A25_SE;

flash->ops = &spi_flash_ops;

return 0;
}
149 changes: 16 additions & 133 deletions src/drivers/spi/atmel.c
Expand Up @@ -34,167 +34,50 @@
#define CMD_AT25_DP 0xb9 /* Deep Power-down */
#define CMD_AT25_RES 0xab /* Release from DP, and Read Signature */

struct atmel_spi_flash_params {
uint16_t id;
/* Log2 of page size in power-of-two mode */
uint8_t l2_page_size;
uint16_t pages_per_sector;
uint16_t sectors_per_block;
uint16_t nr_blocks;
const char *name;
};

static const struct atmel_spi_flash_params atmel_spi_flash_table[] = {
static const struct spi_flash_part_id flash_table[] = {
{
.id = 0x3015,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 32,
.name = "AT25X16",
.nr_sectors_shift = 9,
},
{
.id = 0x47,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 64,
.name = "AT25DF32",
.nr_sectors_shift = 10,
},
{
.id = 0x3017,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 128,
.name = "AT25X64",
.nr_sectors_shift = 11,
},
{
.id = 0x4015,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 32,
.name = "AT25Q16",
.nr_sectors_shift = 9,
},
{
.id = 0x4016,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 64,
.name = "AT25Q32",
.nr_sectors_shift = 10,
},
{
.id = 0x4017,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 128,
.name = "AT25Q64",
.nr_sectors_shift = 11,
},
{
.id = 0x4018,
.l2_page_size = 8,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 256,
.name = "AT25Q128",
.nr_sectors_shift = 12,
},
};

static int atmel_write(const struct spi_flash *flash, u32 offset, size_t len,
const void *buf)
{
unsigned long byte_addr;
unsigned long page_size;
size_t chunk_len;
size_t actual;
int ret;
u8 cmd[4];

page_size = flash->page_size;

for (actual = 0; actual < len; actual += chunk_len) {
byte_addr = offset % page_size;
chunk_len = MIN(len - actual, page_size - byte_addr);
chunk_len = spi_crop_chunk(&flash->spi, sizeof(cmd), chunk_len);

cmd[0] = CMD_AT25_PP;
cmd[1] = (offset >> 16) & 0xff;
cmd[2] = (offset >> 8) & 0xff;
cmd[3] = offset & 0xff;
#if CONFIG(DEBUG_SPI_FLASH)
printk(BIOS_SPEW, "PP: %p => cmd = { 0x%02x 0x%02x%02x%02x }"
" chunk_len = %zu\n", buf + actual,
cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
#endif

ret = spi_flash_cmd(&flash->spi, CMD_AT25_WREN, NULL, 0);
if (ret < 0) {
printk(BIOS_WARNING, "SF: Enabling Write failed\n");
goto out;
}

ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd),
buf + actual, chunk_len);
if (ret < 0) {
printk(BIOS_WARNING, "SF: Atmel Page Program failed\n");
goto out;
}

ret = spi_flash_cmd_wait_ready(flash,
SPI_FLASH_PROG_TIMEOUT_MS);
if (ret)
goto out;

offset += chunk_len;
}

#if CONFIG(DEBUG_SPI_FLASH)
printk(BIOS_SPEW, "SF: Atmel: Successfully programmed %zu bytes @"
" 0x%lx\n", len, (unsigned long)(offset - len));
#endif
ret = 0;

out:
return ret;
}

static const struct spi_flash_ops spi_flash_ops = {
.write = atmel_write,
.erase = spi_flash_cmd_erase,
const struct spi_flash_vendor_info spi_flash_atmel_vi = {
.id = VENDOR_ID_ATMEL,
.page_size_shift = 8,
.sector_size_kib_shift = 2,
.match_id_mask = 0xffff,
.ids = flash_table,
.nr_part_ids = ARRAY_SIZE(flash_table),
.desc = &spi_flash_pp_0x20_sector_desc,
};

int spi_flash_probe_atmel(const struct spi_slave *spi, u8 *idcode,
struct spi_flash *flash)
{
const struct atmel_spi_flash_params *params;
unsigned int i;

for (i = 0; i < ARRAY_SIZE(atmel_spi_flash_table); i++) {
params = &atmel_spi_flash_table[i];
if (params->id == ((idcode[1] << 8) | idcode[2]))
break;
}

if (i == ARRAY_SIZE(atmel_spi_flash_table)) {
printk(BIOS_WARNING, "SF: Unsupported Atmel ID %02x%02x\n",
idcode[1], idcode[2]);
return -1;
}

memcpy(&flash->spi, spi, sizeof(*spi));
flash->name = params->name;

/* Assuming power-of-two page size initially. */
flash->page_size = 1 << params->l2_page_size;
flash->sector_size = flash->page_size * params->pages_per_sector;
flash->size = flash->sector_size * params->sectors_per_block *
params->nr_blocks;
flash->erase_cmd = CMD_AT25_SE;

flash->ops = &spi_flash_ops;

return 0;
}
220 changes: 30 additions & 190 deletions src/drivers/spi/eon.c
Expand Up @@ -55,280 +55,120 @@
#define EON_ID_EN25S32 0x3816
#define EON_ID_EN25S64 0x3817

struct eon_spi_flash_params {
u16 id;
u16 page_size;
u16 pages_per_sector;
u16 sectors_per_block;
u16 nr_sectors;
const char *name;
};

static const struct eon_spi_flash_params eon_spi_flash_table[] = {
static const struct spi_flash_part_id flash_table[] = {
{
.id = EON_ID_EN25B80,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 256,
.name = "EN25B80",
.nr_sectors_shift = 8,
},
{
.id = EON_ID_EN25B16,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 512,
.name = "EN25B16",
.nr_sectors_shift = 9,
},
{
.id = EON_ID_EN25B32,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 1024,
.name = "EN25B32",
.nr_sectors_shift = 10,
},
{
.id = EON_ID_EN25B64,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 2048,
.name = "EN25B64",
.nr_sectors_shift = 11,
},
{
.id = EON_ID_EN25F80,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 256,
.name = "EN25F80",
.nr_sectors_shift = 8,
},
{
.id = EON_ID_EN25F16,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 512,
.name = "EN25F16",
.nr_sectors_shift = 9,
},
{
.id = EON_ID_EN25F32,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 1024,
.name = "EN25F32",
.nr_sectors_shift = 10,
},
{
.id = EON_ID_EN25F64,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 2048,
.name = "EN25F64",
.nr_sectors_shift = 11,
},
{
.id = EON_ID_EN25Q80,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 256,
.name = "EN25Q80(A)",
.nr_sectors_shift = 8,
},
{
.id = EON_ID_EN25Q16,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 512,
.name = "EN25Q16(D16)",
.nr_sectors_shift = 9,
},
{
.id = EON_ID_EN25Q32,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 1024,
.name = "EN25Q32(A/B)",
.nr_sectors_shift = 10,
},
{
.id = EON_ID_EN25Q64,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 2048,
.name = "EN25Q64",
.nr_sectors_shift = 11,
},
{
.id = EON_ID_EN25Q128,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 4096,
.name = "EN25Q128",
.nr_sectors_shift = 12,
},
{
.id = EON_ID_EN25QH16,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 512,
.name = "EN25QH16",
.nr_sectors_shift = 9,
},
{
.id = EON_ID_EN25QH32,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 1024,
.name = "EN25QH32",
.nr_sectors_shift = 10,
},
{
.id = EON_ID_EN25QH64,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 2048,
.name = "EN25QH64",
.nr_sectors_shift = 11,
},
{
.id = EON_ID_EN25QH128,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 4096,
.name = "EN25QH128",
.nr_sectors_shift = 12,
},
{
.id = EON_ID_EN25S80,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 256,
.name = "EN25S80",
.nr_sectors_shift = 8,
},
{
.id = EON_ID_EN25S16,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 512,
.name = "EN25S16",
.nr_sectors_shift = 9,
},
{
.id = EON_ID_EN25S32,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 1024,
.name = "EN25S32",
.nr_sectors_shift = 10,
},
{
.id = EON_ID_EN25S64,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_sectors = 2048,
.name = "EN25S64",
.nr_sectors_shift = 11,
},
};

static int eon_write(const struct spi_flash *flash,
u32 offset, size_t len, const void *buf)
{
unsigned long byte_addr;
unsigned long page_size;
size_t chunk_len;
size_t actual;
int ret = 0;
u8 cmd[4];

page_size = flash->page_size;

for (actual = 0; actual < len; actual += chunk_len) {
byte_addr = offset % page_size;
chunk_len = MIN(len - actual, page_size - byte_addr);
chunk_len = spi_crop_chunk(&flash->spi, sizeof(cmd), chunk_len);

ret = spi_flash_cmd(&flash->spi, CMD_EN25_WREN, NULL, 0);
if (ret < 0) {
printk(BIOS_WARNING, "SF: Enabling Write failed\n");
goto out;
}

cmd[0] = CMD_EN25_PP;
cmd[1] = (offset >> 16) & 0xff;
cmd[2] = (offset >> 8) & 0xff;
cmd[3] = offset & 0xff;

#if CONFIG(DEBUG_SPI_FLASH)
printk(BIOS_SPEW,
"PP: %p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n",
buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
#endif

ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd),
buf + actual, chunk_len);
if (ret < 0) {
printk(BIOS_WARNING, "SF: EON Page Program failed\n");
goto out;
}

ret = spi_flash_cmd_wait_ready(flash,
SPI_FLASH_PROG_TIMEOUT_MS);
if (ret) {
printk(BIOS_WARNING, "SF: EON Page Program timeout\n");
goto out;
}

offset += chunk_len;
}

#if CONFIG(DEBUG_SPI_FLASH)
printk(BIOS_SPEW, "SF: EON: Successfully programmed %zu bytes @ %#x\n",
len, (unsigned int)(offset - len));
#endif

out:
return ret;
}

static const struct spi_flash_ops spi_flash_ops = {
.write = eon_write,
.erase = spi_flash_cmd_erase,
.status = spi_flash_cmd_status,
const struct spi_flash_vendor_info spi_flash_eon_vi = {
.id = VENDOR_ID_EON,
.page_size_shift = 8,
.sector_size_kib_shift = 2,
.match_id_mask = 0xffff,
.ids = flash_table,
.nr_part_ids = ARRAY_SIZE(flash_table),
.desc = &spi_flash_pp_0x20_sector_desc,
};

int spi_flash_probe_eon(const struct spi_slave *spi, u8 *idcode,
struct spi_flash *flash)
{
const struct eon_spi_flash_params *params;
unsigned int i;

for (i = 0; i < ARRAY_SIZE(eon_spi_flash_table); ++i) {
params = &eon_spi_flash_table[i];
if (params->id == ((idcode[1] << 8) | idcode[2]))
break;
}

if (i == ARRAY_SIZE(eon_spi_flash_table)) {
printk(BIOS_WARNING, "SF: Unsupported EON ID %#02x%02x\n",
idcode[1], idcode[2]);
return -1;
}

memcpy(&flash->spi, spi, sizeof(*spi));

flash->name = params->name;
flash->page_size = params->page_size;
flash->sector_size = params->page_size * params->pages_per_sector;
flash->size = flash->sector_size * params->nr_sectors;
flash->erase_cmd = CMD_EN25_SE;
flash->status_cmd = CMD_EN25_RDSR;

flash->ops = &spi_flash_ops;

return 0;
}
17 changes: 8 additions & 9 deletions src/drivers/spi/flashconsole.c
Expand Up @@ -32,7 +32,7 @@ void flashconsole_init(void)
{
uint8_t buffer[READ_BUFFER_SIZE];
size_t size;
size_t offset = 0;
size_t initial_offset = 0;
size_t len = READ_BUFFER_SIZE;
size_t i;

Expand All @@ -52,30 +52,30 @@ void flashconsole_init(void)
* the sector is already erased, so we would need to read
* anyways to check if it's all 0xff).
*/
for (i = 0; i < len && offset < size;) {
for (i = 0; i < len && initial_offset < size;) {
// Fill the buffer on first iteration
if (i == 0) {
len = MIN(READ_BUFFER_SIZE, size - offset);
if (rdev_readat(&rdev, buffer, offset, len) != len)
if (rdev_readat(&rdev, buffer, initial_offset, len) != len)
return;
}
if (buffer[i] == 0xff) {
offset += i;
initial_offset += i;
break;
}
// If we're done, repeat the process for the next sector
if (++i == READ_BUFFER_SIZE) {
offset += len;
initial_offset += len;
i = 0;
}
}
// Make sure there is still space left on the console
if (offset >= size) {
if (initial_offset >= size) {
printk(BIOS_INFO, "No space left on 'console' region in SPI flash\n");
return;
}

offset = offset;
offset = initial_offset;
rdev_ptr = &rdev;
}

Expand All @@ -96,7 +96,6 @@ void flashconsole_tx_byte(unsigned char c)

void flashconsole_tx_flush(void)
{
size_t offset = offset;
size_t len = line_offset;
size_t region_size;
static int busy;
Expand All @@ -122,7 +121,7 @@ void flashconsole_tx_flush(void)
if (offset + len >= region_size)
return;

offset = offset + len;
offset += len;
line_offset = 0;

busy = 0;
Expand Down
214 changes: 34 additions & 180 deletions src/drivers/spi/gigadevice.c
Expand Up @@ -34,238 +34,92 @@
#define CMD_GD25_DP 0xb9 /* Deep Power-down */
#define CMD_GD25_RES 0xab /* Release from DP, and Read Signature */

struct gigadevice_spi_flash_params {
uint16_t id;
uint8_t dual_spi : 1;
uint8_t _reserved_for_flags : 3;
uint8_t l2_page_size_shift : 4;
uint8_t pages_per_sector_shift : 4;
uint8_t sectors_per_block_shift : 4;
uint8_t nr_blocks_shift;
const char name[10];
};

static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = {
static const struct spi_flash_part_id flash_table[] = {
{
.id = 0x3114,
.l2_page_size_shift = 8,
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 4,
.name = "GD25T80",
.nr_sectors_shift = 8,
},
{
.id = 0x4014,
.l2_page_size_shift = 8,
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 4,
.dual_spi = 1,
.name = "GD25Q80",
.nr_sectors_shift = 8,
.fast_read_dual_output_support = 1,
}, /* also GD25Q80B */
{
.id = 0x4015,
.l2_page_size_shift = 8,
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 5,
.dual_spi = 1,
.name = "GD25Q16",
.nr_sectors_shift = 9,
.fast_read_dual_output_support = 1,
}, /* also GD25Q16B */
{
.id = 0x4016,
.l2_page_size_shift = 8,
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 6,
.dual_spi = 1,
.name = "GD25Q32B",
.nr_sectors_shift = 10,
.fast_read_dual_output_support = 1,
}, /* also GD25Q32B */
{
.id = 0x4017,
.l2_page_size_shift = 8,
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 7,
.dual_spi = 1,
.name = "GD25Q64",
.nr_sectors_shift = 11,
.fast_read_dual_output_support = 1,
}, /* also GD25Q64B, GD25B64C */
{
.id = 0x4018,
.l2_page_size_shift = 8,
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 8,
.dual_spi = 1,
.name = "GD25Q128",
.nr_sectors_shift = 12,
.fast_read_dual_output_support = 1,
}, /* also GD25Q128B */
{
.id = 0x4214,
.l2_page_size_shift = 8,
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 4,
.dual_spi = 1,
.name = "GD25VQ80C",
.nr_sectors_shift = 8,
.fast_read_dual_output_support = 1,
},
{
.id = 0x4215,
.l2_page_size_shift = 8,
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 5,
.dual_spi = 1,
.name = "GD25VQ16C",
.nr_sectors_shift = 9,
.fast_read_dual_output_support = 1,
},
{
.id = 0x6014,
.l2_page_size_shift = 8,
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 4,
.dual_spi = 1,
.name = "GD25LQ80",
.nr_sectors_shift = 8,
.fast_read_dual_output_support = 1,
},
{
.id = 0x6015,
.l2_page_size_shift = 8,
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 5,
.dual_spi = 1,
.name = "GD25LQ16",
.nr_sectors_shift = 9,
.fast_read_dual_output_support = 1,
},
{
.id = 0x6016,
.l2_page_size_shift = 8,
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 6,
.dual_spi = 1,
.name = "GD25LQ32",
.nr_sectors_shift = 10,
.fast_read_dual_output_support = 1,
},
{
.id = 0x6017,
.l2_page_size_shift = 8,
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 7,
.dual_spi = 1,
.name = "GD25LQ64C",
.nr_sectors_shift = 11,
.fast_read_dual_output_support = 1,
}, /* also GD25LB64C */
{
.id = 0x6018,
.l2_page_size_shift = 8,
.pages_per_sector_shift = 4,
.sectors_per_block_shift = 4,
.nr_blocks_shift = 8,
.dual_spi = 1,
.name = "GD25LQ128",
.nr_sectors_shift = 12,
.fast_read_dual_output_support = 1,
},
};

static int gigadevice_write(const struct spi_flash *flash, u32 offset,
size_t len, const void *buf)
{
unsigned long byte_addr;
unsigned long page_size;
size_t chunk_len;
size_t actual;
int ret = 0;
u8 cmd[4];

page_size = flash->page_size;

for (actual = 0; actual < len; actual += chunk_len) {
byte_addr = offset % page_size;
chunk_len = MIN(len - actual, page_size - byte_addr);
chunk_len = spi_crop_chunk(&flash->spi, sizeof(cmd), chunk_len);

ret = spi_flash_cmd(&flash->spi, CMD_GD25_WREN, NULL, 0);
if (ret < 0) {
printk(BIOS_WARNING,
"SF gigadevice.c: Enabling Write failed\n");
goto out;
}

cmd[0] = CMD_GD25_PP;
cmd[1] = (offset >> 16) & 0xff;
cmd[2] = (offset >> 8) & 0xff;
cmd[3] = offset & 0xff;
#if CONFIG(DEBUG_SPI_FLASH)
printk(BIOS_SPEW,
"PP gigadevice.c: %p => cmd = { 0x%02x 0x%02x%02x%02x }"
" chunk_len = %zu\n", buf + actual,
cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
#endif

ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd),
buf + actual, chunk_len);
if (ret < 0) {
printk(BIOS_WARNING,
"SF gigadevice.c: Page Program failed\n");
goto out;
}

ret = spi_flash_cmd_wait_ready(flash,
SPI_FLASH_PROG_TIMEOUT_MS);
if (ret)
goto out;

offset += chunk_len;
}

#if CONFIG(DEBUG_SPI_FLASH)
printk(BIOS_SPEW,
"SF gigadevice.c: Successfully programmed %zu bytes @ %#x\n",
len, (unsigned int)(offset - len));
#endif

ret = 0;

out:
return ret;
}

static const struct spi_flash_ops spi_flash_ops = {
.write = gigadevice_write,
.erase = spi_flash_cmd_erase,
.status = spi_flash_cmd_status,
const struct spi_flash_vendor_info spi_flash_gigadevice_vi = {
.id = VENDOR_ID_GIGADEVICE,
.page_size_shift = 8,
.sector_size_kib_shift = 2,
.match_id_mask = 0xffff,
.ids = flash_table,
.nr_part_ids = ARRAY_SIZE(flash_table),
.desc = &spi_flash_pp_0x20_sector_desc,
};

int spi_flash_probe_gigadevice(const struct spi_slave *spi, u8 *idcode,
struct spi_flash *flash)
{
const struct gigadevice_spi_flash_params *params;
unsigned int i;

for (i = 0; i < ARRAY_SIZE(gigadevice_spi_flash_table); i++) {
params = &gigadevice_spi_flash_table[i];
if (params->id == ((idcode[1] << 8) | idcode[2]))
break;
}

if (i == ARRAY_SIZE(gigadevice_spi_flash_table)) {
printk(BIOS_WARNING,
"SF gigadevice.c: Unsupported ID %#02x%02x\n",
idcode[1], idcode[2]);
return -1;
}

memcpy(&flash->spi, spi, sizeof(*spi));
flash->name = params->name;

/* Assuming power-of-two page size initially. */
flash->page_size = 1 << params->l2_page_size_shift;
flash->sector_size = flash->page_size *
(1 << params->pages_per_sector_shift);
flash->size = flash->sector_size *
(1 << params->sectors_per_block_shift) *
(1 << params->nr_blocks_shift);
flash->erase_cmd = CMD_GD25_SE;
flash->status_cmd = CMD_GD25_RDSR;

flash->ops = &spi_flash_ops;

return 0;
}
244 changes: 47 additions & 197 deletions src/drivers/spi/macronix.c
Expand Up @@ -36,260 +36,110 @@

#define MACRONIX_SR_WIP (1 << 0) /* Write-in-Progress */

struct macronix_spi_flash_params {
u16 idcode;
u16 page_size;
u16 pages_per_sector;
u16 sectors_per_block;
u16 nr_blocks;
const char *name;
};

static const struct macronix_spi_flash_params macronix_spi_flash_table[] = {
static const struct spi_flash_part_id flash_table[] = {
{
.idcode = 0x2014,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 16,
.id = 0x2014,
.name = "MX25L8005",
.nr_sectors_shift = 8,
},
{
.idcode = 0x2015,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 32,
.id = 0x2015,
.name = "MX25L1605D",
.nr_sectors_shift = 9,
},
{
.idcode = 0x2016,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 64,
.id = 0x2016,
.name = "MX25L3205D",
.nr_sectors_shift = 10,
},
{
.idcode = 0x2017,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 128,
.id = 0x2017,
.name = "MX25L6405D",
.nr_sectors_shift = 11,
},
{
.idcode = 0x2018,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 256,
.id = 0x2018,
.name = "MX25L12805D",
.nr_sectors_shift = 12,
},
{
.idcode = 0x2019,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 512,
.id = 0x2019,
.name = "MX25L25635F",
.nr_sectors_shift = 13,
},
{
.idcode = 0x201a,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 1024,
.id = 0x201a,
.name = "MX66L51235F",
.nr_sectors_shift = 14,
},
{
.idcode = 0x2415,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 32,
.id = 0x2415,
.name = "MX25L1635D",
.nr_sectors_shift = 9,
},
{
.idcode = 0x2515,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 32,
.id = 0x2515,
.name = "MX25L1635E",
.nr_sectors_shift = 9,
},
{
.idcode = 0x2534,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 16,
.id = 0x2534,
.name = "MX25U8032E",
.nr_sectors_shift = 8,
},
{
.idcode = 0x2535,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 32,
.id = 0x2535,
.name = "MX25U1635E",
.nr_sectors_shift = 9,
},
{
.idcode = 0x2536,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 64,
.id = 0x2536,
.name = "MX25U3235E",
.nr_sectors_shift = 10,
},
{
.idcode = 0x2537,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 128,
.id = 0x2537,
.name = "MX25U6435F",
.nr_sectors_shift = 11,
},
{
.idcode = 0x2538,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 256,
.id = 0x2538,
.name = "MX25U12835F",
.nr_sectors_shift = 12,
},
{
.idcode = 0x2539,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 512,
.id = 0x2539,
.name = "MX25U25635F",
.nr_sectors_shift = 13,
},
{
.idcode = 0x253a,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 1024,
.id = 0x253a,
.name = "MX25U51245G",
.nr_sectors_shift = 14,
},
{
.idcode = 0x2618,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 256,
.id = 0x2618,
.name = "MX25L12855E",
.nr_sectors_shift = 12,
},
{
.idcode = 0x5e16,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 64,
.id = 0x5e16,
.name = "MX25L3235D", /* MX25L3225D/MX25L3236D/MX25L3237D */
.nr_sectors_shift = 10,
},
{
.idcode = 0x9517,
.page_size = 256,
.pages_per_sector = 16,
.sectors_per_block = 16,
.nr_blocks = 128,
.id = 0x9517,
.name = "MX25L6495F",
.nr_sectors_shift = 11,
},
};

static int macronix_write(const struct spi_flash *flash, u32 offset, size_t len,
const void *buf)
{
unsigned long byte_addr;
unsigned long page_size;
size_t chunk_len;
size_t actual;
int ret = 0;
u8 cmd[4];

page_size = flash->page_size;

for (actual = 0; actual < len; actual += chunk_len) {
byte_addr = offset % page_size;
chunk_len = MIN(len - actual, page_size - byte_addr);
chunk_len = spi_crop_chunk(&flash->spi, sizeof(cmd), chunk_len);

cmd[0] = CMD_MX25XX_PP;
cmd[1] = (offset >> 16) & 0xff;
cmd[2] = (offset >> 8) & 0xff;
cmd[3] = offset & 0xff;
#if CONFIG(DEBUG_SPI_FLASH)
printk(BIOS_SPEW, "PP: %p => cmd = { 0x%02x 0x%02x%02x%02x }"
" chunk_len = %zu\n",
buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
#endif

ret = spi_flash_cmd(&flash->spi, CMD_MX25XX_WREN, NULL, 0);
if (ret < 0) {
printk(BIOS_WARNING, "SF: Enabling Write failed\n");
break;
}

ret = spi_flash_cmd_write(&flash->spi, cmd, sizeof(cmd),
buf + actual, chunk_len);
if (ret < 0) {
printk(BIOS_WARNING, "SF: Macronix Page Program failed\n");
break;
}

ret = spi_flash_cmd_wait_ready(flash,
SPI_FLASH_PROG_TIMEOUT_MS);
if (ret)
break;

offset += chunk_len;
}

#if CONFIG(DEBUG_SPI_FLASH)
printk(BIOS_SPEW, "SF: Macronix: Successfully programmed %zu bytes @"
" 0x%lx\n", len, (unsigned long)(offset - len));
#endif

return ret;
}

static const struct spi_flash_ops spi_flash_ops = {
.write = macronix_write,
.erase = spi_flash_cmd_erase,
.status = spi_flash_cmd_status,
const struct spi_flash_vendor_info spi_flash_macronix_vi = {
.id = VENDOR_ID_MACRONIX,
.page_size_shift = 8,
.sector_size_kib_shift = 2,
.match_id_mask = 0xffff,
.ids = flash_table,
.nr_part_ids = ARRAY_SIZE(flash_table),
.desc = &spi_flash_pp_0x20_sector_desc,
};

int spi_flash_probe_macronix(const struct spi_slave *spi, u8 *idcode,
struct spi_flash *flash)
{
const struct macronix_spi_flash_params *params;
unsigned int i;
u16 id = idcode[2] | idcode[1] << 8;

for (i = 0; i < ARRAY_SIZE(macronix_spi_flash_table); i++) {
params = &macronix_spi_flash_table[i];
if (params->idcode == id)
break;
}

if (i == ARRAY_SIZE(macronix_spi_flash_table)) {
printk(BIOS_WARNING, "SF: Unsupported Macronix ID %04x\n", id);
return -1;
}

memcpy(&flash->spi, spi, sizeof(*spi));
flash->name = params->name;
flash->page_size = params->page_size;
flash->sector_size = params->page_size * params->pages_per_sector;
flash->size = flash->sector_size * params->sectors_per_block *
params->nr_blocks;
flash->erase_cmd = CMD_MX25XX_SE;
flash->status_cmd = CMD_MX25XX_RDSR;

flash->ops = &spi_flash_ops;

return 0;
}
284 changes: 62 additions & 222 deletions src/drivers/spi/spansion.c
Expand Up @@ -34,7 +34,6 @@
#define CMD_S25FLXX_DP 0xb9 /* Deep Power-down */
#define CMD_S25FLXX_RES 0xab /* Release from DP, and Read Signature */

#define SPSN_MANUFACTURER_ID_S25FL_K 0x01
#define SPSN_ID_S25FL008A 0x0213
#define SPSN_ID_S25FL016A 0x0214
#define SPSN_ID_S25FL032A 0x0215
Expand All @@ -50,265 +49,106 @@
#define SPSN_EXT_ID_S25FL032P 0x4d00
#define SPSN_EXT_ID_S25FLXXS_64KB 0x4d01

struct spansion_spi_flash_params {
u8 idcode0;
u16 idcode1;
u16 idcode2;
int (*identify) (const struct spansion_spi_flash_params *params,
u8 *idcode);
u16 page_size;
u16 pages_per_sector;
u16 nr_sectors;
const char *name;
};

/*
* returns non-zero if the given idcode matches the ID of the chip. this is for
* chips which use 2nd, 3rd, 4th, and 5th byte.
*/
static int identify_2345(const struct spansion_spi_flash_params *params,
u8 *idcode)
{
u16 jedec = idcode[1] << 8 | idcode[2];
u16 ext_jedec = idcode[3] << 8 | idcode[4];
return (params->idcode1 == jedec) && (params->idcode2 == ext_jedec);
}

/*
* returns non-zero if the given idcode matches the ID of the chip. this is for
* chips which use 1st, 2nd, and 3rd byte.
*/
static int identify_123(const struct spansion_spi_flash_params *params,
u8 *idcode)
{
u16 jedec = idcode[1] << 8 | idcode[2];
return (params->idcode0 == idcode[0]) && (params->idcode1 == jedec);
}

static const struct spansion_spi_flash_params spansion_spi_flash_table[] = {
static const struct spi_flash_part_id flash_table_ext[] = {
{
.idcode0 = 0,
.idcode1 = SPSN_ID_S25FL008A,
.idcode2 = 0,
.identify = identify_2345,
.page_size = 256,
.pages_per_sector = 256,
.nr_sectors = 16,
.id = SPSN_ID_S25FL008A,
.name = "S25FL008A",
.nr_sectors_shift = 4,
},
{
.idcode0 = 0,
.idcode1 = SPSN_ID_S25FL016A,
.idcode2 = 0,
.identify = identify_2345,
.page_size = 256,
.pages_per_sector = 256,
.nr_sectors = 32,
.id = SPSN_ID_S25FL016A,
.name = "S25FL016A",
.nr_sectors_shift = 5,
},
{
.idcode0 = 0,
.idcode1 = SPSN_ID_S25FL032A,
.idcode2 = 0,
.identify = identify_2345,
.page_size = 256,
.pages_per_sector = 256,
.nr_sectors = 64,
.id = SPSN_ID_S25FL032A,
.name = "S25FL032A",
.nr_sectors_shift = 6,
},
{
.idcode0 = 0,
.idcode1 = SPSN_ID_S25FL064A,
.idcode2 = 0,
.identify = identify_2345,
.page_size = 256,
.pages_per_sector = 256,
.nr_sectors = 128,
.id = SPSN_ID_S25FL064A,
.name = "S25FL064A",
.nr_sectors_shift = 7,
},
{
.idcode0 = 0,
.idcode1 = SPSN_ID_S25FL128P,
.idcode2 = SPSN_EXT_ID_S25FL128P_64KB,
.identify = identify_2345,
.page_size = 256,
.pages_per_sector = 256,
.nr_sectors = 256,
.id = (SPSN_EXT_ID_S25FL128P_64KB << 16) | SPSN_ID_S25FL128P,
.name = "S25FL128P_64K",
.nr_sectors_shift = 8,
},
{
.idcode0 = 0,
.idcode1 = SPSN_ID_S25FL128P,
.idcode2 = SPSN_EXT_ID_S25FL128P_256KB,
.identify = identify_2345,
.page_size = 256,
.pages_per_sector = 1024,
.nr_sectors = 64,
.name = "S25FL128P_256K",
},
{
.idcode0 = 0,
.idcode1 = SPSN_ID_S25FL128S,
.idcode2 = SPSN_EXT_ID_S25FLXXS_64KB,
.identify = identify_2345,
.page_size = 256,
.pages_per_sector = 256,
.nr_sectors = 512,
.id = (SPSN_EXT_ID_S25FLXXS_64KB << 16) | SPSN_ID_S25FL128S,
.name = "S25FL128S_256K",
.nr_sectors_shift = 9,
},
{
.idcode0 = 0,
.idcode1 = SPSN_ID_S25FL032A,
.idcode2 = SPSN_EXT_ID_S25FL032P,
.identify = identify_2345,
.page_size = 256,
.pages_per_sector = 256,
.nr_sectors = 64,
.id = (SPSN_EXT_ID_S25FL032P << 16) | SPSN_ID_S25FL032A,
.name = "S25FL032P",
.nr_sectors_shift = 6,
},
{
.idcode0 = 0,
.idcode1 = SPSN_ID_S25FL128P,
.idcode2 = SPSN_EXT_ID_S25FLXXS_64KB,
.identify = identify_2345,
.page_size = 256,
.pages_per_sector = 256,
.nr_sectors = 256,
.id = (SPSN_EXT_ID_S25FLXXS_64KB << 16) | SPSN_ID_S25FL128P,
.name = "S25FS128S",
.nr_sectors_shift = 8,
},
};

static const struct spi_flash_part_id flash_table_256k_sector[] = {
{
.id = (SPSN_EXT_ID_S25FL128P_256KB << 16) | SPSN_ID_S25FL128P,
.name = "S25FL128P_256K",
.nr_sectors_shift = 6,
},
};

static const struct spi_flash_part_id flash_table[] = {
{
.idcode0 = SPSN_MANUFACTURER_ID_S25FL_K,
.idcode1 = SPSN_ID_S25FL208K,
.idcode2 = 0,
.identify = identify_123,
.page_size = 256,
.pages_per_sector = 256,
.nr_sectors = 16,
.id = SPSN_ID_S25FL208K,
.name = "S25FL208K",
.nr_sectors_shift = 4,
},
{
.idcode0 = SPSN_MANUFACTURER_ID_S25FL_K,
.idcode1 = SPSN_ID_S25FL116K,
.idcode2 = 0,
.identify = identify_123,
.page_size = 256,
.pages_per_sector = 256,
.nr_sectors = 32,
.id = SPSN_ID_S25FL116K,
.name = "S25FL116K_16M",
.nr_sectors_shift = 5,
},
{
.idcode0 = SPSN_MANUFACTURER_ID_S25FL_K,
.idcode1 = SPSN_ID_S25FL132K,
.idcode2 = 0,
.identify = identify_123,
.page_size = 256,
.pages_per_sector = 256,
.nr_sectors = 64,
.id = SPSN_ID_S25FL132K,
.name = "S25FL132K",
.nr_sectors_shift = 6,
},
{
.idcode0 = SPSN_MANUFACTURER_ID_S25FL_K,
.idcode1 = SPSN_ID_S25FL164K,
.idcode2 = 0,
.identify = identify_123,
.page_size = 256,
.pages_per_sector = 256,
.nr_sectors = 128,
.id = SPSN_ID_S25FL164K,
.name = "S25FL164K",
.nr_sectors_shift = 7,
},
};

static int spansion_write(const struct spi_flash *flash, u32 offset, size_t len,
const void *buf)
{
unsigned long byte_addr;
unsigned long page_size;
size_t chunk_len;
size_t actual;
int ret = 0;
u8 cmd[4];

page_size = flash->page_size;

for (actual = 0; actual < len; actual += chunk_len) {
byte_addr = offset % page_size;
chunk_len = MIN(len - actual, page_size - byte_addr);
chunk_len = spi_crop_chunk(&flash->spi, sizeof(cmd), chunk_len);

cmd[0] = CMD_S25FLXX_PP;
cmd[1] = (offset >> 16) & 0xff;
cmd[2] = (offset >> 8) & 0xff;
cmd[3] = offset & 0xff;

#if CONFIG(DEBUG_SPI_FLASH)
printk(BIOS_SPEW, "PP: %p => cmd = { 0x%02x 0x%02x%02x%02x }"
" chunk_len = %zu\n",
buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
#endif

ret = spi_flash_cmd(&flash->spi, CMD_S25FLXX_WREN, NULL, 0);
if (ret < 0) {
printk(BIOS_WARNING, "SF: Enabling Write failed\n");
break;
}

ret = spi_flash_cmd_write(&flash->spi, cmd, 4,
buf + actual, chunk_len);
if (ret < 0) {
printk(BIOS_WARNING, "SF: SPANSION Page Program failed\n");
break;
}

ret = spi_flash_cmd_wait_ready(flash,
SPI_FLASH_PROG_TIMEOUT_MS);
if (ret)
break;

offset += chunk_len;
}

#if CONFIG(DEBUG_SPI_FLASH)
printk(BIOS_SPEW, "SF: SPANSION: Successfully programmed %zu bytes @ 0x%x\n",
len, offset);
#endif

return ret;
}

static const struct spi_flash_ops spi_flash_ops = {
.write = spansion_write,
.erase = spi_flash_cmd_erase,
.status = spi_flash_cmd_status,
const struct spi_flash_vendor_info spi_flash_spansion_ext1_vi = {
.id = VENDOR_ID_SPANSION,
.page_size_shift = 8,
.sector_size_kib_shift = 6,
.match_id_mask = 0xffffffff,
.ids = flash_table_ext,
.nr_part_ids = ARRAY_SIZE(flash_table_ext),
.desc = &spi_flash_pp_0xd8_sector_desc,
};

int spi_flash_probe_spansion(const struct spi_slave *spi, u8 *idcode,
struct spi_flash *flash)
{
const struct spansion_spi_flash_params *params;
unsigned int i;

for (i = 0; i < ARRAY_SIZE(spansion_spi_flash_table); i++) {
params = &spansion_spi_flash_table[i];
if (params->identify(params, idcode))
break;
}

if (i == ARRAY_SIZE(spansion_spi_flash_table)) {
printk(BIOS_WARNING,
"SF: Unsupported SPANSION ID %02x %02x %02x %02x %02x\n",
idcode[0], idcode[1], idcode[2], idcode[3], idcode[4]);
return -1;
}

memcpy(&flash->spi, spi, sizeof(*spi));
flash->name = params->name;
flash->page_size = params->page_size;
flash->sector_size = params->page_size * params->pages_per_sector;
flash->size = flash->sector_size * params->nr_sectors;
flash->erase_cmd = CMD_S25FLXX_SE;
flash->status_cmd = CMD_S25FLXX_RDSR;

flash->ops = &spi_flash_ops;
const struct spi_flash_vendor_info spi_flash_spansion_ext2_vi = {
.id = VENDOR_ID_SPANSION,
.page_size_shift = 8,
.sector_size_kib_shift = 8,
.match_id_mask = 0xffffffff,
.ids = flash_table_256k_sector,
.nr_part_ids = ARRAY_SIZE(flash_table_256k_sector),
.desc = &spi_flash_pp_0xd8_sector_desc,
};

return 0;
}
const struct spi_flash_vendor_info spi_flash_spansion_vi = {
.id = VENDOR_ID_SPANSION,
.page_size_shift = 8,
.sector_size_kib_shift = 6,
.match_id_mask = 0xffff,
.ids = flash_table,
.nr_part_ids = ARRAY_SIZE(flash_table),
.desc = &spi_flash_pp_0xd8_sector_desc,
};