From 6f6fc652d5942fa52e80675b6b7f4643759f70b0 Mon Sep 17 00:00:00 2001 From: Evan Lojewski Date: Sun, 17 May 2020 17:19:04 -0600 Subject: [PATCH] flash: Enable read/write/erase access flash from firmware. --- src/arch/8051/arch.mk | 2 +- src/board/system76/common/common.mk | 3 + src/board/system76/common/flash/flash.mk | 55 ++++++ .../common/flash/include/flash/entry.h | 8 + src/board/system76/common/flash/main.c | 173 ++++++++++++++++++ src/board/system76/common/flash/wrapper.c | 78 ++++++++ .../system76/common/include/board/flash.h | 96 ++++++++++ 7 files changed, 414 insertions(+), 1 deletion(-) create mode 100644 src/board/system76/common/flash/flash.mk create mode 100644 src/board/system76/common/flash/include/flash/entry.h create mode 100644 src/board/system76/common/flash/main.c create mode 100644 src/board/system76/common/flash/wrapper.c create mode 100644 src/board/system76/common/include/board/flash.h diff --git a/src/arch/8051/arch.mk b/src/arch/8051/arch.mk index 2553018..2c45d7e 100644 --- a/src/arch/8051/arch.mk +++ b/src/arch/8051/arch.mk @@ -19,7 +19,7 @@ $(BUILD)/ec.rom: $(BUILD)/ec.ihx # Link object files into Intel Hex file $(BUILD)/ec.ihx: $(OBJ) @mkdir -p $(@D) - $(CC) -o $@ $^ + $(CC) $(LDFLAGS) -o $@ $^ # Compile C files into object files $(OBJ): $(BUILD)/%.rel: src/%.c $(INCLUDE) diff --git a/src/board/system76/common/common.mk b/src/board/system76/common/common.mk index e1f6224..4b2a840 100644 --- a/src/board/system76/common/common.mk +++ b/src/board/system76/common/common.mk @@ -27,6 +27,9 @@ CFLAGS+=-I$(SYSTEM76_COMMON_DIR)/include # Add scratch ROM include $(SYSTEM76_COMMON_DIR)/scratch/scratch.mk +# Add scratch ROM for flash access +include $(SYSTEM76_COMMON_DIR)/flash/flash.mk + console_internal: cargo build --manifest-path tool/Cargo.toml --release sudo tool/target/release/system76_ectool console diff --git a/src/board/system76/common/flash/flash.mk b/src/board/system76/common/flash/flash.mk new file mode 100644 index 0000000..9988b70 --- /dev/null +++ b/src/board/system76/common/flash/flash.mk @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +# Set flash ROM parameters +FLASH_OFFSET=2048 +FLASH_SIZE=1024 +CFLAGS+=-DFLASH_OFFSET=$(FLASH_OFFSET) -DFLASH_SIZE=$(FLASH_SIZE) + +# Copy parameters to use when compiling flash ROM +FLASH_INCLUDE=$(INCLUDE) +FLASH_CFLAGS=$(CFLAGS) + +# Include flash source. +FLASH_DIR=$(SYSTEM76_COMMON_DIR)/flash +# Note: main.c *must* be first to ensure that flash_start is at the correct address +FLASH_SRC=$(FLASH_DIR)/main.c +FLASH_INCLUDE+=$(wildcard $(FLASH_DIR)/include/flash/*.h) $(FLASH_DIR)/flash.mk +FLASH_CFLAGS+=-I$(FLASH_DIR)/include -D__FLASH__ + +FLASH_BUILD=$(BUILD)/flash +FLASH_OBJ=$(patsubst src/%.c,$(FLASH_BUILD)/%.rel,$(FLASH_SRC)) +FLASH_CC=\ + sdcc \ + -mmcs51 \ + --model-large \ + --opt-code-size \ + --acall-ajmp \ + --code-loc $(FLASH_OFFSET) \ + --code-size $(FLASH_SIZE) \ + --Werror + +# Convert from binary file to C header +$(BUILD)/include/flash.h: $(FLASH_BUILD)/flash.rom + @mkdir -p $(@D) + xxd -s $(FLASH_OFFSET) --include < $< > $@ + +# Convert from Intel Hex file to binary file +$(FLASH_BUILD)/flash.rom: $(FLASH_BUILD)/flash.ihx + @mkdir -p $(@D) + makebin -p < $< > $@ + +# Link object files into Intel Hex file +$(FLASH_BUILD)/flash.ihx: $(FLASH_OBJ) + @mkdir -p $(@D) + $(FLASH_CC) -o $@ $^ + +# Compile C files into object files +$(FLASH_OBJ): $(FLASH_BUILD)/%.rel: src/%.c $(FLASH_INCLUDE) + @mkdir -p $(@D) + $(FLASH_CC) $(FLASH_CFLAGS) -o $@ -c $< + +# Include flash header in main firmware +CFLAGS+=-I$(BUILD)/include +LDFLAGS+=-Wl -g_flash_entry=$(FLASH_OFFSET) +INCLUDE+=$(BUILD)/include/flash.h +SRC+=$(FLASH_DIR)/wrapper.c diff --git a/src/board/system76/common/flash/include/flash/entry.h b/src/board/system76/common/flash/include/flash/entry.h new file mode 100644 index 0000000..5e477f8 --- /dev/null +++ b/src/board/system76/common/flash/include/flash/entry.h @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef _FLASH_ENTRY_H +#define _FLASH_ENTRY_H + +void flash_entry(uint32_t addr, uint8_t * data, uint32_t length, uint8_t command) __reentrant; + +#endif // _FLASH_ENTRY_H diff --git a/src/board/system76/common/flash/main.c b/src/board/system76/common/flash/main.c new file mode 100644 index 0000000..ec8ac60 --- /dev/null +++ b/src/board/system76/common/flash/main.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2020 Evan Lojewski + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include + +// EC indirect flash access +volatile uint8_t __xdata __at(0x103B) ECINDAR0; +volatile uint8_t __xdata __at(0x103C) ECINDAR1; +volatile uint8_t __xdata __at(0x103D) ECINDAR2; +volatile uint8_t __xdata __at(0x103E) ECINDAR3; +volatile uint8_t __xdata __at(0x103F) ECINDDR; + +#define SPI_DEVICE (0x70) +#define SPI_FOLLOW_MODE (0x0F) +#define SPI_CHIP_SELECT (0xFD) +#define SPI_CHIP_DESELECT (0xFE) + +#define SPI_READ_STATUS_COMMAND (0x05) +#define SPI_READ_COMMAND (0x0B) +#define SPI_WRITE_COMMAND (0x02) +#define SPI_WRITE_ENABLE_COMMAND (0x06) +#define SPI_READ_STATUS_COMMAND (0x05) + +#define SPI_ERASE_SECTOR_COMMAND (0xD7) + +#define SPI_STATUS_WIP (0x01) + +void flash_enter_follow_mode(void); +void flash_exit_follow_mode(void); +void flash_wait(void); +void flash_write_enable(void); + +/** + * Main flash API entry point. + * + * NOTE: This *must* be the first function in this file to ensure that it is placed + * first in the resulting binary. This is required to ensure that address + * matches the address (FLASH_OFFSET) for flash_entry in wrapper.c. + * NOTE: __reentrant so that parameters and temperary vairables are placed on the + * stack, ensuring the main application __data variables are not stomped on. + * NOTE: __critical to ensure interrupts are disabled. This does mean that interrupt + * such as the timer will be block until flash acccess is complete + */ +void flash_entry(uint32_t addr, uint8_t * data, uint32_t length, uint8_t command) __reentrant __critical { + // Only allow access from 64KB to 128KB. + if ((addr < 0x10000) + || (length > 0x10000) + || ((addr + length) > 0x20000)) + return; + + if (command == FLASH_COMMAND_READ) { + while (length) { + // Fast read. + ECINDAR3 = SPI_DEVICE; + ECINDAR2 = addr >> 16; + ECINDAR1 = addr >> 8; + ECINDAR0 = addr; + + *data = ECINDDR; + + addr++; + data++; + length--; + } + } else if (command == FLASH_COMMAND_WRITE) { + flash_enter_follow_mode(); + + while (length) { + // Note, this is the slow way to do it, but it's simple and all bytes are written properly. + flash_write_enable(); + + // Select the device + ECINDAR1 = SPI_CHIP_SELECT; + + // Send write command + ECINDDR = SPI_WRITE_COMMAND; + ECINDDR = addr >> 16; + ECINDDR = addr >> 8; + ECINDDR = addr; + + ECINDDR = *data; + + addr++; + data++; + length--; + + // Deselect + ECINDAR1 = SPI_CHIP_DESELECT; + ECINDDR = 0x00; + + + // Wait WIP to be cleared + flash_wait(); + } + + flash_exit_follow_mode(); + } else if (command == FLASH_COMMAND_ERASE_1K) { + flash_enter_follow_mode(); + + flash_write_enable(); + + // Select the device + ECINDAR1 = SPI_CHIP_SELECT; + + // Send erase command + ECINDDR = SPI_ERASE_SECTOR_COMMAND; + ECINDDR = addr >> 16; + ECINDDR = addr >> 8; + ECINDDR = addr; + + // Deselect + ECINDAR1 = SPI_CHIP_DESELECT; + ECINDDR = 0x00; + + // Wait WIP to be cleared + flash_wait(); + + flash_exit_follow_mode(); + } +} + +void flash_enter_follow_mode(void) { + // Enter follow mode. + ECINDAR3 = SPI_FOLLOW_MODE | SPI_DEVICE; + ECINDAR2 = 0xFF; + ECINDAR1 = SPI_CHIP_DESELECT; + ECINDAR0 = 0x00; + ECINDDR = 0x00; +} + +void flash_exit_follow_mode(void) { + // Exit follow mode + ECINDAR3 = SPI_DEVICE; + ECINDAR2 = 0; + ECINDAR1 = 0; + ECINDAR0 = 0; +} + +void flash_wait(void) { + uint8_t status; + + do { + // Select the device + ECINDAR1 = SPI_CHIP_SELECT; + + // Send command + ECINDDR = SPI_READ_STATUS_COMMAND; + + // read status + status = ECINDDR; + + // Deselect + ECINDAR1 = SPI_CHIP_DESELECT; + ECINDDR = 0x00; + } while(status & SPI_STATUS_WIP); +} + +void flash_write_enable(void) { + // Select the device + ECINDAR1 = SPI_CHIP_SELECT; + + // Send device id command + ECINDDR = SPI_WRITE_ENABLE_COMMAND; + + // Deselect + ECINDAR1 = SPI_CHIP_DESELECT; + ECINDDR = 0x00; +} diff --git a/src/board/system76/common/flash/wrapper.c b/src/board/system76/common/flash/wrapper.c new file mode 100644 index 0000000..4cc63fb --- /dev/null +++ b/src/board/system76/common/flash/wrapper.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 Evan Lojewski + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include +#include +#include "include/flash/entry.h" + +// Include flash ROM +uint8_t __code __at(FLASH_OFFSET) flash_rom[] = { + #include +}; + +static void flash_api(uint32_t addr, uint8_t * data, uint32_t length, uint8_t command) { + // Use DMA mapping to copy flash ROM to scratch ROM + SCARH = 0x80; + SCARL = (uint8_t)(FLASH_OFFSET); + SCARM = (uint8_t)(FLASH_OFFSET >> 8); + SCARH = 0; + + // Jump to flash ROM + flash_entry(addr, data, length, command); + + // Disable scratch ROM + SCARH = 0x07; +} + +void flash_read(uint32_t addr, __xdata uint8_t * data, uint32_t length) { + flash_api(addr, data, length, FLASH_COMMAND_READ); +} + +uint32_t flash_read_u32(uint32_t addr) { + uint32_t data; + + flash_api(addr, (uint8_t *)&data, sizeof(data), FLASH_COMMAND_READ); + + return data; +} + +uint16_t flash_read_u16(uint32_t addr) { + uint16_t data; + + flash_api(addr, (uint8_t *)&data, sizeof(data), FLASH_COMMAND_READ); + + return data; +} + +uint8_t flash_read_u8(uint32_t addr) { + uint8_t data; + + flash_api(addr, &data, sizeof(data), FLASH_COMMAND_READ); + + return data; +} + +void flash_write(uint32_t addr, __xdata uint8_t *data, uint32_t length) { + flash_api(addr, data, length, FLASH_COMMAND_WRITE); +} + +void flash_write_u32(uint32_t addr, uint32_t data) { + flash_api(addr, (uint8_t *)&data, sizeof(data), FLASH_COMMAND_WRITE); +} + +void flash_write_u16(uint32_t addr, uint16_t data) { + flash_api(addr, (uint8_t *)&data, sizeof(data), FLASH_COMMAND_WRITE); +} + +void flash_write_u8(uint32_t addr, uint8_t data) { + flash_api(addr, &data, sizeof(data), FLASH_COMMAND_WRITE); +} + +void flash_erase(uint32_t addr) { + flash_api(addr, NULL, 0, FLASH_COMMAND_ERASE_1K); +} diff --git a/src/board/system76/common/include/board/flash.h b/src/board/system76/common/include/board/flash.h new file mode 100644 index 0000000..348fdac --- /dev/null +++ b/src/board/system76/common/include/board/flash.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2020 Evan Lojewski + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef _BOARD_FLASH_H +#define _BOARD_FLASH_H + +#include + +/** \cond INTERNAL + * Internal defines + */ +#define FLASH_COMMAND_READ (0x0) +#define FLASH_COMMAND_WRITE (0x1) +#define FLASH_COMMAND_ERASE_1K (0x2) +/** \endcond */ + +/** + * Read data from flash to the specified buffer. + * + * \param[in] addr The flash address to read. + * \param[out] data The memory area to copy to. + * \param[in] length The number of bytes to copy. + */ +void flash_read(uint32_t addr, __xdata uint8_t * data, uint32_t length); + +/** + * Read a single byte from flash. + * + * \param[in] addr The flash address to read. + * + * \return The value read from flash. + */ +uint8_t flash_read_u8(uint32_t addr); + +/** + * Read two bytes from flash. + * + * \param[in] addr The flash address to read. + * + * \return The value read from flash. + */ +uint16_t flash_read_u16(uint32_t addr); + +/** + * Read four bytes from flash. + * + * \param[in] addr The flash address to read. + * + * \return The value read from flash. + */ +uint32_t flash_read_u32(uint32_t addr); + +/** + * Write data to flash from the specified buffer. + * + * \param[in] addr The flash address to read. + * \param[in] data The memory area to copy from. + * \param[in] length The number of bytes to copy. + */ +void flash_write(uint32_t addr, __xdata uint8_t * data, uint32_t length); + +/** + * Write a single byte to flash. + * + * \param[in] addr The flash address to read. + * \param[in] data The value to write to flash. + */ +void flash_write_u8(uint32_t addr, uint8_t data); + +/** + * Write two bytes to flash. + * + * \param[in] addr The flash address to read. + * \param[in] data The value to write to flash. + */ +void flash_write_u16(uint32_t addr, uint16_t data); + +/** + * Write two bytes to flash. + * + * \param[in] addr The flash address to read. + * \param[in] data The value to write to flash. + */ +void flash_write_u32(uint32_t addr, uint32_t data); + +/** + * Erase a 1K block of flash. + * + * \param[in] addr The flash address contained in the 1K block. + */ +void flash_erase(uint32_t addr); + +#endif // _BOARD_FLASH_H