blob: bdbcd4727171c6384fa83fe5ad73b032d2f88763 [file] [log] [blame]
/*
* This file is part of the flashrom project.
*
* Copyright (C) 2012 Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of Google or the names of contributors or
* licensors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind.
* ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
* GOOGLE INC AND ITS LICENSORS SHALL NOT BE LIABLE
* FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
* GOOGLE OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
* OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
* PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
* LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF GOOGLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*/
#if defined(__i386__) || defined(__x86_64__)
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include "chipdrivers.h"
#include "flash.h"
#include "programmer.h"
#include "spi.h"
/* Supported ENE ECs, ENE_LAST should always be LAST member */
enum ene_chip_id {
ENE_KB932 = 0,
ENE_KB94X,
ENE_LAST
};
/* EC state */
enum ene_ec_state {
EC_STATE_NORMAL,
EC_STATE_IDLE,
EC_STATE_RESET,
EC_STATE_UNKNOWN
};
/* chip-specific parameters */
typedef struct {
enum ene_chip_id chip_id;
uint8_t hwver;
uint8_t ediid;
uint32_t port_bios;
uint32_t port_ec_command;
uint32_t port_ec_data;
uint8_t ec_reset_cmd;
uint8_t ec_reset_data;
uint8_t ec_restart_cmd;
uint8_t ec_restart_data;
uint8_t ec_pause_cmd;
uint8_t ec_pause_data;
uint16_t ec_status_buf;
uint8_t ec_is_stopping;
uint8_t ec_is_running;
uint8_t ec_is_pausing;
uint32_t port_io_base;
} ene_chip;
/* table of supported chips + parameters */
static ene_chip ene_chips[] = {
{ ENE_KB932, /* chip_id */
0xa2, 0x02, /* hwver + ediid */
0x66, /* port_bios */
0x6c, 0x68, /* port_ec_{command,data} */
0x59, 0xf2, /* ec_reset_{cmd,data} */
0x59, 0xf9, /* ec_restart_{cmd,data} */
0x59, 0xf1, /* ec_pause_{cmd,data} */
0xf554, /* ec_status_buf */
0xa5, 0x00, /* ec_is_{stopping,running} masks */
0x33, /* ec_is_pausing mask */
0xfd60 }, /* port_io_base */
{ ENE_KB94X, /* chip_id */
0xa3, 0x05, /* hwver + ediid */
0x66, /* port_bios */
0x66, 0x68, /* port_ec_{command,data} */
0x7d, 0x10, /* ec_reset_{cmd,data} */
0x7f, 0x10, /* ec_restart_{cmd,data} */
0x7e, 0x10, /* ec_pause_{cmd,data} */
0xf710, /* ec_status_buf */
0x02, 0x00, /* ec_is_{stopping,running} masks */
0x01, /* ec_is_pausing mask */
0x0380 }, /* port_io_base */
};
/* pointer to table entry of identified chip */
static ene_chip *found_chip;
/* current ec state */
static enum ene_ec_state ec_state = EC_STATE_NORMAL;
#define REG_EC_HWVER 0xff00
#define REG_EC_FWVER 0xff01
#define REG_EC_EDIID 0xff24
#define REG_8051_CTRL 0xff14
#define REG_EC_EXTCMD 0xff10
#define CPU_RESET 1
/* Hwardware registers */
#define REG_SPI_DATA 0xfeab
#define REG_SPI_COMMAND 0xfeac
#define REG_SPI_CONFIG 0xfead
#define CFG_CSn_FORCE_LOW (1 << 4)
#define CFG_COMMAND_WRITE_ENABLE (1 << 3)
#define CFG_STATUS (1 << 1)
#define CFG_ENABLE_BUSY_STATUS_CHECK (1 << 0)
/* Timeout */
#define EC_COMMAND_TIMEOUT 4
#define EC_RESTART_TIMEOUT 10
#define ENE_SPI_DELAY_CYCLE 4
#define EC_PAUSE_TIMEOUT 12
#define EC_RESET_TRIES 3
#define ENE_KB94X_PAUSE_WAKEUP_PORT 0x64
#define MASK_INPUT_BUFFER_FULL 2
#define MASK_OUTPUT_BUFFER_FULL 1
const int port_ene_bank = 1;
const int port_ene_offset = 2;
const int port_ene_data = 3;
static struct timeval pause_begin, pause_now;
static void ec_command(uint8_t cmd, uint8_t data)
{
struct timeval begin, now;
/* Spin wait for EC input buffer empty */
gettimeofday(&begin, NULL);
while (INB(found_chip->port_ec_command) & MASK_INPUT_BUFFER_FULL) {
gettimeofday(&now, NULL);
if ((now.tv_sec - begin.tv_sec) >= EC_COMMAND_TIMEOUT) {
msg_pdbg("%s: buf not empty\n", __func__);
return;
}
}
/* Write command */
OUTB(cmd, found_chip->port_ec_command);
if (found_chip->chip_id == ENE_KB932) {
/* Spin wait for EC input buffer empty */
gettimeofday(&begin, NULL);
while (INB(found_chip->port_ec_command) &
MASK_INPUT_BUFFER_FULL) {
gettimeofday(&now, NULL);
if ((now.tv_sec - begin.tv_sec) >=
EC_COMMAND_TIMEOUT) {
msg_pdbg("%s: buf not empty\n", __func__);
return;
}
}
/* Write data */
OUTB(data, found_chip->port_ec_data);
}
}
static uint8_t ene_read(uint16_t addr)
{
uint8_t bank;
uint8_t offset;
uint8_t data;
uint32_t port_io_base;
bank = addr >> 8;
offset = addr & 0xff;
port_io_base = found_chip->port_io_base;
OUTB(bank, port_io_base + port_ene_bank);
OUTB(offset, port_io_base + port_ene_offset);
data = INB(port_io_base + port_ene_data);
return data;
}
static void ene_write(uint16_t addr, uint8_t data)
{
uint8_t bank;
uint8_t offset;
uint32_t port_io_base;
bank = addr >> 8;
offset = addr & 0xff;
port_io_base = found_chip->port_io_base;
OUTB(bank, port_io_base + port_ene_bank);
OUTB(offset, port_io_base + port_ene_offset);
OUTB(data, port_io_base + port_ene_data);
}
/**
* wait_cycles, wait for n LPC bus clock cycles
*
* @param n: number of LPC cycles to wait
* @return void
*/
void wait_cycles(int n)
{
while (n--)
INB(found_chip->port_io_base + port_ene_bank);
}
static int is_spicmd_write(uint8_t cmd)
{
switch (cmd) {
case JEDEC_WREN:
/* Chip Write Enable */
case JEDEC_EWSR:
/* Write Status Enable */
case JEDEC_CE_60:
/* Chip Erase 0x60 */
case JEDEC_CE_C7:
/* Chip Erase 0xc7 */
case JEDEC_BE_52:
/* Block Erase 0x52 */
case JEDEC_BE_D8:
/* Block Erase 0xd8 */
case JEDEC_BE_D7:
/* Block Erase 0xd7 */
case JEDEC_SE:
/* Sector Erase */
case JEDEC_BYTE_PROGRAM:
/* Write memory byte */
case JEDEC_AAI_WORD_PROGRAM:
/* Write AAI word */
return 1;
}
return 0;
}
static void ene_spi_start(void)
{
int cfg;
cfg = ene_read(REG_SPI_CONFIG);
cfg |= CFG_CSn_FORCE_LOW;
cfg |= CFG_COMMAND_WRITE_ENABLE;
ene_write(REG_SPI_CONFIG, cfg);
wait_cycles(ENE_SPI_DELAY_CYCLE);
}
static void ene_spi_end(void)
{
int cfg;
cfg = ene_read(REG_SPI_CONFIG);
cfg &= ~CFG_CSn_FORCE_LOW;
cfg |= CFG_COMMAND_WRITE_ENABLE;
ene_write(REG_SPI_CONFIG, cfg);
wait_cycles(ENE_SPI_DELAY_CYCLE);
}
static int ene_spi_wait(void)
{
struct timeval begin, now;
gettimeofday(&begin, NULL);
while(ene_read(REG_SPI_CONFIG) & CFG_STATUS) {
gettimeofday(&now, NULL);
if ((now.tv_sec - begin.tv_sec) >= EC_COMMAND_TIMEOUT) {
msg_pdbg("%s: spi busy\n", __func__);
return 1;
}
}
return 0;
}
static int ene_pause_ec(void)
{
struct timeval begin, now;
if (!found_chip->ec_pause_cmd)
return -1;
/* EC prepare pause */
ec_command(found_chip->ec_pause_cmd, found_chip->ec_pause_data);
gettimeofday(&begin, NULL);
/* Spin wait for EC ready */
while (ene_read(found_chip->ec_status_buf) !=
found_chip->ec_is_pausing) {
gettimeofday(&now, NULL);
if ((now.tv_sec - begin.tv_sec) >=
EC_COMMAND_TIMEOUT) {
msg_pdbg("%s: unable to pause ec\n", __func__);
return -1;
}
}
gettimeofday(&pause_begin, NULL);
ec_state = EC_STATE_IDLE;
return 0;
}
static int ene_resume_ec(void)
{
struct timeval begin, now;
if (found_chip->chip_id == ENE_KB94X)
OUTB(0xff, ENE_KB94X_PAUSE_WAKEUP_PORT);
else
/* Trigger 8051 interrupt to resume */
ene_write(REG_EC_EXTCMD, 0xff);
gettimeofday(&begin, NULL);
while (ene_read(found_chip->ec_status_buf) !=
found_chip->ec_is_running) {
gettimeofday(&now, NULL);
if ((now.tv_sec - begin.tv_sec) >=
EC_COMMAND_TIMEOUT) {
msg_pdbg("%s: unable to resume ec\n", __func__);
return -1;
}
}
ec_state = EC_STATE_NORMAL;
return 0;
}
static int ene_pause_timeout_check(void)
{
gettimeofday(&pause_now, NULL);
if ((pause_now.tv_sec - pause_begin.tv_sec) >=
EC_PAUSE_TIMEOUT) {
if(ene_resume_ec() == 0)
ene_pause_ec();
}
return 0;
}
static int ene_reset_ec(void)
{
uint8_t reg;
struct timeval begin, now;
gettimeofday(&begin, NULL);
/* EC prepare reset */
ec_command(found_chip->ec_reset_cmd, found_chip->ec_reset_data);
/* Spin wait for EC ready */
while (ene_read(found_chip->ec_status_buf) !=
found_chip->ec_is_stopping) {
gettimeofday(&now, NULL);
if ((now.tv_sec - begin.tv_sec) >=
EC_COMMAND_TIMEOUT) {
msg_pdbg("%s: unable to reset ec\n", __func__);
return -1;
}
}
/* Wait 1 second */
sleep(1);
/* Reset 8051 */
reg = ene_read(REG_8051_CTRL);
reg |= CPU_RESET;
ene_write(REG_8051_CTRL, reg);
ec_state = EC_STATE_RESET;
return 0;
}
static int ene_enter_flash_mode(void)
{
if (ene_pause_ec())
return ene_reset_ec();
return 0;
}
static int ene_spi_send_command(const struct flashctx *flash,
unsigned int writecnt,
unsigned int readcnt,
const unsigned char *writearr,
unsigned char *readarr)
{
int i;
int tries = EC_RESET_TRIES;
if (ec_state == EC_STATE_IDLE && is_spicmd_write(writearr[0])) {
do {
/* Enter reset mode if we need to write/erase */
if (ene_resume_ec())
continue;
if (!ene_reset_ec())
break;
} while (--tries > 0);
if (!tries) {
msg_perr("%s: EC failed reset, skipping write\n",
__func__);
ec_state = EC_STATE_IDLE;
return 1;
}
}
else if(found_chip->chip_id == ENE_KB94X && ec_state == EC_STATE_IDLE)
ene_pause_timeout_check();
ene_spi_start();
for (i = 0; i < writecnt; i++) {
ene_write(REG_SPI_COMMAND, writearr[i]);
if (ene_spi_wait()) {
msg_pdbg("%s: write count %d\n", __func__, i);
return 1;
}
}
for (i = 0; i < readcnt; i++) {
/* Push data by clock the serial bus */
ene_write(REG_SPI_COMMAND, 0);
if (ene_spi_wait()) {
msg_pdbg("%s: read count %d\n", __func__, i);
return 1;
}
readarr[i] = ene_read(REG_SPI_DATA);
if (ene_spi_wait()) {
msg_pdbg("%s: read count %d\n", __func__, i);
return 1;
}
}
ene_spi_end();
return 0;
}
static int ene_leave_flash_mode(void *data)
{
int rv = 0;
uint8_t reg;
struct timeval begin, now;
if (ec_state == EC_STATE_RESET) {
reg = ene_read(REG_8051_CTRL);
reg &= ~CPU_RESET;
ene_write(REG_8051_CTRL, reg);
gettimeofday(&begin, NULL);
/* EC restart */
while (ene_read(found_chip->ec_status_buf) !=
found_chip->ec_is_running) {
gettimeofday(&now, NULL);
if ((now.tv_sec - begin.tv_sec) >=
EC_RESTART_TIMEOUT) {
msg_pdbg("%s: ec restart busy\n", __func__);
rv = 1;
goto exit;
}
}
msg_pdbg("%s: send ec restart\n", __func__);
ec_command(found_chip->ec_restart_cmd,
found_chip->ec_restart_data);
ec_state = EC_STATE_NORMAL;
rv = 0;
goto exit;
}
rv = ene_resume_ec();
exit:
/*
* Trigger ec interrupt after pause/reset by sending 0x80
* to bios command port.
*/
OUTB(0x80, found_chip->port_bios);
return rv;
}
static const struct spi_master spi_master_ene = {
.type = SPI_CONTROLLER_ENE,
.max_data_read = 256,
.max_data_write = 256,
.command = ene_spi_send_command,
.multicommand = default_spi_send_multicommand,
.read = default_spi_read,
.write_256 = default_spi_write_256,
};
int ene_probe_spi_flash(const char *name)
{
uint8_t hwver, ediid, i;
int ret = 0;
char *p = NULL;
if (alias && alias->type != ALIAS_EC)
return 1;
msg_pdbg("%s\n", __func__);
p = extract_programmer_param("type");
if (p && strcmp(p, "ec")) {
msg_pdbg("ene_lpc only supports \"ec\" type devices\n");
ret = 1;
goto ene_probe_spi_flash_exit;
}
for (i = 0; i < ENE_LAST; ++i) {
found_chip = &ene_chips[i];
hwver = ene_read(REG_EC_HWVER);
ediid = ene_read(REG_EC_EDIID);
if(hwver == ene_chips[i].hwver &&
ediid == ene_chips[i].ediid) {
break;
}
}
if (i == ENE_LAST) {
msg_pdbg("ENE EC not found (probe failed)\n");
ret = 1;
goto ene_probe_spi_flash_exit;
}
/* TODO: probe the EC stop protocol
*
* Compal - ec_command(0x41, 0xa1) returns 43 4f 4d 50 41 4c 9c
*/
if (register_shutdown(ene_leave_flash_mode, NULL)) {
ret = 1;
goto ene_probe_spi_flash_exit;
}
ene_enter_flash_mode();
buses_supported |= BUS_LPC;
register_spi_master(&spi_master_ene);
msg_pdbg("%s: successfully initialized ene\n", __func__);
ene_probe_spi_flash_exit:
free(p);
return ret;
}
#endif /* __i386__ || __x86_64__ */