# HG changeset patch # User Paul Boddie # Date 1563747450 -7200 # Node ID e65cdd33bc715f65c9497963bd4187fc56e7dcc9 # Parent 06dcc07352c5d7ce30dc9199892af228fd51ec1e Introduced interrupt-driven writing to I2C bus devices. Changed the setting of the read threshold to be driven by the read request queuing method, also handling limitations on issuing read requests, possibly due to unprocessed requests (such as writes, if the reads are performed soon enough afterwards) preventing the desired number of requests from being issued. Introduced a convenience function for writing to the example program, handling failure conditions slightly better (employing negative result codes). diff -r 06dcc07352c5 -r e65cdd33bc71 pkg/devices/lib/i2c/include/i2c-jz4780.h --- a/pkg/devices/lib/i2c/include/i2c-jz4780.h Sun Jul 21 22:23:06 2019 +0200 +++ b/pkg/devices/lib/i2c/include/i2c-jz4780.h Mon Jul 22 00:17:30 2019 +0200 @@ -52,9 +52,16 @@ uint32_t frequency); void set_target(uint8_t addr); + + // Reading initiation and execution. + void start_read(uint8_t buf[], unsigned int total); void read(); - void write(uint8_t buf[], unsigned int total); + + // Writing initiation and execution. + + void start_write(uint8_t buf[], unsigned int total); + void write(); // Transaction control. @@ -64,8 +71,11 @@ // Specific status conditions. unsigned int have_read(); + unsigned int have_written(); int read_done(); - int read_incomplete(); + int write_done(); + + int failed(); int read_failed(); int write_failed(); @@ -83,7 +93,7 @@ void set_read_threshold(); void queue_reads(); - void queue_writes(uint8_t buf[], unsigned int *pos, unsigned int total); + void queue_writes(); void store_reads(); }; @@ -124,13 +134,19 @@ void jz4780_i2c_read(void *i2c_channel); +void jz4780_i2c_start_write(void *i2c_channel, uint8_t buf[], unsigned int total); + +void jz4780_i2c_write(void *i2c_channel); + int jz4780_i2c_read_done(void *i2c_channel); +int jz4780_i2c_write_done(void *i2c_channel); + unsigned int jz4780_i2c_have_read(void *i2c_channel); -int jz4780_i2c_read_incomplete(void *i2c_channel); +unsigned int jz4780_i2c_have_written(void *i2c_channel); -void jz4780_i2c_write(void *i2c_channel, uint8_t buf[], unsigned int total); +int jz4780_i2c_failed(void *i2c_channel); void jz4780_i2c_stop(void *i2c_channel); diff -r 06dcc07352c5 -r e65cdd33bc71 pkg/devices/lib/i2c/src/jz4780.cc --- a/pkg/devices/lib/i2c/src/jz4780.cc Sun Jul 21 22:23:06 2019 +0200 +++ b/pkg/devices/lib/i2c/src/jz4780.cc Mon Jul 22 00:17:30 2019 +0200 @@ -422,6 +422,36 @@ return _regs[Int_status] & Int_tx_abort; } +int +I2c_jz4780_channel::read_done() +{ + return _pos == _total; +} + +int +I2c_jz4780_channel::write_done() +{ + return _reqpos == _total; +} + +unsigned +I2c_jz4780_channel::have_read() +{ + return _pos; +} + +unsigned +I2c_jz4780_channel::have_written() +{ + return _reqpos; +} + +int +I2c_jz4780_channel::failed() +{ + return _fail; +} + // Send read commands for empty queue entries. @@ -447,12 +477,12 @@ // Queue read requests for any remaining queue entries. - for (unsigned int i = 0; i < can_queue; i++) + while (can_queue && can_send()) + { _regs[Smb_data_command] = Smb_command_read; - - // Track queued messages and requested positions. - - _reqpos += can_queue; + _reqpos++; + can_queue--; + } // Issue the stop condition after the final read request. // In practice, an extra read request works better since it does not risk a @@ -461,19 +491,30 @@ if (_total == _reqpos) _regs[Smb_data_command] = Smb_command_read; //stop(); + + // Update the threshold to be notified of any reduced remaining amount. + + set_read_threshold(); } // Send write commands for empty queue entries. void -I2c_jz4780_channel::queue_writes(uint8_t buf[], unsigned int *pos, unsigned int total) +I2c_jz4780_channel::queue_writes() { + unsigned int remaining = _total - _reqpos; + unsigned int can_queue = Smb_fifo_limit; + + if (remaining < can_queue) + can_queue = remaining; + // Queue write requests for any remaining queue entries. - while ((*pos < total) && can_send() && !write_failed()) + while (can_queue && can_send()) { - _regs[Smb_data_command] = Smb_command_write | buf[*pos]; - (*pos)++; + _regs[Smb_data_command] = Smb_command_write | _buf[_reqpos]; + _reqpos++; + can_queue--; } } @@ -482,36 +523,27 @@ void I2c_jz4780_channel::store_reads() { - int have_read = 0; - // Read any input and store it in the buffer. - while (have_input() && (_pos < _total)) + while (have_input() && (_pos < _reqpos)) { _buf[_pos] = _regs[Smb_data_command] & 0xff; _pos++; - - have_read = 1; } - - // Update the threshold to be notified of any reduced remaining amount. - - if (have_read) - set_read_threshold(); } void I2c_jz4780_channel::set_read_threshold() { - unsigned int remaining = _total - _pos; + unsigned int queued = _reqpos - _pos; - if (!remaining) + if (!queued) return; - // Read all remaining. + // Read all expected. - if (remaining <= Smb_fifo_limit) - _regs[Rx_fifo_thold] = remaining - 1; + if (queued <= Smb_fifo_limit) + _regs[Rx_fifo_thold] = queued - 1; // Read to limit. @@ -538,53 +570,38 @@ { if (read_failed() || write_failed()) { -#if 0 - printf("intst*=%x\n", (uint32_t) _regs[Int_status]); - printf("rxfifo=%d\n", (uint32_t) _regs[Rx_fifo_count] & 0x3f); - printf("smbabtsrc=%x\n", (uint32_t) _regs[Trans_abort_status0]); -#endif _fail = 1; return; } - if (_pos < _total) - { - if (_regs[Int_status] & Int_rx_full) - store_reads(); - if (_regs[Int_status] & Int_tx_empty) - queue_reads(); - } -} - -int -I2c_jz4780_channel::read_done() -{ - return _pos == _total; -} - -unsigned -I2c_jz4780_channel::have_read() -{ - return _pos; -} - -int -I2c_jz4780_channel::read_incomplete() -{ - return _fail; + if (_regs[Int_status] & Int_rx_full) + store_reads(); + if (_regs[Int_status] & Int_tx_empty) + queue_reads(); } // Write to the target device. void -I2c_jz4780_channel::write(uint8_t buf[], unsigned int total) +I2c_jz4780_channel::start_write(uint8_t buf[], unsigned int total) { - unsigned int pos = 0; + _buf = buf; + _total = total; + _reqpos = 0; + _fail = 0; +} - while ((pos < total) && !write_failed()) +void +I2c_jz4780_channel::write() +{ + if (write_failed()) { - queue_writes(buf, &pos, total); + _fail = 1; + return; } + + if (_regs[Int_status] & Int_tx_empty) + queue_writes(); } // Explicitly stop communication. @@ -656,24 +673,39 @@ static_cast(i2c_channel)->read(); } +void jz4780_i2c_start_write(void *i2c_channel, uint8_t buf[], unsigned int total) +{ + static_cast(i2c_channel)->start_write(buf, total); +} + +void jz4780_i2c_write(void *i2c_channel) +{ + static_cast(i2c_channel)->write(); +} + int jz4780_i2c_read_done(void *i2c_channel) { return static_cast(i2c_channel)->read_done(); } +int jz4780_i2c_write_done(void *i2c_channel) +{ + return static_cast(i2c_channel)->write_done(); +} + unsigned int jz4780_i2c_have_read(void *i2c_channel) { return static_cast(i2c_channel)->have_read(); } -int jz4780_i2c_read_incomplete(void *i2c_channel) +unsigned int jz4780_i2c_have_written(void *i2c_channel) { - return static_cast(i2c_channel)->read_incomplete(); + return static_cast(i2c_channel)->have_written(); } -void jz4780_i2c_write(void *i2c_channel, uint8_t buf[], unsigned int total) +int jz4780_i2c_failed(void *i2c_channel) { - static_cast(i2c_channel)->write(buf, total); + return static_cast(i2c_channel)->failed(); } void jz4780_i2c_stop(void *i2c_channel) diff -r 06dcc07352c5 -r e65cdd33bc71 pkg/landfall-examples/ci20_i2c/ci20_i2c.c --- a/pkg/landfall-examples/ci20_i2c/ci20_i2c.c Sun Jul 21 22:23:06 2019 +0200 +++ b/pkg/landfall-examples/ci20_i2c/ci20_i2c.c Mon Jul 22 00:17:30 2019 +0200 @@ -28,6 +28,7 @@ #include #include #include +#include @@ -77,8 +78,8 @@ printf("No reply from bus.\n"); } -static unsigned int i2c_read(void *i2c_channel, uint8_t *buf, unsigned length, - l4_cap_idx_t irqcap) +static long i2c_read(void *i2c_channel, uint8_t *buf, unsigned length, + l4_cap_idx_t irqcap) { l4_msgtag_t tag; long err; @@ -87,30 +88,46 @@ while (!jz4780_i2c_read_done(i2c_channel)) { - if (jz4780_i2c_read_incomplete(i2c_channel)) - { - printf("Failed\n"); - break; - } - tag = l4_irq_receive(irqcap, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4_timeout_rel(1, 20))); if ((err = l4_ipc_error(tag, l4_utcb()))) - { - printf("Error on IRQ receive: %ld\n", err); - break; - } + return err; + + if (jz4780_i2c_failed(i2c_channel)) + return 0; jz4780_i2c_read(i2c_channel); } - jz4780_i2c_stop(i2c_channel); return jz4780_i2c_have_read(i2c_channel); } +static long i2c_write(void *i2c_channel, uint8_t *buf, unsigned length, + l4_cap_idx_t irqcap) +{ + l4_msgtag_t tag; + long err; + + jz4780_i2c_start_write(i2c_channel, buf, length); + + while (!jz4780_i2c_write_done(i2c_channel)) + { + tag = l4_irq_receive(irqcap, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4_timeout_rel(1, 20))); + + if ((err = l4_ipc_error(tag, l4_utcb()))) + return err; + + if (jz4780_i2c_failed(i2c_channel)) + return 0; + + jz4780_i2c_write(i2c_channel); + } + + return jz4780_i2c_have_written(i2c_channel); +} + static void i2c_scan(void *i2c_channel, l4_cap_idx_t irqcap) { - l4_msgtag_t tag; uint8_t buf[1]; unsigned int address; @@ -128,25 +145,11 @@ jz4780_i2c_set_target(i2c_channel, address); buf[0] = 0; - jz4780_i2c_write(i2c_channel, buf, 1); - jz4780_i2c_start_read(i2c_channel, buf, 1); - - while (!jz4780_i2c_read_done(i2c_channel)) - { - if (jz4780_i2c_read_incomplete(i2c_channel)) - break; - - tag = l4_irq_receive(irqcap, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4_timeout_rel(2, 10))); - - if (l4_ipc_error(tag, l4_utcb())) - break; - - jz4780_i2c_read(i2c_channel); - } - + i2c_write(i2c_channel, buf, 1, irqcap); + i2c_read(i2c_channel, buf, 1, irqcap); jz4780_i2c_stop(i2c_channel); - if (jz4780_i2c_have_read(i2c_channel)) + if (!jz4780_i2c_failed(i2c_channel)) printf("%02x ", address); else printf("?? "); @@ -163,7 +166,7 @@ /* Buffer for reading. */ uint8_t buf[128]; - unsigned int pos; + long pos; /* DDC EDID details. */ @@ -176,11 +179,17 @@ jz4780_i2c_set_target(i2c_channel, 0x50); buf[0] = 0; - jz4780_i2c_write(i2c_channel, buf, 1); + i2c_write(i2c_channel, buf, 1, irqcap); + memset(buf, 0, 128); + pos = i2c_read(i2c_channel, buf, 128, irqcap); + jz4780_i2c_stop(i2c_channel); - printf("Waiting...\n"); + if (pos <= 0) + { + printf("Read failed.\n"); + return; + } - pos = i2c_read(i2c_channel, buf, 128, irqcap); show_data(buf, pos); /* Attempt to decode EDID information. */ @@ -201,25 +210,37 @@ jz4780_i2c_set_target(i2c_channel, 0x5a); buf[0] = 0x32; - jz4780_i2c_write(i2c_channel, buf, 1); + i2c_write(i2c_channel, buf, 1, irqcap); i2c_read(i2c_channel, buf, 1, irqcap); - printf("Read %02x\n", buf[0]); + jz4780_i2c_stop(i2c_channel); + + if (!jz4780_i2c_failed(i2c_channel)) + printf("Read: %02x\n", buf[0]); + else + printf("Read failed.\n"); #if 0 /* Disable the regulator. */ printf("Updating...\n"); + buf[1] = buf[0] & ~0x80; buf[0] = 0x32; - jz4780_i2c_write(i2c_channel, buf, 2); + i2c_write(i2c_channel, buf, 2, irqcap); + jz4780_i2c_stop(i2c_channel); /* Read back from the register. Seemed to give 0xff, which makes no real sense, although the regulator did bring the voltage level low. */ buf[0] = 0x32; - jz4780_i2c_write(i2c_channel, buf, 1); + i2c_write(i2c_channel, buf, 1, irqcap); i2c_read(i2c_channel, buf, 1, irqcap); - printf("Read %02x\n", buf[0]); + jz4780_i2c_stop(i2c_channel); + + if (!jz4780_i2c_failed(i2c_channel)) + printf("Read: %02x\n", buf[0]); + else + printf("Read failed.\n"); #endif }