os: move imx8q_evk's i2c driver to imx repo

Ref genodelabs/genode#5252
This commit is contained in:
Stefan Kalkowski 2024-06-19 13:55:55 +02:00 committed by Norman Feske
parent 86386548c0
commit d1123ebe4c
10 changed files with 0 additions and 734 deletions

View File

@ -1,84 +0,0 @@
assert_spec arm_v8a
create_boot_directory
import_from_depot [depot_user]/src/[base_src]
import_from_depot [depot_user]/src/init
import_from_depot [depot_user]/src/platform
build {
core
timer
driver/i2c
test/i2c_mcp9808
}
install_config {
<config>
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="platform" caps="800" managing_system="yes">
<binary name="imx8mq_platform"/>
<resource name="RAM" quantum="4M"/>
<provides>
<service name="Platform"/>
<service name="Regulator"/>
</provides>
<config>
<policy label_prefix="imx8q_evk_i2c"> <device name="i2c2"/> </policy>
</config>
</start>
<start name="imx8q_evk_i2c">
<resource name="RAM" quantum="1M"/>
<provides> <service name="I2c"/> </provides>
<config bus_no="2" verbose="yes">
<policy label_prefix="MCP_9808" bus_address="0x18"/>
</config>
<route>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
<start name="i2c_mcp9808">
<resource name="RAM" quantum="1M"/>
<route>
<service name="I2c"> <child name="imx8q_evk_i2c" label="MCP_9808"/> </service>
<any-service> <parent/> </any-service>
</route>
</start>
</config>
}
file copy [select_from_repositories board/[board]/devices] [run_dir]/genode/devices
build_boot_image [build_artifacts]
run_genode_until forever

View File

@ -1,72 +0,0 @@
This directory contains the implementation of the I2C driver component.
Brief
=====
The driver supports I2C bus controller for imx8q_evk only for master mode.
A platform connection is used to retrieve device's capabilities.
The platform driver must provide a device with the following configuration:
!<!-- I2C controller bus 2 -->
!<device name="i2c2">
!
! <!-- I2C bus 2 ctl register -->
! <io_mem address="0x30a30000" size="0x10000"/>
!
! <!-- iomux's I2C config register and iomux's pull-up resistor config -->
! <io_mem address="0x30330000" size="0x1000"/>
!
! <!-- irq number as the controller is interrupt driven -->
! <irq number="68"/>
!
! <!-- I2C bus 2 clock -->
! <clock name="i2c2_clk_root"
! driver_name="i2c2_root_clk"
! parent="system_pll3_clk"/>
! <clock name="i2c2_gate"/>
!
!</device>
!
!<policy label_prefix="imx8q_evk_i2c"> <device name="i2c2"/> </policy>
The io_mem address, irq number and clock might change from one I2C bus
controller to another. The io_mem address for iomux configuration may
remain the same across I2C bus controllers.
The driver can be extended to another platform. An 'I2c::Driver_base'
interface is provided in 'i2c_interface.h'. The specific platform
implementation must expose a 'driver.h' header file that contains a
declaration of 'I2c::Driver' that implements the 'I2c::Driver_base'
interface. An example of a specific platform implementation can be found
in 'os/src/driver/i2c/imx8q_evk'.
Configuration
=============
The driver acts as a server that must be configured via a policy, that states
which client can access a certain device(s) on the bus:
!<start name="imx8q_evk_i2c">
! <resource name="RAM" quantum="1M"/>
! <provides> <service name="I2c"/> </provides>
!
! <config bus_no="2" verbose="yes">
! <policy label_prefix="MCP_9808" bus_address="0x18"/>
! </config>
!
! <route>
! <any-service> <parent/> <any-child/> </any-service>
! </route>
!</start>
The driver's config must contain the 'bus_no' (bus number) attribute,
'bus_no="2"' for bus 2. And device(s) policy with two attributes
'label_prefix' and 'bus_address' which is an address of a slave
device on the bus. A 'verbose' boolean attribute might be specified
if you want the driver to log error messages.
Example
=======
An example of how to use the I2C driver can be found in the test
script 'os/run/i2c_mcp9808.run'.

View File

@ -1,92 +0,0 @@
/*
* \brief I2C session component
* \author Jean-Adrien Domage <jean-adrien.domage@gapfruit.com>
* \date 2021-02-26
*/
/*
* Copyright (C) 2013-2021 Genode Labs GmbH
* Copyright (C) 2021 gapfruit AG
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _I2C_DRIVER__COMPONENT_H_
#define _I2C_DRIVER__COMPONENT_H_
#include <os/session_policy.h>
#include <root/component.h>
#include <i2c_session/i2c_session.h>
#include "i2c_interface.h"
namespace I2c {
using namespace Genode;
class Session_component;
class Root;
}
class I2c::Session_component : public Rpc_object<I2c::Session>
{
private:
Rpc_entrypoint &_ep;
I2c::Driver_base &_driver;
uint8_t const _device_address;
public:
Session_component(Rpc_entrypoint &ep,
I2c::Driver_base &driver,
uint8_t device_address)
:
_ep(ep), _driver(driver), _device_address(device_address)
{ }
void transmit(Transaction & t) override {
_driver.transmit(_device_address, t); }
};
class I2c::Root : public Root_component<I2c::Session_component>
{
private:
Rpc_entrypoint &_ep;
I2c::Driver_base &_driver;
Xml_node const _config;
protected:
Session_component *_create_session(char const *args) override
{
char device_name_c_string[I2c::Device_name::capacity()] { };
Arg_string::find_arg(args, "label").string(device_name_c_string, sizeof(device_name_c_string), "");
Device_name const device_name(device_name_c_string);
Genode::Session_policy policy(device_name, _config);
uint8_t const device_address = policy.attribute_value("bus_address", static_cast<uint8_t>(0));
/* address 0x0 is reserved, so if we return 0x0 it is an error */
if (device_address == 0) {
warning("Session with label '",
device_name,
"' could not be created, no such policy");
throw Service_denied();
}
return new (md_alloc()) I2c::Session_component(_ep, _driver, device_address);
}
public:
Root(Rpc_entrypoint &ep, Allocator &md_alloc,
I2c::Driver_base &driver, Xml_node const &config)
:
Root_component<I2c::Session_component>(&ep, &md_alloc),
_ep(ep), _driver(driver), _config(config)
{ }
};
#endif /* _I2C_DRIVER__COMPONENT_H_ */

View File

@ -1,64 +0,0 @@
/*
* \brief I2C driver base class to be implemented by platform specific
* \author Jean-Adrien Domage <jean-adrien.domage@gapfruit.com>
* \date 2021-02-08
*/
/*
* Copyright (C) 2013-2021 Genode Labs GmbH
* Copyright (C) 2021 gapfruit AG
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _I2C_DRIVER__INTERFACE_H_
#define _I2C_DRIVER__INTERFACE_H_
/* Genode includes */
#include <base/env.h>
#include <i2c_session/i2c_session.h>
#include <util/xml_node.h>
namespace I2c {
using namespace Genode;
using Device_name = String<64>;
class Driver_base;
}
/**
* Base class for platform specific driver to implement
*
* Note about the endianess: the driver is transparent.
*
* 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.
*/
struct I2c::Driver_base : Interface
{
class Bad_bus_no: Exception {};
/**
* Transaction on the I2C bus
*
* \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 transmit(uint8_t address, I2c::Session::Transaction & t) = 0;
/**
* Driver name getter
*
* \return Driver name string
*
* Details this method is overridable to customise the name based on the platform.
*/
virtual char const *name() const { return "i2c driver"; }
};
#endif /* _I2C_INTERFACE_H_ */

View File

@ -1,194 +0,0 @@
/*
* \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
*/
/*
* Copyright (C) 2013-2021 Genode Labs GmbH
* Copyright (C) 2021 gapfruit AG
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <i2c_session/i2c_session.h>
#include "driver.h"
namespace {
Genode::uint8_t _bus_speed_to_divider(Genode::uint16_t bus_speed_khz)
{
/* the table can be found:
* IMX8MMRM.pdf on 5233
*
* The bus base frequency is 25MHz.
*/
if (bus_speed_khz >= 400) return 0x2a; /* divide by 64 maximal speed supported */
if (bus_speed_khz >= 200) return 0x2f; /* divide by 128 */
if (bus_speed_khz >= 100) return 0x33; /* divide by 256 */
if (bus_speed_khz >= 50) return 0x37; /* divide by 512 */
if (bus_speed_khz >= 25) return 0x3B; /* divide by 1024 */
return 0x3F; /* divide by 2048 minimal speed */
}
}
void I2c::Driver::_wait_for_irq()
{
_sem_cnt++;
while (_sem_cnt > 0)
_env.ep().wait_and_dispatch_one_io_signal();
if (_mmio.read<Mmio::Control::Master_slave_select>() == 0) {
_bus_stop();
if (_args.verbose) {
error("Arbitration lost on bus ", _args.bus_no);
}
throw I2c::Session::Bus_error();
}
}
void I2c::Driver::_bus_busy()
{
uint64_t const start_time = _timer.elapsed_ms();
while (!_mmio.read<Mmio::Status::Busy>()) {
uint64_t const current = _timer.elapsed_ms();
if (current - start_time > 1000) {
_bus_stop();
if (_args.verbose) {
error("Timeout on bus ", _args.bus_no);
}
throw I2c::Session::Bus_error();
}
}
}
void I2c::Driver::_bus_reset()
{
_mmio.write<Mmio::Control>(0);
_mmio.write<Mmio::Status>(0);
}
void I2c::Driver::_bus_start()
{
/* input root 90 is 25Mhz select divisor to approximate desired bus speed */
_mmio.write<Mmio::Freq_divider>(_bus_speed_to_divider(_args.bus_speed_khz));
_mmio.write<Mmio::Status>(0);
_mmio.write<Mmio::Control>(Mmio::Control::Enable::bits(1));
uint64_t const start_time = _timer.elapsed_ms();
while (!_mmio.read<Mmio::Control::Enable>()) {
uint64_t const current = _timer.elapsed_ms();
if (current - start_time > 1000) {
_bus_stop();
if (_args.verbose) {
error("Timeout on bus ", _args.bus_no);
}
throw I2c::Session::Bus_error();
}
}
_mmio.write<Mmio::Control::Master_slave_select>(1);
_bus_busy();
_mmio.write<Mmio::Control>(Mmio::Control::Tx_rx_select::bits(1) |
Mmio::Control::Tx_ack_enable::bits(1) |
Mmio::Control::Irq_enable::bits(1) |
Mmio::Control::Master_slave_select::bits(1) |
Mmio::Control::Enable::bits(1));
_mmio.write<Mmio::Status::Ial>(0);
}
void I2c::Driver::_bus_stop()
{
_mmio.write<Mmio::Control>(0);
}
void I2c::Driver::_bus_write(uint8_t data)
{
_mmio.write<Mmio::Data>(data);
do { _wait_for_irq(); }
while (!_mmio.read<Mmio::Status::Irq>());
_mmio.write<Mmio::Status::Irq>(0);
_irq.ack();
if (_mmio.read<Mmio::Status::Rcv_ack>()) {
_bus_stop();
if (_args.verbose) {
error("Slave did not acknowledge on bus ", _args.bus_no);
}
throw I2c::Session::Bus_error();
}
}
void I2c::Driver::_write(uint8_t address, I2c::Session::Message & m)
{
/* LSB must be 0 for writing on the bus, address is on the 7 hightest bits */
_bus_write(address << 1);
m.for_each([&] (unsigned, uint8_t & byte) { _bus_write(byte); });
}
void I2c::Driver::_read(uint8_t address, I2c::Session::Message & m)
{
/* LSB must be 1 for reading on the bus, address is on the 7 hightest bits */
_bus_write((uint8_t)(address << 1 | 1));
_mmio.write<Mmio::Control::Tx_rx_select>(0);
if (m.count() > 1) {
_mmio.write<Mmio::Control::Tx_ack_enable>(0);
}
_mmio.read<Mmio::Data>();
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 (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 (idx == m.count() - 2) {
_mmio.write<Mmio::Control::Tx_ack_enable>(1);
}
byte = (uint8_t)_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();
}

View File

@ -1,91 +0,0 @@
/*
* \brief Platform specific I2C's driver for imx8q_evk
* \author Jean-Adrien Domage <jean-adrien.domage@gapfruit.com>
* \date 2021-02-08
*/
/*
* Copyright (C) 2013-2021 Genode Labs GmbH
* Copyright (C) 2021 gapfruit AG
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _I2C_DRIVER__IMX8Q_EVK_H_
#define _I2C_DRIVER__IMX8Q_EVK_H_
/* Genode includes */
#include <base/attached_dataspace.h>
#include <base/env.h>
#include <base/log.h>
#include <base/mutex.h>
#include <timer_session/connection.h>
#include <platform_session/connection.h>
#include <irq_session/client.h>
/* Local include */
#include <i2c_interface.h>
#include "mmio.h"
namespace I2c {
using namespace Genode;
class Driver;
}
class I2c::Driver: public I2c::Driver_base
{
public:
struct Args
{
bool verbose;
unsigned bus_no;
Device_name device_name;
uint16_t bus_speed_khz;
};
private:
Env &_env;
Args const _args;
Platform::Connection _platform { _env };
Platform::Device _device { _platform };
I2c::Mmio _mmio { _device };
Platform::Device::Irq _irq { _device };
Io_signal_handler<Driver> _irq_handler;
unsigned _sem_cnt = 1;
Timer::Connection _timer { _env };
void _bus_reset();
void _bus_start();
void _bus_stop();
void _bus_write(Genode::uint8_t data);
void _bus_busy();
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)
:
_env(env), _args(args),
_irq_handler(_env.ep(), *this, &Driver::_irq_handle)
{
_irq.sigh(_irq_handler);
_irq_handle();
_irq.ack();
_bus_reset();
}
void transmit(uint8_t address, I2c::Session::Transaction & t) override;
};
#endif /* _I2C_DRIVER__IMX8Q_EVK_H_ */

View File

@ -1,55 +0,0 @@
/*
* \brief I2C mmio region for platform imx8q_evk
* \author Jean-Adrien Domage <jean-adrien.domage@gapfruit.com>
* \date 2021-02-08
*/
/*
* Copyright (C) 2013-2021 Genode Labs GmbH
* Copyright (C) 2021 gapfruit AG
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _I2C_MMIO_H_
#define _I2C_MMIO_H_
#include <platform_session/device.h>
namespace I2c { struct Mmio; }
struct I2c::Mmio: Platform::Device::Mmio<0x12>
{
struct Address : Mmio::Register<0x0, 16> {
struct Adr : Mmio::Register<0x0, 16>::Bitfield<1, 7> {};
};
struct Freq_divider : Mmio::Register<0x4, 16> {};
struct Control : Mmio::Register<0x8, 16> {
struct Repeat_start : Mmio::Register<0x8, 16>::Bitfield<2, 1> {};
struct Tx_ack_enable : Mmio::Register<0x8, 16>::Bitfield<3, 1> {};
struct Tx_rx_select : Mmio::Register<0x8, 16>::Bitfield<4, 1> {};
struct Master_slave_select : Mmio::Register<0x8, 16>::Bitfield<5, 1> {};
struct Irq_enable : Mmio::Register<0x8, 16>::Bitfield<6, 1> {};
struct Enable : Mmio::Register<0x8, 16>::Bitfield<7, 1> {};
};
struct Status : Mmio::Register<0x0C, 16> {
struct Rcv_ack : Mmio::Register<0x0C, 16>::Bitfield<0, 1> {};
struct Irq : Mmio::Register<0x0C, 16>::Bitfield<1, 1> {};
struct Srw : Mmio::Register<0x0C, 16>::Bitfield<2, 1> {};
struct Ial : Mmio::Register<0x0C, 16>::Bitfield<4, 1> {};
struct Busy : Mmio::Register<0x0C, 16>::Bitfield<5, 1> {};
struct Iaas : Mmio::Register<0x0C, 16>::Bitfield<6, 1> {};
struct Icf : Mmio::Register<0x0C, 16>::Bitfield<7, 1> {};
};
struct Data : Mmio::Register<0x10, 16> {};
Mmio(Platform::Device &device) : Platform::Device::Mmio<SIZE> { device } { }
};
#endif /* _I2C_MMIO_H_ */

View File

@ -1,9 +0,0 @@
TARGET = imx8q_evk_i2c
REQUIRES = arm_v8
SRC_CC += driver.cc
INC_DIR += $(PRG_DIR)/src/driver/i2c/imx8q_evk
include $(REP_DIR)/src/driver/i2c/target.inc

View File

@ -1,64 +0,0 @@
/*
* \brief I2C driver main
* \author Jean-Adrien Domage <jean-adrien.domage@gapfruit.com>
* \date 2021-02-08
*/
/*
* Copyright (C) 2013-2021 Genode Labs GmbH
* Copyright (C) 2021 gapfruit AG
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/attached_rom_dataspace.h>
#include <base/component.h>
#include <base/env.h>
#include <base/heap.h>
#include <util/reconstructible.h>
/* Local includes */
#include "component.h"
#include "driver.h"
namespace I2c { struct Main; }
struct I2c::Main
{
private:
Env &_env;
Sliced_heap _sliced_heap { _env.ram(), _env.rm() };
Attached_rom_dataspace _config { _env, "config" };
static I2c::Driver::Args _driver_args_from_config(Xml_node config)
{
constexpr uint16_t const default_bus_speed_khz { 400 };
return {
.verbose = config.attribute_value("verbose", false),
.bus_no = config.attribute_value("bus_no", 0u),
.device_name = config.attribute_value("device_name", Device_name()),
.bus_speed_khz = config.attribute_value("bus_speed_khz", default_bus_speed_khz)
};
}
I2c::Driver _driver { _env, _driver_args_from_config(_config.xml()) };
I2c::Root _root { _env.ep().rpc_ep(), _sliced_heap,
_driver, _config.xml() };
public:
Main(Env &env) : _env(env)
{
_env.parent().announce(env.ep().manage(_root));
log(_driver.name(), " started");
}
};
void Component::construct(Genode::Env &env) { static I2c::Main main(env); }

View File

@ -1,9 +0,0 @@
LIBS += base
INC_DIR += $(PRG_DIR)
INC_DIR += $(REP_DIR)/src/driver/i2c
SRC_CC += main.cc
vpath %.cc $(REP_DIR)/src/driver/i2c
vpath %.cc $(REP_DIR)