diff --git a/repos/os/include/i2c_session/client.h b/repos/os/include/i2c_session/client.h index 730c0e47df..61a52df67d 100644 --- a/repos/os/include/i2c_session/client.h +++ b/repos/os/include/i2c_session/client.h @@ -28,24 +28,9 @@ struct I2c::Session_client : Rpc_client Rpc_client(session) { } - void write_8bits(uint8_t byte) override + void transmit(Transaction & transaction) override { - call(byte); - } - - uint8_t read_8bits() override - { - return call(); - } - - void write_16bits(uint16_t word) override - { - call(word); - } - - uint16_t read_16bits() override - { - return call(); + call(transaction); } }; diff --git a/repos/os/include/i2c_session/connection.h b/repos/os/include/i2c_session/connection.h index a7f724730e..9f748cda55 100644 --- a/repos/os/include/i2c_session/connection.h +++ b/repos/os/include/i2c_session/connection.h @@ -28,6 +28,32 @@ struct I2c::Connection : Genode::Connection, I2c::Session_client Genode::Connection(env, session(env.parent(), "ram_quota=8K, label=%s", label)), Session_client(cap()) { } + + void write_8bits(uint8_t byte) + { + Transaction t { Message(Message::WRITE, byte) }; + transmit(t); + } + + uint8_t read_8bits() + { + Transaction t { Message(Message::READ, 0) }; + transmit(t); + return t.value(0).value(0); + } + + void write_16bits(uint16_t word) + { + Transaction t { Message(Message::WRITE, word & 0xff, word >> 8) }; + transmit(t); + } + + uint16_t read_16bits() + { + Transaction t { Message(Message::READ, 0, 0) }; + transmit(t); + return t.value(0).value(0) | (t.value(0).value(1) << 8); + } }; #endif /* _INCLUDE__I2C_SESSION__CONNECTION_H_ */ diff --git a/repos/os/include/i2c_session/i2c_session.h b/repos/os/include/i2c_session/i2c_session.h index ab240f08d0..7d8d3c2356 100644 --- a/repos/os/include/i2c_session/i2c_session.h +++ b/repos/os/include/i2c_session/i2c_session.h @@ -17,6 +17,7 @@ #include #include +#include namespace I2c { using namespace Genode; @@ -39,68 +40,58 @@ struct I2c::Session : public Genode::Session ***************/ /** - * Execption thrown in case of a bus error + * Exception thrown in case of a bus error * - * This exception is thrown by the driver incase of a timeout, device missing - * acknoledgement and bus arbitration lost. The driver can be configured in the run script - * to log descriptive messages incase of errors. + * This exception is thrown by the driver in case of a timeout, device missing + * acknowledgement and bus arbitration lost. */ class Bus_error : public Exception {}; + using Byte_array = Array; + + /** + * A message to an I2C slave is either a read or write of one or more bytes + */ + struct Message : Byte_array + { + enum Type { READ, WRITE }; + + Type type { READ }; + + Message() {} + + template + Message(Type type, ARGS ... args) + : Byte_array(args...), type(type) {} + }; + + /** + * A transaction to an I2C slave consists of one, or several messages + */ + struct Transaction : Array + { + using Base = Array; + using Base::Base; + }; + /*********************** ** Session interface ** ***********************/ /** - * Write 8 bits on the bus + * Initiate a transaction on the bus * - * \param byte The 8 bits to be sent + * \param transaction The transaction to be transmitted to the I2c host * * \throw I2c::Session::Bus_error An error occured while performing an operation on the bus */ - virtual void write_8bits(uint8_t byte) = 0; + virtual void transmit(Transaction & transaction) = 0; - /** - * Read 8 bits from the bus - * - * \throw I2c::Session::Bus_error An error occured while performing an operation on the bus - * - * \return The 8 received bits - */ - virtual uint8_t read_8bits() = 0; + GENODE_RPC_THROW(Rpc_transmit, void, transmit, + GENODE_TYPE_LIST(Bus_error), Transaction &); - /** - * Write 16 bits on the bus - * - * \param word The 16 bits to be sent - * - * \throw I2c::Session::Bus_error An error occured while performing an operation on the bus - */ - virtual void write_16bits(uint16_t word) = 0; - - /** - * Read 16 bits from the bus - * - * \throw I2c::Session::Bus_error An error occured while performing an operation on the bus - * - * \return The 16 received bits - */ - virtual uint16_t read_16bits() = 0; - - GENODE_RPC_THROW(Rpc_write_8bits, void, write_8bits, - GENODE_TYPE_LIST(Bus_error), - uint8_t); - GENODE_RPC_THROW(Rpc_read_8bits, uint8_t, read_8bits, - GENODE_TYPE_LIST(Bus_error)); - GENODE_RPC_THROW(Rpc_write_16bits, void, write_16bits, - GENODE_TYPE_LIST(Bus_error), - uint16_t); - GENODE_RPC_THROW(Rpc_read_16bits, uint16_t, read_16bits, - GENODE_TYPE_LIST(Bus_error)); - - GENODE_RPC_INTERFACE(Rpc_write_8bits, Rpc_read_8bits, - Rpc_write_16bits, Rpc_read_16bits); + GENODE_RPC_INTERFACE(Rpc_transmit); }; #endif /* _INCLUDE__I2C_SESSION__I2C_SESSION_H_ */ diff --git a/repos/os/src/drivers/i2c/component.h b/repos/os/src/drivers/i2c/component.h index 05f6f448a1..c3fe296188 100644 --- a/repos/os/src/drivers/i2c/component.h +++ b/repos/os/src/drivers/i2c/component.h @@ -44,29 +44,8 @@ class I2c::Session_component : public Rpc_object _ep(ep), _driver(driver), _device_address(device_address) { } - void write_8bits(uint8_t byte) override - { - _driver.write(_device_address, &byte, sizeof(byte)); - } - - uint8_t read_8bits() override - { - uint8_t data = 0; - _driver.read(_device_address, reinterpret_cast(&data), sizeof(data)); - return data; - } - - void write_16bits(uint16_t word) override - { - _driver.write(_device_address, reinterpret_cast(&word), sizeof(word)); - } - - uint16_t read_16bits() override - { - uint16_t data = 0; - _driver.read(_device_address, reinterpret_cast(&data), sizeof(data)); - return data; - } + void transmit(Transaction & t) override { + _driver.transmit(_device_address, t); } }; diff --git a/repos/os/src/drivers/i2c/i2c_interface.h b/repos/os/src/drivers/i2c/i2c_interface.h index 1200d7c673..0209639ee9 100644 --- a/repos/os/src/drivers/i2c/i2c_interface.h +++ b/repos/os/src/drivers/i2c/i2c_interface.h @@ -17,6 +17,7 @@ /* Genode includes */ #include +#include #include namespace I2c { @@ -34,33 +35,21 @@ namespace I2c { * The driver read/write bytes to memory in the order they are * read/write to the bus. * It is the responsability of the component interacting with - * a slave device on the bus to figure out how to interpret the data. + * a slave device on the bus to figure out how to interpret the data. */ struct I2c::Driver_base : Interface { class Bad_bus_no: Exception {}; /** - * Write to the I2C bus + * Transaction on the I2C bus * - * \param address device address - * \param buffer_in buffer containing data to be send - * \param buffer_size size of the buffer to be send - * - * \throw I2c::Session::Bus_error An error occured while performing an operation on the bus - */ - virtual void write(uint8_t address, uint8_t const *buffer_in, size_t buffer_size) = 0; - - /** - * Read from the I2C bus - * - * \param address device address - * \param buffer_out preallocated buffer to store the data in - * \param buffer_size size of the buffer and to be read + * \param address device address + * \param t transaction to perform * * \throw I2c::Session::Bus_error An error occure while performing an operation on the bus */ - virtual void read(uint8_t address, uint8_t *buffer_out, size_t buffer_size) = 0; + virtual void transmit(uint8_t address, I2c::Session::Transaction & t) = 0; /** * Driver name getter diff --git a/repos/os/src/drivers/i2c/imx8q_evk/driver.cc b/repos/os/src/drivers/i2c/imx8q_evk/driver.cc index 1a342e506b..5eac1e07f5 100644 --- a/repos/os/src/drivers/i2c/imx8q_evk/driver.cc +++ b/repos/os/src/drivers/i2c/imx8q_evk/driver.cc @@ -1,6 +1,7 @@ /* * \brief Platform specific I2C's driver for imx8q_evk * \author Jean-Adrien Domage + * \author Stefan Kalkowski * \date 2021-02-08 */ @@ -25,7 +26,7 @@ void I2c::Driver::_wait_for_irq() if (_mmio.read() == 0) { _bus_stop(); if (_args.verbose) { - error("Arbitrationtion lost on bus ", _args.bus_no); + error("Arbitration lost on bus ", _args.bus_no); } throw I2c::Session::Bus_error(); } @@ -57,8 +58,6 @@ void I2c::Driver::_bus_reset() void I2c::Driver::_bus_start() { - _bus_reset(); - /* input root 90 is 25Mhz target is 400Khz, divide by 64 */ _mmio.write(0x2a); _mmio.write(0); @@ -109,57 +108,66 @@ void I2c::Driver::_bus_write(uint8_t data) if (_mmio.read()) { _bus_stop(); if (_args.verbose) { - error("Slave did not acknoledge on bus ", _args.bus_no); + error("Slave did not acknowledge on bus ", _args.bus_no); } throw I2c::Session::Bus_error(); } } -void I2c::Driver::write(uint8_t address, uint8_t const *buffer_in, size_t const buffer_size) +void I2c::Driver::_write(uint8_t address, I2c::Session::Message & m) { - _bus_start(); - /* LSB must be 0 for writing on the bus, address is on the 7 hightest bits */ _bus_write(address << 1); - for (size_t idx = 0; idx < buffer_size; ++idx) { - _bus_write(buffer_in[idx]); - } - _bus_stop(); + m.for_each([&] (unsigned, uint8_t & byte) { _bus_write(byte); }); } -void I2c::Driver::read(uint8_t address, uint8_t *buffer_out, size_t const buffer_size) +void I2c::Driver::_read(uint8_t address, I2c::Session::Message & m) { - _bus_start(); - /* LSB must be 1 for reading on the bus, address is on the 7 hightest bits */ _bus_write(address << 1 | 1); _mmio.write(0); - if (buffer_size > 1) { + if (m.count() > 1) { _mmio.write(0); } _mmio.read(); - for (size_t i = 0; i < buffer_size; ++i) { + m.for_each([&] (unsigned idx, uint8_t & byte) { do { _wait_for_irq(); } while (!_mmio.read()); _mmio.write(0); - if (i == buffer_size - 1) { + if (idx == m.count() - 1) { _mmio.write(0); _mmio.write(0); while (_mmio.read()); - } else if (i == buffer_size - 2) { + } else if (idx == m.count() - 2) { _mmio.write(1); } - buffer_out[i] = _mmio.read(); + byte = _mmio.read(); _irq.ack(); - } + }); +} + + +void I2c::Driver::transmit(uint8_t address, I2c::Session::Transaction & t) +{ + _bus_start(); + + t.for_each([&] (unsigned idx, I2c::Session::Message & m) { + if (idx > 0) { + _mmio.write(1); + _bus_busy(); + } + + if (m.type == I2c::Session::Message::READ) { _read(address, m); + } else { _write(address, m); } + }); _bus_stop(); } diff --git a/repos/os/src/drivers/i2c/imx8q_evk/driver.h b/repos/os/src/drivers/i2c/imx8q_evk/driver.h index 694e20b02b..3df6a10e2e 100644 --- a/repos/os/src/drivers/i2c/imx8q_evk/driver.h +++ b/repos/os/src/drivers/i2c/imx8q_evk/driver.h @@ -68,6 +68,9 @@ class I2c::Driver: public I2c::Driver_base void _wait_for_irq(); void _irq_handle() { _sem_cnt = 0; } + void _write(uint8_t, I2c::Session::Message&); + void _read(uint8_t, I2c::Session::Message&); + public: Driver(Env &env, Args const &args) @@ -78,10 +81,10 @@ class I2c::Driver: public I2c::Driver_base _irq.sigh(_irq_handler); _irq_handle(); _irq.ack(); + _bus_reset(); } - void write(uint8_t, uint8_t const *, size_t) override; - void read(uint8_t, uint8_t *, size_t) override; + void transmit(uint8_t address, I2c::Session::Transaction & t) override; }; #endif /* _I2C_DRIVER__IMX8Q_EVK_H_ */