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:
2
ecflash
2
ecflash
Submodule ecflash updated: 945ec8d276...fc3f098fda
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -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=\
|
||||||
|
9
src/board/system76/darp5/scratch/stdio.c
Normal file
9
src/board/system76/darp5/scratch/stdio.c
Normal 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;
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -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=\
|
||||||
|
9
src/board/system76/galp3-c/scratch/stdio.c
Normal file
9
src/board/system76/galp3-c/scratch/stdio.c
Normal 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;
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -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=\
|
||||||
|
9
src/board/system76/lemp9/scratch/stdio.c
Normal file
9
src/board/system76/lemp9/scratch/stdio.c
Normal 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;
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
39
src/common/include/common/command.h
Normal file
39
src/common/include/common/command.h
Normal 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
|
146
tool/src/ec.rs
146
tool/src/ec.rs
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
119
tool/src/main.rs
119
tool/src/main.rs
@ -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
165
tool/src/spi.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user