From 2d86bd1f0ca2930264099b5a7ada5150156f26da Mon Sep 17 00:00:00 2001 From: Tim Crawford Date: Fri, 6 Mar 2020 10:47:24 -0700 Subject: [PATCH 1/7] readme: Update flash commands --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6fc58a6..45be037 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Requirements: Use this method for flashing a system already running System76 EC. ``` -make BOARD=/ flash +make BOARD=/ flash_internal ``` ### External programmer @@ -69,5 +69,5 @@ Use this method for first-time flashing or flashing a bricked controller. 8. Flash the firmware ``` -make BOARD=/ isp +make BOARD=/ flash_external ``` From 0f666abb254aade20b9a3fe79ff125c9f6c9e9b8 Mon Sep 17 00:00:00 2001 From: Tim Crawford Date: Fri, 6 Mar 2020 10:49:19 -0700 Subject: [PATCH 2/7] mega2560: Implement debug console command Reconfigure the Mega 2560 as a parallel peripheral device to receive logs from the EC over the keyboard port and output them to the serial console. --- src/board/arduino/mega2560/parallel.c | 169 +++++++++++++++++++------- 1 file changed, 128 insertions(+), 41 deletions(-) diff --git a/src/board/arduino/mega2560/parallel.c b/src/board/arduino/mega2560/parallel.c index 38d1568..136fde6 100644 --- a/src/board/arduino/mega2560/parallel.c +++ b/src/board/arduino/mega2560/parallel.c @@ -112,41 +112,75 @@ void parallel_hiz(struct Parallel * port) { #undef PIN } +// Place all data lines in high or low impendance state +void parallel_data_dir(struct Parallel * port, bool dir) { + #define DATA_BIT(B) gpio_set_dir(port->d ## B, dir); + DATA_BITS + #undef DATA_BIT +} +#define parallel_data_forward(P) parallel_data_dir(P, true) +#define parallel_data_reverse(P) parallel_data_dir(P, false) + +void parallel_data_set_high(struct Parallel * port, uint8_t byte) { + // By convention all lines are high, so only set the ones needed + #define DATA_BIT(B) if (!(byte & (1 << B))) gpio_set(port->d ## B, true); + DATA_BITS + #undef DATA_BIT +} + // Set port to initial state required before being able to perform cycles -void parallel_reset(struct Parallel * port) { +void parallel_reset(struct Parallel * port, bool host) { parallel_hiz(port); - // Set reset line low - gpio_set_dir(port->reset_n, true); + // nRESET: output on host, input on peripherals + gpio_set_dir(port->reset_n, host); - // Wait 1 microsecond _delay_us(1); - // Make sure strobes are high outputs + // nDATASTB: output on host, input on peripherals gpio_set(port->data_n, true); + gpio_set_dir(port->data_n, host); + + // nADDRSTB: output on host, input on peripherals gpio_set(port->addr_n, true); - gpio_set_dir(port->data_n, true); - gpio_set_dir(port->addr_n, true); + gpio_set_dir(port->addr_n, host); - // Set write line high output + // nWRITE: output on host, input on peripherals gpio_set(port->write_n, true); - gpio_set_dir(port->write_n, true); + gpio_set_dir(port->write_n, host); - // Pull up wait line - gpio_set(port->wait_n, true); + // nWAIT: input on host, output on peripherals + gpio_set(port->wait_n, host); + gpio_set_dir(port->wait_n, !host); - // Pull up data lines - #define DATA_BIT(B) gpio_set(port->d ## B, true); + // Pull up data lines on host, leave floating on peripherals + #define DATA_BIT(B) gpio_set(port->d ## B, host); DATA_BITS #undef DATA_BIT //TODO: something with straps - // Wait 1 microsecond _delay_us(1); - // Set reset line high, ending reset - gpio_set(port->reset_n, true); + if (host) { + // Set reset line high, ending reset + gpio_set(port->reset_n, true); + } +} + +uint8_t parallel_read_data(struct Parallel * port) { + uint8_t byte = 0; + #define DATA_BIT(B) if (gpio_get(port->d ## B)) byte |= (1 << B); + DATA_BITS + #undef DATA_BIT + return byte; +} + +void parallel_write_data(struct Parallel * port, uint8_t byte) { + // By convention all lines are high, so only set the ones needed + #define DATA_BIT(B) if (!(byte & (1 << B))) gpio_set(port->d ## B, false); + DATA_BITS + #undef DATA_BIT } //TODO: timeout @@ -155,24 +189,20 @@ int parallel_transaction(struct Parallel * port, uint8_t * data, int length, boo // Set write line low gpio_set(port->write_n, false); - // Set data direction out - #define DATA_BIT(B) gpio_set_dir(port->d ## B, true); - DATA_BITS - #undef DATA_BIT + // Set data lines as outputs to write to peripheral + parallel_data_forward(port); } int i; - uint8_t byte; + uint8_t byte = 0; for (i = 0; i < length; i++) { - // Wait for wait line to be low + // Wait for peripheral to indicate it's ready for next cycle while (gpio_get(port->wait_n)) {} if (!read) { - // Set data low where necessary byte = data[i]; - #define DATA_BIT(B) if (!(byte & (1 << B))) gpio_set(port->d ## B, false); - DATA_BITS - #undef DATA_BIT + parallel_write_data(port, byte); + _delay_us(1); } if (addr) { @@ -182,16 +212,13 @@ int parallel_transaction(struct Parallel * port, uint8_t * data, int length, boo // Set data strobe low gpio_set(port->data_n, false); } + _delay_us(1); - // Wait for wait line to be high + // Wait for peripheral to indicate it's ready while (!gpio_get(port->wait_n)) {} if (read) { - byte = 0; - #define DATA_BIT(B) if (gpio_get(port->d ## B)) byte |= (1 << B); - DATA_BITS - #undef DATA_BIT - data[i] = byte; + data[i] = parallel_read_data(port); } if (addr) { @@ -201,20 +228,18 @@ int parallel_transaction(struct Parallel * port, uint8_t * data, int length, boo // Set data strobe high gpio_set(port->data_n, true); } + // XXX: Arduino peripheral not fast enough to get the data? + _delay_us(5); if (!read) { - // Set data high where necessary - #define DATA_BIT(B) if (!(byte & (1 << B))) gpio_set(port->d ## B, true); - DATA_BITS - #undef DATA_BIT + // Reset data lines to high + parallel_data_set_high(port, byte); } } if (!read) { - // Set data direction in - #define DATA_BIT(B) gpio_set_dir(port->d ## B, false); - DATA_BITS - #undef DATA_BIT + // Set data lines back to inputs + parallel_data_reverse(port); // Set write line high gpio_set(port->write_n, true); @@ -228,6 +253,45 @@ int parallel_transaction(struct Parallel * port, uint8_t * data, int length, boo #define parallel_read(P, D, L) parallel_transaction(P, D, L, true, false) #define parallel_write(P, D, L) parallel_transaction(P, D, L, false, false) +// host write -> peripheral read +// host read -> peripheral write +bool parallel_peripheral_cycle(struct Parallel * port, uint8_t * data, bool * read, bool * addr) { + if (!gpio_get(port->reset_n)) { + // XXX: Give host some time to get ready + _delay_ms(1); + return false; + } + + while (gpio_get(port->data_n) && gpio_get(port->addr_n)) {} + + *read = gpio_get(port->write_n); + *addr = !gpio_get(port->addr_n); + + if (*read) { + // Host is reading, send the data + parallel_data_forward(port); + parallel_write_data(port, *data); + } + + gpio_set(port->wait_n, true); + + // Wait for host to finish strobe + while (!gpio_get(port->addr_n) || !gpio_get(port->data_n)) {} + + if (*read) { + // Set data lines back to inputs + parallel_data_reverse(port); + } else { + // Host is writing, read the data + *data = parallel_read_data(port); + } + + // Tell host we're ready for next cycle + gpio_set(port->wait_n, false); + + return true; +} + static uint8_t ADDRESS_INDAR1 = 0x05; static uint8_t ADDRESS_INDDR = 0x08; @@ -343,7 +407,7 @@ int parallel_main(void) { int res = 0; struct Parallel * port = &PORT; - parallel_reset(port); + parallel_reset(port, true); static uint8_t data[128]; char command; @@ -352,6 +416,9 @@ int parallel_main(void) { uint8_t address; bool set_address = false; bool program_aai = false; + + unsigned char console_msg[] = "Entering console mode\n"; + for (;;) { // Read command and length res = serial_read(data, 2); @@ -393,6 +460,26 @@ int parallel_main(void) { break; + // Debug console + case 'C': + serial_write(console_msg, sizeof(console_msg)); + + // Reconfigure as a peripheral + parallel_reset(port, false); + + for (;;) { + bool read = false; + bool addr = false; + bool ret = parallel_peripheral_cycle(port, data, &read, &addr); + + if (ret && !read && !addr) { + res = serial_write(data, 1); + if (res < 0) goto err; + } + } + + break; + // Echo case 'E': // Read data from serial From 72b453ebd3ae287d289109bea8c3ac4993dbb155 Mon Sep 17 00:00:00 2001 From: Tim Crawford Date: Fri, 6 Mar 2020 10:47:52 -0700 Subject: [PATCH 3/7] it8587e, it5570e: Implement logging to parport Implement configuring the keyboard controller as a parallel host device and writing to the parallel port. This allows the EC to send log messages over the port. --- src/ec/it5570e/include/ec/parallel.h | 9 ++ src/ec/it5570e/parallel.c | 122 +++++++++++++++++++++++++++ src/ec/it8587e/include/ec/parallel.h | 9 ++ src/ec/it8587e/parallel.c | 122 +++++++++++++++++++++++++++ 4 files changed, 262 insertions(+) create mode 100644 src/ec/it5570e/include/ec/parallel.h create mode 100644 src/ec/it5570e/parallel.c create mode 100644 src/ec/it8587e/include/ec/parallel.h create mode 100644 src/ec/it8587e/parallel.c diff --git a/src/ec/it5570e/include/ec/parallel.h b/src/ec/it5570e/include/ec/parallel.h new file mode 100644 index 0000000..3a0110a --- /dev/null +++ b/src/ec/it5570e/include/ec/parallel.h @@ -0,0 +1,9 @@ +#ifndef _EC_PARALLEL_H +#define _EC_PARALLEL_H + +#include + +void parport_init(void); +int parport_write(uint8_t * data, int length); + +#endif // _EC_PARALLEL_H diff --git a/src/ec/it5570e/parallel.c b/src/ec/it5570e/parallel.c new file mode 100644 index 0000000..b744beb --- /dev/null +++ b/src/ec/it5570e/parallel.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include + +#include + +/* + * nWRITE = KSI[0] + * nDATASTB = KSI[1] + * nRESET = KSI[2] + * nADDRSTB = KSI[3] + * + * AD[8:1] = KSOL[7:0] + * nINTR = KSOH[0] + * nWAIT = KSOH[1] + */ + +#define CTL_WRITE (1 << 0) +#define CTL_DATA (1 << 1) +#define CTL_RESET (1 << 2) +#define CTL_ADDR (1 << 3) + +#define STS_WAIT (1 << 1) + +// Maximum peripheral response time in ms +#define PARPORT_TIMEOUT 35 + +void parport_init(void) { + // XXX: Needed? Pull-ups, open-drain are always disabled in GPIO mode + KSOCTRL = 0; + // XXX: Needed? OVRPPK is for KBS mode, pull-ups are always disabled in GPIO mode + KSICTRLR = (1 << 4); + + // Set all outputs to GPIO mode, low, and inputs + KSOL = 0; + KSOLGCTRL = 0xFF; + KSOLGOEN = 0; + KSOH1 = 0; + KSOHGCTRL = 0xFF; + KSOHGOEN = 0; + KSOH2 = 0; + + // Set control lines as outputs, low + KSIGCTRL = 0xFF; + KSIGOEN = 0x0F; + KSIGDAT = 0; + + // Assert nRESET + KSIGDAT &= ~CTL_RESET; + + // Deassert nWRITE, nDATASTB, nADDRSTB + KSIGDAT |= CTL_WRITE | CTL_DATA | CTL_ADDR; + + // Set nWAIT high + KSOH1 |= STS_WAIT; + + // Pull up data lines + KSOL = 0xFF; + + // Deassert nRESET + KSIGDAT |= CTL_RESET; +} + +bool parport_wait_peripheral(uint8_t mask, uint8_t value) { + uint32_t start = time_get(); + + while (time_get() < start + PARPORT_TIMEOUT) { + if ((KSOHGDMRR & mask) == value) { + return true; + } + } + + return false; +} + +int parport_write(uint8_t * data, int length) { + // Assert nWRITE + KSIGDAT &= ~CTL_WRITE; + + // Set data lines as outputs + KSOLGOEN = 0xFF; + + int i; + for (i = 0; i < length; i++) { + // Wait for peripheral to indicate it's ready for next cycle + if (!parport_wait_peripheral(STS_WAIT, 0)) { + break; + } + + // Write data to port + KSOL = data[i]; + + // Assert nDATASTB + KSIGDAT &= ~CTL_DATA; + delay_us(1); + + // Wait for peripheral to indicate it's ready + if (!parport_wait_peripheral(STS_WAIT, STS_WAIT)) { + KSIGDAT |= CTL_DATA; + break; + } + + // Deassert nDATASTB + KSIGDAT |= CTL_DATA; + delay_us(1); + + // XXX: Arduino takes a while to read? + delay_us(5); + + // Reset data lines to high + KSOL = 0xFF; + } + + // Set data lines back to inputs + KSOLGOEN = 0; + + // Deassert nWRITE + KSIGDAT |= CTL_WRITE; + + return i; +} diff --git a/src/ec/it8587e/include/ec/parallel.h b/src/ec/it8587e/include/ec/parallel.h new file mode 100644 index 0000000..3a0110a --- /dev/null +++ b/src/ec/it8587e/include/ec/parallel.h @@ -0,0 +1,9 @@ +#ifndef _EC_PARALLEL_H +#define _EC_PARALLEL_H + +#include + +void parport_init(void); +int parport_write(uint8_t * data, int length); + +#endif // _EC_PARALLEL_H diff --git a/src/ec/it8587e/parallel.c b/src/ec/it8587e/parallel.c new file mode 100644 index 0000000..b744beb --- /dev/null +++ b/src/ec/it8587e/parallel.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include + +#include + +/* + * nWRITE = KSI[0] + * nDATASTB = KSI[1] + * nRESET = KSI[2] + * nADDRSTB = KSI[3] + * + * AD[8:1] = KSOL[7:0] + * nINTR = KSOH[0] + * nWAIT = KSOH[1] + */ + +#define CTL_WRITE (1 << 0) +#define CTL_DATA (1 << 1) +#define CTL_RESET (1 << 2) +#define CTL_ADDR (1 << 3) + +#define STS_WAIT (1 << 1) + +// Maximum peripheral response time in ms +#define PARPORT_TIMEOUT 35 + +void parport_init(void) { + // XXX: Needed? Pull-ups, open-drain are always disabled in GPIO mode + KSOCTRL = 0; + // XXX: Needed? OVRPPK is for KBS mode, pull-ups are always disabled in GPIO mode + KSICTRLR = (1 << 4); + + // Set all outputs to GPIO mode, low, and inputs + KSOL = 0; + KSOLGCTRL = 0xFF; + KSOLGOEN = 0; + KSOH1 = 0; + KSOHGCTRL = 0xFF; + KSOHGOEN = 0; + KSOH2 = 0; + + // Set control lines as outputs, low + KSIGCTRL = 0xFF; + KSIGOEN = 0x0F; + KSIGDAT = 0; + + // Assert nRESET + KSIGDAT &= ~CTL_RESET; + + // Deassert nWRITE, nDATASTB, nADDRSTB + KSIGDAT |= CTL_WRITE | CTL_DATA | CTL_ADDR; + + // Set nWAIT high + KSOH1 |= STS_WAIT; + + // Pull up data lines + KSOL = 0xFF; + + // Deassert nRESET + KSIGDAT |= CTL_RESET; +} + +bool parport_wait_peripheral(uint8_t mask, uint8_t value) { + uint32_t start = time_get(); + + while (time_get() < start + PARPORT_TIMEOUT) { + if ((KSOHGDMRR & mask) == value) { + return true; + } + } + + return false; +} + +int parport_write(uint8_t * data, int length) { + // Assert nWRITE + KSIGDAT &= ~CTL_WRITE; + + // Set data lines as outputs + KSOLGOEN = 0xFF; + + int i; + for (i = 0; i < length; i++) { + // Wait for peripheral to indicate it's ready for next cycle + if (!parport_wait_peripheral(STS_WAIT, 0)) { + break; + } + + // Write data to port + KSOL = data[i]; + + // Assert nDATASTB + KSIGDAT &= ~CTL_DATA; + delay_us(1); + + // Wait for peripheral to indicate it's ready + if (!parport_wait_peripheral(STS_WAIT, STS_WAIT)) { + KSIGDAT |= CTL_DATA; + break; + } + + // Deassert nDATASTB + KSIGDAT |= CTL_DATA; + delay_us(1); + + // XXX: Arduino takes a while to read? + delay_us(5); + + // Reset data lines to high + KSOL = 0xFF; + } + + // Set data lines back to inputs + KSOLGOEN = 0; + + // Deassert nWRITE + KSIGDAT |= CTL_WRITE; + + return i; +} From e5afb927b83b28efbd044cb2939ca8c8273b518f Mon Sep 17 00:00:00 2001 From: Tim Crawford Date: Fri, 6 Mar 2020 10:57:26 -0700 Subject: [PATCH 4/7] system76/*: Add parport logging Add a build-time option to configure the parallel port for logging instead of using it as a keyboard. --- src/board/system76/darp5/board.mk | 3 +++ src/board/system76/darp5/main.c | 10 ++++++++++ src/board/system76/darp5/stdio.c | 7 +++++++ src/board/system76/galp3-c/board.mk | 3 +++ src/board/system76/galp3-c/main.c | 10 ++++++++++ src/board/system76/galp3-c/stdio.c | 7 +++++++ src/board/system76/lemp9/board.mk | 3 +++ src/board/system76/lemp9/main.c | 10 ++++++++++ src/board/system76/lemp9/stdio.c | 7 +++++++ 9 files changed, 60 insertions(+) diff --git a/src/board/system76/darp5/board.mk b/src/board/system76/darp5/board.mk index 2032260..5b8f47c 100644 --- a/src/board/system76/darp5/board.mk +++ b/src/board/system76/darp5/board.mk @@ -22,6 +22,9 @@ CFLAGS+=-DI2C_SMBUS=I2C_0 # Set keyboard LED I2C bus CFLAGS+=-DI2C_KBLED=I2C_1 +# Enable debug logging over keyboard parallel port +#CFLAGS+=-DPARPORT_DEBUG + # Set scratch ROM parameters SCRATCH_OFFSET=1024 SCRATCH_SIZE=1024 diff --git a/src/board/system76/darp5/main.c b/src/board/system76/darp5/main.c index 291fb86..3596b12 100644 --- a/src/board/system76/darp5/main.c +++ b/src/board/system76/darp5/main.c @@ -23,6 +23,10 @@ #include #include +#ifdef PARPORT_DEBUG + #include +#endif + void external_0(void) __interrupt(0) {} // timer_0 is in time.c void timer_0(void) __interrupt(1); @@ -43,7 +47,11 @@ void init(void) { ecpm_init(); kbc_init(); kbled_init(); +#ifdef PARPORT_DEBUG + parport_init(); +#else kbscan_init(); +#endif peci_init(); pmc_init(); pwm_init(); @@ -105,8 +113,10 @@ void main(void) { power_event(); break; case 1: +#ifndef PARPORT_DEBUG // Scans keyboard and sends keyboard packets kbscan_event(); +#endif break; case 2: // Passes through touchpad packets diff --git a/src/board/system76/darp5/stdio.c b/src/board/system76/darp5/stdio.c index 6cb8951..97faa0f 100644 --- a/src/board/system76/darp5/stdio.c +++ b/src/board/system76/darp5/stdio.c @@ -10,6 +10,10 @@ #include #endif +#ifdef PARPORT_DEBUG + #include +#endif + int putchar(int c) { unsigned char byte = (unsigned char)c; smfi_debug(byte); @@ -18,6 +22,9 @@ int putchar(int c) { #endif #ifdef I2C_DEBUGGER i2c_send(&I2C_SMBUS, I2C_DEBUGGER, &byte, 1); +#endif +#ifdef PARPORT_DEBUG + parport_write(&byte, 1); #endif return (int)byte; } diff --git a/src/board/system76/galp3-c/board.mk b/src/board/system76/galp3-c/board.mk index 81dc07e..da4daa9 100644 --- a/src/board/system76/galp3-c/board.mk +++ b/src/board/system76/galp3-c/board.mk @@ -19,6 +19,9 @@ CFLAGS+=-DLEVEL=4 # Set battery I2C bus CFLAGS+=-DI2C_SMBUS=I2C_0 +# Enable debug logging over keyboard parallel port +#CFLAGS+=-DPARPORT_DEBUG + # Set scratch ROM parameters SCRATCH_OFFSET=1024 SCRATCH_SIZE=1024 diff --git a/src/board/system76/galp3-c/main.c b/src/board/system76/galp3-c/main.c index 291fb86..3596b12 100644 --- a/src/board/system76/galp3-c/main.c +++ b/src/board/system76/galp3-c/main.c @@ -23,6 +23,10 @@ #include #include +#ifdef PARPORT_DEBUG + #include +#endif + void external_0(void) __interrupt(0) {} // timer_0 is in time.c void timer_0(void) __interrupt(1); @@ -43,7 +47,11 @@ void init(void) { ecpm_init(); kbc_init(); kbled_init(); +#ifdef PARPORT_DEBUG + parport_init(); +#else kbscan_init(); +#endif peci_init(); pmc_init(); pwm_init(); @@ -105,8 +113,10 @@ void main(void) { power_event(); break; case 1: +#ifndef PARPORT_DEBUG // Scans keyboard and sends keyboard packets kbscan_event(); +#endif break; case 2: // Passes through touchpad packets diff --git a/src/board/system76/galp3-c/stdio.c b/src/board/system76/galp3-c/stdio.c index 6cb8951..97faa0f 100644 --- a/src/board/system76/galp3-c/stdio.c +++ b/src/board/system76/galp3-c/stdio.c @@ -10,6 +10,10 @@ #include #endif +#ifdef PARPORT_DEBUG + #include +#endif + int putchar(int c) { unsigned char byte = (unsigned char)c; smfi_debug(byte); @@ -18,6 +22,9 @@ int putchar(int c) { #endif #ifdef I2C_DEBUGGER i2c_send(&I2C_SMBUS, I2C_DEBUGGER, &byte, 1); +#endif +#ifdef PARPORT_DEBUG + parport_write(&byte, 1); #endif return (int)byte; } diff --git a/src/board/system76/lemp9/board.mk b/src/board/system76/lemp9/board.mk index a05948b..5b665e6 100644 --- a/src/board/system76/lemp9/board.mk +++ b/src/board/system76/lemp9/board.mk @@ -22,6 +22,9 @@ CFLAGS+=-DI2C_SMBUS=I2C_4 # Set type-c port manager I2C bus CFLAGS+=-DI2C_TCPM=I2C_1 +# Enable debug logging over keyboard parallel port +#CFLAGS+=-DPARPORT_DEBUG + # Set scratch ROM parameters SCRATCH_OFFSET=1024 SCRATCH_SIZE=1024 diff --git a/src/board/system76/lemp9/main.c b/src/board/system76/lemp9/main.c index 3f40d44..ba9f8fc 100644 --- a/src/board/system76/lemp9/main.c +++ b/src/board/system76/lemp9/main.c @@ -24,6 +24,10 @@ #include #include +#ifdef PARPORT_DEBUG + #include +#endif + void external_0(void) __interrupt(0) {} // timer_0 is in time.c void timer_0(void) __interrupt(1); @@ -44,7 +48,11 @@ void init(void) { ecpm_init(); kbc_init(); kbled_init(); +#ifdef PARPORT_DEBUG + parport_init(); +#else kbscan_init(); +#endif peci_init(); pmc_init(); pwm_init(); @@ -107,8 +115,10 @@ void main(void) { power_event(); break; case 1: +#ifndef PARPORT_DEBUG // Scans keyboard and sends keyboard packets kbscan_event(); +#endif break; case 2: // Passes through touchpad packets diff --git a/src/board/system76/lemp9/stdio.c b/src/board/system76/lemp9/stdio.c index 6cb8951..97faa0f 100644 --- a/src/board/system76/lemp9/stdio.c +++ b/src/board/system76/lemp9/stdio.c @@ -10,6 +10,10 @@ #include #endif +#ifdef PARPORT_DEBUG + #include +#endif + int putchar(int c) { unsigned char byte = (unsigned char)c; smfi_debug(byte); @@ -18,6 +22,9 @@ int putchar(int c) { #endif #ifdef I2C_DEBUGGER i2c_send(&I2C_SMBUS, I2C_DEBUGGER, &byte, 1); +#endif +#ifdef PARPORT_DEBUG + parport_write(&byte, 1); #endif return (int)byte; } From f9a775e71bf5e2e1c15da40a7b44fb3c93a6e821 Mon Sep 17 00:00:00 2001 From: Tim Crawford Date: Fri, 6 Mar 2020 11:00:03 -0700 Subject: [PATCH 5/7] docs: Add info for logging through parport --- doc/debugging.md | 77 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 8 deletions(-) diff --git a/doc/debugging.md b/doc/debugging.md index 2181990..dac5678 100644 --- a/doc/debugging.md +++ b/doc/debugging.md @@ -1,15 +1,76 @@ # Debugging the EC firmware +Terms used: +- *target*: The laptop system that has the EC to be tested +- *host*: The system that will have all devices connected to it and + will receive the EC logs + +## Parallel port + +This method replaces the keyboard with a device used for debug logging. +An alternate method of interacting with the target is needed; e.g., an +external USB keyboard or SSH session. + +Requirements: +- Arduino Mega 2560 compatible board +- 24 pin flexible printed circuit + - 0.5mm or 1.0mm pitch, depending on target connector +- 24 pin FPC breakout board with connectors +- USB-C cable + +### Configuring the Mega 2560 + +If the breakout board did not come preassembled, assemble it before +starting. This will require soldering. + +1. Connect the breakout board to the Mega 2560 + - Orientation will affect the mapping of GPIO pins +2. Connect the Mega 2560 to the host +3. Configure GPIO pin mapping in `parallel.c` based on how it will + connect to the target parallel port + - Trace pin 1 on motherboard connector to Mega's GPIO pins +4. Build and flash the firmware for the Mega 2560 +``` +make BOARD=arduino/mega2560 +make BOARD=arduino/mega2560 flash +``` + +### Setup + +1. Enable parallel port debugging in the EC firmware + - Uncomment `PARPORT_DEBUG` in `board.mk` + - Build and flash the firmware for the target +2. Power off target +3. Remove bottom panel +4. Unplug keyboard cable + - May require removing keyboard depending on port location +5. Connect Mega 2560 to host + - This will create an ACM device at `/dev/ttyACM*` +6. Connect to ACM device from host +``` +sudo tio -b 1000000 -m INLCRNL /dev/ttyACM0 +``` +7. Set Mega to peripheral mode +``` +echo 'C' > /dev/ttyACM0 +``` +8. Ground target to host + - Connect USB cable from USB-C port on target to host +9. Connect Mega 2560 to target + +EC logs should now print to the console on the host. This can be tested +by removing or inserting the AC adapter to trigger a power event. + +To return the Mega to host mode, reset the device. + +If logs are corrupted, try power cycling the Mega. + +## I2C connection + **Failure to follow steps in order, or performing steps on an unsupported board, may result in damaged board components.** -Terms used: - -- *target*: The laptop system that has the EC to be tested. -- *host*: The system that will have all devices connected to it and - will receive the EC logs. - -## Wiring the target +### Wiring the target These steps apply to the following models: - darp5 @@ -29,7 +90,7 @@ These steps apply to the following models: 9. Reconnect battery 10. Replace bottom panel -## Setup +### Setup Requirements: - Target wired for EC debugging From abf5c3f20beb7715e5b3ce0faecda61fcca21cf4 Mon Sep 17 00:00:00 2001 From: Tim Crawford Date: Fri, 6 Mar 2020 11:15:04 -0700 Subject: [PATCH 6/7] mega2560: Add pin configuration for IT5570 --- src/board/arduino/mega2560/parallel.c | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/board/arduino/mega2560/parallel.c b/src/board/arduino/mega2560/parallel.c index 136fde6..5c79cd6 100644 --- a/src/board/arduino/mega2560/parallel.c +++ b/src/board/arduino/mega2560/parallel.c @@ -10,6 +10,8 @@ #include // Mapping of 24-pin ribbon cable to parallel pins. See schematic +#define IT8587 +#if defined(IT8587) #define PINS \ /* Data (KSO0 - KSO7) - bi-directional */ \ PIN(d0, 1) \ @@ -41,6 +43,42 @@ /* Strap1 (KSI5) */ \ /* 1K-Ohm pull-down resistor */ \ PIN(strap_1, 12) +#elif defined(IT5570) +#define PINS \ + /* Data (KSO0 - KSO7) - bi-directional */ \ + PIN(d0, 24) \ + PIN(d1, 23) \ + PIN(d2, 22) \ + PIN(d3, 18) \ + PIN(d4, 16) \ + PIN(d5, 15) \ + PIN(d6, 12) \ + PIN(d7, 9) \ + /* Wait# (KSO9) - input */ \ + /* low to indicate cycle may begin, high to indicate cycle may end */ \ + PIN(wait_n, 7) \ + /* Write# (KSI0) - output */ \ + /* low to indicate write cycle, high to indicate read cycle */ \ + PIN(write_n, 21) \ + /* DataStrobe# (KSI1) - output */ \ + /* low indicates a data cycle */ \ + PIN(data_n, 20) \ + /* Reset# (KSI2) - output */ \ + /* low requests device reset */ \ + PIN(reset_n, 19) \ + /* AddressStrobe# (KSI3) - output */ \ + /* low indicates an address cycle */ \ + PIN(addr_n, 17) \ + /* Strap0 (KSI4) */ \ + /* 1K-Ohm pull-down resistor */ \ + PIN(strap_0, 14) \ + /* Strap1 (KSI5) */ \ + /* 1K-Ohm pull-down resistor */ \ + PIN(strap_1, 13) +#else + #error "Unknown pin configuration: EC not specified" +#endif + #define DATA_BITS \ DATA_BIT(0) \ From 85cd16ca30f34b2b13a2d6a8e0271f0ef950580d Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 12 Mar 2020 14:19:55 -0600 Subject: [PATCH 7/7] WIP: Support parallel port logging with Arduino Uno --- src/board/arduino/uno/main.c | 33 +- src/board/arduino/uno/parallel.c | 538 +++++++++++++++++++++++++++++++ 2 files changed, 544 insertions(+), 27 deletions(-) create mode 100644 src/board/arduino/uno/parallel.c diff --git a/src/board/arduino/uno/main.c b/src/board/arduino/uno/main.c index 8b5aaff..bd064e0 100644 --- a/src/board/arduino/uno/main.c +++ b/src/board/arduino/uno/main.c @@ -1,46 +1,25 @@ #include #include -#include #include -#include -#include void init(void) { uart_stdio_init(0, __CONSOLE_BAUD__); - i2c_init(50000); -} - -static void i2c_slave_new() {} - -static void i2c_slave_recv(uint8_t data) { - printf("%c", data); -} - -static uint8_t i2c_slave_send() { - return 0; } struct Gpio LED = GPIO(B, 5); +//TODO: .h file +void parallel_main(void); + int main(void) { init(); gpio_set_dir(&LED, true); gpio_set(&LED, false); - printf("Hello from System76 EC for the Arduino Uno!\n"); - battery_debug(); + parallel_main(); - for (;;) { - i2c_slave_init(0x76, i2c_slave_new, i2c_slave_recv, i2c_slave_send); - int c = getchar(); - i2c_slave_stop(); - if (c == '\r') { - putchar('\n'); - battery_debug(); - } else if (c > 0) { - putchar(c); - } - } + // If parallel_main exits with an error, wait for reset + for (;;) {} } diff --git a/src/board/arduino/uno/parallel.c b/src/board/arduino/uno/parallel.c new file mode 100644 index 0000000..48ecd34 --- /dev/null +++ b/src/board/arduino/uno/parallel.c @@ -0,0 +1,538 @@ +// High resolution pinout can be found here: +// https://osoyoo.com/wp-content/uploads/2017/08/arduino_mega_2560_pinout.png + +#include +#include +#include +#include + +#include +#include + +// Mapping of 24-pin ribbon cable to parallel pins. See schematic +#define PINS \ + /* Data (KSO0 - KSO7) - bi-directional */ \ + PIN(d0, 1) \ + PIN(d1, 2) \ + PIN(d2, 3) \ + PIN(d3, 4) \ + PIN(d4, 5) \ + PIN(d5, 6) \ + PIN(d6, 7) \ + PIN(d7, 8) \ + /* Wait# (KSO9) - input */ \ + /* low to indicate cycle may begin, high to indicate cycle may end */ \ + PIN(wait_n, 9) \ + /* Write# (KSI0) - output */ \ + /* low to indicate write cycle, high to indicate read cycle */ \ + PIN(write_n, 10) \ + /* DataStrobe# (KSI1) - output */ \ + /* low indicates a data cycle */ \ + PIN(data_n, 11) \ + /* Reset# (KSI2) - output */ \ + /* low requests device reset */ \ + PIN(reset_n, 12) \ + /* AddressStrobe# (KSI3) - output */ \ + /* low indicates an address cycle */ \ + PIN(addr_n, 13) \ + +#define DATA_BITS \ + DATA_BIT(0) \ + DATA_BIT(1) \ + DATA_BIT(2) \ + DATA_BIT(3) \ + DATA_BIT(4) \ + DATA_BIT(5) \ + DATA_BIT(6) \ + DATA_BIT(7) + +// Mapping of 24-pin ribbon cable to GPIOs +static struct Gpio GPIOS[13] = { + GPIO(D, 2), + GPIO(D, 3), + GPIO(D, 4), + GPIO(D, 5), + GPIO(D, 6), + GPIO(D, 7), + GPIO(B, 0), + GPIO(B, 1), + GPIO(B, 2), + GPIO(C, 3), + GPIO(C, 2), + GPIO(C, 1), + GPIO(C, 0), +}; + + +// Parallel struct definition +// See http://efplus.com/techref/io/parallel/1284/eppmode.htm +struct Parallel { + #define PIN(N, P) struct Gpio * N; + PINS + #undef PIN +}; + +// Parallel struct instance +static struct Parallel PORT = { + #define PIN(N, P) .N = &GPIOS[P - 1], + PINS + #undef PIN +}; + +// Set port to all high-impedance inputs +void parallel_hiz(struct Parallel * port) { + #define PIN(N, P) \ + gpio_set_dir(port->N, false); \ + gpio_set(port->N, false); + PINS + #undef PIN +} + +// Place all data lines in high or low impendance state +void parallel_data_dir(struct Parallel * port, bool dir) { + #define DATA_BIT(B) gpio_set_dir(port->d ## B, dir); + DATA_BITS + #undef DATA_BIT +} +#define parallel_data_forward(P) parallel_data_dir(P, true) +#define parallel_data_reverse(P) parallel_data_dir(P, false) + +void parallel_data_set_high(struct Parallel * port, uint8_t byte) { + // By convention all lines are high, so only set the ones needed + #define DATA_BIT(B) if (!(byte & (1 << B))) gpio_set(port->d ## B, true); + DATA_BITS + #undef DATA_BIT +} + +// Set port to initial state required before being able to perform cycles +void parallel_reset(struct Parallel * port, bool host) { + parallel_hiz(port); + + // nRESET: output on host, input on peripherals + gpio_set_dir(port->reset_n, host); + + _delay_us(1); + + // nDATASTB: output on host, input on peripherals + gpio_set(port->data_n, true); + gpio_set_dir(port->data_n, host); + + // nADDRSTB: output on host, input on peripherals + gpio_set(port->addr_n, true); + gpio_set_dir(port->addr_n, host); + + // nWRITE: output on host, input on peripherals + gpio_set(port->write_n, true); + gpio_set_dir(port->write_n, host); + + // nWAIT: input on host, output on peripherals + gpio_set(port->wait_n, host); + gpio_set_dir(port->wait_n, !host); + + // Pull up data lines on host, leave floating on peripherals + #define DATA_BIT(B) gpio_set(port->d ## B, host); + DATA_BITS + #undef DATA_BIT + + //TODO: something with straps + + _delay_us(1); + + if (host) { + // Set reset line high, ending reset + gpio_set(port->reset_n, true); + } +} + +uint8_t parallel_read_data(struct Parallel * port) { + uint8_t byte = 0; + #define DATA_BIT(B) if (gpio_get(port->d ## B)) byte |= (1 << B); + DATA_BITS + #undef DATA_BIT + return byte; +} + +void parallel_write_data(struct Parallel * port, uint8_t byte) { + // By convention all lines are high, so only set the ones needed + #define DATA_BIT(B) if (!(byte & (1 << B))) gpio_set(port->d ## B, false); + DATA_BITS + #undef DATA_BIT +} + +//TODO: timeout +int parallel_transaction(struct Parallel * port, uint8_t * data, int length, bool read, bool addr) { + if (!read) { + // Set write line low + gpio_set(port->write_n, false); + + // Set data lines as outputs to write to peripheral + parallel_data_forward(port); + } + + int i; + uint8_t byte = 0; + for (i = 0; i < length; i++) { + // Wait for peripheral to indicate it's ready for next cycle + while (gpio_get(port->wait_n)) {} + + if (!read) { + byte = data[i]; + parallel_write_data(port, byte); + _delay_us(1); + } + + if (addr) { + // Set address strobe low + gpio_set(port->addr_n, false); + } else { + // Set data strobe low + gpio_set(port->data_n, false); + } + _delay_us(1); + + // Wait for peripheral to indicate it's ready + while (!gpio_get(port->wait_n)) {} + + if (read) { + data[i] = parallel_read_data(port); + } + + if (addr) { + // Set address strobe high + gpio_set(port->addr_n, true); + } else { + // Set data strobe high + gpio_set(port->data_n, true); + } + // XXX: Arduino peripheral not fast enough to get the data? + _delay_us(5); + + if (!read) { + // Reset data lines to high + parallel_data_set_high(port, byte); + } + } + + if (!read) { + // Set data lines back to inputs + parallel_data_reverse(port); + + // Set write line high + gpio_set(port->write_n, true); + } + + return i; +} + +#define parallel_get_address(P, D, L) parallel_transaction(P, D, L, true, true) +#define parallel_set_address(P, D, L) parallel_transaction(P, D, L, false, true) +#define parallel_read(P, D, L) parallel_transaction(P, D, L, true, false) +#define parallel_write(P, D, L) parallel_transaction(P, D, L, false, false) + +// host write -> peripheral read +// host read -> peripheral write +bool parallel_peripheral_cycle(struct Parallel * port, uint8_t * data, bool * read, bool * addr) { + if (!gpio_get(port->reset_n)) { + // XXX: Give host some time to get ready + _delay_ms(1); + return false; + } + + while (gpio_get(port->data_n) && gpio_get(port->addr_n)) {} + + *read = gpio_get(port->write_n); + *addr = !gpio_get(port->addr_n); + + if (*read) { + // Host is reading, send the data + parallel_data_forward(port); + parallel_write_data(port, *data); + } + + gpio_set(port->wait_n, true); + + // Wait for host to finish strobe + while (!gpio_get(port->addr_n) || !gpio_get(port->data_n)) {} + + if (*read) { + // Set data lines back to inputs + parallel_data_reverse(port); + } else { + // Host is writing, read the data + *data = parallel_read_data(port); + } + + // Tell host we're ready for next cycle + gpio_set(port->wait_n, false); + + return true; +} + +static uint8_t ADDRESS_INDAR1 = 0x05; +static uint8_t ADDRESS_INDDR = 0x08; + +static uint8_t ZERO = 0x00; +static uint8_t SPI_ENABLE = 0xFE; +static uint8_t SPI_DATA = 0xFD; + +// Disable chip +int parallel_spi_reset(struct Parallel *port) { + int res; + + res = parallel_set_address(port, &ADDRESS_INDAR1, 1); + if (res < 0) return res; + + res = parallel_write(port, &SPI_ENABLE, 1); + if (res < 0) return res; + + res = parallel_set_address(port, &ADDRESS_INDDR, 1); + if (res < 0) return res; + + return parallel_write(port, &ZERO, 1); +} + +// Enable chip and read or write data +int parallel_spi_transaction(struct Parallel *port, uint8_t * data, int length, bool read) { + int res; + + res = parallel_set_address(port, &ADDRESS_INDAR1, 1); + if (res < 0) return res; + + res = parallel_write(port, &SPI_DATA, 1); + if (res < 0) return res; + + res = parallel_set_address(port, &ADDRESS_INDDR, 1); + if (res < 0) return res; + + return parallel_transaction(port, data, length, read, false); +} + +#define parallel_spi_read(P, D, L) parallel_spi_transaction(P, D, L, true) +#define parallel_spi_write(P, D, L) parallel_spi_transaction(P, D, L, false) + +// "Hardware" accelerated SPI programming, requires ECINDARs to be set +int parallel_spi_program(struct Parallel * port, uint8_t * data, int length, bool initialized) { + static uint8_t aai[6] = { 0xAD, 0, 0, 0, 0, 0 }; + int res; + int i; + uint8_t status; + + for(i = 0; (i + 1) < length; i+=2) { + // Disable chip to begin command + res = parallel_spi_reset(port); + if (res < 0) return res; + + if (!initialized) { + // If not initialized, the start address must be sent + aai[1] = 0; + aai[2] = 0; + aai[3] = 0; + + aai[4] = data[i]; + aai[5] = data[i + 1]; + + res = parallel_spi_write(port, aai, 6); + if (res < 0) return res; + + initialized = true; + } else { + aai[1] = data[i]; + aai[2] = data[i + 1]; + + res = parallel_spi_write(port, aai, 3); + if (res < 0) return res; + } + + // Wait for SPI busy flag to clear + for (;;) { + // Disable chip to begin command + res = parallel_spi_reset(port); + if (res < 0) return res; + + status = 0x05; + res = parallel_spi_write(port, &status, 1); + if (res < 0) return res; + + res = parallel_spi_read(port, &status, 1); + if (res < 0) return res; + + if (!(status & 1)) break; + } + } + + return i; +} + +int serial_transaction(uint8_t * data, int length, bool read) { + int i; + for (i = 0; i < length; i++) { + if (read) { + data[i] = (uint8_t)uart_read(uart_stdio); + } else { + uart_write(uart_stdio, (unsigned char)data[i]); + } + } + + return i; +} + +#define serial_read(D, L) serial_transaction(D, L, true) +#define serial_write(D, L) serial_transaction(D, L, false) + +int parallel_main(void) { + int res = 0; + + struct Parallel * port = &PORT; + parallel_reset(port, true); + + static uint8_t data[128]; + char command; + int length; + int i; + uint8_t address; + bool set_address = false; + bool program_aai = false; + + unsigned char console_msg[] = "Entering console mode\n"; + + for (;;) { + // Read command and length + res = serial_read(data, 2); + if (res < 0) goto err; + // Command is a character + command = (char)data[0]; + + // Special address prefix + if (command == 'A') { + // Prepare to set address on next parallel cycle + address = data[1]; + set_address = true; + continue; + } else if (command != 'P') { + // End accelerated programming + program_aai = false; + } + + // Length is received data + 1 + length = ((int)data[1]) + 1; + // Truncate length to size of data + if (length > sizeof(data)) length = sizeof(data); + + switch (command) { + // Buffer size + case 'B': + // Fill buffer size - 1 + for (i = 0; i < length; i++) { + if (i == 0) { + data[i] = (uint8_t)(sizeof(data) - 1); + } else { + data[i] = 0; + } + } + + // Write data to serial + res = serial_write(data, length); + if (res < 0) goto err; + + break; + + // Debug console + case 'C': + serial_write(console_msg, sizeof(console_msg)); + + // Reconfigure as a peripheral + parallel_reset(port, false); + + for (;;) { + bool read = false; + bool addr = false; + bool ret = parallel_peripheral_cycle(port, data, &read, &addr); + + if (ret && !read && !addr) { + res = serial_write(data, 1); + if (res < 0) goto err; + } + } + + break; + + // Echo + case 'E': + // Read data from serial + res = serial_read(data, length); + if (res < 0) goto err; + + // Write data to serial + res = serial_write(data, length); + if (res < 0) goto err; + + break; + + // Read data + case 'R': + // Update parallel address if necessary + if (set_address) { + res = parallel_set_address(port, &address, 1); + if (res < 0) goto err; + set_address = false; + } + + // Read data from parallel + res = parallel_read(port, data, length); + if (res < 0) goto err; + + // Write data to serial + res = serial_write(data, length); + if (res < 0) goto err; + + break; + + // Accelerated program function + case 'P': + // Read data from serial + res = serial_read(data, length); + if (res < 0) goto err; + + // Run accelerated programming function + res = parallel_spi_program(port, data, length, program_aai); + if (res < 0) goto err; + program_aai = true; + + // Send ACK of data length + data[0] = (uint8_t)(length - 1); + res = serial_write(data, 1); + if (res < 0) goto err; + + break; + + // Write data + case 'W': + // Read data from serial + res = serial_read(data, length); + if (res < 0) goto err; + + // Update parallel address if necessary + if (set_address) { + res = parallel_set_address(port, &address, 1); + if (res < 0) goto err; + set_address = false; + } + + // Write data to parallel + res = parallel_write(port, data, length); + if (res < 0) goto err; + + // Send ACK of data length + data[0] = (uint8_t)(length - 1); + res = serial_write(data, 1); + if (res < 0) goto err; + + break; + } + } + +err: + parallel_hiz(port); + + return res; +}