i2c: extend API to support transactions

Introduces the notion of a transaction that consists of one or more
messages. Whereby a message has a read or write direction and consists
of one or more bytes.

Issue 
Fixes 
This commit is contained in:
Stefan Kalkowski 2021-05-21 16:08:17 +02:00 committed by Christian Helmuth
parent 9f099bd61c
commit ed7d6c74f4
7 changed files with 106 additions and 125 deletions
repos/os

@ -28,24 +28,9 @@ struct I2c::Session_client : Rpc_client<I2c::Session>
Rpc_client<I2c::Session>(session)
{ }
void write_8bits(uint8_t byte) override
void transmit(Transaction & transaction) override
{
call<Rpc_write_8bits>(byte);
}
uint8_t read_8bits() override
{
return call<Rpc_read_8bits>();
}
void write_16bits(uint16_t word) override
{
call<Rpc_write_16bits>(word);
}
uint16_t read_16bits() override
{
return call<Rpc_read_16bits>();
call<Rpc_transmit>(transaction);
}
};

@ -28,6 +28,32 @@ struct I2c::Connection : Genode::Connection<I2c::Session>, I2c::Session_client
Genode::Connection<Session>(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_ */

@ -17,6 +17,7 @@
#include <session/session.h>
#include <base/stdint.h>
#include <util/array.h>
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<uint8_t, 8>;
/**
* 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<typename ... ARGS>
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<Message,2>
{
using Base = Array<Message,2>;
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_ */

@ -44,29 +44,8 @@ class I2c::Session_component : public Rpc_object<I2c::Session>
_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<uint8_t*>(&data), sizeof(data));
return data;
}
void write_16bits(uint16_t word) override
{
_driver.write(_device_address, reinterpret_cast<uint8_t const *>(&word), sizeof(word));
}
uint16_t read_16bits() override
{
uint16_t data = 0;
_driver.read(_device_address, reinterpret_cast<uint8_t*>(&data), sizeof(data));
return data;
}
void transmit(Transaction & t) override {
_driver.transmit(_device_address, t); }
};

@ -17,6 +17,7 @@
/* Genode includes */
#include <base/env.h>
#include <i2c_session/i2c_session.h>
#include <util/xml_node.h>
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

@ -1,6 +1,7 @@
/*
* \brief Platform specific I2C's driver for imx8q_evk
* \author Jean-Adrien Domage <jean-adrien.domage@gapfruit.com>
* \author Stefan Kalkowski
* \date 2021-02-08
*/
@ -25,7 +26,7 @@ void I2c::Driver::_wait_for_irq()
if (_mmio.read<Mmio::Control::Master_slave_select>() == 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<Mmio::Freq_divider>(0x2a);
_mmio.write<Mmio::Status>(0);
@ -109,57 +108,66 @@ void I2c::Driver::_bus_write(uint8_t data)
if (_mmio.read<Mmio::Status::Rcv_ack>()) {
_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<Mmio::Control::Tx_rx_select>(0);
if (buffer_size > 1) {
if (m.count() > 1) {
_mmio.write<Mmio::Control::Tx_ack_enable>(0);
}
_mmio.read<Mmio::Data>();
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::Status::Irq>());
_mmio.write<Mmio::Status::Irq>(0);
if (i == buffer_size - 1) {
if (idx == m.count() - 1) {
_mmio.write<Mmio::Control::Tx_rx_select>(0);
_mmio.write<Mmio::Control::Master_slave_select>(0);
while (_mmio.read<Mmio::Status::Busy>());
} else if (i == buffer_size - 2) {
} else if (idx == m.count() - 2) {
_mmio.write<Mmio::Control::Tx_ack_enable>(1);
}
buffer_out[i] = _mmio.read<Mmio::Data>();
byte = _mmio.read<Mmio::Data>();
_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<Mmio::Control::Repeat_start>(1);
_bus_busy();
}
if (m.type == I2c::Session::Message::READ) { _read(address, m);
} else { _write(address, m); }
});
_bus_stop();
}

@ -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_ */