123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572 |
- /*
- * 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_programmer spi_programmer_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_programmer(&spi_programmer_ene);
- msg_pdbg("%s: successfully initialized ene\n", __func__);
- ene_probe_spi_flash_exit:
- free(p);
- return ret;
- }
- #endif /* __i386__ || __x86_64__ */
|