Merge pull request #35 from system76/wip/parport-logging
Debug logging over parallel port
This commit is contained in:
		@@ -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
 | 
			
		||||
```
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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 (;;) {}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user