Faster flashing with SMFI (#32)

* WIP: support for new flashing API

* Add SPI flashing support to tool

* Add timeouts when flashing with ectool

* Test SPI reading

* Use chunks for SPI commands

* Sanity test of flash size

* Read rom in sectors

* Relocate memmap region, remove PMC3

* Use ectool to flash

* Remove debugging of spi command

* Fix flashing over smfi
This commit is contained in:
Jeremy Soller
2020-02-26 09:04:40 -07:00
committed by GitHub
parent a7e47d8d58
commit 657437e1ce
34 changed files with 826 additions and 791 deletions

Submodule ecflash updated: 945ec8d276...fc3f098fda

View File

@ -27,11 +27,15 @@ SCRATCH_OFFSET=1024
SCRATCH_SIZE=1024 SCRATCH_SIZE=1024
CFLAGS+=-DSCRATCH_OFFSET=$(SCRATCH_OFFSET) -DSCRATCH_SIZE=$(SCRATCH_SIZE) CFLAGS+=-DSCRATCH_OFFSET=$(SCRATCH_OFFSET) -DSCRATCH_SIZE=$(SCRATCH_SIZE)
# Copy parameters to use when compiling scratch ROM
SCRATCH_INCLUDE=$(INCLUDE)
SCRATCH_CFLAGS=$(CFLAGS)
# Add scratch ROM source # Add scratch ROM source
SCRATCH_DIR=$(BOARD_DIR)/scratch SCRATCH_DIR=$(BOARD_DIR)/scratch
SCRATCH_SRC=$(wildcard $(SCRATCH_DIR)/*.c) SCRATCH_SRC=$(wildcard $(SCRATCH_DIR)/*.c)
SCRATCH_INCLUDE=$(wildcard $(SCRATCH_DIR)/include/scratch/*.h) $(SCRATCH_DIR)/scratch.mk SCRATCH_INCLUDE+=$(wildcard $(SCRATCH_DIR)/include/scratch/*.h) $(SCRATCH_DIR)/scratch.mk
SCRATCH_CFLAGS=-I$(SCRATCH_DIR)/include SCRATCH_CFLAGS+=-I$(SCRATCH_DIR)/include -D__SCRATCH__
include $(SCRATCH_DIR)/scratch.mk include $(SCRATCH_DIR)/scratch.mk
# Include scratch header in main firmware # Include scratch header in main firmware
@ -43,8 +47,8 @@ console:
sudo tool/target/release/system76_ectool console sudo tool/target/release/system76_ectool console
flash: $(BUILD)/ec.rom flash: $(BUILD)/ec.rom
cargo build --manifest-path ecflash/Cargo.toml --example isp --release cargo build --manifest-path tool/Cargo.toml --release
sudo ecflash/target/release/examples/isp --internal $< sudo tool/target/release/system76_ectool flash $<
isp: $(BUILD)/ec.rom isp: $(BUILD)/ec.rom
cargo build --manifest-path ecflash/Cargo.toml --example isp --release cargo build --manifest-path ecflash/Cargo.toml --example isp --release

View File

@ -2,7 +2,6 @@
#include <board/acpi.h> #include <board/acpi.h>
#include <board/gpio.h> #include <board/gpio.h>
#include <board/pmc.h> #include <board/pmc.h>
#include <board/scratch.h>
#include <common/debug.h> #include <common/debug.h>
void pmc_init(void) { void pmc_init(void) {
@ -102,12 +101,6 @@ void pmc_event(struct Pmc * pmc) {
// Clear SCI queue // Clear SCI queue
pmc_sci_queue = 0; pmc_sci_queue = 0;
break; break;
case 0xEC:
TRACE(" scratch rom\n");
pmc_write(pmc, 0x76);
scratch_trampoline();
break;
} }
} else { } else {
TRACE("pmc data: %02X\n", data); TRACE("pmc data: %02X\n", data);

View File

@ -28,10 +28,6 @@ void pnp_enable() {
// Enable PMC1 // Enable PMC1
pnp_write(0x07, 0x11); pnp_write(0x07, 0x11);
pnp_write(0x30, 0x01); pnp_write(0x30, 0x01);
//
// Enable PMC3
pnp_write(0x07, 0x17);
pnp_write(0x30, 0x01);
// Enable KBC keyboard // Enable KBC keyboard
pnp_write(0x07, 0x06); pnp_write(0x07, 0x06);

View File

@ -1,53 +0,0 @@
#ifndef _EC_PMC_H
#define _EC_PMC_H
#include <stdbool.h>
#include <stdint.h>
struct Pmc {
// Status register
volatile uint8_t * status;
// Data out register
volatile uint8_t * data_out;
// Data in register
volatile uint8_t * data_in;
// Control register
volatile uint8_t * control;
};
extern struct Pmc __code PMC_3;
#define PMC_STS_OBF (1 << 0)
#define PMC_STS_IBF (1 << 1)
#define PMC_STS_CMD (1 << 3)
uint8_t pmc_status(struct Pmc * pmc);
uint8_t pmc_read(struct Pmc * pmc);
bool pmc_write(struct Pmc * pmc, uint8_t data);
volatile uint8_t __xdata __at(0x1500) PM1STS;
volatile uint8_t __xdata __at(0x1501) PM1DO;
volatile uint8_t __xdata __at(0x1504) PM1DI;
volatile uint8_t __xdata __at(0x1506) PM1CTL;
volatile uint8_t __xdata __at(0x1510) PM2STS;
volatile uint8_t __xdata __at(0x1511) PM2DO;
volatile uint8_t __xdata __at(0x1514) PM2DI;
volatile uint8_t __xdata __at(0x1516) PM2CTL;
volatile uint8_t __xdata __at(0x1520) PM3STS;
volatile uint8_t __xdata __at(0x1521) PM3DO;
volatile uint8_t __xdata __at(0x1522) PM3DI;
volatile uint8_t __xdata __at(0x1523) PM3CTL;
volatile uint8_t __xdata __at(0x1530) PM4STS;
volatile uint8_t __xdata __at(0x1531) PM4DO;
volatile uint8_t __xdata __at(0x1532) PM4DI;
volatile uint8_t __xdata __at(0x1533) PM4CTL;
volatile uint8_t __xdata __at(0x1540) PM5STS;
volatile uint8_t __xdata __at(0x1541) PM5DO;
volatile uint8_t __xdata __at(0x1542) PM5DI;
volatile uint8_t __xdata __at(0x1543) PM5CTL;
#endif // _EC_PMC_H

View File

@ -1,130 +1,8 @@
#include <stdbool.h> #include <board/smfi.h>
#include <stdint.h>
#include <scratch/pmc.h>
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;
volatile uint8_t __xdata __at(0x1F01) ETWCFG;
volatile uint8_t __xdata __at(0x1F07) EWDKEYR;
uint8_t acpi_read(uint8_t addr) {
uint8_t data = 0;
switch (addr) {
case 4:
data = ECINDAR0;
break;
case 5:
data = ECINDAR1;
break;
case 6:
data = ECINDAR2;
break;
case 7:
data = ECINDAR3;
break;
case 8:
data = ECINDDR;
break;
}
return data;
}
void acpi_write(uint8_t addr, uint8_t data) {
switch (addr) {
case 4:
ECINDAR0 = data;
break;
case 5:
ECINDAR1 = data;
break;
case 6:
ECINDAR2 = data;
break;
case 7:
ECINDAR3 = data;
break;
case 8:
ECINDDR = data;
break;
}
}
enum PmcState {
PMC_STATE_DEFAULT,
PMC_STATE_WRITE,
PMC_STATE_ACPI_READ,
PMC_STATE_ACPI_WRITE,
PMC_STATE_ACPI_WRITE_ADDR,
};
void pmc_event(struct Pmc * pmc) {
static enum PmcState state = PMC_STATE_DEFAULT;
static uint8_t state_data = 0;
uint8_t sts = pmc_status(pmc);
// Read command/data if available
if (sts & PMC_STS_IBF) {
uint8_t data = pmc_read(pmc);
if (sts & PMC_STS_CMD) {
state = PMC_STATE_DEFAULT;
switch (data) {
case 0x80:
state = PMC_STATE_ACPI_READ;
break;
case 0x81:
state = PMC_STATE_ACPI_WRITE;
break;
case 0xEC:
for (;;) {
// Attempt to trigger watchdog reset
ETWCFG |= (1 << 5);
EWDKEYR = 0;
}
break;
}
} else {
switch (state) {
case PMC_STATE_ACPI_READ:
state = PMC_STATE_WRITE;
state_data = acpi_read(data);
break;
case PMC_STATE_ACPI_WRITE:
state = PMC_STATE_ACPI_WRITE_ADDR;
state_data = data;
break;
case PMC_STATE_ACPI_WRITE_ADDR:
state = PMC_STATE_DEFAULT;
acpi_write(state_data, data);
break;
default:
state = PMC_STATE_DEFAULT;
break;
}
}
}
// Write data if possible
if (!(sts & PMC_STS_OBF)) {
switch (state) {
case PMC_STATE_WRITE:
state = PMC_STATE_DEFAULT;
pmc_write(pmc, state_data);
break;
}
}
}
// Main program while running in scratch ROM // Main program while running in scratch ROM
void main(void) { void main(void) {
for (;;) { for (;;) {
pmc_event(&PMC_3); smfi_event();
} }
} }

View File

@ -1,23 +0,0 @@
#include <scratch/pmc.h>
#define PMC(NUM) { \
.status = &PM ## NUM ## STS, \
.data_out = &PM ## NUM ## DO, \
.data_in = &PM ## NUM ## DI, \
.control = &PM ## NUM ## CTL, \
}
struct Pmc __code PMC_3 = PMC(3);
uint8_t pmc_status(struct Pmc * pmc) {
return *(pmc->status);
}
uint8_t pmc_read(struct Pmc * pmc) {
return *(pmc->data_in);
}
bool pmc_write(struct Pmc * pmc, uint8_t data) {
*(pmc->data_out) = data;
return true;
}

View File

@ -1,3 +1,7 @@
SCRATCH_SRC+=\
$(COMMON_DIR)/version.c \
$(BOARD_DIR)/smfi.c
SCRATCH_BUILD=$(BUILD)/scratch SCRATCH_BUILD=$(BUILD)/scratch
SCRATCH_OBJ=$(patsubst src/%.c,$(SCRATCH_BUILD)/%.rel,$(SCRATCH_SRC)) SCRATCH_OBJ=$(patsubst src/%.c,$(SCRATCH_BUILD)/%.rel,$(SCRATCH_SRC))
SCRATCH_CC=\ SCRATCH_CC=\

View File

@ -0,0 +1,9 @@
#include <stdio.h>
#include <board/smfi.h>
int putchar(int c) {
unsigned char byte = (unsigned char)c;
smfi_debug(byte);
return (int)byte;
}

View File

@ -2,7 +2,11 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#ifndef __SCRATCH__
#include <board/scratch.h>
#endif
#include <board/smfi.h> #include <board/smfi.h>
#include <common/command.h>
#include <common/macro.h> #include <common/macro.h>
#include <common/version.h> #include <common/version.h>
@ -19,84 +23,149 @@ volatile uint8_t __xdata __at(0x105D) HRAMW0AAS;
// Host RAM window 1 access allow size // Host RAM window 1 access allow size
volatile uint8_t __xdata __at(0x105E) HRAMW1AAS; volatile uint8_t __xdata __at(0x105E) HRAMW1AAS;
static volatile uint8_t __xdata __at(0xC00) smfi_cmd[256]; volatile uint8_t __xdata __at(0x103B) ECINDAR0;
static volatile uint8_t __xdata __at(0xD00) smfi_dbg[256]; 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;
enum SmfiCmd { volatile uint8_t __xdata __at(0x1F01) ETWCFG;
SMFI_CMD_NONE = 0, volatile uint8_t __xdata __at(0x1F07) EWDKEYR;
SMFI_CMD_PROBE = 1,
SMFI_CMD_BOARD = 2,
SMFI_CMD_VERSION = 3,
SMFI_CMD_DEBUG = 4,
//TODO
};
enum SmfiRes { static volatile uint8_t __xdata __at(0xE00) smfi_cmd[256];
SMFI_RES_OK = 0, static volatile uint8_t __xdata __at(0xF00) smfi_dbg[256];
SMFI_RES_ERR = 1,
//TODO
};
void smfi_init(void) { void smfi_init(void) {
int i; int i;
// Clear command region // Clear command region
for (i = 0; i < ARRAY_SIZE(smfi_cmd); i++) { for (i = 1; i < ARRAY_SIZE(smfi_cmd); i++) {
smfi_cmd[i] = 0x00; smfi_cmd[i] = 0x00;
} }
// Clear host command last
smfi_cmd[0] = 0x00;
// Clear debug region // Clear debug region
for (i = 0; i < ARRAY_SIZE(smfi_dbg); i++) { for (i = 1; i < ARRAY_SIZE(smfi_dbg); i++) {
smfi_dbg[i] = 0x00; smfi_dbg[i] = 0x00;
} }
// Clear tail last
smfi_dbg[0] = 0x00;
// H2RAM window 0 address 0xC00 - 0xCFF, read/write // H2RAM window 0 address 0xE00 - 0xEFF, read/write
HRAMW0BA = 0xC0; HRAMW0BA = 0xE0;
HRAMW0AAS = 0x04; HRAMW0AAS = 0x04;
// H2RAM window 1 address 0xD00 - 0xDFF, read/write // H2RAM window 1 address 0xF00 - 0xFFF, read/write
HRAMW1BA = 0xD0; HRAMW1BA = 0xF0;
HRAMW1AAS = 0x04; HRAMW1AAS = 0x04;
// Enable H2RAM window 0 and 1 using LPC I/O // Enable H2RAM window 0 and 1 using LPC I/O
HRAMWC |= 0x13; HRAMWC |= 0x13;
} }
void smfi_event(void) { static enum Result cmd_debug(void) {
int i; int i;
if (smfi_cmd[0]) { for (i = 2; i < ARRAY_SIZE(smfi_cmd); i++) {
// Default to success uint8_t b = smfi_cmd[i];
smfi_cmd[1] = SMFI_RES_OK; if (b == 0) break;
putchar(b);
}
return RES_OK;
}
static enum Result cmd_spi(void) {
uint8_t flags = smfi_cmd[2];
#ifdef __SCRATCH__
int len = (int)smfi_cmd[3];
// Enable chip (internal)
ECINDAR3 = 0x7F;
ECINDAR2 = 0xFF;
ECINDAR1 = 0xFD;
ECINDAR0 = 0x00;
// Read or write len bytes
int i;
for (i = 0; (i < len) && ((i + 4) < ARRAY_SIZE(smfi_cmd)); i++) {
if (flags & CMD_SPI_FLAG_READ) {
smfi_cmd[i + 4] = ECINDDR;
} else {
ECINDDR = smfi_cmd[i + 4];
}
}
// Set actually read/written count
smfi_cmd[3] = (uint8_t)i;
if (flags & CMD_SPI_FLAG_DISABLE) {
// Disable chip
ECINDAR1 = 0xFE;
ECINDDR = 0;
}
return RES_OK;
#else
if (flags & CMD_SPI_FLAG_SCRATCH) {
scratch_trampoline();
}
// Cannot use SPI bus while running EC from SPI, or trampoline failed
return RES_ERR;
#endif
}
static enum Result cmd_reset(void) {
// Attempt to trigger watchdog reset
ETWCFG |= (1 << 5);
EWDKEYR = 0;
// Failed if it got this far
return RES_ERR;
}
void smfi_event(void) {
if (smfi_cmd[0]) {
switch (smfi_cmd[0]) { switch (smfi_cmd[0]) {
case SMFI_CMD_PROBE: case CMD_PROBE:
// Signature // Signature
smfi_cmd[2] = 0x76; smfi_cmd[2] = 0x76;
smfi_cmd[3] = 0xEC; smfi_cmd[3] = 0xEC;
// Version // Version
smfi_cmd[4] = 0x01; smfi_cmd[4] = 0x01;
// Always successful
smfi_cmd[1] = RES_OK;
break; break;
case SMFI_CMD_BOARD: case CMD_BOARD:
strncpy(&smfi_cmd[2], board(), ARRAY_SIZE(smfi_cmd) - 2); strncpy(&smfi_cmd[2], board(), ARRAY_SIZE(smfi_cmd) - 2);
// Always successful
smfi_cmd[1] = RES_OK;
break; break;
case SMFI_CMD_VERSION: case CMD_VERSION:
strncpy(&smfi_cmd[2], version(), ARRAY_SIZE(smfi_cmd) - 2); strncpy(&smfi_cmd[2], version(), ARRAY_SIZE(smfi_cmd) - 2);
// Always successful
smfi_cmd[1] = RES_OK;
break;
case CMD_DEBUG:
smfi_cmd[1] = cmd_debug();
break;
case CMD_SPI:
smfi_cmd[1] = cmd_spi();
break;
case CMD_RESET:
smfi_cmd[1] = cmd_reset();
break; break;
case SMFI_CMD_DEBUG:
for (i = 2; i < ARRAY_SIZE(smfi_cmd) - 2; i++) {
uint8_t b = smfi_cmd[i];
if (b == 0) break;
putchar(b);
}
default: default:
// Command not found // Command not found
smfi_cmd[1] = SMFI_RES_ERR; smfi_cmd[1] = RES_ERR;
break; break;
} }
// Mark command as finished // Mark command as finished
smfi_cmd[0] = SMFI_CMD_NONE; smfi_cmd[0] = CMD_NONE;
} }
} }

View File

@ -24,11 +24,15 @@ SCRATCH_OFFSET=1024
SCRATCH_SIZE=1024 SCRATCH_SIZE=1024
CFLAGS+=-DSCRATCH_OFFSET=$(SCRATCH_OFFSET) -DSCRATCH_SIZE=$(SCRATCH_SIZE) CFLAGS+=-DSCRATCH_OFFSET=$(SCRATCH_OFFSET) -DSCRATCH_SIZE=$(SCRATCH_SIZE)
# Copy parameters to use when compiling scratch ROM
SCRATCH_INCLUDE=$(INCLUDE)
SCRATCH_CFLAGS=$(CFLAGS)
# Add scratch ROM source # Add scratch ROM source
SCRATCH_DIR=$(BOARD_DIR)/scratch SCRATCH_DIR=$(BOARD_DIR)/scratch
SCRATCH_SRC=$(wildcard $(SCRATCH_DIR)/*.c) SCRATCH_SRC=$(wildcard $(SCRATCH_DIR)/*.c)
SCRATCH_INCLUDE=$(wildcard $(SCRATCH_DIR)/include/scratch/*.h) $(SCRATCH_DIR)/scratch.mk SCRATCH_INCLUDE+=$(wildcard $(SCRATCH_DIR)/include/scratch/*.h) $(SCRATCH_DIR)/scratch.mk
SCRATCH_CFLAGS=-I$(SCRATCH_DIR)/include SCRATCH_CFLAGS+=-I$(SCRATCH_DIR)/include -D__SCRATCH__
include $(SCRATCH_DIR)/scratch.mk include $(SCRATCH_DIR)/scratch.mk
# Include scratch header in main firmware # Include scratch header in main firmware
@ -40,8 +44,8 @@ console:
sudo tool/target/release/system76_ectool console sudo tool/target/release/system76_ectool console
flash: $(BUILD)/ec.rom flash: $(BUILD)/ec.rom
cargo build --manifest-path ecflash/Cargo.toml --example isp --release cargo build --manifest-path tool/Cargo.toml --release
sudo ecflash/target/release/examples/isp --internal $< sudo tool/target/release/system76_ectool flash $<
isp: $(BUILD)/ec.rom isp: $(BUILD)/ec.rom
cargo build --manifest-path ecflash/Cargo.toml --example isp --release cargo build --manifest-path ecflash/Cargo.toml --example isp --release

View File

@ -2,7 +2,6 @@
#include <board/acpi.h> #include <board/acpi.h>
#include <board/gpio.h> #include <board/gpio.h>
#include <board/pmc.h> #include <board/pmc.h>
#include <board/scratch.h>
#include <common/debug.h> #include <common/debug.h>
void pmc_init(void) { void pmc_init(void) {
@ -102,12 +101,6 @@ void pmc_event(struct Pmc * pmc) {
// Clear SCI queue // Clear SCI queue
pmc_sci_queue = 0; pmc_sci_queue = 0;
break; break;
case 0xEC:
TRACE(" scratch rom\n");
pmc_write(pmc, 0x76);
scratch_trampoline();
break;
} }
} else { } else {
TRACE("pmc data: %02X\n", data); TRACE("pmc data: %02X\n", data);

View File

@ -28,10 +28,6 @@ void pnp_enable() {
// Enable PMC1 // Enable PMC1
pnp_write(0x07, 0x11); pnp_write(0x07, 0x11);
pnp_write(0x30, 0x01); pnp_write(0x30, 0x01);
//
// Enable PMC3
pnp_write(0x07, 0x17);
pnp_write(0x30, 0x01);
// Enable KBC keyboard // Enable KBC keyboard
pnp_write(0x07, 0x06); pnp_write(0x07, 0x06);

View File

@ -1,53 +0,0 @@
#ifndef _EC_PMC_H
#define _EC_PMC_H
#include <stdbool.h>
#include <stdint.h>
struct Pmc {
// Status register
volatile uint8_t * status;
// Data out register
volatile uint8_t * data_out;
// Data in register
volatile uint8_t * data_in;
// Control register
volatile uint8_t * control;
};
extern struct Pmc __code PMC_3;
#define PMC_STS_OBF (1 << 0)
#define PMC_STS_IBF (1 << 1)
#define PMC_STS_CMD (1 << 3)
uint8_t pmc_status(struct Pmc * pmc);
uint8_t pmc_read(struct Pmc * pmc);
bool pmc_write(struct Pmc * pmc, uint8_t data);
volatile uint8_t __xdata __at(0x1500) PM1STS;
volatile uint8_t __xdata __at(0x1501) PM1DO;
volatile uint8_t __xdata __at(0x1504) PM1DI;
volatile uint8_t __xdata __at(0x1506) PM1CTL;
volatile uint8_t __xdata __at(0x1510) PM2STS;
volatile uint8_t __xdata __at(0x1511) PM2DO;
volatile uint8_t __xdata __at(0x1514) PM2DI;
volatile uint8_t __xdata __at(0x1516) PM2CTL;
volatile uint8_t __xdata __at(0x1520) PM3STS;
volatile uint8_t __xdata __at(0x1521) PM3DO;
volatile uint8_t __xdata __at(0x1522) PM3DI;
volatile uint8_t __xdata __at(0x1523) PM3CTL;
volatile uint8_t __xdata __at(0x1530) PM4STS;
volatile uint8_t __xdata __at(0x1531) PM4DO;
volatile uint8_t __xdata __at(0x1532) PM4DI;
volatile uint8_t __xdata __at(0x1533) PM4CTL;
volatile uint8_t __xdata __at(0x1540) PM5STS;
volatile uint8_t __xdata __at(0x1541) PM5DO;
volatile uint8_t __xdata __at(0x1542) PM5DI;
volatile uint8_t __xdata __at(0x1543) PM5CTL;
#endif // _EC_PMC_H

View File

@ -1,130 +1,8 @@
#include <stdbool.h> #include <board/smfi.h>
#include <stdint.h>
#include <scratch/pmc.h>
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;
volatile uint8_t __xdata __at(0x1F01) ETWCFG;
volatile uint8_t __xdata __at(0x1F07) EWDKEYR;
uint8_t acpi_read(uint8_t addr) {
uint8_t data = 0;
switch (addr) {
case 4:
data = ECINDAR0;
break;
case 5:
data = ECINDAR1;
break;
case 6:
data = ECINDAR2;
break;
case 7:
data = ECINDAR3;
break;
case 8:
data = ECINDDR;
break;
}
return data;
}
void acpi_write(uint8_t addr, uint8_t data) {
switch (addr) {
case 4:
ECINDAR0 = data;
break;
case 5:
ECINDAR1 = data;
break;
case 6:
ECINDAR2 = data;
break;
case 7:
ECINDAR3 = data;
break;
case 8:
ECINDDR = data;
break;
}
}
enum PmcState {
PMC_STATE_DEFAULT,
PMC_STATE_WRITE,
PMC_STATE_ACPI_READ,
PMC_STATE_ACPI_WRITE,
PMC_STATE_ACPI_WRITE_ADDR,
};
void pmc_event(struct Pmc * pmc) {
static enum PmcState state = PMC_STATE_DEFAULT;
static uint8_t state_data = 0;
uint8_t sts = pmc_status(pmc);
// Read command/data if available
if (sts & PMC_STS_IBF) {
uint8_t data = pmc_read(pmc);
if (sts & PMC_STS_CMD) {
state = PMC_STATE_DEFAULT;
switch (data) {
case 0x80:
state = PMC_STATE_ACPI_READ;
break;
case 0x81:
state = PMC_STATE_ACPI_WRITE;
break;
case 0xEC:
for (;;) {
// Attempt to trigger watchdog reset
ETWCFG |= (1 << 5);
EWDKEYR = 0;
}
break;
}
} else {
switch (state) {
case PMC_STATE_ACPI_READ:
state = PMC_STATE_WRITE;
state_data = acpi_read(data);
break;
case PMC_STATE_ACPI_WRITE:
state = PMC_STATE_ACPI_WRITE_ADDR;
state_data = data;
break;
case PMC_STATE_ACPI_WRITE_ADDR:
state = PMC_STATE_DEFAULT;
acpi_write(state_data, data);
break;
default:
state = PMC_STATE_DEFAULT;
break;
}
}
}
// Write data if possible
if (!(sts & PMC_STS_OBF)) {
switch (state) {
case PMC_STATE_WRITE:
state = PMC_STATE_DEFAULT;
pmc_write(pmc, state_data);
break;
}
}
}
// Main program while running in scratch ROM // Main program while running in scratch ROM
void main(void) { void main(void) {
for (;;) { for (;;) {
pmc_event(&PMC_3); smfi_event();
} }
} }

View File

@ -1,23 +0,0 @@
#include <scratch/pmc.h>
#define PMC(NUM) { \
.status = &PM ## NUM ## STS, \
.data_out = &PM ## NUM ## DO, \
.data_in = &PM ## NUM ## DI, \
.control = &PM ## NUM ## CTL, \
}
struct Pmc __code PMC_3 = PMC(3);
uint8_t pmc_status(struct Pmc * pmc) {
return *(pmc->status);
}
uint8_t pmc_read(struct Pmc * pmc) {
return *(pmc->data_in);
}
bool pmc_write(struct Pmc * pmc, uint8_t data) {
*(pmc->data_out) = data;
return true;
}

View File

@ -1,3 +1,7 @@
SCRATCH_SRC+=\
$(COMMON_DIR)/version.c \
$(BOARD_DIR)/smfi.c
SCRATCH_BUILD=$(BUILD)/scratch SCRATCH_BUILD=$(BUILD)/scratch
SCRATCH_OBJ=$(patsubst src/%.c,$(SCRATCH_BUILD)/%.rel,$(SCRATCH_SRC)) SCRATCH_OBJ=$(patsubst src/%.c,$(SCRATCH_BUILD)/%.rel,$(SCRATCH_SRC))
SCRATCH_CC=\ SCRATCH_CC=\

View File

@ -0,0 +1,9 @@
#include <stdio.h>
#include <board/smfi.h>
int putchar(int c) {
unsigned char byte = (unsigned char)c;
smfi_debug(byte);
return (int)byte;
}

View File

@ -2,7 +2,11 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#ifndef __SCRATCH__
#include <board/scratch.h>
#endif
#include <board/smfi.h> #include <board/smfi.h>
#include <common/command.h>
#include <common/macro.h> #include <common/macro.h>
#include <common/version.h> #include <common/version.h>
@ -19,84 +23,149 @@ volatile uint8_t __xdata __at(0x105D) HRAMW0AAS;
// Host RAM window 1 access allow size // Host RAM window 1 access allow size
volatile uint8_t __xdata __at(0x105E) HRAMW1AAS; volatile uint8_t __xdata __at(0x105E) HRAMW1AAS;
static volatile uint8_t __xdata __at(0xC00) smfi_cmd[256]; volatile uint8_t __xdata __at(0x103B) ECINDAR0;
static volatile uint8_t __xdata __at(0xD00) smfi_dbg[256]; 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;
enum SmfiCmd { volatile uint8_t __xdata __at(0x1F01) ETWCFG;
SMFI_CMD_NONE = 0, volatile uint8_t __xdata __at(0x1F07) EWDKEYR;
SMFI_CMD_PROBE = 1,
SMFI_CMD_BOARD = 2,
SMFI_CMD_VERSION = 3,
SMFI_CMD_DEBUG = 4,
//TODO
};
enum SmfiRes { static volatile uint8_t __xdata __at(0xE00) smfi_cmd[256];
SMFI_RES_OK = 0, static volatile uint8_t __xdata __at(0xF00) smfi_dbg[256];
SMFI_RES_ERR = 1,
//TODO
};
void smfi_init(void) { void smfi_init(void) {
int i; int i;
// Clear command region // Clear command region
for (i = 0; i < ARRAY_SIZE(smfi_cmd); i++) { for (i = 1; i < ARRAY_SIZE(smfi_cmd); i++) {
smfi_cmd[i] = 0x00; smfi_cmd[i] = 0x00;
} }
// Clear host command last
smfi_cmd[0] = 0x00;
// Clear debug region // Clear debug region
for (i = 0; i < ARRAY_SIZE(smfi_dbg); i++) { for (i = 1; i < ARRAY_SIZE(smfi_dbg); i++) {
smfi_dbg[i] = 0x00; smfi_dbg[i] = 0x00;
} }
// Clear tail last
smfi_dbg[0] = 0x00;
// H2RAM window 0 address 0xC00 - 0xCFF, read/write // H2RAM window 0 address 0xE00 - 0xEFF, read/write
HRAMW0BA = 0xC0; HRAMW0BA = 0xE0;
HRAMW0AAS = 0x04; HRAMW0AAS = 0x04;
// H2RAM window 1 address 0xD00 - 0xDFF, read/write // H2RAM window 1 address 0xF00 - 0xFFF, read/write
HRAMW1BA = 0xD0; HRAMW1BA = 0xF0;
HRAMW1AAS = 0x04; HRAMW1AAS = 0x04;
// Enable H2RAM window 0 and 1 using LPC I/O // Enable H2RAM window 0 and 1 using LPC I/O
HRAMWC |= 0x13; HRAMWC |= 0x13;
} }
void smfi_event(void) { static enum Result cmd_debug(void) {
int i; int i;
if (smfi_cmd[0]) { for (i = 2; i < ARRAY_SIZE(smfi_cmd); i++) {
// Default to success uint8_t b = smfi_cmd[i];
smfi_cmd[1] = SMFI_RES_OK; if (b == 0) break;
putchar(b);
}
return RES_OK;
}
static enum Result cmd_spi(void) {
uint8_t flags = smfi_cmd[2];
#ifdef __SCRATCH__
uint8_t len = smfi_cmd[3];
// Enable chip (internal)
ECINDAR3 = 0x7F;
ECINDAR2 = 0xFF;
ECINDAR1 = 0xFD;
ECINDAR0 = 0x00;
// Read or write len bytes
uint8_t i;
for (i = 0; (i < len) && ((i + 4) < ARRAY_SIZE(smfi_cmd)); i++) {
if (flags & CMD_SPI_FLAG_READ) {
smfi_cmd[i + 4] = ECINDDR;
} else {
ECINDDR = smfi_cmd[i + 4];
}
}
// Set actually read/written count
smfi_cmd[3] = i;
if (flags & CMD_SPI_FLAG_DISABLE) {
// Disable chip
ECINDAR1 = 0xFE;
ECINDDR = 0;
}
return RES_OK;
#else
if (flags & CMD_SPI_FLAG_SCRATCH) {
scratch_trampoline();
}
// Cannot use SPI bus while running EC from SPI, or trampoline failed
return RES_ERR;
#endif
}
static enum Result cmd_reset(void) {
// Attempt to trigger watchdog reset
ETWCFG |= (1 << 5);
EWDKEYR = 0;
// Failed if it got this far
return RES_ERR;
}
void smfi_event(void) {
if (smfi_cmd[0]) {
switch (smfi_cmd[0]) { switch (smfi_cmd[0]) {
case SMFI_CMD_PROBE: case CMD_PROBE:
// Signature // Signature
smfi_cmd[2] = 0x76; smfi_cmd[2] = 0x76;
smfi_cmd[3] = 0xEC; smfi_cmd[3] = 0xEC;
// Version // Version
smfi_cmd[4] = 0x01; smfi_cmd[4] = 0x01;
// Always successful
smfi_cmd[1] = RES_OK;
break; break;
case SMFI_CMD_BOARD: case CMD_BOARD:
strncpy(&smfi_cmd[2], board(), ARRAY_SIZE(smfi_cmd) - 2); strncpy(&smfi_cmd[2], board(), ARRAY_SIZE(smfi_cmd) - 2);
// Always successful
smfi_cmd[1] = RES_OK;
break; break;
case SMFI_CMD_VERSION: case CMD_VERSION:
strncpy(&smfi_cmd[2], version(), ARRAY_SIZE(smfi_cmd) - 2); strncpy(&smfi_cmd[2], version(), ARRAY_SIZE(smfi_cmd) - 2);
// Always successful
smfi_cmd[1] = RES_OK;
break;
case CMD_DEBUG:
smfi_cmd[1] = cmd_debug();
break;
case CMD_SPI:
smfi_cmd[1] = cmd_spi();
break;
case CMD_RESET:
smfi_cmd[1] = cmd_reset();
break; break;
case SMFI_CMD_DEBUG:
for (i = 2; i < ARRAY_SIZE(smfi_cmd) - 2; i++) {
uint8_t b = smfi_cmd[i];
if (b == 0) break;
putchar(b);
}
default: default:
// Command not found // Command not found
smfi_cmd[1] = SMFI_RES_ERR; smfi_cmd[1] = RES_ERR;
break; break;
} }
// Mark command as finished // Mark command as finished
smfi_cmd[0] = SMFI_CMD_NONE; smfi_cmd[0] = CMD_NONE;
} }
} }

View File

@ -27,11 +27,15 @@ SCRATCH_OFFSET=1024
SCRATCH_SIZE=1024 SCRATCH_SIZE=1024
CFLAGS+=-DSCRATCH_OFFSET=$(SCRATCH_OFFSET) -DSCRATCH_SIZE=$(SCRATCH_SIZE) CFLAGS+=-DSCRATCH_OFFSET=$(SCRATCH_OFFSET) -DSCRATCH_SIZE=$(SCRATCH_SIZE)
# Copy parameters to use when compiling scratch ROM
SCRATCH_INCLUDE=$(INCLUDE)
SCRATCH_CFLAGS=$(CFLAGS)
# Add scratch ROM source # Add scratch ROM source
SCRATCH_DIR=$(BOARD_DIR)/scratch SCRATCH_DIR=$(BOARD_DIR)/scratch
SCRATCH_SRC=$(wildcard $(SCRATCH_DIR)/*.c) SCRATCH_SRC=$(wildcard $(SCRATCH_DIR)/*.c)
SCRATCH_INCLUDE=$(wildcard $(SCRATCH_DIR)/include/scratch/*.h) $(SCRATCH_DIR)/scratch.mk SCRATCH_INCLUDE+=$(wildcard $(SCRATCH_DIR)/include/scratch/*.h) $(SCRATCH_DIR)/scratch.mk
SCRATCH_CFLAGS=-I$(SCRATCH_DIR)/include SCRATCH_CFLAGS+=-I$(SCRATCH_DIR)/include -D__SCRATCH__
include $(SCRATCH_DIR)/scratch.mk include $(SCRATCH_DIR)/scratch.mk
# Include scratch header in main firmware # Include scratch header in main firmware
@ -43,8 +47,8 @@ console:
sudo tool/target/release/system76_ectool console sudo tool/target/release/system76_ectool console
flash: $(BUILD)/ec.rom flash: $(BUILD)/ec.rom
cargo build --manifest-path ecflash/Cargo.toml --example isp --release cargo build --manifest-path tool/Cargo.toml --release
sudo ecflash/target/release/examples/isp --internal $< sudo tool/target/release/system76_ectool flash $<
isp: $(BUILD)/ec.rom isp: $(BUILD)/ec.rom
cargo build --manifest-path ecflash/Cargo.toml --example isp --release cargo build --manifest-path ecflash/Cargo.toml --example isp --release

View File

@ -2,7 +2,6 @@
#include <board/acpi.h> #include <board/acpi.h>
#include <board/gpio.h> #include <board/gpio.h>
#include <board/pmc.h> #include <board/pmc.h>
#include <board/scratch.h>
#include <common/debug.h> #include <common/debug.h>
void pmc_init(void) { void pmc_init(void) {
@ -102,12 +101,6 @@ void pmc_event(struct Pmc * pmc) {
// Clear SCI queue // Clear SCI queue
pmc_sci_queue = 0; pmc_sci_queue = 0;
break; break;
case 0xEC:
TRACE(" scratch rom\n");
pmc_write(pmc, 0x76);
scratch_trampoline();
break;
} }
} else { } else {
TRACE("pmc data: %02X\n", data); TRACE("pmc data: %02X\n", data);

View File

@ -28,10 +28,6 @@ void pnp_enable() {
// Enable PMC1 // Enable PMC1
pnp_write(0x07, 0x11); pnp_write(0x07, 0x11);
pnp_write(0x30, 0x01); pnp_write(0x30, 0x01);
//
// Enable PMC3
pnp_write(0x07, 0x17);
pnp_write(0x30, 0x01);
// Enable KBC keyboard // Enable KBC keyboard
pnp_write(0x07, 0x06); pnp_write(0x07, 0x06);

View File

@ -1,53 +0,0 @@
#ifndef _EC_PMC_H
#define _EC_PMC_H
#include <stdbool.h>
#include <stdint.h>
struct Pmc {
// Status register
volatile uint8_t * status;
// Data out register
volatile uint8_t * data_out;
// Data in register
volatile uint8_t * data_in;
// Control register
volatile uint8_t * control;
};
extern struct Pmc __code PMC_3;
#define PMC_STS_OBF (1 << 0)
#define PMC_STS_IBF (1 << 1)
#define PMC_STS_CMD (1 << 3)
uint8_t pmc_status(struct Pmc * pmc);
uint8_t pmc_read(struct Pmc * pmc);
bool pmc_write(struct Pmc * pmc, uint8_t data);
volatile uint8_t __xdata __at(0x1500) PM1STS;
volatile uint8_t __xdata __at(0x1501) PM1DO;
volatile uint8_t __xdata __at(0x1504) PM1DI;
volatile uint8_t __xdata __at(0x1506) PM1CTL;
volatile uint8_t __xdata __at(0x1510) PM2STS;
volatile uint8_t __xdata __at(0x1511) PM2DO;
volatile uint8_t __xdata __at(0x1514) PM2DI;
volatile uint8_t __xdata __at(0x1516) PM2CTL;
volatile uint8_t __xdata __at(0x1520) PM3STS;
volatile uint8_t __xdata __at(0x1521) PM3DO;
volatile uint8_t __xdata __at(0x1522) PM3DI;
volatile uint8_t __xdata __at(0x1523) PM3CTL;
volatile uint8_t __xdata __at(0x1530) PM4STS;
volatile uint8_t __xdata __at(0x1531) PM4DO;
volatile uint8_t __xdata __at(0x1532) PM4DI;
volatile uint8_t __xdata __at(0x1533) PM4CTL;
volatile uint8_t __xdata __at(0x1540) PM5STS;
volatile uint8_t __xdata __at(0x1541) PM5DO;
volatile uint8_t __xdata __at(0x1542) PM5DI;
volatile uint8_t __xdata __at(0x1543) PM5CTL;
#endif // _EC_PMC_H

View File

@ -1,130 +1,8 @@
#include <stdbool.h> #include <board/smfi.h>
#include <stdint.h>
#include <scratch/pmc.h>
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;
volatile uint8_t __xdata __at(0x1F01) ETWCFG;
volatile uint8_t __xdata __at(0x1F07) EWDKEYR;
uint8_t acpi_read(uint8_t addr) {
uint8_t data = 0;
switch (addr) {
case 4:
data = ECINDAR0;
break;
case 5:
data = ECINDAR1;
break;
case 6:
data = ECINDAR2;
break;
case 7:
data = ECINDAR3;
break;
case 8:
data = ECINDDR;
break;
}
return data;
}
void acpi_write(uint8_t addr, uint8_t data) {
switch (addr) {
case 4:
ECINDAR0 = data;
break;
case 5:
ECINDAR1 = data;
break;
case 6:
ECINDAR2 = data;
break;
case 7:
ECINDAR3 = data;
break;
case 8:
ECINDDR = data;
break;
}
}
enum PmcState {
PMC_STATE_DEFAULT,
PMC_STATE_WRITE,
PMC_STATE_ACPI_READ,
PMC_STATE_ACPI_WRITE,
PMC_STATE_ACPI_WRITE_ADDR,
};
void pmc_event(struct Pmc * pmc) {
static enum PmcState state = PMC_STATE_DEFAULT;
static uint8_t state_data = 0;
uint8_t sts = pmc_status(pmc);
// Read command/data if available
if (sts & PMC_STS_IBF) {
uint8_t data = pmc_read(pmc);
if (sts & PMC_STS_CMD) {
state = PMC_STATE_DEFAULT;
switch (data) {
case 0x80:
state = PMC_STATE_ACPI_READ;
break;
case 0x81:
state = PMC_STATE_ACPI_WRITE;
break;
case 0xEC:
for (;;) {
// Attempt to trigger watchdog reset
ETWCFG |= (1 << 5);
EWDKEYR = 0;
}
break;
}
} else {
switch (state) {
case PMC_STATE_ACPI_READ:
state = PMC_STATE_WRITE;
state_data = acpi_read(data);
break;
case PMC_STATE_ACPI_WRITE:
state = PMC_STATE_ACPI_WRITE_ADDR;
state_data = data;
break;
case PMC_STATE_ACPI_WRITE_ADDR:
state = PMC_STATE_DEFAULT;
acpi_write(state_data, data);
break;
default:
state = PMC_STATE_DEFAULT;
break;
}
}
}
// Write data if possible
if (!(sts & PMC_STS_OBF)) {
switch (state) {
case PMC_STATE_WRITE:
state = PMC_STATE_DEFAULT;
pmc_write(pmc, state_data);
break;
}
}
}
// Main program while running in scratch ROM // Main program while running in scratch ROM
void main(void) { void main(void) {
for (;;) { for (;;) {
pmc_event(&PMC_3); smfi_event();
} }
} }

View File

@ -1,23 +0,0 @@
#include <scratch/pmc.h>
#define PMC(NUM) { \
.status = &PM ## NUM ## STS, \
.data_out = &PM ## NUM ## DO, \
.data_in = &PM ## NUM ## DI, \
.control = &PM ## NUM ## CTL, \
}
struct Pmc __code PMC_3 = PMC(3);
uint8_t pmc_status(struct Pmc * pmc) {
return *(pmc->status);
}
uint8_t pmc_read(struct Pmc * pmc) {
return *(pmc->data_in);
}
bool pmc_write(struct Pmc * pmc, uint8_t data) {
*(pmc->data_out) = data;
return true;
}

View File

@ -1,3 +1,7 @@
SCRATCH_SRC+=\
$(COMMON_DIR)/version.c \
$(BOARD_DIR)/smfi.c
SCRATCH_BUILD=$(BUILD)/scratch SCRATCH_BUILD=$(BUILD)/scratch
SCRATCH_OBJ=$(patsubst src/%.c,$(SCRATCH_BUILD)/%.rel,$(SCRATCH_SRC)) SCRATCH_OBJ=$(patsubst src/%.c,$(SCRATCH_BUILD)/%.rel,$(SCRATCH_SRC))
SCRATCH_CC=\ SCRATCH_CC=\

View File

@ -0,0 +1,9 @@
#include <stdio.h>
#include <board/smfi.h>
int putchar(int c) {
unsigned char byte = (unsigned char)c;
smfi_debug(byte);
return (int)byte;
}

View File

@ -2,7 +2,11 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#ifndef __SCRATCH__
#include <board/scratch.h>
#endif
#include <board/smfi.h> #include <board/smfi.h>
#include <common/command.h>
#include <common/macro.h> #include <common/macro.h>
#include <common/version.h> #include <common/version.h>
@ -19,84 +23,149 @@ volatile uint8_t __xdata __at(0x105D) HRAMW0AAS;
// Host RAM window 1 access allow size // Host RAM window 1 access allow size
volatile uint8_t __xdata __at(0x105E) HRAMW1AAS; volatile uint8_t __xdata __at(0x105E) HRAMW1AAS;
static volatile uint8_t __xdata __at(0xC00) smfi_cmd[256]; volatile uint8_t __xdata __at(0x103B) ECINDAR0;
static volatile uint8_t __xdata __at(0xD00) smfi_dbg[256]; 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;
enum SmfiCmd { volatile uint8_t __xdata __at(0x1F01) ETWCFG;
SMFI_CMD_NONE = 0, volatile uint8_t __xdata __at(0x1F07) EWDKEYR;
SMFI_CMD_PROBE = 1,
SMFI_CMD_BOARD = 2,
SMFI_CMD_VERSION = 3,
SMFI_CMD_DEBUG = 4,
//TODO
};
enum SmfiRes { static volatile uint8_t __xdata __at(0xE00) smfi_cmd[256];
SMFI_RES_OK = 0, static volatile uint8_t __xdata __at(0xF00) smfi_dbg[256];
SMFI_RES_ERR = 1,
//TODO
};
void smfi_init(void) { void smfi_init(void) {
int i; int i;
// Clear command region // Clear command region
for (i = 0; i < ARRAY_SIZE(smfi_cmd); i++) { for (i = 1; i < ARRAY_SIZE(smfi_cmd); i++) {
smfi_cmd[i] = 0x00; smfi_cmd[i] = 0x00;
} }
// Clear host command last
smfi_cmd[0] = 0x00;
// Clear debug region // Clear debug region
for (i = 0; i < ARRAY_SIZE(smfi_dbg); i++) { for (i = 1; i < ARRAY_SIZE(smfi_dbg); i++) {
smfi_dbg[i] = 0x00; smfi_dbg[i] = 0x00;
} }
// Clear tail last
smfi_dbg[0] = 0x00;
// H2RAM window 0 address 0xC00 - 0xCFF, read/write // H2RAM window 0 address 0xE00 - 0xEFF, read/write
HRAMW0BA = 0xC0; HRAMW0BA = 0xE0;
HRAMW0AAS = 0x04; HRAMW0AAS = 0x04;
// H2RAM window 1 address 0xD00 - 0xDFF, read/write // H2RAM window 1 address 0xF00 - 0xFFF, read/write
HRAMW1BA = 0xD0; HRAMW1BA = 0xF0;
HRAMW1AAS = 0x04; HRAMW1AAS = 0x04;
// Enable H2RAM window 0 and 1 using LPC I/O // Enable H2RAM window 0 and 1 using LPC I/O
HRAMWC |= 0x13; HRAMWC |= 0x13;
} }
void smfi_event(void) { static enum Result cmd_debug(void) {
int i; int i;
if (smfi_cmd[0]) { for (i = 2; i < ARRAY_SIZE(smfi_cmd); i++) {
// Default to success uint8_t b = smfi_cmd[i];
smfi_cmd[1] = SMFI_RES_OK; if (b == 0) break;
putchar(b);
}
return RES_OK;
}
static enum Result cmd_spi(void) {
uint8_t flags = smfi_cmd[2];
#ifdef __SCRATCH__
int len = (int)smfi_cmd[3];
// Enable chip (internal)
ECINDAR3 = 0x7F;
ECINDAR2 = 0xFF;
ECINDAR1 = 0xFD;
ECINDAR0 = 0x00;
// Read or write len bytes
int i;
for (i = 0; (i < len) && ((i + 4) < ARRAY_SIZE(smfi_cmd)); i++) {
if (flags & CMD_SPI_FLAG_READ) {
smfi_cmd[i + 4] = ECINDDR;
} else {
ECINDDR = smfi_cmd[i + 4];
}
}
// Set actually read/written count
smfi_cmd[3] = (uint8_t)i;
if (flags & CMD_SPI_FLAG_DISABLE) {
// Disable chip
ECINDAR1 = 0xFE;
ECINDDR = 0;
}
return RES_OK;
#else
if (flags & CMD_SPI_FLAG_SCRATCH) {
scratch_trampoline();
}
// Cannot use SPI bus while running EC from SPI, or trampoline failed
return RES_ERR;
#endif
}
static enum Result cmd_reset(void) {
// Attempt to trigger watchdog reset
ETWCFG |= (1 << 5);
EWDKEYR = 0;
// Failed if it got this far
return RES_ERR;
}
void smfi_event(void) {
if (smfi_cmd[0]) {
switch (smfi_cmd[0]) { switch (smfi_cmd[0]) {
case SMFI_CMD_PROBE: case CMD_PROBE:
// Signature // Signature
smfi_cmd[2] = 0x76; smfi_cmd[2] = 0x76;
smfi_cmd[3] = 0xEC; smfi_cmd[3] = 0xEC;
// Version // Version
smfi_cmd[4] = 0x01; smfi_cmd[4] = 0x01;
// Always successful
smfi_cmd[1] = RES_OK;
break; break;
case SMFI_CMD_BOARD: case CMD_BOARD:
strncpy(&smfi_cmd[2], board(), ARRAY_SIZE(smfi_cmd) - 2); strncpy(&smfi_cmd[2], board(), ARRAY_SIZE(smfi_cmd) - 2);
// Always successful
smfi_cmd[1] = RES_OK;
break; break;
case SMFI_CMD_VERSION: case CMD_VERSION:
strncpy(&smfi_cmd[2], version(), ARRAY_SIZE(smfi_cmd) - 2); strncpy(&smfi_cmd[2], version(), ARRAY_SIZE(smfi_cmd) - 2);
// Always successful
smfi_cmd[1] = RES_OK;
break;
case CMD_DEBUG:
smfi_cmd[1] = cmd_debug();
break;
case CMD_SPI:
smfi_cmd[1] = cmd_spi();
break;
case CMD_RESET:
smfi_cmd[1] = cmd_reset();
break; break;
case SMFI_CMD_DEBUG:
for (i = 2; i < ARRAY_SIZE(smfi_cmd) - 2; i++) {
uint8_t b = smfi_cmd[i];
if (b == 0) break;
putchar(b);
}
default: default:
// Command not found // Command not found
smfi_cmd[1] = SMFI_RES_ERR; smfi_cmd[1] = RES_ERR;
break; break;
} }
// Mark command as finished // Mark command as finished
smfi_cmd[0] = SMFI_CMD_NONE; smfi_cmd[0] = CMD_NONE;
} }
} }

View File

@ -0,0 +1,39 @@
#ifndef _COMMON_COMMAND_H
#define _COMMON_COMMAND_H
enum Command {
// Indicates that EC is ready to accept commands
CMD_NONE = 0,
// Probe for System76 EC protocol
CMD_PROBE = 1,
// Read board string
CMD_BOARD = 2,
// Read version string
CMD_VERSION = 3,
// Write bytes to console
CMD_DEBUG = 4,
// Access SPI chip
CMD_SPI = 5,
// Reset EC
CMD_RESET = 6,
//TODO
};
enum Result {
// Command executed successfully
RES_OK = 0,
// Command failed with generic error
RES_ERR = 1,
//TODO
};
enum CommandSpiFlag {
// Read from SPI chip if set, write otherwise
CMD_SPI_FLAG_READ = (1 << 0),
// Disable SPI chip after executing command
CMD_SPI_FLAG_DISABLE = (1 << 1),
// Run firmware from scratch RAM if necessary
CMD_SPI_FLAG_SCRATCH = (1 << 2),
};
#endif // _COMMON_COMMAND_H

View File

@ -2,6 +2,7 @@ use hwio::{Io, Pio};
use crate::{ use crate::{
Error, Error,
Spi,
SuperIo, SuperIo,
Timeout, Timeout,
timeout timeout
@ -9,13 +10,20 @@ use crate::{
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[repr(u8)] #[repr(u8)]
pub enum EcCmd { pub enum Cmd {
None = 0, None = 0,
Probe = 1, Probe = 1,
Board = 2, Board = 2,
Version = 3, Version = 3,
Debug = 4,
Spi = 5,
Reset = 6,
} }
pub const CMD_SPI_FLAG_READ: u8 = (1 << 0);
pub const CMD_SPI_FLAG_DISABLE: u8 = (1 << 1);
pub const CMD_SPI_FLAG_SCRATCH: u8 = (1 << 2);
pub struct Ec<T: Timeout> { pub struct Ec<T: Timeout> {
cmd: u16, cmd: u16,
dbg: u16, dbg: u16,
@ -24,8 +32,8 @@ pub struct Ec<T: Timeout> {
impl<T: Timeout> Ec<T> { impl<T: Timeout> Ec<T> {
/// Probes for a compatible EC /// Probes for a compatible EC
pub unsafe fn new(primary: bool, timeout: T) -> Result<Self, Error> { pub unsafe fn new(timeout: T) -> Result<Self, Error> {
let mut sio = SuperIo::new(if primary { 0x2E } else { 0x4E }); let mut sio = SuperIo::new(0x2E);
let id = let id =
(sio.read(0x20) as u16) << 8 | (sio.read(0x20) as u16) << 8 |
@ -37,8 +45,8 @@ impl<T: Timeout> Ec<T> {
} }
let mut ec = Ec { let mut ec = Ec {
cmd: if primary { 0xC00 } else { 0xE00 }, cmd: 0xE00,
dbg: if primary { 0xD00 } else { 0xF00 }, dbg: 0xF00,
timeout, timeout,
}; };
@ -68,44 +76,39 @@ impl<T: Timeout> Ec<T> {
).read() ).read()
} }
/// Returns true if a command can be sent /// Returns Ok if a command can be sent
pub unsafe fn can_command(&mut self) -> bool { unsafe fn command_check(&mut self) -> Result<(), Error> {
self.read(0) == EcCmd::None as u8 if self.read(0) == Cmd::None as u8 {
}
/// Start an EC command
pub unsafe fn command_start(&mut self, cmd: EcCmd) -> Result<(), Error> {
if self.can_command() {
self.write(0, cmd as u8);
Ok(()) Ok(())
} else { } else {
Err(Error::WouldBlock) Err(Error::WouldBlock)
} }
} }
/// Finish an EC command /// Wait until a command can be sent
pub unsafe fn command_finish(&mut self) -> Result<(), Error> { unsafe fn command_wait(&mut self) -> Result<(), Error> {
if self.can_command() { self.timeout.reset();
timeout!(self.timeout, self.command_check())
}
/// Run an EC command
pub unsafe fn command(&mut self, cmd: Cmd) -> Result<(), Error> {
// All previous commands should be finished
self.command_check()?;
// Write command byte
self.write(0, cmd as u8);
// Wait for command to finish
self.command_wait()?;
// Read response byte and test for error
match self.read(1) { match self.read(1) {
0 => Ok(()), 0 => Ok(()),
err => Err(Error::Protocol(err)), err => Err(Error::Protocol(err)),
} }
} else {
Err(Error::WouldBlock)
}
}
/// Run an EC command (start and finish)
pub unsafe fn command(&mut self, cmd: EcCmd) -> Result<(), Error> {
self.timeout.reset();
timeout!(self.timeout, self.command_start(cmd))?;
timeout!(self.timeout, self.command_finish())
} }
/// Probe for EC /// Probe for EC
pub unsafe fn probe(&mut self) -> Result<u8, Error> { pub unsafe fn probe(&mut self) -> Result<u8, Error> {
self.command(EcCmd::Probe)?; self.command(Cmd::Probe)?;
let signature = ( let signature = (
self.read(2), self.read(2),
self.read(3) self.read(3)
@ -120,7 +123,7 @@ impl<T: Timeout> Ec<T> {
/// Read board from EC /// Read board from EC
pub unsafe fn board(&mut self, data: &mut [u8]) -> Result<usize, Error> { pub unsafe fn board(&mut self, data: &mut [u8]) -> Result<usize, Error> {
self.command(EcCmd::Board)?; self.command(Cmd::Board)?;
let mut i = 0; let mut i = 0;
while i < data.len() && (i + 2) < 256 { while i < data.len() && (i + 2) < 256 {
data[i] = self.read((i + 2) as u8); data[i] = self.read((i + 2) as u8);
@ -134,7 +137,7 @@ impl<T: Timeout> Ec<T> {
/// Read version from EC /// Read version from EC
pub unsafe fn version(&mut self, data: &mut [u8]) -> Result<usize, Error> { pub unsafe fn version(&mut self, data: &mut [u8]) -> Result<usize, Error> {
self.command(EcCmd::Version)?; self.command(Cmd::Version)?;
let mut i = 0; let mut i = 0;
while i < data.len() && (i + 2) < 256 { while i < data.len() && (i + 2) < 256 {
data[i] = self.read((i + 2) as u8); data[i] = self.read((i + 2) as u8);
@ -145,4 +148,85 @@ impl<T: Timeout> Ec<T> {
} }
Ok(i) Ok(i)
} }
pub unsafe fn spi(&mut self, scratch: bool) -> Result<EcSpi<T>, Error> {
let mut spi = EcSpi {
ec: self,
scratch,
};
spi.reset()?;
Ok(spi)
}
pub unsafe fn reset(&mut self) -> Result<(), Error> {
self.command(Cmd::Reset)
}
}
pub struct EcSpi<'a, T: Timeout> {
ec: &'a mut Ec<T>,
scratch: bool,
}
impl<'a, T: Timeout> Spi for EcSpi<'a, T> {
/// Disable SPI chip, must be done before and after a transaction
unsafe fn reset(&mut self) -> Result<(), Error> {
let flags =
CMD_SPI_FLAG_DISABLE |
if self.scratch { CMD_SPI_FLAG_SCRATCH } else { 0 };
self.ec.write(2, flags);
self.ec.write(3, 0);
self.ec.command(Cmd::Spi)?;
assert_eq!(self.ec.read(3), 0);
Ok(())
}
/// SPI read
unsafe fn read(&mut self, data: &mut [u8]) -> Result<usize, Error> {
let flags =
CMD_SPI_FLAG_READ |
if self.scratch { CMD_SPI_FLAG_SCRATCH } else { 0 };
for chunk in data.chunks_mut(256 - 4) {
self.ec.write(2, flags);
self.ec.write(3, chunk.len() as u8);
self.ec.command(Cmd::Spi)?;
assert_eq!(self.ec.read(3), chunk.len() as u8);
for i in 0..chunk.len() {
chunk[i] = self.ec.read(i as u8 + 4);
}
}
Ok(data.len())
}
/// SPI write
unsafe fn write(&mut self, data: &[u8]) -> Result<usize, Error> {
let flags =
if self.scratch { CMD_SPI_FLAG_SCRATCH } else { 0 };
for chunk in data.chunks(256 - 4) {
for i in 0..chunk.len() {
self.ec.write(i as u8 + 4, chunk[i]);
}
self.ec.write(2, flags);
self.ec.write(3, chunk.len() as u8);
self.ec.command(Cmd::Spi)?;
assert_eq!(self.ec.read(3), chunk.len() as u8);
}
Ok(data.len())
}
}
impl<'a, T: Timeout> Drop for EcSpi<'a, T> {
fn drop(&mut self) {
unsafe {
let _ = self.reset();
}
}
} }

View File

@ -1,9 +1,11 @@
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
Parameter,
Protocol(u8), Protocol(u8),
Signature((u8, u8)), Signature((u8, u8)),
SuperIoId(u16), SuperIoId(u16),
Timeout, Timeout,
Verify,
WouldBlock, WouldBlock,
} }

View File

@ -15,6 +15,9 @@ mod legacy;
pub use self::pmc::Pmc; pub use self::pmc::Pmc;
mod pmc; mod pmc;
pub use self::spi::{Spi, SpiRom};
mod spi;
pub use self::super_io::SuperIo; pub use self::super_io::SuperIo;
mod super_io; mod super_io;

View File

@ -2,6 +2,7 @@ use ectool::{
Ec, Ec,
Error, Error,
Firmware, Firmware,
SpiRom,
Timeout, Timeout,
}; };
use std::{ use std::{
@ -53,7 +54,6 @@ unsafe fn console() -> Result<(), Error> {
iopl(); iopl();
let mut ec = Ec::new( let mut ec = Ec::new(
true,
StdTimeout::new(Duration::new(1, 0)), StdTimeout::new(Duration::new(1, 0)),
)?; )?;
@ -73,6 +73,98 @@ unsafe fn console() -> Result<(), Error> {
} }
} }
unsafe fn flash_inner(ec: &mut Ec<StdTimeout>, firmware: &Firmware) -> Result<(), Error> {
let rom_size = 128 * 1024;
let sector_size = 1024;
let mut spi_bus = ec.spi(true)?;
let mut spi = SpiRom::new(
&mut spi_bus,
StdTimeout::new(Duration::new(1, 0))
);
// Read entire ROM
let mut rom = vec![0; rom_size];
eprintln!("SPI read");
spi.read_at(0, &mut rom)?;
eprintln!("Saving ROM to backup.rom");
fs::write("backup.rom", &rom).map_err(|_| Error::Verify)?;
let mut matches = true;
for i in 0..rom.len() {
if &rom[i] != firmware.data.get(i).unwrap_or(&0xFF) {
matches = false;
break;
}
}
if matches {
eprintln!("ROM matches specified firmware");
return Ok(());
}
{
// Chip erase
// eprintln!("SPI chip erase");
// spi.erase_chip()?;
// Sector erase
let mut address = 0;
while address < rom_size {
let mut erased = true;
for &b in &rom[address..address + sector_size] {
if b != 0xFF {
erased =false;
break;
}
}
if erased {
eprintln!("SPI sector already erased {:06X}", address);
address += sector_size;
} else {
eprintln!("SPI sector erase {:06X}", address);
address += spi.erase_sector(address as u32)?;
}
}
// Read entire ROM
eprintln!("SPI read");
spi.read_at(0, &mut rom)?;
}
// Verify chip erase
for i in 0..rom.len() {
if rom[i] != 0xFF {
eprintln!("Failed to erase: {:X} is {:X} instead of {:X}", i, rom[i], 0xFF);
return Err(Error::Verify);
}
}
// Program
{
eprintln!("SPI AAI word program");
spi.write_at(0, &firmware.data)?;
// Read entire ROM
eprintln!("SPI read");
spi.read_at(0, &mut rom)?;
}
// Verify program
for i in 0..rom.len() {
if &rom[i] != firmware.data.get(i).unwrap_or(&0xFF) {
eprintln!("Failed to program: {:X} is {:X} instead of {:X}", i, rom[i], firmware.data[i]);
return Err(Error::Verify);
}
}
eprintln!("Successfully programmed SPI ROM");
Ok(())
}
unsafe fn flash(path: &str) -> Result<(), Error> { unsafe fn flash(path: &str) -> Result<(), Error> {
//TODO: remove unwraps //TODO: remove unwraps
let firmware_data = fs::read(path).unwrap(); let firmware_data = fs::read(path).unwrap();
@ -83,7 +175,6 @@ unsafe fn flash(path: &str) -> Result<(), Error> {
iopl(); iopl();
let mut ec = Ec::new( let mut ec = Ec::new(
true,
StdTimeout::new(Duration::new(1, 0)), StdTimeout::new(Duration::new(1, 0)),
)?; )?;
@ -107,14 +198,34 @@ unsafe fn flash(path: &str) -> Result<(), Error> {
println!("ec version: {:?}", str::from_utf8(ec_version)); println!("ec version: {:?}", str::from_utf8(ec_version));
} }
Ok(()) // Wait for any key releases
eprintln!("Waiting 5 seconds for all keys to be released");
thread::sleep(Duration::new(5, 0));
eprintln!("Sync");
let _ = process::Command::new("sync").status();
let res = flash_inner(&mut ec, &firmware);
eprintln!("Result: {:X?}", res);
eprintln!("Sync");
let _ = process::Command::new("sync").status();
eprintln!("System will shut off in 5 seconds");
thread::sleep(Duration::new(5, 0));
eprintln!("Sync");
let _ = process::Command::new("sync").status();
ec.reset()?;
res
} }
unsafe fn info() -> Result<(), Error> { unsafe fn info() -> Result<(), Error> {
iopl(); iopl();
let mut ec = Ec::new( let mut ec = Ec::new(
true,
StdTimeout::new(Duration::new(1, 0)), StdTimeout::new(Duration::new(1, 0)),
)?; )?;

165
tool/src/spi.rs Normal file
View File

@ -0,0 +1,165 @@
use crate::{
Error,
Timeout,
};
pub trait Spi {
unsafe fn reset(&mut self) -> Result<(), Error>;
unsafe fn read(&mut self, data: &mut [u8]) -> Result<usize, Error>;
unsafe fn write(&mut self, data: &[u8]) -> Result<usize, Error>;
}
pub struct SpiRom<'a, S: Spi, T: Timeout> {
spi: &'a mut S,
timeout: T,
}
impl<'a, S: Spi, T: Timeout> SpiRom<'a, S, T> {
pub fn new(spi: &'a mut S, timeout: T) -> Self {
Self {
spi,
timeout,
}
}
pub unsafe fn status(&mut self) -> Result<u8, Error> {
let mut status = [0];
self.spi.reset()?;
self.spi.write(&[0x05])?;
self.spi.read(&mut status)?;
Ok(status[0])
}
pub unsafe fn status_wait(&mut self, mask: u8, value: u8) -> Result<(), Error> {
self.timeout.reset();
while self.timeout.running() {
if self.status()? & mask == value {
return Ok(());
}
}
Err(Error::Timeout)
}
pub unsafe fn write_disable(&mut self) -> Result<(), Error> {
self.spi.reset()?;
self.spi.write(&[0x04])?;
// Poll status for busy unset and write enable unset
self.status_wait(3, 0)?;
Ok(())
}
pub unsafe fn write_enable(&mut self) -> Result<(), Error> {
self.spi.reset()?;
self.spi.write(&[0x06])?;
// Poll status for busy unset and write enable set
self.status_wait(3, 2)?;
Ok(())
}
pub unsafe fn erase_chip(&mut self) -> Result<(), Error> {
self.write_enable()?;
self.spi.reset()?;
self.spi.write(&[0x60])?;
// Poll status for busy unset
self.status_wait(1, 0)?;
self.write_disable()?;
Ok(())
}
pub unsafe fn erase_sector(&mut self, address: u32) -> Result<usize, Error> {
if (address & 0xFF00_0000) > 0 {
return Err(Error::Parameter);
}
self.write_enable()?;
self.spi.reset()?;
self.spi.write(&[
0xD7,
(address >> 16) as u8,
(address >> 8) as u8,
address as u8,
])?;
// Poll status for busy unset
self.status_wait(1, 0)?;
self.write_disable()?;
//TODO: dynamically figure out this value
Ok(1024)
}
pub unsafe fn read_at(&mut self, address: u32, data: &mut [u8]) -> Result<usize, Error> {
if (address & 0xFF00_0000) > 0 {
return Err(Error::Parameter);
}
self.spi.reset()?;
self.spi.write(&[
0x0B,
(address >> 16) as u8,
(address >> 8) as u8,
address as u8,
0,
])?;
self.spi.read(data)
}
pub unsafe fn write_at(&mut self, address: u32, data: &[u8]) -> Result<usize, Error> {
if (address & 0xFF00_0000) > 0 {
return Err(Error::Parameter);
}
self.write_enable()?;
for (i, word) in data.chunks(2).enumerate() {
let low = *word.get(0).unwrap_or(&0xFF);
let high = *word.get(1).unwrap_or(&0xFF);
self.spi.reset()?;
if i == 0 {
self.spi.write(&[
0xAD,
(address >> 16) as u8,
(address >> 8) as u8,
address as u8,
low,
high
])?;
} else {
self.spi.write(&[
0xAD,
low,
high
])?;
}
// Poll status for busy unset
self.status_wait(1, 0)?;
}
self.write_disable()?;
Ok(data.len())
}
}
impl<'a, S: Spi, T: Timeout> Drop for SpiRom<'a, S, T> {
fn drop(&mut self) {
unsafe {
let _ = self.write_disable();
}
}
}