Merge pull request #35 from system76/wip/parport-logging
Debug logging over parallel port
This commit is contained in:
commit
61c5ab1b3e
@ -50,7 +50,7 @@ Requirements:
|
|||||||
Use this method for flashing a system already running System76 EC.
|
Use this method for flashing a system already running System76 EC.
|
||||||
|
|
||||||
```
|
```
|
||||||
make BOARD=<vendor>/<model> flash
|
make BOARD=<vendor>/<model> flash_internal
|
||||||
```
|
```
|
||||||
|
|
||||||
### External programmer
|
### External programmer
|
||||||
@ -69,5 +69,5 @@ Use this method for first-time flashing or flashing a bricked controller.
|
|||||||
8. Flash the firmware
|
8. Flash the firmware
|
||||||
|
|
||||||
```
|
```
|
||||||
make BOARD=<vendor>/<model> isp
|
make BOARD=<vendor>/<model> flash_external
|
||||||
```
|
```
|
||||||
|
@ -1,15 +1,76 @@
|
|||||||
# Debugging the EC firmware
|
# 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
|
**Failure to follow steps in order, or performing steps on an
|
||||||
unsupported board, may result in damaged board components.**
|
unsupported board, may result in damaged board components.**
|
||||||
|
|
||||||
Terms used:
|
### Wiring the target
|
||||||
|
|
||||||
- *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
|
|
||||||
|
|
||||||
These steps apply to the following models:
|
These steps apply to the following models:
|
||||||
- darp5
|
- darp5
|
||||||
@ -29,7 +90,7 @@ These steps apply to the following models:
|
|||||||
9. Reconnect battery
|
9. Reconnect battery
|
||||||
10. Replace bottom panel
|
10. Replace bottom panel
|
||||||
|
|
||||||
## Setup
|
### Setup
|
||||||
|
|
||||||
Requirements:
|
Requirements:
|
||||||
- Target wired for EC debugging
|
- Target wired for EC debugging
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
#include <arch/uart.h>
|
#include <arch/uart.h>
|
||||||
|
|
||||||
// Mapping of 24-pin ribbon cable to parallel pins. See schematic
|
// Mapping of 24-pin ribbon cable to parallel pins. See schematic
|
||||||
|
#define IT8587
|
||||||
|
#if defined(IT8587)
|
||||||
#define PINS \
|
#define PINS \
|
||||||
/* Data (KSO0 - KSO7) - bi-directional */ \
|
/* Data (KSO0 - KSO7) - bi-directional */ \
|
||||||
PIN(d0, 1) \
|
PIN(d0, 1) \
|
||||||
@ -41,6 +43,42 @@
|
|||||||
/* Strap1 (KSI5) */ \
|
/* Strap1 (KSI5) */ \
|
||||||
/* 1K-Ohm pull-down resistor */ \
|
/* 1K-Ohm pull-down resistor */ \
|
||||||
PIN(strap_1, 12)
|
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 \
|
#define DATA_BITS \
|
||||||
DATA_BIT(0) \
|
DATA_BIT(0) \
|
||||||
@ -112,42 +150,76 @@ void parallel_hiz(struct Parallel * port) {
|
|||||||
#undef PIN
|
#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
|
// 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);
|
parallel_hiz(port);
|
||||||
|
|
||||||
// Set reset line low
|
// nRESET: output on host, input on peripherals
|
||||||
gpio_set_dir(port->reset_n, true);
|
gpio_set_dir(port->reset_n, host);
|
||||||
|
|
||||||
// Wait 1 microsecond
|
|
||||||
_delay_us(1);
|
_delay_us(1);
|
||||||
|
|
||||||
// Make sure strobes are high outputs
|
// nDATASTB: output on host, input on peripherals
|
||||||
gpio_set(port->data_n, true);
|
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(port->addr_n, true);
|
||||||
gpio_set_dir(port->data_n, true);
|
gpio_set_dir(port->addr_n, host);
|
||||||
gpio_set_dir(port->addr_n, true);
|
|
||||||
|
|
||||||
// Set write line high output
|
// nWRITE: output on host, input on peripherals
|
||||||
gpio_set(port->write_n, true);
|
gpio_set(port->write_n, true);
|
||||||
gpio_set_dir(port->write_n, true);
|
gpio_set_dir(port->write_n, host);
|
||||||
|
|
||||||
// Pull up wait line
|
// nWAIT: input on host, output on peripherals
|
||||||
gpio_set(port->wait_n, true);
|
gpio_set(port->wait_n, host);
|
||||||
|
gpio_set_dir(port->wait_n, !host);
|
||||||
|
|
||||||
// Pull up data lines
|
// Pull up data lines on host, leave floating on peripherals
|
||||||
#define DATA_BIT(B) gpio_set(port->d ## B, true);
|
#define DATA_BIT(B) gpio_set(port->d ## B, host);
|
||||||
DATA_BITS
|
DATA_BITS
|
||||||
#undef DATA_BIT
|
#undef DATA_BIT
|
||||||
|
|
||||||
//TODO: something with straps
|
//TODO: something with straps
|
||||||
|
|
||||||
// Wait 1 microsecond
|
|
||||||
_delay_us(1);
|
_delay_us(1);
|
||||||
|
|
||||||
|
if (host) {
|
||||||
// Set reset line high, ending reset
|
// Set reset line high, ending reset
|
||||||
gpio_set(port->reset_n, true);
|
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
|
//TODO: timeout
|
||||||
int parallel_transaction(struct Parallel * port, uint8_t * data, int length, bool read, bool addr) {
|
int parallel_transaction(struct Parallel * port, uint8_t * data, int length, bool read, bool addr) {
|
||||||
@ -155,24 +227,20 @@ int parallel_transaction(struct Parallel * port, uint8_t * data, int length, boo
|
|||||||
// Set write line low
|
// Set write line low
|
||||||
gpio_set(port->write_n, false);
|
gpio_set(port->write_n, false);
|
||||||
|
|
||||||
// Set data direction out
|
// Set data lines as outputs to write to peripheral
|
||||||
#define DATA_BIT(B) gpio_set_dir(port->d ## B, true);
|
parallel_data_forward(port);
|
||||||
DATA_BITS
|
|
||||||
#undef DATA_BIT
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
uint8_t byte;
|
uint8_t byte = 0;
|
||||||
for (i = 0; i < length; i++) {
|
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)) {}
|
while (gpio_get(port->wait_n)) {}
|
||||||
|
|
||||||
if (!read) {
|
if (!read) {
|
||||||
// Set data low where necessary
|
|
||||||
byte = data[i];
|
byte = data[i];
|
||||||
#define DATA_BIT(B) if (!(byte & (1 << B))) gpio_set(port->d ## B, false);
|
parallel_write_data(port, byte);
|
||||||
DATA_BITS
|
_delay_us(1);
|
||||||
#undef DATA_BIT
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addr) {
|
if (addr) {
|
||||||
@ -182,16 +250,13 @@ int parallel_transaction(struct Parallel * port, uint8_t * data, int length, boo
|
|||||||
// Set data strobe low
|
// Set data strobe low
|
||||||
gpio_set(port->data_n, false);
|
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)) {}
|
while (!gpio_get(port->wait_n)) {}
|
||||||
|
|
||||||
if (read) {
|
if (read) {
|
||||||
byte = 0;
|
data[i] = parallel_read_data(port);
|
||||||
#define DATA_BIT(B) if (gpio_get(port->d ## B)) byte |= (1 << B);
|
|
||||||
DATA_BITS
|
|
||||||
#undef DATA_BIT
|
|
||||||
data[i] = byte;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addr) {
|
if (addr) {
|
||||||
@ -201,20 +266,18 @@ int parallel_transaction(struct Parallel * port, uint8_t * data, int length, boo
|
|||||||
// Set data strobe high
|
// Set data strobe high
|
||||||
gpio_set(port->data_n, true);
|
gpio_set(port->data_n, true);
|
||||||
}
|
}
|
||||||
|
// XXX: Arduino peripheral not fast enough to get the data?
|
||||||
|
_delay_us(5);
|
||||||
|
|
||||||
if (!read) {
|
if (!read) {
|
||||||
// Set data high where necessary
|
// Reset data lines to high
|
||||||
#define DATA_BIT(B) if (!(byte & (1 << B))) gpio_set(port->d ## B, true);
|
parallel_data_set_high(port, byte);
|
||||||
DATA_BITS
|
|
||||||
#undef DATA_BIT
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!read) {
|
if (!read) {
|
||||||
// Set data direction in
|
// Set data lines back to inputs
|
||||||
#define DATA_BIT(B) gpio_set_dir(port->d ## B, false);
|
parallel_data_reverse(port);
|
||||||
DATA_BITS
|
|
||||||
#undef DATA_BIT
|
|
||||||
|
|
||||||
// Set write line high
|
// Set write line high
|
||||||
gpio_set(port->write_n, true);
|
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_read(P, D, L) parallel_transaction(P, D, L, true, false)
|
||||||
#define parallel_write(P, D, L) parallel_transaction(P, D, L, false, 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_INDAR1 = 0x05;
|
||||||
static uint8_t ADDRESS_INDDR = 0x08;
|
static uint8_t ADDRESS_INDDR = 0x08;
|
||||||
|
|
||||||
@ -343,7 +445,7 @@ int parallel_main(void) {
|
|||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
struct Parallel * port = &PORT;
|
struct Parallel * port = &PORT;
|
||||||
parallel_reset(port);
|
parallel_reset(port, true);
|
||||||
|
|
||||||
static uint8_t data[128];
|
static uint8_t data[128];
|
||||||
char command;
|
char command;
|
||||||
@ -352,6 +454,9 @@ int parallel_main(void) {
|
|||||||
uint8_t address;
|
uint8_t address;
|
||||||
bool set_address = false;
|
bool set_address = false;
|
||||||
bool program_aai = false;
|
bool program_aai = false;
|
||||||
|
|
||||||
|
unsigned char console_msg[] = "Entering console mode\n";
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
// Read command and length
|
// Read command and length
|
||||||
res = serial_read(data, 2);
|
res = serial_read(data, 2);
|
||||||
@ -393,6 +498,26 @@ int parallel_main(void) {
|
|||||||
|
|
||||||
break;
|
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
|
// Echo
|
||||||
case 'E':
|
case 'E':
|
||||||
// Read data from serial
|
// Read data from serial
|
||||||
|
@ -1,46 +1,25 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <arch/gpio.h>
|
#include <arch/gpio.h>
|
||||||
#include <arch/i2c_slave.h>
|
|
||||||
#include <arch/uart.h>
|
#include <arch/uart.h>
|
||||||
#include <board/battery.h>
|
|
||||||
#include <board/i2c.h>
|
|
||||||
|
|
||||||
void init(void) {
|
void init(void) {
|
||||||
uart_stdio_init(0, __CONSOLE_BAUD__);
|
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);
|
struct Gpio LED = GPIO(B, 5);
|
||||||
|
|
||||||
|
//TODO: .h file
|
||||||
|
void parallel_main(void);
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
init();
|
init();
|
||||||
|
|
||||||
gpio_set_dir(&LED, true);
|
gpio_set_dir(&LED, true);
|
||||||
gpio_set(&LED, false);
|
gpio_set(&LED, false);
|
||||||
printf("Hello from System76 EC for the Arduino Uno!\n");
|
|
||||||
|
|
||||||
battery_debug();
|
parallel_main();
|
||||||
|
|
||||||
for (;;) {
|
// If parallel_main exits with an error, wait for reset
|
||||||
i2c_slave_init(0x76, i2c_slave_new, i2c_slave_recv, i2c_slave_send);
|
for (;;) {}
|
||||||
int c = getchar();
|
|
||||||
i2c_slave_stop();
|
|
||||||
if (c == '\r') {
|
|
||||||
putchar('\n');
|
|
||||||
battery_debug();
|
|
||||||
} else if (c > 0) {
|
|
||||||
putchar(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
538
src/board/arduino/uno/parallel.c
Normal file
538
src/board/arduino/uno/parallel.c
Normal 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;
|
||||||
|
}
|
@ -22,6 +22,9 @@ CFLAGS+=-DI2C_SMBUS=I2C_0
|
|||||||
# Set keyboard LED I2C bus
|
# Set keyboard LED I2C bus
|
||||||
CFLAGS+=-DI2C_KBLED=I2C_1
|
CFLAGS+=-DI2C_KBLED=I2C_1
|
||||||
|
|
||||||
|
# Enable debug logging over keyboard parallel port
|
||||||
|
#CFLAGS+=-DPARPORT_DEBUG
|
||||||
|
|
||||||
# Set scratch ROM parameters
|
# Set scratch ROM parameters
|
||||||
SCRATCH_OFFSET=1024
|
SCRATCH_OFFSET=1024
|
||||||
SCRATCH_SIZE=1024
|
SCRATCH_SIZE=1024
|
||||||
|
@ -23,6 +23,10 @@
|
|||||||
#include <common/macro.h>
|
#include <common/macro.h>
|
||||||
#include <common/version.h>
|
#include <common/version.h>
|
||||||
|
|
||||||
|
#ifdef PARPORT_DEBUG
|
||||||
|
#include <ec/parallel.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
void external_0(void) __interrupt(0) {}
|
void external_0(void) __interrupt(0) {}
|
||||||
// timer_0 is in time.c
|
// timer_0 is in time.c
|
||||||
void timer_0(void) __interrupt(1);
|
void timer_0(void) __interrupt(1);
|
||||||
@ -43,7 +47,11 @@ void init(void) {
|
|||||||
ecpm_init();
|
ecpm_init();
|
||||||
kbc_init();
|
kbc_init();
|
||||||
kbled_init();
|
kbled_init();
|
||||||
|
#ifdef PARPORT_DEBUG
|
||||||
|
parport_init();
|
||||||
|
#else
|
||||||
kbscan_init();
|
kbscan_init();
|
||||||
|
#endif
|
||||||
peci_init();
|
peci_init();
|
||||||
pmc_init();
|
pmc_init();
|
||||||
pwm_init();
|
pwm_init();
|
||||||
@ -105,8 +113,10 @@ void main(void) {
|
|||||||
power_event();
|
power_event();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
|
#ifndef PARPORT_DEBUG
|
||||||
// Scans keyboard and sends keyboard packets
|
// Scans keyboard and sends keyboard packets
|
||||||
kbscan_event();
|
kbscan_event();
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
// Passes through touchpad packets
|
// Passes through touchpad packets
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
#include <ec/i2c.h>
|
#include <ec/i2c.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef PARPORT_DEBUG
|
||||||
|
#include <ec/parallel.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
int putchar(int c) {
|
int putchar(int c) {
|
||||||
unsigned char byte = (unsigned char)c;
|
unsigned char byte = (unsigned char)c;
|
||||||
smfi_debug(byte);
|
smfi_debug(byte);
|
||||||
@ -18,6 +22,9 @@ int putchar(int c) {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef I2C_DEBUGGER
|
#ifdef I2C_DEBUGGER
|
||||||
i2c_send(&I2C_SMBUS, I2C_DEBUGGER, &byte, 1);
|
i2c_send(&I2C_SMBUS, I2C_DEBUGGER, &byte, 1);
|
||||||
|
#endif
|
||||||
|
#ifdef PARPORT_DEBUG
|
||||||
|
parport_write(&byte, 1);
|
||||||
#endif
|
#endif
|
||||||
return (int)byte;
|
return (int)byte;
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,9 @@ CFLAGS+=-DLEVEL=4
|
|||||||
# Set battery I2C bus
|
# Set battery I2C bus
|
||||||
CFLAGS+=-DI2C_SMBUS=I2C_0
|
CFLAGS+=-DI2C_SMBUS=I2C_0
|
||||||
|
|
||||||
|
# Enable debug logging over keyboard parallel port
|
||||||
|
#CFLAGS+=-DPARPORT_DEBUG
|
||||||
|
|
||||||
# Set scratch ROM parameters
|
# Set scratch ROM parameters
|
||||||
SCRATCH_OFFSET=1024
|
SCRATCH_OFFSET=1024
|
||||||
SCRATCH_SIZE=1024
|
SCRATCH_SIZE=1024
|
||||||
|
@ -23,6 +23,10 @@
|
|||||||
#include <common/macro.h>
|
#include <common/macro.h>
|
||||||
#include <common/version.h>
|
#include <common/version.h>
|
||||||
|
|
||||||
|
#ifdef PARPORT_DEBUG
|
||||||
|
#include <ec/parallel.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
void external_0(void) __interrupt(0) {}
|
void external_0(void) __interrupt(0) {}
|
||||||
// timer_0 is in time.c
|
// timer_0 is in time.c
|
||||||
void timer_0(void) __interrupt(1);
|
void timer_0(void) __interrupt(1);
|
||||||
@ -43,7 +47,11 @@ void init(void) {
|
|||||||
ecpm_init();
|
ecpm_init();
|
||||||
kbc_init();
|
kbc_init();
|
||||||
kbled_init();
|
kbled_init();
|
||||||
|
#ifdef PARPORT_DEBUG
|
||||||
|
parport_init();
|
||||||
|
#else
|
||||||
kbscan_init();
|
kbscan_init();
|
||||||
|
#endif
|
||||||
peci_init();
|
peci_init();
|
||||||
pmc_init();
|
pmc_init();
|
||||||
pwm_init();
|
pwm_init();
|
||||||
@ -105,8 +113,10 @@ void main(void) {
|
|||||||
power_event();
|
power_event();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
|
#ifndef PARPORT_DEBUG
|
||||||
// Scans keyboard and sends keyboard packets
|
// Scans keyboard and sends keyboard packets
|
||||||
kbscan_event();
|
kbscan_event();
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
// Passes through touchpad packets
|
// Passes through touchpad packets
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
#include <ec/i2c.h>
|
#include <ec/i2c.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef PARPORT_DEBUG
|
||||||
|
#include <ec/parallel.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
int putchar(int c) {
|
int putchar(int c) {
|
||||||
unsigned char byte = (unsigned char)c;
|
unsigned char byte = (unsigned char)c;
|
||||||
smfi_debug(byte);
|
smfi_debug(byte);
|
||||||
@ -18,6 +22,9 @@ int putchar(int c) {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef I2C_DEBUGGER
|
#ifdef I2C_DEBUGGER
|
||||||
i2c_send(&I2C_SMBUS, I2C_DEBUGGER, &byte, 1);
|
i2c_send(&I2C_SMBUS, I2C_DEBUGGER, &byte, 1);
|
||||||
|
#endif
|
||||||
|
#ifdef PARPORT_DEBUG
|
||||||
|
parport_write(&byte, 1);
|
||||||
#endif
|
#endif
|
||||||
return (int)byte;
|
return (int)byte;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,9 @@ CFLAGS+=-DI2C_SMBUS=I2C_4
|
|||||||
# Set type-c port manager I2C bus
|
# Set type-c port manager I2C bus
|
||||||
CFLAGS+=-DI2C_TCPM=I2C_1
|
CFLAGS+=-DI2C_TCPM=I2C_1
|
||||||
|
|
||||||
|
# Enable debug logging over keyboard parallel port
|
||||||
|
#CFLAGS+=-DPARPORT_DEBUG
|
||||||
|
|
||||||
# Set scratch ROM parameters
|
# Set scratch ROM parameters
|
||||||
SCRATCH_OFFSET=1024
|
SCRATCH_OFFSET=1024
|
||||||
SCRATCH_SIZE=1024
|
SCRATCH_SIZE=1024
|
||||||
|
@ -24,6 +24,10 @@
|
|||||||
#include <common/macro.h>
|
#include <common/macro.h>
|
||||||
#include <common/version.h>
|
#include <common/version.h>
|
||||||
|
|
||||||
|
#ifdef PARPORT_DEBUG
|
||||||
|
#include <ec/parallel.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
void external_0(void) __interrupt(0) {}
|
void external_0(void) __interrupt(0) {}
|
||||||
// timer_0 is in time.c
|
// timer_0 is in time.c
|
||||||
void timer_0(void) __interrupt(1);
|
void timer_0(void) __interrupt(1);
|
||||||
@ -44,7 +48,11 @@ void init(void) {
|
|||||||
ecpm_init();
|
ecpm_init();
|
||||||
kbc_init();
|
kbc_init();
|
||||||
kbled_init();
|
kbled_init();
|
||||||
|
#ifdef PARPORT_DEBUG
|
||||||
|
parport_init();
|
||||||
|
#else
|
||||||
kbscan_init();
|
kbscan_init();
|
||||||
|
#endif
|
||||||
peci_init();
|
peci_init();
|
||||||
pmc_init();
|
pmc_init();
|
||||||
pwm_init();
|
pwm_init();
|
||||||
@ -107,8 +115,10 @@ void main(void) {
|
|||||||
power_event();
|
power_event();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
|
#ifndef PARPORT_DEBUG
|
||||||
// Scans keyboard and sends keyboard packets
|
// Scans keyboard and sends keyboard packets
|
||||||
kbscan_event();
|
kbscan_event();
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
// Passes through touchpad packets
|
// Passes through touchpad packets
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
#include <ec/i2c.h>
|
#include <ec/i2c.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef PARPORT_DEBUG
|
||||||
|
#include <ec/parallel.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
int putchar(int c) {
|
int putchar(int c) {
|
||||||
unsigned char byte = (unsigned char)c;
|
unsigned char byte = (unsigned char)c;
|
||||||
smfi_debug(byte);
|
smfi_debug(byte);
|
||||||
@ -18,6 +22,9 @@ int putchar(int c) {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef I2C_DEBUGGER
|
#ifdef I2C_DEBUGGER
|
||||||
i2c_send(&I2C_SMBUS, I2C_DEBUGGER, &byte, 1);
|
i2c_send(&I2C_SMBUS, I2C_DEBUGGER, &byte, 1);
|
||||||
|
#endif
|
||||||
|
#ifdef PARPORT_DEBUG
|
||||||
|
parport_write(&byte, 1);
|
||||||
#endif
|
#endif
|
||||||
return (int)byte;
|
return (int)byte;
|
||||||
}
|
}
|
||||||
|
9
src/ec/it5570e/include/ec/parallel.h
Normal file
9
src/ec/it5570e/include/ec/parallel.h
Normal 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
122
src/ec/it5570e/parallel.c
Normal 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;
|
||||||
|
}
|
9
src/ec/it8587e/include/ec/parallel.h
Normal file
9
src/ec/it8587e/include/ec/parallel.h
Normal 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
122
src/ec/it8587e/parallel.c
Normal 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;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user