Merge pull request #35 from system76/wip/parport-logging

Debug logging over parallel port
This commit is contained in:
Jeremy Soller 2020-03-13 07:41:04 -06:00 committed by GitHub
commit 61c5ab1b3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1103 additions and 78 deletions

View File

@ -50,7 +50,7 @@ Requirements:
Use this method for flashing a system already running System76 EC.
```
make BOARD=<vendor>/<model> flash
make BOARD=<vendor>/<model> 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=<vendor>/<model> isp
make BOARD=<vendor>/<model> flash_external
```

View File

@ -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

View File

@ -10,6 +10,8 @@
#include <arch/uart.h>
// 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) \
@ -112,41 +150,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 +227,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 +250,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 +266,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 +291,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 +445,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 +454,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 +498,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

View File

@ -1,46 +1,25 @@
#include <stdio.h>
#include <arch/gpio.h>
#include <arch/i2c_slave.h>
#include <arch/uart.h>
#include <board/battery.h>
#include <board/i2c.h>
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 (;;) {}
}

View File

@ -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 <stdint.h>
#include <stdio.h>
#include <board/cpu.h>
#include <util/delay.h>
#include <arch/gpio.h>
#include <arch/uart.h>
// 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;
}

View File

@ -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

View File

@ -23,6 +23,10 @@
#include <common/macro.h>
#include <common/version.h>
#ifdef PARPORT_DEBUG
#include <ec/parallel.h>
#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

View File

@ -10,6 +10,10 @@
#include <ec/i2c.h>
#endif
#ifdef PARPORT_DEBUG
#include <ec/parallel.h>
#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;
}

View File

@ -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

View File

@ -23,6 +23,10 @@
#include <common/macro.h>
#include <common/version.h>
#ifdef PARPORT_DEBUG
#include <ec/parallel.h>
#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

View File

@ -10,6 +10,10 @@
#include <ec/i2c.h>
#endif
#ifdef PARPORT_DEBUG
#include <ec/parallel.h>
#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;
}

View File

@ -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

View File

@ -24,6 +24,10 @@
#include <common/macro.h>
#include <common/version.h>
#ifdef PARPORT_DEBUG
#include <ec/parallel.h>
#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

View File

@ -10,6 +10,10 @@
#include <ec/i2c.h>
#endif
#ifdef PARPORT_DEBUG
#include <ec/parallel.h>
#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;
}

View File

@ -0,0 +1,9 @@
#ifndef _EC_PARALLEL_H
#define _EC_PARALLEL_H
#include <stdint.h>
void parport_init(void);
int parport_write(uint8_t * data, int length);
#endif // _EC_PARALLEL_H

122
src/ec/it5570e/parallel.c Normal file
View File

@ -0,0 +1,122 @@
#include <arch/delay.h>
#include <arch/time.h>
#include <ec/kbscan.h>
#include <ec/parallel.h>
#include <stdbool.h>
/*
* 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;
}

View File

@ -0,0 +1,9 @@
#ifndef _EC_PARALLEL_H
#define _EC_PARALLEL_H
#include <stdint.h>
void parport_init(void);
int parport_write(uint8_t * data, int length);
#endif // _EC_PARALLEL_H

122
src/ec/it8587e/parallel.c Normal file
View File

@ -0,0 +1,122 @@
#include <arch/delay.h>
#include <arch/time.h>
#include <ec/kbscan.h>
#include <ec/parallel.h>
#include <stdbool.h>
/*
* 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;
}