mirror of
https://github.com/genodelabs/genode.git
synced 2025-03-24 04:55:42 +00:00
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 #4170 Fixes #4169
This commit is contained in:
parent
9f099bd61c
commit
ed7d6c74f4
repos/os
include/i2c_session
src/drivers/i2c
@ -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_ */
|
||||
|
Loading…
x
Reference in New Issue
Block a user