# HG changeset patch # User Paul Boddie # Date 1686429934 -7200 # Node ID b0793bbce22511dd34ee8afad951f9a6b7934a34 # Parent b0145ae94a490f296839182dc14beb523d318985# Parent 5e02ce0315af7b636579826902214f8b7fcae29e Merged the letux400-i2c-state-machine branch. diff -r b0145ae94a49 -r b0793bbce225 pkg/devices/lib/i2c/include/i2c-jz4730.h --- a/pkg/devices/lib/i2c/include/i2c-jz4730.h Sun Jan 31 22:47:14 2021 +0100 +++ b/pkg/devices/lib/i2c/include/i2c-jz4730.h Sat Jun 10 22:45:34 2023 +0200 @@ -31,6 +31,22 @@ #include #include + + +// State machine states. + +enum I2c_jz4730_state +{ + I2c_jz4730_end = 0, + I2c_jz4730_pre_start, + I2c_jz4730_start_read, + I2c_jz4730_perform_read, + I2c_jz4730_perform_write, + I2c_jz4730_stop_write +}; + + + // I2C channel. class I2c_jz4730_channel @@ -51,9 +67,30 @@ unsigned int read(uint8_t address, uint8_t buf[], unsigned int length); unsigned int write(uint8_t address, uint8_t buf[], unsigned int length); + // Statistics. + + unsigned int start_not_possible = 0, read_not_ready = 0, read_not_ready_clear = 0, read_not_ready_stx = 0, + read_not_possible = 0, read_nack = 0, read_complete = 0, + write_not_possible = 0, write_complete = 0, total_reads = 0; + protected: void set_frequency(); + // State machine activities. + + unsigned int _nread, _nwritten, _length, _limit; + uint8_t _address; + uint8_t *_buf; + bool _read; + + void communicate(); + + enum I2c_jz4730_state pre_start(); + enum I2c_jz4730_state start_read(); + enum I2c_jz4730_state perform_read(); + enum I2c_jz4730_state perform_write(); + enum I2c_jz4730_state stop_write(); + // Transaction control. bool set_address(uint8_t address, bool read); @@ -113,4 +150,24 @@ unsigned int jz4730_i2c_write(void *i2c_channel, uint8_t address, uint8_t buf[], unsigned int length); +unsigned int jz4730_i2c_start_not_possible(void *i2c_channel); + +unsigned int jz4730_i2c_read_not_ready(void *i2c_channel); + +unsigned int jz4730_i2c_read_not_ready_clear(void *i2c_channel); + +unsigned int jz4730_i2c_read_not_ready_stx(void *i2c_channel); + +unsigned int jz4730_i2c_read_not_possible(void *i2c_channel); + +unsigned int jz4730_i2c_read_nack(void *i2c_channel); + +unsigned int jz4730_i2c_read_complete(void *i2c_channel); + +unsigned int jz4730_i2c_write_not_possible(void *i2c_channel); + +unsigned int jz4730_i2c_write_complete(void *i2c_channel); + +unsigned int jz4730_i2c_total_reads(void *i2c_channel); + EXTERN_C_END diff -r b0145ae94a49 -r b0793bbce225 pkg/devices/lib/i2c/src/jz4730.cc --- a/pkg/devices/lib/i2c/src/jz4730.cc Sun Jan 31 22:47:14 2021 +0100 +++ b/pkg/devices/lib/i2c/src/jz4730.cc Sat Jun 10 22:45:34 2023 +0200 @@ -133,31 +133,207 @@ _regs[I2c_clock] = division; } -// Present the address on the bus. +// State machine controller. -bool -I2c_jz4730_channel::set_address(uint8_t address, bool read) +void +I2c_jz4730_channel::communicate() { - // Waiting for long enough may eliminate a busy condition and thus permit a - // new transaction. 10ms appears to be long enough, whereas 1ms does not - // appear to be. In case this is insufficient, permit failure. - - unsigned int limit = 10; + enum I2c_jz4730_state state = I2c_jz4730_pre_start; + _limit = 1; do { - if (!wait_for_irq(1000) && !(--limit)) - return false; + wait_for_irq(100000); + + switch (state) + { + case I2c_jz4730_pre_start: + state = pre_start(); + break; + + case I2c_jz4730_start_read: + state = start_read(); + break; + + case I2c_jz4730_perform_read: + state = perform_read(); + break; + + case I2c_jz4730_perform_write: + state = perform_write(); + break; + + case I2c_jz4730_stop_write: + state = stop_write(); + break; + + default: + break; + } } - while (busy()); + while (state != I2c_jz4730_end); +} + +// State machine implementation handlers. + +// Assert not busy state, issue start, present the address on the bus. + +enum I2c_jz4730_state +I2c_jz4730_channel::pre_start() +{ + // Wait again if busy up to the limit. + + if (busy()) + { + if (!(--_limit)) + { + start_not_possible++; + return I2c_jz4730_end; + } + else + return I2c_jz4730_pre_start; + } + + // Use a longer time limit in subsequent activities. + + _limit = 1; + + // Start, send address, proceed to the operation. start(); - _regs[I2c_data] = (address << 1) | (read ? 1 : 0); + _regs[I2c_data] = (_address << 1) | (_read ? 1 : 0); send_next(); - return true; + return _read ? I2c_jz4730_start_read : I2c_jz4730_perform_write; +} + +// Wait for an opportunity to begin reading. + +enum I2c_jz4730_state +I2c_jz4730_channel::start_read() +{ + // Wait again if not ready to read. + + if (transferring() || (!data_valid() && !nack())) + { + read_not_ready++; + if (transferring()) + read_not_ready_stx++; + else + read_not_ready_clear++; + return I2c_jz4730_start_read; + } + + return I2c_jz4730_perform_read; +} + +// Attempt to read from the device. + +enum I2c_jz4730_state +I2c_jz4730_channel::perform_read() +{ + // Wait again if no available data. + + if (!data_valid() && !nack()) + { + if (!(--_limit)) + { + read_not_possible++; + stop(); + return I2c_jz4730_end; + } + else + return I2c_jz4730_perform_read; + } + + // Stop if NACK received. + + if (nack()) + { + read_nack++; + stop(); + return I2c_jz4730_end; + } + + // Signal last byte if appropriate. + + if ((!_nread && (_length == 1)) || (_nread == _length - 2)) + signal_last(); + + // Store and solicit data. + + _buf[_nread++] = _regs[I2c_data]; + clear_next(); + + // Stop if all data received. + + if (_nread >= _length) + { + read_complete++; + stop(); + return I2c_jz4730_end; + } + + // Wait for more data otherwise. + + _limit = 1; + return I2c_jz4730_perform_read; +} + +// Attempt to write to the device. + +enum I2c_jz4730_state +I2c_jz4730_channel::perform_write() +{ + // Wait for data (address or previous data) to be sent. + + if (data_valid() && !nack()) + { + if (!(--_limit)) + { + write_not_possible++; + stop(); + return I2c_jz4730_end; + } + else + return I2c_jz4730_perform_write; + } + + // Stop if all data written or NACK received. + + if ((_nwritten >= _length) || nack()) + { + write_complete++; + stop(); + _limit = 1; + return I2c_jz4730_stop_write; + } + + // Write more data. + + _regs[I2c_data] = _buf[_nwritten++]; + send_next(); + + // Wait for the data to be sent. + + _limit = 1; + return I2c_jz4730_perform_write; +} + +// Terminate the write transaction. + +enum I2c_jz4730_state +I2c_jz4730_channel::stop_write() +{ + if (!transferred()) + { + if (--_limit) + return I2c_jz4730_stop_write; + } + + return I2c_jz4730_end; } // Wait up to the given timeout (in microseconds) for an interrupt request, @@ -174,54 +350,16 @@ unsigned int I2c_jz4730_channel::read(uint8_t address, uint8_t buf[], unsigned int length) { - unsigned int nread = 0; - - if (!set_address(address, true)) - return 0; - - // Wait for an opportunity to begin reading. - - do - { - if (!wait_for_irq(1000000)) - printf("start (no irq): status = %x\n", (uint32_t) _regs[I2c_status]); - } - while (transferring() || (!data_valid() && !nack())); - - // Device apparently unavailable. - - if (nack()) - { - stop(); - return nread; - } - - // Attempt to read from the device. + _nread = 0; + _length = length; + _address = address; + _buf = &buf[0]; + _read = true; + total_reads++; - while (nread < length) - { - do - { - if (!wait_for_irq(1000000)) - { - stop(); - return nread; - } - } - while (!data_valid() && !nack()); + communicate(); - if (nack()) - break; - - if ((!nread && (length == 1)) || (nread == length - 2)) - signal_last(); - - buf[nread++] = _regs[I2c_data]; - clear_next(); - } - - stop(); - return nread; + return _nread; } // Write data to the bus. @@ -229,49 +367,15 @@ unsigned int I2c_jz4730_channel::write(uint8_t address, uint8_t buf[], unsigned int length) { - unsigned int nwritten = 0; - - if (!set_address(address, false)) - return 0; - - do - { - if (!wait_for_irq(1000000)) - { - printf("write (no irq): status = %x\n", (uint32_t) _regs[I2c_status]); - stop(); - return nwritten; - } - } - while (data_valid() && !nack()); - - while ((nwritten < length) && !nack()) - { - _regs[I2c_data] = buf[nwritten++]; - send_next(); + _nwritten = 0; + _length = length; + _address = address; + _buf = &buf[0]; + _read = false; - do - { - if (!wait_for_irq(1000000)) - { - printf("write (no irq): status = %x\n", (uint32_t) _regs[I2c_status]); - stop(); - return nwritten; - } - } - while (data_valid() && !nack()); - } + communicate(); - stop(); - - do - { - if (!wait_for_irq(1000000)) - break; - } - while (!transferred()); - - return nwritten; + return _nwritten; } // Test for data validity. @@ -409,3 +513,33 @@ { return static_cast(i2c_channel)->write(address, buf, length); } + +unsigned int jz4730_i2c_start_not_possible(void *i2c_channel) +{ return reinterpret_cast(i2c_channel)->start_not_possible; } + +unsigned int jz4730_i2c_read_not_ready(void *i2c_channel) +{ return reinterpret_cast(i2c_channel)->read_not_ready; } + +unsigned int jz4730_i2c_read_not_ready_clear(void *i2c_channel) +{ return reinterpret_cast(i2c_channel)->read_not_ready_clear; } + +unsigned int jz4730_i2c_read_not_ready_stx(void *i2c_channel) +{ return reinterpret_cast(i2c_channel)->read_not_ready_stx; } + +unsigned int jz4730_i2c_read_not_possible(void *i2c_channel) +{ return reinterpret_cast(i2c_channel)->read_not_possible; } + +unsigned int jz4730_i2c_read_nack(void *i2c_channel) +{ return reinterpret_cast(i2c_channel)->read_nack; } + +unsigned int jz4730_i2c_read_complete(void *i2c_channel) +{ return reinterpret_cast(i2c_channel)->read_complete; } + +unsigned int jz4730_i2c_write_not_possible(void *i2c_channel) +{ return reinterpret_cast(i2c_channel)->write_not_possible; } + +unsigned int jz4730_i2c_write_complete(void *i2c_channel) +{ return reinterpret_cast(i2c_channel)->write_complete; } + +unsigned int jz4730_i2c_total_reads(void *i2c_channel) +{ return reinterpret_cast(i2c_channel)->total_reads; } diff -r b0145ae94a49 -r b0793bbce225 pkg/landfall-examples/letux400_i2c/letux400_i2c.cc --- a/pkg/landfall-examples/letux400_i2c/letux400_i2c.cc Sun Jan 31 22:47:14 2021 +0100 +++ b/pkg/landfall-examples/letux400_i2c/letux400_i2c.cc Sat Jun 10 22:45:34 2023 +0200 @@ -258,6 +258,22 @@ sleep(5); } + printf("start_not_possible = %d\n" + "read_not_ready = %d\n" + "read_not_ready_clear = %d\n" + "read_not_ready_stx = %d\n" + "read_not_possible = %d\n" + "read_nack = %d\n" + "read_complete = %d\n" + "write_not_possible = %d\n" + "write_complete = %d\n" + "total_reads = %d\n", + jz4730_i2c_start_not_possible(i2c0), jz4730_i2c_read_not_ready(i2c0), jz4730_i2c_read_not_ready_clear(i2c0), jz4730_i2c_read_not_ready_stx(i2c0), + jz4730_i2c_read_not_possible(i2c0), jz4730_i2c_read_nack(i2c0), jz4730_i2c_read_complete(i2c0), + jz4730_i2c_write_not_possible(i2c0), jz4730_i2c_write_complete(i2c0), jz4730_i2c_total_reads(i2c0)); + + sleep(30); + /* Issue a shutdown request. */ buf[0] = 0xd8; buf[1] = 1;