coreboot-libre-fam15h-rdimm/3rdparty/chromeec/chip/lm4/eeprom.c

269 lines
5.6 KiB
C

/* Copyright 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* EEPROM module for Chrome EC */
#include "clock.h"
#include "console.h"
#include "eeprom.h"
#include "registers.h"
#include "timer.h"
#include "util.h"
#include "watchdog.h"
/* Size of EEPROM block in bytes */
#define EEPROM_BLOCK_SIZE 64
/* Count of EEPROM blocks */
static int block_count;
/*
* Wait for the current EEPROM operation to finish; all operations but write
* should normally finish in 4 system clocks, but worst case is up to
* 1800ms if the EEPROM needs to do an internal page erase/copy. We must
* spin-wait for this delay, because EEPROM operations will fail if the chip
* drops to sleep mode.
*/
static int wait_for_done(void)
{
int j;
for (j = 0; j < 20; j++) { /* 20 * 100 ms = 2000 ms */
uint64_t tstop = get_time().val + 100 * MSEC;
while (get_time().val < tstop) {
if (!(LM4_EEPROM_EEDONE & 0x01))
return EC_SUCCESS;
}
watchdog_reload();
}
return EC_ERROR_UNKNOWN;
}
int eeprom_get_block_count(void)
{
return block_count;
}
int eeprom_get_block_size(void)
{
return EEPROM_BLOCK_SIZE;
}
int eeprom_read(int block, int offset, int size, char *data)
{
uint32_t *d = (uint32_t *)data;
int rv;
if (block < 0 || block >= block_count ||
offset < 0 || offset > EEPROM_BLOCK_SIZE || offset & 3 ||
size < 0 || offset + size > EEPROM_BLOCK_SIZE || size & 3)
return EC_ERROR_UNKNOWN;
rv = wait_for_done();
if (rv)
return rv;
LM4_EEPROM_EEBLOCK = block;
if (LM4_EEPROM_EEBLOCK != block)
return EC_ERROR_UNKNOWN; /* Error setting block */
LM4_EEPROM_EEOFFSET = offset >> 2;
for (; size; size -= sizeof(uint32_t))
*(d++) = LM4_EEPROM_EERDWRINC;
return EC_SUCCESS;
}
int eeprom_write(int block, int offset, int size, const char *data)
{
uint32_t *d = (uint32_t *)data;
int rv;
if (block < 0 || block >= block_count ||
offset < 0 || offset > EEPROM_BLOCK_SIZE || offset & 3 ||
size < 0 || offset + size > EEPROM_BLOCK_SIZE || size & 3)
return EC_ERROR_UNKNOWN;
rv = wait_for_done();
if (rv)
return rv;
LM4_EEPROM_EEBLOCK = block;
if (LM4_EEPROM_EEBLOCK != block)
return EC_ERROR_UNKNOWN; /* Error setting block */
LM4_EEPROM_EEOFFSET = offset >> 2;
/* Write 32 bits at a time; wait for each write to complete */
for (; size; size -= sizeof(uint32_t)) {
LM4_EEPROM_EERDWRINC = *(d++);
rv = wait_for_done();
if (rv)
return rv;
if (LM4_EEPROM_EEDONE & 0x10) {
/* Failed due to write protect */
return EC_ERROR_ACCESS_DENIED;
} else if (LM4_EEPROM_EEDONE & 0x100) {
/* Failed due to program voltage level */
return EC_ERROR_UNKNOWN;
}
}
return EC_SUCCESS;
}
int eeprom_hide(int block)
{
/* Block 0 can't be hidden */
if (block <= 0 || block >= block_count)
return EC_ERROR_UNKNOWN;
LM4_EEPROM_EEHIDE |= 1 << block;
return EC_SUCCESS;
}
/*****************************************************************************/
/* Console commands */
static int command_eeprom_info(int argc, char **argv)
{
ccprintf("%d blocks @ %d bytes, hide=0x%08x\n",
eeprom_get_block_count(), eeprom_get_block_size(),
LM4_EEPROM_EEHIDE);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(eeinfo, command_eeprom_info,
NULL,
"Print EEPROM info");
static int command_eeprom_read(int argc, char **argv)
{
int block = 0;
int offset = 0;
char *e;
int rv;
uint32_t d;
if (argc < 2)
return EC_ERROR_PARAM_COUNT;
block = strtoi(argv[1], &e, 0);
if (*e)
return EC_ERROR_PARAM1;
if (argc > 2) {
offset = strtoi(argv[2], &e, 0);
if (*e)
return EC_ERROR_PARAM2;
}
rv = eeprom_read(block, offset, sizeof(d), (char *)&d);
if (rv == EC_SUCCESS)
ccprintf("%d:%d = 0x%08x\n", block, offset, d);
return rv;
}
DECLARE_CONSOLE_COMMAND(eeread, command_eeprom_read,
"block [offset]",
"Read a word of EEPROM");
static int command_eeprom_write(int argc, char **argv)
{
int block = 0;
int offset = 0;
char *e;
uint32_t d;
if (argc < 4)
return EC_ERROR_PARAM_COUNT;
block = strtoi(argv[1], &e, 0);
if (*e)
return EC_ERROR_PARAM1;
offset = strtoi(argv[2], &e, 0);
if (*e)
return EC_ERROR_PARAM2;
d = strtoi(argv[3], &e, 0);
if (*e)
return EC_ERROR_PARAM3;
ccprintf("Writing 0x%08x to %d:%d...\n", d, block, offset);
return eeprom_write(block, offset, sizeof(d), (char *)&d);
}
DECLARE_CONSOLE_COMMAND(eewrite, command_eeprom_write,
"block offset value",
"Write a word of EEPROM");
#ifdef CONSOLE_COMMAND_EEHIDE
static int command_eeprom_hide(int argc, char **argv)
{
int block = 0;
char *e;
if (argc < 2)
return EC_ERROR_PARAM_COUNT;
block = strtoi(argv[1], &e, 0);
if (*e)
return EC_ERROR_PARAM1;
ccprintf("Hiding block %d\n", block);
return eeprom_hide(block);
}
DECLARE_CONSOLE_COMMAND(eehide, command_eeprom_hide,
"block",
"Hide a block of EEPROM");
#endif
/*****************************************************************************/
/* Initialization */
int eeprom_init(void)
{
/* Enable the EEPROM module in run and sleep modes. */
clock_enable_peripheral(CGC_OFFSET_EEPROM, 0x1,
CGC_MODE_RUN | CGC_MODE_SLEEP);
/* Wait for internal EEPROM init to finish */
wait_for_done();
/* Store block count */
block_count = LM4_EEPROM_EESIZE >> 16;
/*
* Handle resetting the EEPROM module to clear state from a previous
* error condition.
*/
if (LM4_EEPROM_EESUPP & 0xc0) {
LM4_SYSTEM_SREEPROM = 1;
clock_wait_cycles(200);
LM4_SYSTEM_SREEPROM = 0;
/* Wait again for internal init to finish */
clock_wait_cycles(6);
wait_for_done();
/* Fail if error condition didn't clear */
if (LM4_EEPROM_EESUPP & 0xc0)
return EC_ERROR_UNKNOWN;
}
return EC_SUCCESS;
}