# HG changeset patch # User Paul Boddie # Date 1609890411 -3600 # Node ID 8f8a0f10122af370b5654f60b9f8492ca50bb3e6 # Parent 29d152b683bba284585d2ca5ed5f9b9b93dce424# Parent 99302c954b99fa520e9e944084502406e5c4e584 Merged changes from the default branch. diff -r 29d152b683bb -r 8f8a0f10122a pkg/devices/lib/i2c/include/i2c-jz4730.h --- a/pkg/devices/lib/i2c/include/i2c-jz4730.h Sun Jan 03 17:53:56 2021 +0100 +++ b/pkg/devices/lib/i2c/include/i2c-jz4730.h Wed Jan 06 00:46:51 2021 +0100 @@ -39,10 +39,11 @@ Hw::Register_block<32> _regs; Cpm_jz4730_chip *_cpm; uint32_t _frequency; + l4_cap_idx_t _irq=L4_INVALID_CAP; public: I2c_jz4730_channel(l4_addr_t start, Cpm_jz4730_chip *cpm, - uint32_t frequency); + uint32_t frequency, l4_cap_idx_t irq); void disable(); void enable(); @@ -55,20 +56,24 @@ // Transaction control. - void set_address(uint8_t address, bool read); + bool set_address(uint8_t address, bool read); - void request_next(); + void clear_next(); void send_next(); void signal_last(); void start(); void stop(); + bool wait_for_irq(unsigned int timeout); + // Specific status conditions. + bool busy(); bool data_valid(); bool nack(); bool transferred(); + bool transferring(); }; // I2C device control. @@ -84,7 +89,7 @@ I2c_jz4730_chip(l4_addr_t start, l4_addr_t end, Cpm_jz4730_chip *cpm, uint32_t frequency); - I2c_jz4730_channel *get_channel(uint8_t channel); + I2c_jz4730_channel *get_channel(uint8_t channel, l4_cap_idx_t irq); }; #endif /* __cplusplus */ @@ -98,7 +103,7 @@ void *jz4730_i2c_init(l4_addr_t start, l4_addr_t end, void *cpm, uint32_t frequency); -void *jz4730_i2c_get_channel(void *i2c, uint8_t channel); +void *jz4730_i2c_get_channel(void *i2c, uint8_t channel, l4_cap_idx_t irq); void jz4730_i2c_disable(void *i2c_channel); diff -r 29d152b683bb -r 8f8a0f10122a pkg/devices/lib/i2c/src/jz4730.cc --- a/pkg/devices/lib/i2c/src/jz4730.cc Sun Jan 03 17:53:56 2021 +0100 +++ b/pkg/devices/lib/i2c/src/jz4730.cc Wed Jan 06 00:46:51 2021 +0100 @@ -23,6 +23,8 @@ #include #include +#include +#include #include #include @@ -73,8 +75,9 @@ I2c_jz4730_channel::I2c_jz4730_channel(l4_addr_t start, Cpm_jz4730_chip *cpm, - uint32_t frequency) -: _cpm(cpm), _frequency(frequency) + uint32_t frequency, + l4_cap_idx_t irq) +: _cpm(cpm), _frequency(frequency), _irq(irq) { _regs = new Hw::Mmio_register_block<32>(start); } @@ -132,20 +135,34 @@ // Present the address on the bus. -void +bool I2c_jz4730_channel::set_address(uint8_t address, bool read) { + unsigned int limit = 10; + + do + { + if (!wait_for_irq(1000) && !(--limit)) + return false; + } + while (busy()); + start(); _regs[I2c_data] = (address << 1) | (read ? 1 : 0); - while (nack()); send_next(); - if (read) - while ((data_valid() || !transferred()) && !nack()); - else - while (data_valid() && !nack()); + return true; +} + +// Wait up to the given timeout (in microseconds) for an interrupt request, +// returning true if one was delivered. + +bool +I2c_jz4730_channel::wait_for_irq(unsigned int timeout) +{ + return !l4_error(l4_irq_receive(_irq, l4_timeout(L4_IPC_TIMEOUT_NEVER, l4util_micros2l4to(timeout)))); } // Read data from the bus. @@ -155,27 +172,39 @@ { unsigned int nread = 0; - set_address(address, true); + if (!set_address(address, true)) + return 0; + + do + { + if (!wait_for_irq(1000000)) + printf("start (no irq): status = %x\n", (uint32_t) _regs[I2c_status]); + } + while (transferring() || (!data_valid() && !nack())); - if (!nack()) + while (nread < length) { - if (length == 1) + do + { + if (!wait_for_irq(1000000)) + { + stop(); + return nread; + } + } + while (!data_valid() && !nack()); + + if (nack()) + break; + + if ((!nread && (length == 1)) || (nread == length - 2)) signal_last(); - while (nread < length) - { - while (!data_valid()); - - if (nread == length - 2) - signal_last(); - - buf[nread++] = _regs[I2c_data]; - request_next(); - } + buf[nread++] = _regs[I2c_data]; + clear_next(); } stop(); - return nread; } @@ -186,18 +215,44 @@ { unsigned int nwritten = 0; - set_address(address, false); + 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(); + 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()); } stop(); + do + { + if (!wait_for_irq(1000000)) + break; + } while (!transferred()); return nwritten; @@ -214,7 +269,7 @@ // Request the next byte by clearing the data validity flag. void -I2c_jz4730_channel::request_next() +I2c_jz4730_channel::clear_next() { _regs[I2c_status] = _regs[I2c_status] & ~I2c_status_data_valid; } @@ -243,6 +298,22 @@ _regs[I2c_control] = _regs[I2c_control] | I2c_control_nack; } +// Test for bus activity. + +bool +I2c_jz4730_channel::busy() +{ + return (_regs[I2c_status] & I2c_status_busy) ? true : false; +} + +// Test for transfer activity. + +bool +I2c_jz4730_channel::transferring() +{ + return (_regs[I2c_status] & I2c_status_buffer_nempty) ? true : false; +} + // Test for write transfer completion. bool @@ -281,10 +352,10 @@ // Obtain a channel object. Only one channel is supported. I2c_jz4730_channel * -I2c_jz4730_chip::get_channel(uint8_t channel) +I2c_jz4730_chip::get_channel(uint8_t channel, l4_cap_idx_t irq) { if (channel == 0) - return new I2c_jz4730_channel(_start, _cpm, _frequency); + return new I2c_jz4730_channel(_start, _cpm, _frequency, irq); else throw -L4_EINVAL; } @@ -298,9 +369,9 @@ return (void *) new I2c_jz4730_chip(start, end, static_cast(cpm), frequency); } -void *jz4730_i2c_get_channel(void *i2c, uint8_t channel) +void *jz4730_i2c_get_channel(void *i2c, uint8_t channel, l4_cap_idx_t irq) { - return static_cast(i2c)->get_channel(channel); + return static_cast(i2c)->get_channel(channel, irq); } void jz4730_i2c_disable(void *i2c_channel) diff -r 29d152b683bb -r 8f8a0f10122a pkg/landfall-examples/letux400_i2c/letux400_i2c.cc --- a/pkg/landfall-examples/letux400_i2c/letux400_i2c.cc Sun Jan 03 17:53:56 2021 +0100 +++ b/pkg/landfall-examples/letux400_i2c/letux400_i2c.cc Wed Jan 06 00:46:51 2021 +0100 @@ -23,10 +23,27 @@ #include #include +#include +#include +#include +#include +#include + #include +#include +/* Device and resource discovery. */ + +static long item_in_range(long start, long end, long index) +{ + if (start < end) + return start + index; + else + return start - index; +} + /* Scan the I2C bus by performing speculative reads from each device address. */ static void i2c_scan(void *i2c_channel) @@ -87,6 +104,71 @@ void *cpm; void *i2c, *i2c0; + /* Interrupts. */ + + l4_uint32_t i2c_irq_start = 0, i2c_irq_end = 0; + l4_cap_idx_t icucap, irq0cap; + + /* Obtain resource details describing the interrupt for I2C channel 0. */ + + printf("Access IRQ...\n"); + + if (get_irq("jz4730-i2c", &i2c_irq_start, &i2c_irq_end) < 0) + return 1; + + printf("IRQ range at %d...%d.\n", i2c_irq_start, i2c_irq_end); + + /* Obtain capabilities for the interrupt controller and an interrupt. */ + + irq0cap = l4re_util_cap_alloc(); + icucap = l4re_env_get_cap("icu"); + + if (l4_is_invalid_cap(icucap)) + { + printf("No 'icu' capability available in the virtual bus.\n"); + return 1; + } + + if (l4_is_invalid_cap(irq0cap)) + { + printf("Capabilities not available for interrupts.\n"); + return 1; + } + + /* Create interrupt objects. */ + + long err; + + err = l4_error(l4_factory_create_irq(l4re_global_env->factory, irq0cap)); + + if (err) + { + printf("Could not create IRQ object: %lx\n", err); + return 1; + } + + /* Bind interrupt objects to IRQ numbers. */ + + err = l4_error(l4_icu_bind(icucap, + item_in_range(i2c_irq_start, i2c_irq_end, 0), + irq0cap)); + + if (err) + { + printf("Could not bind IRQ to the ICU: %ld\n", err); + return 1; + } + + /* Attach ourselves to the interrupt handler. */ + + err = l4_error(l4_rcv_ep_bind_thread(irq0cap, l4re_env()->main_thread, 0)); + + if (err) + { + printf("Could not attach to IRQs: %ld\n", err); + return 1; + } + /* Peripheral memory. */ l4_addr_t cpm_base = 0, cpm_base_end = 0; @@ -112,7 +194,7 @@ cpm = jz4730_cpm_init(cpm_base); i2c = jz4730_i2c_init(i2c_base, i2c_base_end, cpm, 100000); /* 100 kHz */ - i2c0 = jz4730_i2c_get_channel(i2c, 0); + i2c0 = jz4730_i2c_get_channel(i2c, 0, irq0cap); /* Enable I2C. */ @@ -128,30 +210,43 @@ printf("Written: %d\n", nwritten); - /* Issue selection of device register 0. */ + do + { + /* Issue selection of device register 0. */ - buf[0] = 0; - nwritten = jz4730_i2c_write(i2c0, 0x51, buf, 1); + buf[0] = 0; + nwritten = jz4730_i2c_write(i2c0, 0x51, buf, 1); + + printf("Written: %d\n", nwritten); - printf("Written: %d\n", nwritten); + /* Read the contents of 16 registers. */ + + unsigned int nread = jz4730_i2c_read(i2c0, 0x51, buf, 16); - /* Read the contents of 16 registers. */ - - unsigned int nread = jz4730_i2c_read(i2c0, 0x51, buf, 16); + printf("Read: %d\n", nread); + for (unsigned int i = 0; i < nread; i++) + printf("%02x ", buf[i]); + printf("\n"); - printf("Read: %d\n", nread); - for (unsigned int i = 0; i < nread; i++) - printf("%02x ", buf[i]); - printf("\n"); + /* Show the interpreted date and time. */ + + rtc_datetime(buf); - /* Show the interpreted date and time. */ + /* Scan the bus. */ + + printf("Scan I2C0...\n"); + i2c_scan(i2c0); - rtc_datetime(buf); + sleep(5); + } + while(1); - /* Scan the bus. */ + /* Detach from the interrupt. */ - printf("Scan I2C0...\n"); - i2c_scan(i2c0); + err = l4_error(l4_irq_detach(irq0cap)); + + if (err) + printf("Error detaching from IRQ objects: %ld\n", err); return 0; }