blob: a2219ac6c4262fe2397d94fbabf6a3c92ee589a1 [file] [log] [blame]
/*
* This file is part of the flashrom project.
*
* Copyright (C) 2015 Paul Kocialkowski <contact@paulk.fr>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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 <string.h>
#include "flash.h"
#include "chipdrivers.h"
#include "ene.h"
#include "edi.h"
static unsigned int edi_read_buffer_length = EDI_READ_BUFFER_LENGTH_DEFAULT;
static const struct ene_chip ene_kb9012 = {
.hwversion = ENE_KB9012_HWVERSION,
.ediid = ENE_KB9012_EDIID,
};
static void edi_write_cmd(unsigned char *cmd, unsigned short address, unsigned char data)
{
cmd[0] = EDI_WRITE; /* EDI write command. */
cmd[1] = 0x00; /* Address is only 2 bytes. */
cmd[2] = (address >> 8) & 0xff; /* Address higher byte. */
cmd[3] = (address >> 0) & 0xff; /* Address lower byte. */
cmd[4] = data; /* Write data. */
}
static void edi_read_cmd(unsigned char *cmd, unsigned short address)
{
cmd[0] = EDI_READ; /* EDI read command. */
cmd[1] = 0x00; /* Address is only 2 bytes. */
cmd[2] = (address >> 8) & 0xff; /* Address higher byte. */
cmd[3] = (address >> 0) & 0xff; /* Address lower byte. */
}
static int edi_write(struct flashctx *flash, unsigned short address, unsigned char data)
{
unsigned char cmd[5];
int rc;
edi_write_cmd(cmd, address, data);
rc = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL);
if (rc)
return -1;
return 0;
}
static int edi_read_byte(struct flashctx *flash, unsigned short address, unsigned char *data)
{
unsigned char cmd[4];
unsigned char buffer[edi_read_buffer_length];
unsigned int index;
unsigned int i;
int rc;
edi_read_cmd(cmd, address);
rc = spi_send_command(flash, sizeof(cmd), sizeof(buffer), cmd, buffer);
if (rc)
return -1;
index = 0;
for (i = 0; i < sizeof(buffer); i++) {
index = i;
if (buffer[i] == EDI_NOT_READY)
continue;
if (buffer[i] == EDI_READY) {
if (i == (sizeof(buffer) - 1)) {
/*
* Buffer size was too small for receiving the value.
* This is as good as getting only EDI_NOT_READY.
*/
buffer[i] = EDI_NOT_READY;
break;
}
*data = buffer[i + 1];
return 0;
}
}
if (buffer[index] == EDI_NOT_READY)
return -EDI_NOT_READY;
return -1;
}
static int edi_read(struct flashctx *flash, unsigned short address, unsigned char *data)
{
int rc;
do {
rc = edi_read_byte(flash, address, data);
if (rc == -EDI_NOT_READY) {
/*
* Buffer size is increased, one step at a time,
* to hold more data if we only catch EDI_NOT_READY.
* Once CS is deasserted, no more data will be sent by the EC,
* so we cannot keep reading afterwards and have to start a new
* transaction with a longer buffer, to be safe.
*/
if (edi_read_buffer_length < EDI_READ_BUFFER_LENGTH_MAX) {
msg_pwarn("%s: Retrying read with greater buffer length!\n", __func__);
edi_read_buffer_length++;
} else {
msg_perr("%s: Maximum buffer length reached and data still not ready!\n", __func__);
return -1;
}
} else if (rc < 0) {
return -1;
}
} while (rc == -EDI_NOT_READY);
return 0;
}
static int edi_disable(struct flashctx *flash)
{
unsigned char cmd = EDI_DISABLE;
int rc;
rc = spi_send_command(flash, sizeof(cmd), 0, &cmd, NULL);
if (rc)
return -1;
return 0;
}
static int edi_chip_probe(struct flashctx *flash, const struct ene_chip *chip)
{
unsigned char hwversion;
unsigned char ediid;
int rc;
rc = edi_read(flash, ENE_EC_HWVERSION, &hwversion);
if (rc < 0) {
msg_cdbg("%s: reading hwversion failed\n", __func__);
return 0;
}
rc = edi_read(flash, ENE_EC_EDIID, &ediid);
if (rc < 0) {
msg_cdbg("%s: reading ediid failed\n", __func__);
return 0;
}
msg_cdbg("%s: hwversion 0x%02x, ediid 0x%02x\n", __func__, hwversion, ediid);
if (chip->hwversion == hwversion && chip->ediid == ediid)
return 1;
return 0;
}
static int edi_spi_enable(struct flashctx *flash)
{
unsigned char buffer;
int rc;
rc = edi_read(flash, ENE_XBI_EFCFG, &buffer);
if (rc < 0)
return -1;
buffer |= ENE_XBI_EFCFG_CMD_WE;
rc = edi_write(flash, ENE_XBI_EFCFG, buffer);
if (rc < 0)
return -1;
return 0;
}
static int edi_spi_disable(struct flashctx *flash)
{
unsigned char buffer;
int rc;
rc = edi_read(flash, ENE_XBI_EFCFG, &buffer);
if (rc < 0)
return -1;
buffer &= ~ENE_XBI_EFCFG_CMD_WE;
rc = edi_write(flash, ENE_XBI_EFCFG, buffer);
if (rc < 0)
return -1;
return 0;
}
static int edi_spi_busy(struct flashctx *flash)
{
unsigned char buffer;
int rc;
rc = edi_read(flash, ENE_XBI_EFCFG, &buffer);
if (rc < 0)
return -1;
return !!(buffer & ENE_XBI_EFCFG_BUSY);
}
static int edi_spi_address(struct flashctx *flash, unsigned int start, unsigned int address)
{
int rc;
if ((address == start) || (((address - 1) & 0xff) != (address & 0xff))) {
rc = edi_write(flash, ENE_XBI_EFA0, ((address & 0xff) >> 0));
if (rc < 0)
return -1;
}
if ((address == start) || (((address - 1) & 0xff00) != (address & 0xff00))) {
rc = edi_write(flash, ENE_XBI_EFA1, ((address & 0xff00) >> 8));
if (rc < 0)
return -1;
}
if ((address == start) || (((address - 1) & 0xff0000) != (address & 0xff0000))) {
rc = edi_write(flash, ENE_XBI_EFA2, ((address & 0xff0000) >> 16));
if (rc < 0)
return -1;
}
return 0;
}
static int edi_8051_reset(struct flashctx *flash)
{
unsigned char buffer;
int rc;
rc = edi_read(flash, ENE_EC_PXCFG, &buffer);
if (rc < 0)
return -1;
buffer |= ENE_EC_PXCFG_8051_RESET;
rc = edi_write(flash, ENE_EC_PXCFG, buffer);
if (rc < 0)
return -1;
return 0;
}
static int edi_8051_execute(struct flashctx *flash)
{
unsigned char buffer;
int rc;
rc = edi_read(flash, ENE_EC_PXCFG, &buffer);
if (rc < 0)
return -1;
buffer &= ~ENE_EC_PXCFG_8051_RESET;
rc = edi_write(flash, ENE_EC_PXCFG, buffer);
if (rc < 0)
return -1;
return 0;
}
int edi_chip_block_erase(struct flashctx *flash, unsigned int page, unsigned int size)
{
unsigned int timeout = 64;
int rc;
if (size != flash->chip->page_size) {
msg_perr("%s: Block erase size is not page size!\n", __func__);
return -1;
}
rc = edi_spi_enable(flash);
if (rc < 0) {
msg_perr("%s: Unable to enable SPI!\n", __func__);
return -1;
}
rc = edi_spi_address(flash, page, page);
if (rc < 0)
return -1;
rc = edi_write(flash, ENE_XBI_EFCMD, ENE_XBI_EFCMD_ERASE);
if (rc < 0)
return -1;
while (edi_spi_busy(flash) == 1 && timeout) {
programmer_delay(10);
timeout--;
}
if (!timeout) {
msg_perr("%s: Timed out waiting for SPI not busy!\n", __func__);
return -1;
}
rc = edi_spi_disable(flash);
if (rc < 0) {
msg_perr("%s: Unable to disable SPI!\n", __func__);
return -1;
}
return 0;
}
int edi_chip_write(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
{
unsigned int address = start;
unsigned int pages;
unsigned int timeout;
unsigned int i, j;
int rc;
if ((start % flash->chip->page_size) != 0) {
msg_perr("%s: Start address is not page-aligned!\n", __func__);
return -1;
}
if ((len % flash->chip->page_size) != 0) {
msg_perr("%s: Length is not page-aligned!\n", __func__);
return -1;
}
pages = len / flash->chip->page_size;
rc = edi_spi_enable(flash);
if (rc < 0) {
msg_perr("%s: Unable to enable SPI!\n", __func__);
return -1;
}
for (i = 0; i < pages; i++) {
timeout = 64;
/* Clear page buffer. */
rc = edi_write(flash, ENE_XBI_EFCMD, ENE_XBI_EFCMD_HVPL_CLEAR);
if (rc < 0)
return -1;
for (j = 0; j < flash->chip->page_size; j++) {
rc = edi_spi_address(flash, start, address);
if (rc < 0)
return -1;
rc = edi_write(flash, ENE_XBI_EFDAT, *buf);
if (rc < 0)
return -1;
rc = edi_write(flash, ENE_XBI_EFCMD, ENE_XBI_EFCMD_HVPL_LATCH);
if (rc < 0)
return -1;
buf++;
address++;
}
/* Program page buffer to flash. */
rc = edi_write(flash, ENE_XBI_EFCMD, ENE_XBI_EFCMD_PROGRAM);
if (rc < 0)
return -1;
while (edi_spi_busy(flash) == 1 && timeout) {
programmer_delay(10);
timeout--;
}
if (!timeout) {
msg_perr("%s: Timed out waiting for SPI not busy!\n", __func__);
return -1;
}
}
rc = edi_spi_disable(flash);
if (rc < 0) {
msg_perr("%s: Unable to disable SPI!\n", __func__);
return -1;
}
return 0;
}
int edi_chip_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len)
{
unsigned int address = start;
unsigned int i;
unsigned int timeout;
int rc;
rc = edi_spi_enable(flash);
if (rc < 0) {
msg_perr("%s: Unable to enable SPI!\n", __func__);
return -1;
}
/*
* EDI brings such a drastic overhead that there is about no need to
* have any delay in between calls. The EDI protocol will handle wait
* I/O times on its own anyway.
*/
for (i = 0; i < len; i++) {
timeout = 64;
rc = edi_spi_address(flash, start, address);
if (rc < 0)
return -1;
rc = edi_write(flash, ENE_XBI_EFCMD, ENE_XBI_EFCMD_READ);
if (rc < 0)
return -1;
do {
rc = edi_read(flash, ENE_XBI_EFDAT, buf);
if (rc == 0)
break;
/* Just in case. */
while (edi_spi_busy(flash) == 1 && timeout) {
programmer_delay(10);
timeout--;
}
if (!timeout) {
msg_perr("%s: Timed out waiting for SPI not busy!\n", __func__);
return -1;
}
} while (1);
buf++;
address++;
}
rc = edi_spi_disable(flash);
if (rc < 0) {
msg_perr("%s: Unable to disable SPI!\n", __func__);
return -1;
}
return 0;
}
static int edi_shutdown(void *data)
{
struct flashctx *flash;
int rc;
if (data == NULL)
return -1;
flash = (struct flashctx *)data;
rc = edi_8051_execute(flash);
if (rc < 0) {
msg_perr("%s: Unable to execute 8051!\n", __func__);
return -1;
}
rc = edi_disable(flash);
if (rc < 0) {
msg_perr("%s: Unable to disable EDI!\n", __func__);
return -1;
}
return 0;
}
int edi_probe_kb9012(struct flashctx *flash)
{
int probe;
int rc;
unsigned char hwversion;
/*
* ENE chips enable EDI by detecting a clock frequency between 1 MHz and
* 8 MHz. In many cases, the chip won't be able to both detect the clock
* signal and serve the associated request at the same time.
*
* Thus, a dummy read has to be added to ensure that EDI is enabled and
* operational starting from the next request. This dummy read below
* draws the chip's attention and as result the chip enables its EDI.
*/
edi_read(flash, ENE_EC_HWVERSION, &hwversion);
probe = edi_chip_probe(flash, &ene_kb9012);
if (!probe)
return 0;
rc = edi_8051_reset(flash);
if (rc < 0) {
msg_perr("%s: Unable to reset 8051!\n", __func__);
return 0;
}
register_shutdown(edi_shutdown, (void *)flash);
return 1;
}