Edward O'Callaghan has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Initial Realtek MST i2c_spi support
NOT-FOR-MERGE.
Good news everyone - ID/SZ and READ works now. cleanups++.
Change-Id: I892e0be776fe605e69fb39c77abf3016591d7123 Signed-off-by: Edward O'Callaghan quasisec@google.com --- M flashrom.c M meson.build M meson_options.txt M programmer.h A realtek_mst_i2c_spi.c 5 files changed, 454 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/67/40667/1
diff --git a/flashrom.c b/flashrom.c index 4224637..e58c4b7 100644 --- a/flashrom.c +++ b/flashrom.c @@ -401,6 +401,18 @@ }, #endif
+#if CONFIG_REALTEK_MST_I2C_SPI == 1 + { + .name = "realtek_mst_i2c_spi", + .type = OTHER, + .devs.note = "Device files /dev/i2c-*.\n", + .init = realtek_mst_i2c_spi_init, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .delay = internal_delay, + }, +#endif + #if CONFIG_USBBLASTER_SPI == 1 { .name = "usbblaster_spi", diff --git a/meson.build b/meson.build index 699370a..5d8e630 100644 --- a/meson.build +++ b/meson.build @@ -64,6 +64,7 @@ config_usbblaster_spi = get_option('config_usbblaster_spi') config_stlinkv3_spi = get_option('config_stlinkv3_spi') config_lspcon_i2c_spi = get_option('config_lspcon_i2c_spi') +config_realtek_mst_i2c_spi = get_option('config_realtek_mst_i2c_spi')
cargs = [] deps = [] @@ -288,6 +289,10 @@ srcs += 'lspcon_i2c_spi.c' cargs += '-DCONFIG_LSPCON_I2C_SPI=1' endif +if config_realtek_mst_i2c_spi + srcs += 'realtek_mst_i2c_spi.c' + cargs += '-DCONFIG_REALTEK_MST_I2C_SPI=1' +endif
# bitbanging SPI infrastructure if config_bitbang_spi diff --git a/meson_options.txt b/meson_options.txt index a103dc6..ac48e4e 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -36,3 +36,4 @@ option('config_usbblaster_spi', type : 'boolean', value : true, description : 'Altera USB-Blaster dongles') option('config_stlinkv3_spi', type : 'boolean', value : true, description : 'STMicroelectronics STLINK-V3') option('config_lspcon_i2c_spi', type : 'boolean', value : false, description : 'Parade lspcon USB-C to HDMI protocol translator') +option('config_realtek_mst_i2c_spi', type : 'boolean', value : true, description : 'Realtek MultiStream Transport MST') diff --git a/programmer.h b/programmer.h index f6c5399..61e53da 100644 --- a/programmer.h +++ b/programmer.h @@ -133,6 +133,9 @@ #if CONFIG_LSPCON_I2C_SPI == 1 PROGRAMMER_LSPCON_I2C_SPI, #endif +#if CONFIG_REALTEK_MST_I2C_SPI == 1 + PROGRAMMER_REALTEK_MST_I2C_SPI, +#endif PROGRAMMER_INVALID /* This must always be the last entry. */ };
@@ -819,4 +822,9 @@ int lspcon_i2c_spi_init(void); #endif
+/* realtek_mst_i2c_spi.c */ +#if CONFIG_REALTEK_MST_I2C_SPI == 1 +int realtek_mst_i2c_spi_init(void); +#endif + #endif /* !__PROGRAMMER_H__ */ diff --git a/realtek_mst_i2c_spi.c b/realtek_mst_i2c_spi.c new file mode 100644 index 0000000..16e5507 --- /dev/null +++ b/realtek_mst_i2c_spi.c @@ -0,0 +1,428 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2020 The Chromium OS Authors + * + * 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 <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <errno.h> + +#include "programmer.h" +#include "spi.h" +#include "i2c_helper.h" + + +#define MCU_I2C_SLAVE_ADDR 0x94 +#define REGISTER_ADDRESS (0x94 >> 1) +#define PAGE_SIZE 256 +#define MAX_SPI_WAIT_RETRIES 1000 + +//opcodes +#define OPCODE_READ 3 +#define OPCODE_WRITE 2 + + +struct realtek_mst_i2c_spi_data { + int fd; +}; + +static int realtek_mst_i2c_spi_write_data(int fd, uint16_t addr, void *buf, uint16_t len) +{ + i2c_buffer_t data; + if (i2c_buffer_t_fill(&data, buf, len)) + return SPI_GENERIC_ERROR; + + return i2c_write(fd, addr, &data) == len ? 0 : SPI_GENERIC_ERROR; +} + +static int realtek_mst_i2c_spi_read_data(int fd, uint16_t addr, void *buf, uint16_t len) +{ + i2c_buffer_t data; + if (i2c_buffer_t_fill(&data, buf, len)) + return SPI_GENERIC_ERROR; + + return i2c_read(fd, addr, &data) == len ? 0 : SPI_GENERIC_ERROR; +} + +static int get_fd_from_context(const struct flashctx *flash) +{ + if (!flash || !flash->mst || !flash->mst->spi.data) { + msg_perr("Unable to extract fd from flash context.\n"); + return SPI_GENERIC_ERROR; + } + const struct realtek_mst_i2c_spi_data *data = + (const struct realtek_mst_i2c_spi_data *)flash->mst->spi.data; + + return data->fd; +} + +static int realtek_mst_i2c_spi_write_register(int fd, uint8_t reg, uint8_t value) +{ + uint8_t command[] = { reg, value }; + return realtek_mst_i2c_spi_write_data(fd, REGISTER_ADDRESS, command, 2); +} + +static int realtek_mst_i2c_spi_read_register(int fd, uint8_t reg, uint8_t *value) +{ + uint8_t command[] = { reg }; + int ret = realtek_mst_i2c_spi_write_data(fd, REGISTER_ADDRESS, command, 1); + ret |= realtek_mst_i2c_spi_read_data(fd, REGISTER_ADDRESS, value, 1); + + return ret ? SPI_GENERIC_ERROR : 0; +} + +static int realtek_mst_i2c_spi_wait_command_done(int fd, unsigned int offset, int mask) +{ + uint8_t val; + int tried = 0; + int ret = 0; + do { + ret |= realtek_mst_i2c_spi_read_register(fd, offset, &val); + } while(!ret && (val & mask) && ++tried < MAX_SPI_WAIT_RETRIES); + + if (tried == MAX_SPI_WAIT_RETRIES) { + msg_perr("%s: Time out on sending command.\n", __func__); + return -MAX_SPI_WAIT_RETRIES; + } + + return (val & mask) ? SPI_GENERIC_ERROR : ret; +} + +static int realtek_mst_i2c_spi_enter_isp_mode(int fd) +{ + // 0xFF6F = 0x80; + int ret = realtek_mst_i2c_spi_write_register(fd, 0x6F, 0x80); // enter isp mode + + // set internal osc divider register to default to speed up MCU + // 0x06A0 = 0x74 + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF4, 0x9F); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF5, 0x06); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF4, 0xA0); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF5, 0x74); + + return ret; +} + +static int realtek_mst_i2c_spi_reset_mpu(int fd) +{ + int ret = 0; + // 0xFFEE[1] = 1; + uint8_t val = 0; + ret |= realtek_mst_i2c_spi_read_register(fd, 0xEE, &val); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xEE, (val & 0xFD) | 0x02); + return ret; +} + +#if 0 +static int realtek_mst_i2c_spi_set_defaults(int fd) +{ + // 0xFF1B = 0x02; + int ret = realtek_mst_i2c_spi_write_register(fd, 0x1B, 0x02); + ret = realtek_mst_i2c_spi_write_register(fd, 0x1C, 0x30); + ret = realtek_mst_i2c_spi_write_register(fd, 0x1D, 0x1C); + ret = realtek_mst_i2c_spi_write_register(fd, 0x1E, 0x02); + ret = realtek_mst_i2c_spi_write_register(fd, 0x1F, 0x00); + + ret = realtek_mst_i2c_spi_write_register(fd, 0x20, 0x1C); + ret = realtek_mst_i2c_spi_write_register(fd, 0x2C, 0x02); + ret = realtek_mst_i2c_spi_write_register(fd, 0x2D, 0x00); + ret = realtek_mst_i2c_spi_write_register(fd, 0x2E, 0x1C); + + ret = realtek_mst_i2c_spi_write_register(fd, 0x62, 0x06); + ret = realtek_mst_i2c_spi_write_register(fd, 0x6A, 0x03); + ret = realtek_mst_i2c_spi_write_register(fd, 0x6B, 0x0B); + ret = realtek_mst_i2c_spi_write_register(fd, 0x6C, 0x00); + + ret = realtek_mst_i2c_spi_write_register(fd, 0xED, 0x88); + ret = realtek_mst_i2c_spi_write_register(fd, 0xEE, 0x04); + + return ret; +} +#endif + +static int realtek_mst_i2c_spi_disable_protection(int fd) +{ + int ret = 0; + uint8_t val = 0; + // 0xAB[2:0] = b001 + + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF4, 0x9F); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF5, 0x10); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF4, 0xAB); + + ret |= realtek_mst_i2c_spi_read_register(fd, 0xF5, &val); + + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF4, 0x9F); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF5, 0x10); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF4, 0xAB); + + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF5, (val & 0xF8) | 0x01); + + return ret; +} + +static int realtek_mst_i2c_spi_send_command(const struct flashctx *flash, + unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr) +{ + unsigned i; + int ret = 0; + + if (writecnt > 4 || readcnt > 3 || writecnt == 0) { + //msg_perr("%s: Invalid read/write count for send command.\n", __func__); + return SPI_GENERIC_ERROR; + } + //printf("%s: writearr[0]=0x%x\n", __func__, writearr[0]); + + int fd = get_fd_from_context(flash); + if (fd < 0) + return SPI_GENERIC_ERROR; + + /* First byte of writearr should be the command value, followed by the value to write. */ + // 0xFF60 = cmd; + writecnt--; + uint8_t ctrl_reg_val = (writecnt << 3) | (readcnt << 1) | (2 << 5); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x60, ctrl_reg_val); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x61, writearr[0]); + + for (i = 0; i < writecnt; ++i) + ret |= realtek_mst_i2c_spi_write_register(fd, 0x64 + i, writearr[i + 1]); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x60, ctrl_reg_val | 0x1); + if (ret) + return ret; + + ret = realtek_mst_i2c_spi_wait_command_done(fd, 0x60, 0x01); + if (ret) + return ret; + + for (i = 0; i < readcnt; ++i) + ret |= realtek_mst_i2c_spi_read_register(fd, 0x67 + i, &readarr[i]); + + //for (i = 0; i< readcnt; i++) + // printf("DEBUG: readarr[%d]=0x%02x\n", i, readarr[i]); + + return ret; +} + +static int realtek_mst_i2c_spi_map_page(int fd, uint8_t block_idx, uint8_t page_idx, uint8_t byte_idx) +{ + int ret = 0; + ret |= realtek_mst_i2c_spi_write_register(fd, 0x64, block_idx); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x65, page_idx); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x66, byte_idx); + + return ret ? SPI_GENERIC_ERROR : 0; +} + +static int realtek_mst_i2c_spi_read(struct flashctx *flash, uint8_t *buf, + unsigned int start, unsigned int len) +{ + unsigned i; + int ret = 0; + + if (start & 0xff) + return default_spi_read(flash, buf, start, len); + + int fd = get_fd_from_context(flash); + if (fd < 0) + return SPI_GENERIC_ERROR; + + start--; + ret |= realtek_mst_i2c_spi_write_register(fd, 0x60, 0x46); // ** + ret |= realtek_mst_i2c_spi_write_register(fd, 0x61, OPCODE_READ); + uint8_t block_idx = start >> 16; + uint8_t page_idx = start >> 8; + uint8_t byte_idx = start; + ret |= realtek_mst_i2c_spi_map_page(fd, block_idx, page_idx, byte_idx); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x6a, 0x03); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x60, 0x47); // ** + if (ret) + return ret; + + ret = realtek_mst_i2c_spi_wait_command_done(fd, 0x60, 0x01); + if (ret) + return ret; + + uint8_t dummy; + realtek_mst_i2c_spi_read_register(fd, 0x70, &dummy); + + for (i = 0; i < len; i += PAGE_SIZE) { + ret |= realtek_mst_i2c_spi_read_data(fd, REGISTER_ADDRESS, + buf + i, min(len - i, PAGE_SIZE)); + if (ret) + return ret; + } + + return ret; +} + +static int realtek_mst_i2c_spi_write_256(struct flashctx *flash, const uint8_t *buf, + unsigned int start, unsigned int len) +{ + unsigned i; + int ret = 0; + + if (start & 0xff) + return default_spi_write_256(flash, buf, start, len); + + int fd = get_fd_from_context(flash); + if (fd < 0) + return SPI_GENERIC_ERROR; + + ret = realtek_mst_i2c_spi_disable_protection(fd); + if (ret) + return ret; + + start--; + ret |= realtek_mst_i2c_spi_write_register(fd, 0x60, 0x46); // ** + ret |= realtek_mst_i2c_spi_write_register(fd, 0x61, OPCODE_WRITE); + uint8_t block_idx = start >> 16; + uint8_t page_idx = start >> 8; + uint8_t byte_idx = start; + ret |= realtek_mst_i2c_spi_map_page(fd, block_idx, page_idx, byte_idx); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x6a, 0x03); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x60, 0x47); // ** + if (ret) + return ret; + + ret = realtek_mst_i2c_spi_wait_command_done(fd, 0x60, 0x01); + if (ret) + return ret; + + // XXX?? + //uint8_t dummy; + //realtek_mst_i2c_spi_read_register(fd, 0x70, &dummy); + + for (i = 0; i < len; i += PAGE_SIZE) { + ret |= realtek_mst_i2c_spi_write_data(fd, REGISTER_ADDRESS, + (uint8_t *)buf + i, min(len - i, PAGE_SIZE)); + if (ret) + return ret; + } + +// ret |= realtek_mst_i2c_spi_enable_write_protection(fd); +// ret |= realtek_mst_i2c_spi_toggle_register_protection(fd, 0); + + return ret; +} + +static int realtek_mst_i2c_spi_write_aai(struct flashctx *flash, const uint8_t *buf, + unsigned int start, unsigned int len) +{ + msg_perr("%s: AAI write function is not supported.\n", __func__); + return SPI_GENERIC_ERROR; +} + +static struct spi_master spi_master_i2c_realtek_mst = { + .max_data_read = 16, + .max_data_write = 8, + .command = realtek_mst_i2c_spi_send_command, + .multicommand = default_spi_send_multicommand, + .read = realtek_mst_i2c_spi_read, + .write_256 = realtek_mst_i2c_spi_write_256, + .write_aai = realtek_mst_i2c_spi_write_aai, +}; + +static int realtek_mst_i2c_spi_shutdown(void *data) +{ + int ret = 0; + struct realtek_mst_i2c_spi_data *realtek_mst_data = + (struct realtek_mst_i2c_spi_data *)data; + int fd = realtek_mst_data->fd; + ret |= realtek_mst_i2c_spi_reset_mpu(fd); + i2c_close(fd); + free(data); + + return ret; +} + +/* TODO: remove this out of the specific SPI master implementation. */ +static int get_bus(void) +{ + char *bus_str = extract_programmer_param("bus"); + int ret = SPI_GENERIC_ERROR; + if (bus_str) { + char *bus_suffix; + errno = 0; + int bus = (int)strtol(bus_str, &bus_suffix, 10); + if (errno != 0 || bus_str == bus_suffix) { + msg_perr("%s: Could not convert 'bus'.\n", __func__); + goto get_bus_done; + } + + if (bus < 0 || bus > 255) { + msg_perr("%s: Value for 'bus' is out of range(0-255).\n", __func__); + goto get_bus_done; + } + + if (strlen(bus_suffix) > 0) { + msg_perr("%s: Garbage following 'bus' value.\n", __func__); + goto get_bus_done; + } + + msg_pinfo("Using i2c bus %i.\n", bus); + ret = bus; + goto get_bus_done; + } else { + msg_perr("%s: Bus number not specified.\n", __func__); + } +get_bus_done: + if (bus_str) + free(bus_str); + + return ret; +} + +int realtek_mst_i2c_spi_init(void) +{ + int realtek_mst_i2c_spi_bus = get_bus(); + if (realtek_mst_i2c_spi_bus < 0) + return SPI_GENERIC_ERROR; + + int ret = 0; + int fd = i2c_open(realtek_mst_i2c_spi_bus, REGISTER_ADDRESS, 0); + if (fd < 0) + return fd; + + /* XXX ensure we are in a known state before entering ISP mode */ + ret |= realtek_mst_i2c_spi_reset_mpu(fd); + if (ret) + return ret; + + ret |= realtek_mst_i2c_spi_enter_isp_mode(fd); + if (ret) + return ret; +// XXX: maybe make into a mode:defaults cli param? +// ret |= realtek_mst_i2c_spi_set_defaults(fd); +// if (ret) +// return ret; + + struct realtek_mst_i2c_spi_data *data = calloc(1, sizeof(struct realtek_mst_i2c_spi_data)); + if (!data) { + msg_perr("Unable to allocate space for extra SPI master data.\n"); + return SPI_GENERIC_ERROR; + } + + data->fd = fd; + ret |= register_shutdown(realtek_mst_i2c_spi_shutdown, data); + + spi_master_i2c_realtek_mst.data = data; + ret |= register_spi_master(&spi_master_i2c_realtek_mst); + + return ret; +}
Edward O'Callaghan has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Patch Set 1: Code-Review-2
Angel Pons has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Patch Set 1:
(1 comment)
I assume this is WIP, just had a quick look
https://review.coreboot.org/c/flashrom/+/40667/1/realtek_mst_i2c_spi.c File realtek_mst_i2c_spi.c:
https://review.coreboot.org/c/flashrom/+/40667/1/realtek_mst_i2c_spi.c@95 PS1, Line 95: while missing space after while
Edward O'Callaghan has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Patch Set 1:
(1 comment)
https://review.coreboot.org/c/flashrom/+/40667/1/realtek_mst_i2c_spi.c File realtek_mst_i2c_spi.c:
https://review.coreboot.org/c/flashrom/+/40667/1/realtek_mst_i2c_spi.c@95 PS1, Line 95: while
missing space after while
Done, however that is the least of the problems here no point in commenting on style Angel.
That said lspcon_i2c_spi.c looks like it needs that fix as well.
Hello build bot (Jenkins), Shiyu Sun, Angel Pons,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/flashrom/+/40667
to look at the new patch set (#2).
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Initial Realtek MST i2c_spi support
NOT-FOR-MERGE.
Good news everyone - ID/SZ and READ works now. cleanups++.
Change-Id: I892e0be776fe605e69fb39c77abf3016591d7123 Signed-off-by: Edward O'Callaghan quasisec@google.com --- M flashrom.c M meson.build M meson_options.txt M programmer.h A realtek_mst_i2c_spi.c 5 files changed, 454 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/67/40667/2
Paul Menzel has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Patch Set 2:
(1 comment)
https://review.coreboot.org/c/flashrom/+/40667/2//COMMIT_MSG Commit Message:
https://review.coreboot.org/c/flashrom/+/40667/2//COMMIT_MSG@7 PS2, Line 7: Initial Realtek MST i2c_spi support Add initial …
Edward O'Callaghan has removed Paul Menzel from this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Removed reviewer Paul Menzel.
Edward O'Callaghan has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Patch Set 2:
(1 comment)
https://review.coreboot.org/c/flashrom/+/40667/2//COMMIT_MSG Commit Message:
https://review.coreboot.org/c/flashrom/+/40667/2//COMMIT_MSG@7 PS2, Line 7: Initial Realtek MST i2c_spi support
Add initial …
this isn't helpful.
Shiyu Sun has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Patch Set 2:
(1 comment)
This change is ready for review.
https://review.coreboot.org/c/flashrom/+/40667/2//COMMIT_MSG Commit Message:
https://review.coreboot.org/c/flashrom/+/40667/2//COMMIT_MSG@11 PS2, Line 11: Good news everyone - ID/SZ and READ works now. cleanups++. I just cherry-picked the commit and tested, however on my side it still doesn't seems good.
localhost ~ # ./flashrom -p realtek_mst_i2c_spi:bus=8 --flash-name flashrom v1.2-37-gace20744-dirty on Linux 4.19.96 (x86_64) flashrom is free software, get the source code at https://flashrom.org
Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns). Using i2c bus 8. No EEPROM/flash device found. Note: flashrom can never write if the flash chip isn't found automatically.
That actually makes me to suspect there are something wrong on my board. All the commands fails for this same reason.
Hello build bot (Jenkins), Shiyu Sun, Angel Pons,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/flashrom/+/40667
to look at the new patch set (#3).
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Initial Realtek MST i2c_spi support
This spi master allows for programming of a Realtek RTD2142 MST with external SPI flash chip routed via its internal i2c transport mechanism.
BUG=b:152558985,b:148745673 BRANCH=none TEST=echo "00000000:0004ffff fw" > layout && \ flashrom -p realtek_mst_i2c_spi:bus=8 -l layout -i fw:dump.bin -r && \ flashrom -p realtek_mst_i2c_spi:bus=8 -l layout -i fw:dump.bin -w && \ flashrom -p realtek_mst_i2c_spi:bus=8 --flash-size && \ flashrom -p realtek_mst_i2c_spi:bus=8 --flash-name
Change-Id: I892e0be776fe605e69fb39c77abf3016591d7123 Signed-off-by: Edward O'Callaghan quasisec@google.com --- M flashrom.c M meson.build M meson_options.txt M programmer.h A realtek_mst_i2c_spi.c 5 files changed, 461 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/67/40667/3
Edward O'Callaghan has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Patch Set 3: -Code-Review
Shiyu Sun has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Patch Set 3:
(1 comment)
https://review.coreboot.org/c/flashrom/+/40667/2//COMMIT_MSG Commit Message:
https://review.coreboot.org/c/flashrom/+/40667/2//COMMIT_MSG@11 PS2, Line 11: Good news everyone - ID/SZ and READ works now. cleanups++.
I just cherry-picked the commit and tested, however on my side it still doesn't seems good. […]
Done
Shiyu Sun has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Patch Set 3:
(1 comment)
https://review.coreboot.org/c/flashrom/+/40667/3/realtek_mst_i2c_spi.c File realtek_mst_i2c_spi.c:
https://review.coreboot.org/c/flashrom/+/40667/3/realtek_mst_i2c_spi.c@323 PS3, Line 323: // ret |= realtek_mst_i2c_spi_enable_write_protection(fd); probably remove the comment?
Hello build bot (Jenkins), Shiyu Sun, Angel Pons, Edward Hill,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/flashrom/+/40667
to look at the new patch set (#4).
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Initial Realtek MST i2c_spi support
This spi master allows for programming of a Realtek RTD2142 MST with external SPI flash chip routed via its internal i2c transport mechanism.
BUG=b:152558985,b:148745673 BRANCH=none TEST=echo "00000000:0004ffff fw" > layout && \ flashrom -p realtek_mst_i2c_spi:bus=8 -l layout -i fw:dump.bin -r && \ flashrom -p realtek_mst_i2c_spi:bus=8 -l layout -i fw:dump.bin -w && \ flashrom -p realtek_mst_i2c_spi:bus=8 --flash-size && \ flashrom -p realtek_mst_i2c_spi:bus=8 --flash-name
Change-Id: I892e0be776fe605e69fb39c77abf3016591d7123 Signed-off-by: Edward O'Callaghan quasisec@google.com --- M Makefile M flashrom.c M meson.build M meson_options.txt M programmer.h A realtek_mst_i2c_spi.c 6 files changed, 487 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/67/40667/4
Edward O'Callaghan has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Patch Set 4:
(1 comment)
Patch Set 2:
(1 comment)
This change is ready for review.
https://review.coreboot.org/c/flashrom/+/40667/3/realtek_mst_i2c_spi.c File realtek_mst_i2c_spi.c:
https://review.coreboot.org/c/flashrom/+/40667/3/realtek_mst_i2c_spi.c@323 PS3, Line 323: // ret |= realtek_mst_i2c_spi_enable_write_protection(fd);
probably remove the comment?
Done
Edward O'Callaghan has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Patch Set 4:
(1 comment)
https://review.coreboot.org/c/flashrom/+/40667/1/realtek_mst_i2c_spi.c File realtek_mst_i2c_spi.c:
https://review.coreboot.org/c/flashrom/+/40667/1/realtek_mst_i2c_spi.c@95 PS1, Line 95: while
Ack
Angel, I would like to get this initial support merged in if you are ok with this patch? I am trying to get a hold of realtek to get them to follow up with more advise/documentation however this is everything I know about the hw so far and seems to work as-is.
Hello build bot (Jenkins), Shiyu Sun, Angel Pons, Edward Hill,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/flashrom/+/40667
to look at the new patch set (#5).
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Initial Realtek MST i2c_spi support
This spi master allows for programming of a Realtek RTD2142 MST with external SPI flash chip routed via its internal i2c transport mechanism.
BUG=b:152558985,b:148745673 BRANCH=none TEST=echo "00000000:0004ffff fw" > layout && \ flashrom -p realtek_mst_i2c_spi:bus=8 -l layout -i fw:dump.bin -r && \ flashrom -p realtek_mst_i2c_spi:bus=8 -l layout -i fw:dump.bin -w && \ flashrom -p realtek_mst_i2c_spi:bus=8 --flash-size && \ flashrom -p realtek_mst_i2c_spi:bus=8 --flash-name
Change-Id: I892e0be776fe605e69fb39c77abf3016591d7123 Signed-off-by: Edward O'Callaghan quasisec@google.com --- M Makefile M flashrom.c M meson.build M meson_options.txt M programmer.h A realtek_mst_i2c_spi.c 6 files changed, 487 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/67/40667/5
Shiyu Sun has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Patch Set 5: Code-Review+1
Edward Hill has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Patch Set 6: Code-Review+2
In realtek_mst_i2c_spi.c I see some "#if 0" and debug code and questions, but this seems ok as a starting point, and doesn't look like it will break anything else.
Edward O'Callaghan has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Patch Set 6:
Patch Set 6: Code-Review+2
In realtek_mst_i2c_spi.c I see some "#if 0" and debug code and questions, but this seems ok as a starting point, and doesn't look like it will break anything else.
Thanks for the review Ed! Yes, this is so when I get a hold of Realtek I can get them to help me fill in some of the remaining gaps, otherwise I will timeout on that and sent a follow up that cleans it up.
Edward O'Callaghan has submitted this change. ( https://review.coreboot.org/c/flashrom/+/40667 )
Change subject: Initial Realtek MST i2c_spi support ......................................................................
Initial Realtek MST i2c_spi support
This spi master allows for programming of a Realtek RTD2142 MST with external SPI flash chip routed via its internal i2c transport mechanism.
BUG=b:152558985,b:148745673 BRANCH=none TEST=echo "00000000:0004ffff fw" > layout && \ flashrom -p realtek_mst_i2c_spi:bus=8 -l layout -i fw:dump.bin -r && \ flashrom -p realtek_mst_i2c_spi:bus=8 -l layout -i fw:dump.bin -w && \ flashrom -p realtek_mst_i2c_spi:bus=8 --flash-size && \ flashrom -p realtek_mst_i2c_spi:bus=8 --flash-name
Change-Id: I892e0be776fe605e69fb39c77abf3016591d7123 Signed-off-by: Edward O'Callaghan quasisec@google.com Reviewed-on: https://review.coreboot.org/c/flashrom/+/40667 Reviewed-by: Edward Hill ecgh@chromium.org Reviewed-by: Shiyu Sun sshiyu@google.com Tested-by: build bot (Jenkins) no-reply@coreboot.org --- M Makefile M flashrom.c M meson.build M meson_options.txt M programmer.h A realtek_mst_i2c_spi.c 6 files changed, 487 insertions(+), 0 deletions(-)
Approvals: build bot (Jenkins): Verified Edward Hill: Looks good to me, approved Shiyu Sun: Looks good to me, but someone else must approve
diff --git a/Makefile b/Makefile index a533acf..31e987c 100644 --- a/Makefile +++ b/Makefile @@ -200,6 +200,11 @@ else override CONFIG_LSPCON_I2C_SPI = no endif +ifeq ($(CONFIG_REALTEK_MST_I2C_SPI), yes) +UNSUPPORTED_FEATURES += CONFIG_REALTEK_MST_I2C_SPI=yes +else +override CONFIG_REALTEK_MST_I2C_SPI = no +endif # libjaylink is also not available for DOS ifeq ($(CONFIG_JLINK_SPI), yes) UNSUPPORTED_FEATURES += CONFIG_JLINK_SPI=yes @@ -316,6 +321,11 @@ else override CONFIG_LSPCON_I2C_SPI = no endif +ifeq ($(CONFIG_REALTEK_MST_I2C_SPI), yes) +UNSUPPORTED_FEATURES += CONFIG_REALTEK_MST_I2C_SPI=yes +else +override CONFIG_REALTEK_MST_I2C_SPI = no +endif endif
ifneq ($(TARGET_OS), MinGW) @@ -396,6 +406,11 @@ else override CONFIG_LSPCON_I2C_SPI = no endif +ifeq ($(CONFIG_REALTEK_MST_I2C_SPI), yes) +UNSUPPORTED_FEATURES += CONFIG_REALTEK_MST_I2C_SPI=yes +else +override CONFIG_REALTEK_MST_I2C_SPI = no +endif ifeq ($(CONFIG_CH341A_SPI), yes) UNSUPPORTED_FEATURES += CONFIG_CH341A_SPI=yes else @@ -674,6 +689,9 @@ # Disables LSPCON support until the i2c helper supports multiple systems. CONFIG_LSPCON_I2C_SPI ?= no
+# Disables REALTEK_MST support until the i2c helper supports multiple systems. +CONFIG_REALTEK_MST_I2C_SPI ?= no + # Always enable dummy tracing for now. CONFIG_DUMMY ?= yes
@@ -755,6 +773,7 @@ override CONFIG_RAIDEN = no override CONFIG_STLINKV3_SPI = no override CONFIG_LSPCON_I2C_SPI = no +override CONFIG_REALTEK_MST_I2C_SPI = no endif ifeq ($(CONFIG_ENABLE_LIBPCI_PROGRAMMERS), no) override CONFIG_INTERNAL = no @@ -939,6 +958,11 @@ NEED_LIBUSB1 += CONFIG_LSPCON_I2C_SPI endif
+ifeq ($(CONFIG_REALTEK_MST_I2C_SPI), yes) +FEATURE_CFLAGS += -D'CONFIG_REALTEK_MST_I2C_SPI=1' +PROGRAMMER_OBJS += realtek_mst_i2c_spi.o +endif + ifneq ($(NEED_LIBFTDI), ) FTDILIBS := $(call debug_shell,[ -n "$(PKG_CONFIG_LIBDIR)" ] && export PKG_CONFIG_LIBDIR="$(PKG_CONFIG_LIBDIR)" ; $(PKG_CONFIG) --libs libftdi1 || $(PKG_CONFIG) --libs libftdi || printf "%s" "-lftdi -lusb") FEATURE_CFLAGS += $(call debug_shell,grep -q "FT232H := yes" .features && printf "%s" "-D'HAVE_FT232H=1'") diff --git a/flashrom.c b/flashrom.c index 07ce734..4a30986 100644 --- a/flashrom.c +++ b/flashrom.c @@ -401,6 +401,18 @@ }, #endif
+#if CONFIG_REALTEK_MST_I2C_SPI == 1 + { + .name = "realtek_mst_i2c_spi", + .type = OTHER, + .devs.note = "Device files /dev/i2c-*.\n", + .init = realtek_mst_i2c_spi_init, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .delay = internal_delay, + }, +#endif + #if CONFIG_USBBLASTER_SPI == 1 { .name = "usbblaster_spi", diff --git a/meson.build b/meson.build index 699370a..5d8e630 100644 --- a/meson.build +++ b/meson.build @@ -64,6 +64,7 @@ config_usbblaster_spi = get_option('config_usbblaster_spi') config_stlinkv3_spi = get_option('config_stlinkv3_spi') config_lspcon_i2c_spi = get_option('config_lspcon_i2c_spi') +config_realtek_mst_i2c_spi = get_option('config_realtek_mst_i2c_spi')
cargs = [] deps = [] @@ -288,6 +289,10 @@ srcs += 'lspcon_i2c_spi.c' cargs += '-DCONFIG_LSPCON_I2C_SPI=1' endif +if config_realtek_mst_i2c_spi + srcs += 'realtek_mst_i2c_spi.c' + cargs += '-DCONFIG_REALTEK_MST_I2C_SPI=1' +endif
# bitbanging SPI infrastructure if config_bitbang_spi diff --git a/meson_options.txt b/meson_options.txt index a103dc6..ac48e4e 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -36,3 +36,4 @@ option('config_usbblaster_spi', type : 'boolean', value : true, description : 'Altera USB-Blaster dongles') option('config_stlinkv3_spi', type : 'boolean', value : true, description : 'STMicroelectronics STLINK-V3') option('config_lspcon_i2c_spi', type : 'boolean', value : false, description : 'Parade lspcon USB-C to HDMI protocol translator') +option('config_realtek_mst_i2c_spi', type : 'boolean', value : true, description : 'Realtek MultiStream Transport MST') diff --git a/programmer.h b/programmer.h index 49a9d29..c5cab18 100644 --- a/programmer.h +++ b/programmer.h @@ -133,6 +133,9 @@ #if CONFIG_LSPCON_I2C_SPI == 1 PROGRAMMER_LSPCON_I2C_SPI, #endif +#if CONFIG_REALTEK_MST_I2C_SPI == 1 + PROGRAMMER_REALTEK_MST_I2C_SPI, +#endif PROGRAMMER_INVALID /* This must always be the last entry. */ };
@@ -819,4 +822,9 @@ int lspcon_i2c_spi_init(void); #endif
+/* realtek_mst_i2c_spi.c */ +#if CONFIG_REALTEK_MST_I2C_SPI == 1 +int realtek_mst_i2c_spi_init(void); +#endif + #endif /* !__PROGRAMMER_H__ */ diff --git a/realtek_mst_i2c_spi.c b/realtek_mst_i2c_spi.c new file mode 100644 index 0000000..ba41ed2 --- /dev/null +++ b/realtek_mst_i2c_spi.c @@ -0,0 +1,437 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2020 The Chromium OS Authors + * + * 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 <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <errno.h> + +#include "programmer.h" +#include "spi.h" +#include "i2c_helper.h" + + +#define MCU_I2C_SLAVE_ADDR 0x94 +#define REGISTER_ADDRESS (0x94 >> 1) +#define PAGE_SIZE 256 +#define MAX_SPI_WAIT_RETRIES 1000 + +//opcodes +#define OPCODE_READ 3 +#define OPCODE_WRITE 2 + + +struct realtek_mst_i2c_spi_data { + int fd; +}; + +static int realtek_mst_i2c_spi_write_data(int fd, uint16_t addr, void *buf, uint16_t len) +{ + i2c_buffer_t data; + if (i2c_buffer_t_fill(&data, buf, len)) + return SPI_GENERIC_ERROR; + + return i2c_write(fd, addr, &data) == len ? 0 : SPI_GENERIC_ERROR; +} + +static int realtek_mst_i2c_spi_read_data(int fd, uint16_t addr, void *buf, uint16_t len) +{ + i2c_buffer_t data; + if (i2c_buffer_t_fill(&data, buf, len)) + return SPI_GENERIC_ERROR; + + return i2c_read(fd, addr, &data) == len ? 0 : SPI_GENERIC_ERROR; +} + +static int get_fd_from_context(const struct flashctx *flash) +{ + if (!flash || !flash->mst || !flash->mst->spi.data) { + msg_perr("Unable to extract fd from flash context.\n"); + return SPI_GENERIC_ERROR; + } + const struct realtek_mst_i2c_spi_data *data = + (const struct realtek_mst_i2c_spi_data *)flash->mst->spi.data; + + return data->fd; +} + +static int realtek_mst_i2c_spi_write_register(int fd, uint8_t reg, uint8_t value) +{ + uint8_t command[] = { reg, value }; + return realtek_mst_i2c_spi_write_data(fd, REGISTER_ADDRESS, command, 2); +} + +static int realtek_mst_i2c_spi_read_register(int fd, uint8_t reg, uint8_t *value) +{ + uint8_t command[] = { reg }; + int ret = realtek_mst_i2c_spi_write_data(fd, REGISTER_ADDRESS, command, 1); + ret |= realtek_mst_i2c_spi_read_data(fd, REGISTER_ADDRESS, value, 1); + + return ret ? SPI_GENERIC_ERROR : 0; +} + +static int realtek_mst_i2c_spi_wait_command_done(int fd, unsigned int offset, int mask) +{ + uint8_t val; + int tried = 0; + int ret = 0; + do { + ret |= realtek_mst_i2c_spi_read_register(fd, offset, &val); + } while (!ret && (val & mask) && ++tried < MAX_SPI_WAIT_RETRIES); + + if (tried == MAX_SPI_WAIT_RETRIES) { + msg_perr("%s: Time out on sending command.\n", __func__); + return -MAX_SPI_WAIT_RETRIES; + } + + return (val & mask) ? SPI_GENERIC_ERROR : ret; +} + +static int realtek_mst_i2c_spi_enter_isp_mode(int fd) +{ + // 0xFF6F = 0x80; + int ret = realtek_mst_i2c_spi_write_register(fd, 0x6F, 0x80); // enter isp mode + + // set internal osc divider register to default to speed up MCU + // 0x06A0 = 0x74 + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF4, 0x9F); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF5, 0x06); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF4, 0xA0); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF5, 0x74); + + return ret; +} + +static int realtek_mst_i2c_spi_reset_mpu(int fd) +{ + int ret = 0; + // 0xFFEE[1] = 1; + uint8_t val = 0; + ret |= realtek_mst_i2c_spi_read_register(fd, 0xEE, &val); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xEE, (val & 0xFD) | 0x02); + return ret; +} + +#if 0 +static int realtek_mst_i2c_spi_set_defaults(int fd) +{ + // 0xFF1B = 0x02; + int ret = realtek_mst_i2c_spi_write_register(fd, 0x1B, 0x02); + ret = realtek_mst_i2c_spi_write_register(fd, 0x1C, 0x30); + ret = realtek_mst_i2c_spi_write_register(fd, 0x1D, 0x1C); + ret = realtek_mst_i2c_spi_write_register(fd, 0x1E, 0x02); + ret = realtek_mst_i2c_spi_write_register(fd, 0x1F, 0x00); + + ret = realtek_mst_i2c_spi_write_register(fd, 0x20, 0x1C); + ret = realtek_mst_i2c_spi_write_register(fd, 0x2C, 0x02); + ret = realtek_mst_i2c_spi_write_register(fd, 0x2D, 0x00); + ret = realtek_mst_i2c_spi_write_register(fd, 0x2E, 0x1C); + + ret = realtek_mst_i2c_spi_write_register(fd, 0x62, 0x06); + ret = realtek_mst_i2c_spi_write_register(fd, 0x6A, 0x03); + ret = realtek_mst_i2c_spi_write_register(fd, 0x6B, 0x0B); + ret = realtek_mst_i2c_spi_write_register(fd, 0x6C, 0x00); + + ret = realtek_mst_i2c_spi_write_register(fd, 0xED, 0x88); + ret = realtek_mst_i2c_spi_write_register(fd, 0xEE, 0x04); + + return ret; +} +#endif + +static int realtek_mst_i2c_spi_disable_protection(int fd) +{ + int ret = 0; + uint8_t val = 0; + // 0xAB[2:0] = b001 + + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF4, 0x9F); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF5, 0x10); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF4, 0xAB); + + ret |= realtek_mst_i2c_spi_read_register(fd, 0xF5, &val); + + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF4, 0x9F); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF5, 0x10); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF4, 0xAB); + + ret |= realtek_mst_i2c_spi_write_register(fd, 0xF5, (val & 0xF8) | 0x01); + + /* Set pin value to high, 0xFFD7[0] = 1. */ + ret |= realtek_mst_i2c_spi_read_register(fd, 0xD7, &val); + ret |= realtek_mst_i2c_spi_write_register(fd, 0xD7, (val & 0xFE) | 0x01); + + return ret; +} + +static int realtek_mst_i2c_spi_send_command(const struct flashctx *flash, + unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr) +{ + unsigned i; + int ret = 0; + + if (writecnt > 4 || readcnt > 3 || writecnt == 0) { + //msg_perr("%s: Invalid read/write count for send command.\n", __func__); + return SPI_GENERIC_ERROR; + } + //printf("%s: writearr[0]=0x%x\n", __func__, writearr[0]); + + int fd = get_fd_from_context(flash); + if (fd < 0) + return SPI_GENERIC_ERROR; + + /* First byte of writearr should be the command value, followed by the value to write. */ + // 0xFF60 = cmd; + writecnt--; + uint8_t ctrl_reg_val = (writecnt << 3) | (readcnt << 1) | (2 << 5); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x60, ctrl_reg_val); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x61, writearr[0]); + + for (i = 0; i < writecnt; ++i) + ret |= realtek_mst_i2c_spi_write_register(fd, 0x64 + i, writearr[i + 1]); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x60, ctrl_reg_val | 0x1); + if (ret) + return ret; + + ret = realtek_mst_i2c_spi_wait_command_done(fd, 0x60, 0x01); + if (ret) + return ret; + + for (i = 0; i < readcnt; ++i) + ret |= realtek_mst_i2c_spi_read_register(fd, 0x67 + i, &readarr[i]); + + //for (i = 0; i< readcnt; i++) + // printf("DEBUG: readarr[%d]=0x%02x\n", i, readarr[i]); + + return ret; +} + +static int realtek_mst_i2c_spi_map_page(int fd, uint8_t block_idx, uint8_t page_idx, uint8_t byte_idx) +{ + int ret = 0; + ret |= realtek_mst_i2c_spi_write_register(fd, 0x64, block_idx); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x65, page_idx); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x66, byte_idx); + + return ret ? SPI_GENERIC_ERROR : 0; +} + +static int realtek_mst_i2c_spi_read(struct flashctx *flash, uint8_t *buf, + unsigned int start, unsigned int len) +{ + unsigned i; + int ret = 0; + + if (start & 0xff) + return default_spi_read(flash, buf, start, len); + + int fd = get_fd_from_context(flash); + if (fd < 0) + return SPI_GENERIC_ERROR; + + start--; + ret |= realtek_mst_i2c_spi_write_register(fd, 0x60, 0x46); // ** + ret |= realtek_mst_i2c_spi_write_register(fd, 0x61, OPCODE_READ); + uint8_t block_idx = start >> 16; + uint8_t page_idx = start >> 8; + uint8_t byte_idx = start; + ret |= realtek_mst_i2c_spi_map_page(fd, block_idx, page_idx, byte_idx); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x6a, 0x03); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x60, 0x47); // ** + if (ret) + return ret; + + ret = realtek_mst_i2c_spi_wait_command_done(fd, 0x60, 0x01); + if (ret) + return ret; + + /** + * The first byte is just a null, probably a status code? + * Advance the read by a offset of one byte and continue. + */ + uint8_t dummy; + realtek_mst_i2c_spi_read_register(fd, 0x70, &dummy); + + for (i = 0; i < len; i += PAGE_SIZE) { + ret |= realtek_mst_i2c_spi_read_data(fd, REGISTER_ADDRESS, + buf + i, min(len - i, PAGE_SIZE)); + if (ret) + return ret; + } + + return ret; +} + +static int realtek_mst_i2c_spi_write_256(struct flashctx *flash, const uint8_t *buf, + unsigned int start, unsigned int len) +{ + unsigned i; + int ret = 0; + + if (start & 0xff) + return default_spi_write_256(flash, buf, start, len); + + int fd = get_fd_from_context(flash); + if (fd < 0) + return SPI_GENERIC_ERROR; + + ret = realtek_mst_i2c_spi_disable_protection(fd); + if (ret) + return ret; + + start--; + ret |= realtek_mst_i2c_spi_write_register(fd, 0x60, 0x46); // ** + ret |= realtek_mst_i2c_spi_write_register(fd, 0x61, OPCODE_WRITE); + uint8_t block_idx = start >> 16; + uint8_t page_idx = start >> 8; + uint8_t byte_idx = start; + ret |= realtek_mst_i2c_spi_map_page(fd, block_idx, page_idx, byte_idx); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x6a, 0x03); + ret |= realtek_mst_i2c_spi_write_register(fd, 0x60, 0x47); // ** + if (ret) + goto fail; + + ret = realtek_mst_i2c_spi_wait_command_done(fd, 0x60, 0x01); + if (ret) + goto fail; + + for (i = 0; i < len; i += PAGE_SIZE) { + ret |= realtek_mst_i2c_spi_write_data(fd, REGISTER_ADDRESS, + (uint8_t *)buf + i, min(len - i, PAGE_SIZE)); + if (ret) + break; + } + +fail: + /* TODO: re-enable the write protection? */ + + return ret; +} + +static int realtek_mst_i2c_spi_write_aai(struct flashctx *flash, const uint8_t *buf, + unsigned int start, unsigned int len) +{ + msg_perr("%s: AAI write function is not supported.\n", __func__); + return SPI_GENERIC_ERROR; +} + +static struct spi_master spi_master_i2c_realtek_mst = { + .max_data_read = 16, + .max_data_write = 8, + .command = realtek_mst_i2c_spi_send_command, + .multicommand = default_spi_send_multicommand, + .read = realtek_mst_i2c_spi_read, + .write_256 = realtek_mst_i2c_spi_write_256, + .write_aai = realtek_mst_i2c_spi_write_aai, +}; + +static int realtek_mst_i2c_spi_shutdown(void *data) +{ + int ret = 0; + struct realtek_mst_i2c_spi_data *realtek_mst_data = + (struct realtek_mst_i2c_spi_data *)data; + int fd = realtek_mst_data->fd; + ret |= realtek_mst_i2c_spi_reset_mpu(fd); + i2c_close(fd); + free(data); + + return ret; +} + +static int get_params(int *i2c_bus) +{ + char *bus_str = NULL; + int ret = SPI_GENERIC_ERROR; + + bus_str = extract_programmer_param("bus"); + if (bus_str) { + char *bus_suffix; + errno = 0; + int bus = (int)strtol(bus_str, &bus_suffix, 10); + if (errno != 0 || bus_str == bus_suffix) { + msg_perr("%s: Could not convert 'bus'.\n", __func__); + goto get_params_done; + } + + if (bus < 0 || bus > 255) { + msg_perr("%s: Value for 'bus' is out of range(0-255).\n", __func__); + goto get_params_done; + } + + if (strlen(bus_suffix) > 0) { + msg_perr("%s: Garbage following 'bus' value.\n", __func__); + goto get_params_done; + } + + msg_pinfo("Using i2c bus %i.\n", bus); + *i2c_bus = bus; + ret = 0; + goto get_params_done; + } else { + msg_perr("%s: Bus number not specified.\n", __func__); + } +get_params_done: + if (bus_str) + free(bus_str); + + return ret; +} + +int realtek_mst_i2c_spi_init(void) +{ + int ret = 0; + int i2c_bus = 0; + + if (get_params(&i2c_bus)) + return SPI_GENERIC_ERROR; + + int fd = i2c_open(i2c_bus, REGISTER_ADDRESS, 0); + if (fd < 0) + return fd; + + /* Ensure we are in a known state before entering ISP mode */ + ret |= realtek_mst_i2c_spi_reset_mpu(fd); + if (ret) + return ret; + + ret |= realtek_mst_i2c_spi_enter_isp_mode(fd); + if (ret) + return ret; +// XXX: maybe make into a mode:defaults cli param? +#if 0 + ret |= realtek_mst_i2c_spi_set_defaults(fd); + if (ret) + return ret; +#endif + + struct realtek_mst_i2c_spi_data *data = calloc(1, sizeof(struct realtek_mst_i2c_spi_data)); + if (!data) { + msg_perr("Unable to allocate space for extra SPI master data.\n"); + return SPI_GENERIC_ERROR; + } + + data->fd = fd; + ret |= register_shutdown(realtek_mst_i2c_spi_shutdown, data); + + spi_master_i2c_realtek_mst.data = data; + ret |= register_spi_master(&spi_master_i2c_realtek_mst); + + return ret; +}