diff options
Diffstat (limited to 'it87spi.c')
-rw-r--r-- | it87spi.c | 438 |
1 files changed, 237 insertions, 201 deletions
@@ -19,28 +19,42 @@ * Contains the ITE IT87* SPI specific routines */ -#if defined(__i386__) || defined(__x86_64__) - #include <string.h> +#include <stdbool.h> #include <stdlib.h> #include <errno.h> #include "flash.h" #include "chipdrivers.h" #include "programmer.h" -#include "hwaccess.h" +#include "hwaccess_physmap.h" +#include "hwaccess_x86_io.h" #include "spi.h" #define ITE_SUPERIO_PORT1 0x2e #define ITE_SUPERIO_PORT2 0x4e -static uint16_t it8716f_flashport = 0; -/* use fast 33MHz SPI (<>0) or slow 16MHz (0) */ -static int fast_spi = 1; - -/* Helper functions for most recent ITE IT87xx Super I/O chips */ #define CHIP_ID_BYTE1_REG 0x20 #define CHIP_ID_BYTE2_REG 0x21 #define CHIP_VER_REG 0x22 + +struct it8716f_spi_data { + uint16_t flashport; + /* use fast 33MHz SPI (<>0) or slow 16MHz (0) */ + bool fast_spi; +}; + +static int get_data_from_context(const struct flashctx *flash, struct it8716f_spi_data **data) +{ + if (!flash || !flash->mst || !flash->mst->spi.data) { + msg_perr("Unable to extract fd from flash context.\n"); + return SPI_GENERIC_ERROR; + } + *data = (struct it8716f_spi_data *)flash->mst->spi.data; + + return 0; +} + +/* Helper functions for most recent ITE IT87xx Super I/O chips */ void enter_conf_mode_ite(uint16_t port) { OUTB(0x87, port); @@ -98,33 +112,224 @@ void probe_superio_ite(void) return; } +/* Page size is usually 256 bytes */ +static int it8716f_spi_page_program(struct flashctx *flash, const uint8_t *buf, unsigned int start) +{ + unsigned int i; + int result; + chipaddr bios = flash->virtual_memory; + struct it8716f_spi_data *data; + + if (get_data_from_context(flash, &data) < 0) + return SPI_GENERIC_ERROR; + + result = spi_write_enable(flash); + if (result) + return result; + /* FIXME: The command below seems to be redundant or wrong. */ + OUTB(0x06, data->flashport + 1); + OUTB(((2 + (data->fast_spi ? 1 : 0)) << 4), data->flashport); + for (i = 0; i < flash->chip->page_size; i++) + mmio_writeb(buf[i], (void *)(bios + start + i)); + OUTB(0, data->flashport); + /* Wait until the Write-In-Progress bit is cleared. + * This usually takes 1-10 ms, so wait in 1 ms steps. + * + * FIXME: This should timeout after some number of retries. + */ + while (true) { + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; + + if((status & SPI_SR_WIP) == 0) + return 0; + + default_delay(1000); + } + return 0; +} + +/* + * The IT8716F only supports commands with length 1,2,4,5 bytes including + * command byte and can not read more than 3 bytes from the device. + * + * This function expects writearr[0] to be the first byte sent to the device, + * whereas the IT8716F splits commands internally into address and non-address + * commands with the address in inverse wire order. That's why the register + * ordering in case 4 and 5 may seem strange. + */ static int it8716f_spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, - unsigned char *readarr); + unsigned char *readarr) +{ + uint8_t busy, writeenc; + struct it8716f_spi_data *data; + + if (get_data_from_context(flash, &data) < 0) + return SPI_GENERIC_ERROR; + + do { + busy = INB(data->flashport) & 0x80; + } while (busy); + if (readcnt > 3) { + msg_pinfo("%s called with unsupported readcnt %i.\n", + __func__, readcnt); + return SPI_INVALID_LENGTH; + } + switch (writecnt) { + case 1: + OUTB(writearr[0], data->flashport + 1); + writeenc = 0x0; + break; + case 2: + OUTB(writearr[0], data->flashport + 1); + OUTB(writearr[1], data->flashport + 7); + writeenc = 0x1; + break; + case 4: + OUTB(writearr[0], data->flashport + 1); + OUTB(writearr[1], data->flashport + 4); + OUTB(writearr[2], data->flashport + 3); + OUTB(writearr[3], data->flashport + 2); + writeenc = 0x2; + break; + case 5: + OUTB(writearr[0], data->flashport + 1); + OUTB(writearr[1], data->flashport + 4); + OUTB(writearr[2], data->flashport + 3); + OUTB(writearr[3], data->flashport + 2); + OUTB(writearr[4], data->flashport + 7); + writeenc = 0x3; + break; + default: + msg_pinfo("%s called with unsupported writecnt %i.\n", + __func__, writecnt); + return SPI_INVALID_LENGTH; + } + /* + * Start IO, 33 or 16 MHz, readcnt input bytes, writecnt output bytes. + * Note: + * We can't use writecnt directly, but have to use a strange encoding. + */ + OUTB(((0x4 + (data->fast_spi ? 1 : 0)) << 4) + | ((readcnt & 0x3) << 2) | (writeenc), data->flashport); + + if (readcnt > 0) { + unsigned int i; + + do { + busy = INB(data->flashport) & 0x80; + } while (busy); + + for (i = 0; i < readcnt; i++) + readarr[i] = INB(data->flashport + 5 + i); + } + + return 0; +} + +/* + * IT8716F only allows maximum of 512 kb SPI mapped to LPC memory cycles + * Need to read this big flash using firmware cycles 3 byte at a time. + */ static int it8716f_spi_chip_read(struct flashctx *flash, uint8_t *buf, - unsigned int start, unsigned int len); + unsigned int start, unsigned int len) +{ + struct it8716f_spi_data *data; + + if (get_data_from_context(flash, &data) < 0) + return SPI_GENERIC_ERROR; + + data->fast_spi = false; + + /* FIXME: Check if someone explicitly requested to use IT87 SPI although + * the mainboard does not use IT87 SPI translation. This should be done + * via a programmer parameter for the internal programmer. + */ + if ((flash->chip->total_size * 1024 > 512 * 1024)) { + default_spi_read(flash, buf, start, len); + } else { + mmio_readn((void *)(flash->virtual_memory + start), buf, len); + } + + return 0; +} + static int it8716f_spi_chip_write_256(struct flashctx *flash, const uint8_t *buf, - unsigned int start, unsigned int len); + unsigned int start, unsigned int len) +{ + const struct flashchip *chip = flash->chip; + /* + * IT8716F only allows maximum of 512 kb SPI chip size for memory + * mapped access. It also can't write more than 1+3+256 bytes at once, + * so page_size > 256 bytes needs a fallback. + * FIXME: Split too big page writes into chunks IT87* can handle instead + * of degrading to single-byte program. + * FIXME: Check if someone explicitly requested to use IT87 SPI although + * the mainboard does not use IT87 SPI translation. This should be done + * via a programmer parameter for the internal programmer. + */ + if ((chip->total_size * 1024 > 512 * 1024) || (chip->page_size > 256)) { + spi_chip_write_1(flash, buf, start, len); + } else { + unsigned int lenhere; + + if (start % chip->page_size) { + /* start to the end of the page or to start + len, + * whichever is smaller. + */ + lenhere = min(len, chip->page_size - start % chip->page_size); + spi_chip_write_1(flash, buf, start, lenhere); + start += lenhere; + len -= lenhere; + buf += lenhere; + } + + while (len >= chip->page_size) { + int ret = it8716f_spi_page_program(flash, buf, start); + if (ret) + return ret; + update_progress(flash, FLASHROM_PROGRESS_WRITE, chip->page_size - len, chip->page_size); + start += chip->page_size; + len -= chip->page_size; + buf += chip->page_size; + } + if (len) + spi_chip_write_1(flash, buf, start, len); + } + + return 0; +} + +static int it8716f_shutdown(void *data) +{ + free(data); + return 0; +} static const struct spi_master spi_master_it87xx = { - .max_data_read = MAX_DATA_UNSPECIFIED, + .max_data_read = 3, .max_data_write = MAX_DATA_UNSPECIFIED, .command = it8716f_spi_send_command, - .multicommand = default_spi_send_multicommand, + .map_flash_region = physmap, + .unmap_flash_region = physunmap, .read = it8716f_spi_chip_read, .write_256 = it8716f_spi_chip_write_256, .write_aai = spi_chip_write_1, + .shutdown = it8716f_shutdown, }; -static uint16_t it87spi_probe(uint16_t port) +static uint16_t it87spi_probe(const struct programmer_cfg *cfg, uint16_t port) { uint8_t tmp = 0; uint16_t flashport = 0; enter_conf_mode_ite(port); - char *param = extract_programmer_param("dualbiosindex"); + char *param = extract_programmer_param_str(cfg, "dualbiosindex"); if (param != NULL) { sio_write(port, 0x07, 0x07); /* Select GPIO LDN */ tmp = sio_read(port, 0xEF); @@ -134,12 +339,13 @@ static uint16_t it87spi_probe(uint16_t port) char *dualbiosindex_suffix; errno = 0; long chip_index = strtol(param, &dualbiosindex_suffix, 0); - free(param); if (errno != 0 || *dualbiosindex_suffix != '\0' || chip_index < 0 || chip_index > 1) { msg_perr("DualBIOS: Invalid chip index requested - choose 0 or 1.\n"); + free(param); exit_conf_mode_ite(port); return 1; } + free(param); if (chip_index != (tmp & 1)) { msg_pdbg("DualBIOS: Previous chip index: %d\n", tmp & 1); sio_write(port, 0xEF, (tmp & 0xFE) | chip_index); @@ -189,7 +395,7 @@ static uint16_t it87spi_probe(uint16_t port) flashport |= sio_read(port, 0x65); msg_pdbg("Serial flash port 0x%04x\n", flashport); /* Non-default port requested? */ - param = extract_programmer_param("it87spiport"); + param = extract_programmer_param_str(cfg, "it87spiport"); if (param) { char *endptr = NULL; unsigned long forced_flashport; @@ -219,15 +425,23 @@ static uint16_t it87spi_probe(uint16_t port) } free(param); exit_conf_mode_ite(port); - it8716f_flashport = flashport; + + struct it8716f_spi_data *data = calloc(1, sizeof(*data)); + if (!data) { + msg_perr("Unable to allocate space for extra SPI master data.\n"); + return SPI_GENERIC_ERROR; + } + + data->flashport = flashport; + data->fast_spi = true; + if (internal_buses_supported & BUS_SPI) msg_pdbg("Overriding chipset SPI with IT87 SPI.\n"); /* FIXME: Add the SPI bus or replace the other buses with it? */ - register_spi_master(&spi_master_it87xx); - return 0; + return register_spi_master(&spi_master_it87xx, data); } -int init_superio_ite(void) +int init_superio_ite(const struct programmer_cfg *cfg) { int i; int ret = 0; @@ -237,26 +451,15 @@ int init_superio_ite(void) continue; switch (superios[i].model) { - case 0x8500: - case 0x8502: - case 0x8510: - case 0x8511: - case 0x8512: - /* FIXME: This should be enabled, but we need a check - * for laptop whitelisting due to the amount of things - * which can go wrong if the EC firmware does not - * implement the interface we want. - */ - //it85xx_spi_init(superios[i]); - break; case 0x8705: ret |= it8705f_write_enable(superios[i].port); break; + case 0x8686: case 0x8716: case 0x8718: case 0x8720: case 0x8728: - ret |= it87spi_probe(superios[i].port); + ret |= it87spi_probe(cfg, superios[i].port); break; default: msg_pdbg2("Super I/O ID 0x%04hx is not on the list of flash-capable controllers.\n", @@ -265,170 +468,3 @@ int init_superio_ite(void) } return ret; } - -/* - * The IT8716F only supports commands with length 1,2,4,5 bytes including - * command byte and can not read more than 3 bytes from the device. - * - * This function expects writearr[0] to be the first byte sent to the device, - * whereas the IT8716F splits commands internally into address and non-address - * commands with the address in inverse wire order. That's why the register - * ordering in case 4 and 5 may seem strange. - */ -static int it8716f_spi_send_command(const struct flashctx *flash, - unsigned int writecnt, unsigned int readcnt, - const unsigned char *writearr, - unsigned char *readarr) -{ - uint8_t busy, writeenc; - - do { - busy = INB(it8716f_flashport) & 0x80; - } while (busy); - if (readcnt > 3) { - msg_pinfo("%s called with unsupported readcnt %i.\n", - __func__, readcnt); - return SPI_INVALID_LENGTH; - } - switch (writecnt) { - case 1: - OUTB(writearr[0], it8716f_flashport + 1); - writeenc = 0x0; - break; - case 2: - OUTB(writearr[0], it8716f_flashport + 1); - OUTB(writearr[1], it8716f_flashport + 7); - writeenc = 0x1; - break; - case 4: - OUTB(writearr[0], it8716f_flashport + 1); - OUTB(writearr[1], it8716f_flashport + 4); - OUTB(writearr[2], it8716f_flashport + 3); - OUTB(writearr[3], it8716f_flashport + 2); - writeenc = 0x2; - break; - case 5: - OUTB(writearr[0], it8716f_flashport + 1); - OUTB(writearr[1], it8716f_flashport + 4); - OUTB(writearr[2], it8716f_flashport + 3); - OUTB(writearr[3], it8716f_flashport + 2); - OUTB(writearr[4], it8716f_flashport + 7); - writeenc = 0x3; - break; - default: - msg_pinfo("%s called with unsupported writecnt %i.\n", - __func__, writecnt); - return SPI_INVALID_LENGTH; - } - /* - * Start IO, 33 or 16 MHz, readcnt input bytes, writecnt output bytes. - * Note: - * We can't use writecnt directly, but have to use a strange encoding. - */ - OUTB(((0x4 + (fast_spi ? 1 : 0)) << 4) - | ((readcnt & 0x3) << 2) | (writeenc), it8716f_flashport); - - if (readcnt > 0) { - unsigned int i; - - do { - busy = INB(it8716f_flashport) & 0x80; - } while (busy); - - for (i = 0; i < readcnt; i++) - readarr[i] = INB(it8716f_flashport + 5 + i); - } - - return 0; -} - -/* Page size is usually 256 bytes */ -static int it8716f_spi_page_program(struct flashctx *flash, const uint8_t *buf, unsigned int start) -{ - unsigned int i; - int result; - chipaddr bios = flash->virtual_memory; - - result = spi_write_enable(flash); - if (result) - return result; - /* FIXME: The command below seems to be redundant or wrong. */ - OUTB(0x06, it8716f_flashport + 1); - OUTB(((2 + (fast_spi ? 1 : 0)) << 4), it8716f_flashport); - for (i = 0; i < flash->chip->page_size; i++) - mmio_writeb(buf[i], (void *)(bios + start + i)); - OUTB(0, it8716f_flashport); - /* Wait until the Write-In-Progress bit is cleared. - * This usually takes 1-10 ms, so wait in 1 ms steps. - */ - while (spi_read_status_register(flash) & SPI_SR_WIP) - programmer_delay(1000); - return 0; -} - -/* - * IT8716F only allows maximum of 512 kb SPI mapped to LPC memory cycles - * Need to read this big flash using firmware cycles 3 byte at a time. - */ -static int it8716f_spi_chip_read(struct flashctx *flash, uint8_t *buf, - unsigned int start, unsigned int len) -{ - fast_spi = 0; - - /* FIXME: Check if someone explicitly requested to use IT87 SPI although - * the mainboard does not use IT87 SPI translation. This should be done - * via a programmer parameter for the internal programmer. - */ - if ((flash->chip->total_size * 1024 > 512 * 1024)) { - spi_read_chunked(flash, buf, start, len, 3); - } else { - mmio_readn((void *)(flash->virtual_memory + start), buf, len); - } - - return 0; -} - -static int it8716f_spi_chip_write_256(struct flashctx *flash, const uint8_t *buf, - unsigned int start, unsigned int len) -{ - const struct flashchip *chip = flash->chip; - /* - * IT8716F only allows maximum of 512 kb SPI chip size for memory - * mapped access. It also can't write more than 1+3+256 bytes at once, - * so page_size > 256 bytes needs a fallback. - * FIXME: Split too big page writes into chunks IT87* can handle instead - * of degrading to single-byte program. - * FIXME: Check if someone explicitly requested to use IT87 SPI although - * the mainboard does not use IT87 SPI translation. This should be done - * via a programmer parameter for the internal programmer. - */ - if ((chip->total_size * 1024 > 512 * 1024) || (chip->page_size > 256)) { - spi_chip_write_1(flash, buf, start, len); - } else { - unsigned int lenhere; - - if (start % chip->page_size) { - /* start to the end of the page or to start + len, - * whichever is smaller. - */ - lenhere = min(len, chip->page_size - start % chip->page_size); - spi_chip_write_1(flash, buf, start, lenhere); - start += lenhere; - len -= lenhere; - buf += lenhere; - } - - while (len >= chip->page_size) { - it8716f_spi_page_program(flash, buf, start); - start += chip->page_size; - len -= chip->page_size; - buf += chip->page_size; - } - if (len) - spi_chip_write_1(flash, buf, start, len); - } - - return 0; -} - -#endif |