mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-11 13:22:33 +00:00
add I2c driver for imx8q based platform
This commit introduces a new i2c driver for the imx8q_evk based platform. Fixes #4052
This commit is contained in:
parent
eb89b13327
commit
32169cd137
@ -60,8 +60,8 @@ Bootstrap::Platform::Board::Board()
|
||||
{ 0x303301EC, 0x0 },
|
||||
{ 0x303301FC, 0x1 },
|
||||
{ 0x30330200, 0x1 },
|
||||
{ 0x3033021C, 0x5 },
|
||||
{ 0x30330220, 0x5 },
|
||||
{ 0x3033021C, 0x10 }, /* Enable SION I2c2_scl */
|
||||
{ 0x30330220, 0x10 }, /* Enable SION I2c2_sda */
|
||||
{ 0x30330224, 0x10 },
|
||||
{ 0x30330228, 0x10 },
|
||||
{ 0x3033022C, 0x12 },
|
||||
@ -126,8 +126,8 @@ Bootstrap::Platform::Board::Board()
|
||||
{ 0x30330464, 0x49 },
|
||||
{ 0x30330468, 0x49 },
|
||||
{ 0x3033046C, 0x16 },
|
||||
{ 0x30330484, 0x16 },
|
||||
{ 0x30330488, 0x16 },
|
||||
{ 0x30330484, 0x67 }, /* I2c2_scl pullup resistor 40 ohm */
|
||||
{ 0x30330488, 0x67 }, /* I2c2_sda pullup resistor 40 ohm */
|
||||
{ 0x3033048C, 0x67 },
|
||||
{ 0x30330490, 0x67 },
|
||||
{ 0x30330494, 0x76 },
|
||||
|
23
repos/os/include/i2c_session/capability.h
Normal file
23
repos/os/include/i2c_session/capability.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* \brief I2C session capability type
|
||||
* \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 _INCLUDE__I2C_SESSION__CAPABILITY_H_
|
||||
#define _INCLUDE__I2C_SESSION__CAPABILITY_H_
|
||||
|
||||
#include <base/capability.h>
|
||||
#include <i2c_session/i2c_session.h>
|
||||
|
||||
namespace I2c { typedef Genode::Capability<Session> Session_capability; }
|
||||
|
||||
#endif /*_INCLUDE__I2C_SESSION__CAPABILITY_H_ */
|
52
repos/os/include/i2c_session/client.h
Normal file
52
repos/os/include/i2c_session/client.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* \brief I2C session client implementation
|
||||
* \author Jean-Adrien Domage <jean-adrien.domage@gapfruit.com>
|
||||
* \date 2021-02-25
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 _INCLUDE__I2C_SESSION__CLIENT_H_
|
||||
#define _INCLUDE__I2C_SESSION__CLIENT_H_
|
||||
|
||||
#include <base/rpc_client.h>
|
||||
#include <i2c_session/capability.h>
|
||||
|
||||
namespace I2c { struct Session_client; }
|
||||
|
||||
|
||||
struct I2c::Session_client : Rpc_client<I2c::Session>
|
||||
{
|
||||
explicit Session_client(I2c::Session_capability session)
|
||||
:
|
||||
Rpc_client<I2c::Session>(session)
|
||||
{ }
|
||||
|
||||
void write_8bits(uint8_t byte) 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>();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__I2C_SESSION__CLIENT_H_ */
|
33
repos/os/include/i2c_session/connection.h
Normal file
33
repos/os/include/i2c_session/connection.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* \brief I2C session connection
|
||||
* \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 _INCLUDE__I2C_SESSION__CONNECTION_H_
|
||||
#define _INCLUDE__I2C_SESSION__CONNECTION_H_
|
||||
|
||||
#include <i2c_session/client.h>
|
||||
#include <base/connection.h>
|
||||
|
||||
namespace I2c { struct Connection; }
|
||||
|
||||
|
||||
struct I2c::Connection : Genode::Connection<I2c::Session>, I2c::Session_client
|
||||
{
|
||||
Connection(Genode::Env &env, char const *label = "")
|
||||
:
|
||||
Genode::Connection<Session>(env, session(env.parent(), "ram_quota=8K, label=%s", label)),
|
||||
Session_client(cap())
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__I2C_SESSION__CONNECTION_H_ */
|
106
repos/os/include/i2c_session/i2c_session.h
Normal file
106
repos/os/include/i2c_session/i2c_session.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* \brief I2C session client implementation
|
||||
* \author Jean-Adrien Domage <jean-adrien.domage@gapfruit.com>
|
||||
* \date 2021-02-25
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 _INCLUDE__I2C_SESSION__I2C_SESSION_H_
|
||||
#define _INCLUDE__I2C_SESSION__I2C_SESSION_H_
|
||||
|
||||
#include <session/session.h>
|
||||
#include <base/stdint.h>
|
||||
|
||||
namespace I2c {
|
||||
using namespace Genode;
|
||||
struct Session;
|
||||
}
|
||||
|
||||
|
||||
struct I2c::Session : public Genode::Session
|
||||
{
|
||||
/**
|
||||
* \noapi
|
||||
*/
|
||||
static char const *service_name() { return "I2c"; }
|
||||
|
||||
enum { CAP_QUOTA = 2 };
|
||||
|
||||
|
||||
/***************
|
||||
** Exception **
|
||||
***************/
|
||||
|
||||
/**
|
||||
* Execption 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.
|
||||
*/
|
||||
class Bus_error : public Exception {};
|
||||
|
||||
|
||||
/***********************
|
||||
** Session interface **
|
||||
***********************/
|
||||
|
||||
/**
|
||||
* Write 8 bits on the bus
|
||||
*
|
||||
* \param byte The 8 bits to be sent
|
||||
*
|
||||
* \throw I2c::Session::Bus_error An error occured while performing an operation on the bus
|
||||
*/
|
||||
virtual void write_8bits(uint8_t byte) = 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;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__I2C_SESSION__I2C_SESSION_H_ */
|
106
repos/os/run/i2c_mcp9808.run
Normal file
106
repos/os/run/i2c_mcp9808.run
Normal file
@ -0,0 +1,106 @@
|
||||
|
||||
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_drv
|
||||
|
||||
set build_components {
|
||||
core
|
||||
timer
|
||||
drivers/i2c
|
||||
test/i2c_mcp9808
|
||||
}
|
||||
|
||||
source ${genode_dir}/repos/base/run/platform_drv.inc
|
||||
|
||||
build $build_components
|
||||
|
||||
set 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_drv" caps="800" managing_system="yes">
|
||||
<binary name="imx8mq_platform_drv"/>
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<provides>
|
||||
<service name="Platform"/>
|
||||
<service name="Regulator"/>
|
||||
</provides>
|
||||
<config>
|
||||
|
||||
<device name="i2c2">
|
||||
<!-- I2C ctl register -->
|
||||
<io_mem address="0x30a30000" size="0x10000"/>
|
||||
|
||||
<irq number="68"/>
|
||||
<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_drv"> <device name="i2c2"/> </policy>
|
||||
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="imx8q_evk_i2c_drv">
|
||||
<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_drv" label="MCP_9808"/> </service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
|
||||
</start>
|
||||
|
||||
</config>
|
||||
}
|
||||
|
||||
install_config $config
|
||||
|
||||
set boot_modules {
|
||||
core
|
||||
timer
|
||||
imx8q_evk_i2c_drv
|
||||
i2c_mcp9808
|
||||
}
|
||||
|
||||
build_boot_image $boot_modules
|
||||
|
||||
run_genode_until forever
|
72
repos/os/src/drivers/i2c/README
Normal file
72
repos/os/src/drivers/i2c/README
Normal file
@ -0,0 +1,72 @@
|
||||
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_drv"> <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/drivers/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_drv">
|
||||
! <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'.
|
113
repos/os/src/drivers/i2c/component.h
Normal file
113
repos/os/src/drivers/i2c/component.h
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* \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 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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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_ */
|
75
repos/os/src/drivers/i2c/i2c_interface.h
Normal file
75
repos/os/src/drivers/i2c/i2c_interface.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* \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 <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 {};
|
||||
|
||||
/**
|
||||
* Write to 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
|
||||
*
|
||||
* \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;
|
||||
|
||||
/**
|
||||
* 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_ */
|
165
repos/os/src/drivers/i2c/imx8q_evk/driver.cc
Normal file
165
repos/os/src/drivers/i2c/imx8q_evk/driver.cc
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* \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.
|
||||
*/
|
||||
|
||||
#include <i2c_session/i2c_session.h>
|
||||
#include "driver.h"
|
||||
|
||||
|
||||
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("Arbitrationtion 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()
|
||||
{
|
||||
_bus_reset();
|
||||
|
||||
/* input root 90 is 25Mhz target is 400Khz, divide by 64 */
|
||||
_mmio.write<Mmio::Freq_divider>(0x2a);
|
||||
_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_irq();
|
||||
|
||||
if (_mmio.read<Mmio::Status::Rcv_ack>()) {
|
||||
_bus_stop();
|
||||
if (_args.verbose) {
|
||||
error("Slave did not acknoledge 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)
|
||||
{
|
||||
_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();
|
||||
}
|
||||
|
||||
|
||||
void I2c::Driver::read(uint8_t address, uint8_t *buffer_out, size_t const buffer_size)
|
||||
{
|
||||
_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) {
|
||||
_mmio.write<Mmio::Control::Tx_ack_enable>(0);
|
||||
}
|
||||
_mmio.read<Mmio::Data>();
|
||||
|
||||
for (size_t i = 0; i < buffer_size; ++i) {
|
||||
|
||||
do { _wait_for_irq(); }
|
||||
while (!_mmio.read<Mmio::Status::Irq>());
|
||||
|
||||
_mmio.write<Mmio::Status::Irq>(0);
|
||||
|
||||
if (i == buffer_size - 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) {
|
||||
_mmio.write<Mmio::Control::Tx_ack_enable>(1);
|
||||
}
|
||||
|
||||
buffer_out[i] = _mmio.read<Mmio::Data>();
|
||||
_irq.ack_irq();
|
||||
}
|
||||
|
||||
_bus_stop();
|
||||
}
|
92
repos/os/src/drivers/i2c/imx8q_evk/driver.h
Normal file
92
repos/os/src/drivers/i2c/imx8q_evk/driver.h
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* \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;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Env &_env;
|
||||
Args const _args;
|
||||
|
||||
Platform::Connection _platform_connection { _env };
|
||||
Platform::Device_client _device { _platform_connection.device_by_index(0) };
|
||||
|
||||
/* iomem region for I2C control register */
|
||||
Attached_dataspace _i2c_ctl_ds { _env.rm(), _device.io_mem_dataspace(0) };
|
||||
I2c::Mmio _mmio { reinterpret_cast<addr_t>(_i2c_ctl_ds.local_addr<uint16_t>()) };
|
||||
|
||||
/* interrupt handler */
|
||||
Irq_session_client _irq { _device.irq() };
|
||||
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; }
|
||||
|
||||
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_irq();
|
||||
}
|
||||
|
||||
void write(uint8_t, uint8_t const *, size_t) override;
|
||||
void read(uint8_t, uint8_t *, size_t) override;
|
||||
};
|
||||
|
||||
#endif /* _I2C_DRIVER__IMX8Q_EVK_H_ */
|
55
repos/os/src/drivers/i2c/imx8q_evk/mmio.h
Normal file
55
repos/os/src/drivers/i2c/imx8q_evk/mmio.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* \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 <util/mmio.h>
|
||||
|
||||
namespace I2c { struct Mmio; }
|
||||
|
||||
|
||||
struct I2c::Mmio: Genode::Mmio
|
||||
{
|
||||
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(Genode::addr_t base) : Genode::Mmio { base } { }
|
||||
};
|
||||
|
||||
#endif /* _I2C_MMIO_H_ */
|
9
repos/os/src/drivers/i2c/imx8q_evk/target.mk
Normal file
9
repos/os/src/drivers/i2c/imx8q_evk/target.mk
Normal file
@ -0,0 +1,9 @@
|
||||
TARGET = imx8q_evk_i2c_drv
|
||||
|
||||
REQUIRES = arm_v8
|
||||
|
||||
SRC_CC += driver.cc
|
||||
|
||||
INC_DIR += $(PRG_DIR)/src/drivers/i2c/imx8q_evk
|
||||
|
||||
include $(REP_DIR)/src/drivers/i2c/target.inc
|
62
repos/os/src/drivers/i2c/main.cc
Normal file
62
repos/os/src/drivers/i2c/main.cc
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* \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)
|
||||
{
|
||||
return {
|
||||
.verbose = config.attribute_value("verbose", false),
|
||||
.bus_no = config.attribute_value("bus_no", 0u),
|
||||
.device_name = config.attribute_value("device_name", Device_name())
|
||||
};
|
||||
}
|
||||
|
||||
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); }
|
9
repos/os/src/drivers/i2c/target.inc
Normal file
9
repos/os/src/drivers/i2c/target.inc
Normal file
@ -0,0 +1,9 @@
|
||||
LIBS += base
|
||||
|
||||
INC_DIR += $(PRG_DIR)
|
||||
INC_DIR += $(REP_DIR)/src/drivers/i2c
|
||||
|
||||
SRC_CC += main.cc
|
||||
|
||||
vpath %.cc $(REP_DIR)/src/drivers/i2c
|
||||
vpath %.cc $(REP_DIR)
|
15
repos/os/src/test/i2c_mcp9808/README
Normal file
15
repos/os/src/test/i2c_mcp9808/README
Normal file
@ -0,0 +1,15 @@
|
||||
i2c_mcp9808 read ambient temperature from an mcp9808 sensor connected
|
||||
on an I2c bus and print it to standard output.
|
||||
|
||||
Example config of a component using the imx8q_evk_i2c_drv driver:
|
||||
|
||||
!<start name="i2c_mcp9808">
|
||||
!
|
||||
! <resource name="RAM" quantum="1M"/>
|
||||
!
|
||||
! <route>
|
||||
! <service name="I2c"> <child name="imx8q_evk_i2c_drv" label="MCP_9808"/> </service>
|
||||
! <any-service> <parent/> </any-service>
|
||||
! </route>
|
||||
!
|
||||
!</start>
|
81
repos/os/src/test/i2c_mcp9808/main.cc
Normal file
81
repos/os/src/test/i2c_mcp9808/main.cc
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* \brief Test I2C driver with MCP9808 sensor
|
||||
* \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.
|
||||
*/
|
||||
|
||||
#include <base/component.h>
|
||||
#include <i2c_session/connection.h>
|
||||
|
||||
namespace Test {
|
||||
using namespace Genode;
|
||||
struct Main;
|
||||
}
|
||||
|
||||
|
||||
struct Test::Main
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
I2c::Connection _sensor { _env, "MCP_9808" };
|
||||
|
||||
Main(Env &env) : _env(env)
|
||||
{
|
||||
uint16_t raw_data = 0;
|
||||
|
||||
try{
|
||||
/* Config ambient mode */
|
||||
_sensor.write_8bits(0x05);
|
||||
|
||||
/* Read ambient temperature */
|
||||
raw_data = _sensor.read_16bits();
|
||||
} catch (I2c::Session::Bus_error) {
|
||||
error("Bus operation could not be completed.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t* temp_data = reinterpret_cast<uint8_t*>(&raw_data);
|
||||
|
||||
/* Convert the temperature data */
|
||||
/* Check flag bits */
|
||||
if ((temp_data[0] & 0x80) == 0x80) {
|
||||
/* T A is T CRIT */
|
||||
warning("Temperature is critical for the sensor.");
|
||||
}
|
||||
if ((temp_data[0] & 0x40) == 0x40) {
|
||||
/* T A > T UPPER */
|
||||
warning("Temperature is upper the bound of the sensor.");
|
||||
}
|
||||
if ((temp_data[0] & 0x20) == 0x20) {
|
||||
/* T A < T LOWER */
|
||||
warning("Temperature is lower the bound of the sensor.");
|
||||
}
|
||||
|
||||
/* Clear the flag's bits */
|
||||
temp_data[0] = temp_data[0] & 0x1F;
|
||||
int temperature = 0;
|
||||
|
||||
/* T A is negative */
|
||||
if ((temp_data[0] & 0x10) == 0x10){
|
||||
/* Clear SIGN */
|
||||
temp_data[0] = temp_data[0] & 0x0F;
|
||||
temperature = 256 - (temp_data[0] * 16 + temp_data[1] / 16);
|
||||
} else {
|
||||
/* T A is positive */
|
||||
temperature = (temp_data[0] * 16 + temp_data [1] / 16);
|
||||
}
|
||||
|
||||
log("temperature is ", temperature, " C");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env) { static Test::Main main(env); }
|
3
repos/os/src/test/i2c_mcp9808/target.mk
Normal file
3
repos/os/src/test/i2c_mcp9808/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = i2c_mcp9808
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
Loading…
x
Reference in New Issue
Block a user