os: add new platform driver for Raspberry Pi

Fix #3864
This commit is contained in:
Stefan Kalkowski 2020-08-21 13:54:05 +02:00 committed by Norman Feske
parent 5f5ad41ad3
commit 73d3698e2f
9 changed files with 857 additions and 0 deletions

View File

@ -0,0 +1,80 @@
/*
* \brief Platform driver - Device model policy for rpi
* \author Stefan Kalkowski
* \date 2020-08-20
*/
/*
* Copyright (C) 2020 Genode Labs GmbH
*
* 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 <env.h>
#include <rpi_device.h>
using Driver::Device_model;
using Driver::Device;
using Driver::Rpi_device;
void Device_model::destroy_element(Device & dev)
{
Rpi_device & device = static_cast<Rpi_device&>(dev);
{
Irq_update_policy policy(_env.heap);
device._irq_list.destroy_all_elements(policy);
}
{
Io_mem_update_policy policy(_env.heap);
device._io_mem_list.destroy_all_elements(policy);
}
{
Property_update_policy policy(_env.heap);
device._property_list.destroy_all_elements(policy);
}
{
Power_domain_update_policy policy(_env.heap);
device._power_domain_list.destroy_all_elements(policy);
}
Genode::destroy(_env.heap, &device);
}
Device & Device_model::create_element(Genode::Xml_node node)
{
Device::Name name = node.attribute_value("name", Device::Name());
return *(new (_env.heap) Rpi_device(name));
}
void Device_model::update_element(Device & dev,
Genode::Xml_node node)
{
Rpi_device & device = static_cast<Rpi_device&>(dev);
{
Irq_update_policy policy(_env.heap);
device._irq_list.update_from_xml(policy, node);
}
{
Io_mem_update_policy policy(_env.heap);
device._io_mem_list.update_from_xml(policy, node);
}
{
Property_update_policy policy(_env.heap);
device._property_list.update_from_xml(policy, node);
}
{
Power_domain_update_policy policy(_env.heap);
device._power_domain_list.update_from_xml(policy, node);
}
}

View File

@ -0,0 +1,43 @@
/*
* \brief Platform driver for rpi
* \author Stefan Kalkowski
* \date 2020-08-20
*/
/*
* Copyright (C) 2020 Genode Labs GmbH
*
* 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 _SRC__DRIVERS__PLATFORM__RPI__ENV_H_
#define _SRC__DRIVERS__PLATFORM__RPI__ENV_H_
#include <base/attached_rom_dataspace.h>
#include <base/env.h>
#include <base/heap.h>
#include <device.h>
#include <mbox.h>
namespace Driver {
using namespace Genode;
struct Env;
};
struct Driver::Env
{
Genode::Env & env;
Heap heap { env.ram(), env.rm() };
Sliced_heap sliced_heap { env.ram(), env.rm() };
Attached_rom_dataspace config { env, "config" };
Device_model devices { *this };
Mbox mbox { env };
Env(Genode::Env &env) : env(env) { }
};
#endif /* _SRC__DRIVERS__PLATFORM__RPI__ENV_H_ */

View File

@ -0,0 +1,63 @@
/*
* \brief Marshalling of mbox messages for framebuffer channel
* \author Norman Feske
* \date 2013-09-15
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* 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 _FRAMEBUFFER_MESSAGE_H_
#define _FRAMEBUFFER_MESSAGE_H_
/* Genode includes */
#include <util/misc_math.h>
#include <base/log.h>
/* board-specific includes */
#include <drivers/defs/rpi.h>
#include <platform/framebuffer_info.h>
namespace Platform { struct Framebuffer_message; }
/**
* Mailbox message buffer for the framebuffer channel
*/
struct Platform::Framebuffer_message : Framebuffer_info
{
Framebuffer_message(Framebuffer_info const &info) : Framebuffer_info(info) { }
void finalize() { }
static unsigned channel() { return 1; }
static Rpi::Videocore_cache_policy cache_policy() {
return Rpi::COHERENT;
}
void dump(char const *label)
{
using Genode::log;
log(label, " message:");
log(" phys_width: ", phys_width);
log(" phys_height: ", phys_height);
log(" virt_width: ", virt_width);
log(" virt_height: ", virt_height);
log(" pitch: ", pitch);
log(" depth: ", depth);
log(" x_offset: ", x_offset);
log(" y_offset: ", y_offset);
log(" addr: ", Genode::Hex(addr));
log(" size: ", Genode::Hex(size));
}
inline void *operator new (__SIZE_TYPE__, void *ptr) { return ptr; }
};
#endif /* _FRAMEBUFFER_MESSAGE_H_ */

View File

@ -0,0 +1,127 @@
/*
* \brief Mbox for communicating between Videocore and ARM
* \author Norman Feske
* \date 2013-09-14
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* 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 _DRIVERS__PLATFORM__SPEC__RPI__MBOX_H_
#define _DRIVERS__PLATFORM__SPEC__RPI__MBOX_H_
/* Genode includes */
#include <util/mmio.h>
#include <base/env.h>
#include <os/attached_mmio.h>
#include <base/attached_ram_dataspace.h>
#include <base/log.h>
#include <dataspace/client.h>
#include <timer_session/connection.h>
class Mbox : Genode::Attached_mmio
{
private:
Genode::Env &_env;
enum { verbose = false };
typedef Genode::addr_t addr_t;
typedef Genode::uint32_t uint32_t;
typedef Genode::Dataspace_client Dataspace_client;
enum { BASE = 0x2000b800,
SIZE = 0x100 };
struct Read : Register<0x80, 32> { };
struct Status : Register<0x98, 32>
{
struct Rd_empty : Bitfield<30, 1> { };
struct Wr_full : Bitfield<31, 1> { };
};
struct Write : Register<0xa0, 32>
{
struct Channel : Bitfield<0, 4> { };
struct Value : Bitfield<4, 26> { };
struct Cache_policy : Bitfield<30, 2> { };
};
enum { MSG_BUFFER_SIZE = 0x1000 };
Genode::Attached_ram_dataspace _msg_buffer { _env.ram(), _env.rm(),
MSG_BUFFER_SIZE };
addr_t const _msg_phys = { Dataspace_client(_msg_buffer.cap()).phys_addr() };
struct Delayer : Mmio::Delayer
{
Timer::Connection timer;
void usleep(Genode::uint64_t us) override { timer.usleep(us); }
Delayer(Genode::Env &env) : timer(env) { }
} _delayer { _env };
template <typename MESSAGE>
MESSAGE &_message()
{
return *_msg_buffer.local_addr<MESSAGE>();
}
public:
Mbox(Genode::Env &env)
: Genode::Attached_mmio(env, BASE, SIZE), _env(env) { }
/**
* Return reference to typed message buffer
*/
template <typename MESSAGE, typename... ARGS>
MESSAGE &message(ARGS... args)
{
return *(new (_msg_buffer.local_addr<void>()) MESSAGE(args...));
}
template <typename MESSAGE>
void call()
{
_message<MESSAGE>().finalize();
if (verbose)
_message<MESSAGE>().dump("Input");
/* flush pending data in the read buffer */
while (!read<Status::Rd_empty>())
read<Read>();
try { wait_for(Attempts(500), Microseconds(1), _delayer,
Status::Wr_full::Equal(0)); }
catch (Polling_timeout) {
Genode::error("Mbox: timeout waiting for ready-to-write");
return;
}
Write::access_t value = 0;
Write::Channel:: set(value, MESSAGE::channel());
Write::Value:: set(value, _msg_phys >> Write::Value::SHIFT);
Write::Cache_policy::set(value, MESSAGE::cache_policy());
write<Write>(value);
try { wait_for(Attempts(500), Microseconds(1), _delayer,
Status::Rd_empty::Equal(0)); }
catch (Polling_timeout) {
Genode::error("Mbox: timeout waiting for response");
return;
}
if (verbose)
_message<MESSAGE>().dump("Output");
}
};
#endif /* _DRIVERS__PLATFORM__SPEC__RPI__MBOX_H_ */

View File

@ -0,0 +1,133 @@
/*
* \brief Command definitions for the property mbox channel
* \author Norman Feske
* \date 2013-09-15
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* 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 _PROPERTY_COMMAND_H_
#define _PROPERTY_COMMAND_H_
/* Genode includes */
#include <base/stdint.h>
namespace Property_command {
using namespace Genode;
struct Get_power_state
{
static uint32_t opcode() { return 0x00020001; };
struct Request
{
uint32_t const device_id;
Request(uint32_t device_id) : device_id(device_id) { }
};
struct Response
{
uint32_t const device_id = 0;
uint32_t const state = 0;
};
};
struct Set_power_state
{
static uint32_t opcode() { return 0x00028001; };
struct Request
{
uint32_t const device_id;
uint32_t const state;
Request(uint32_t device_id, bool enable, bool wait)
:
device_id(device_id),
state(enable | (wait << 1))
{ }
};
struct Response
{
uint32_t const device_id = 0;
uint32_t const state = 0;
};
};
struct Get_clock_rate
{
static uint32_t opcode() { return 0x00030002; };
struct Request
{
uint32_t const clock_id;
Request(uint32_t clock_id) : clock_id(clock_id) { }
};
struct Response
{
uint32_t const clock_id = 0;
uint32_t const hz = 0;
};
};
struct Allocate_buffer
{
static uint32_t opcode() { return 0x00040001; };
struct Request
{
uint32_t const alignment = 0x100;
};
struct Response
{
uint32_t const address = 0;
uint32_t const size = 0;
};
};
struct Release_buffer
{
static uint32_t opcode() { return 0x00048001; };
};
struct Get_physical_w_h
{
static uint32_t opcode() { return 0x00040003; };
struct Response
{
uint32_t const width = 0;
uint32_t const height = 0;
};
};
struct Set_physical_w_h
{
static uint32_t opcode() { return 0x00048003; };
struct Request
{
uint32_t const width;
uint32_t const height;
Request(uint32_t width, uint32_t height)
: width(width), height(height) { }
};
};
}
#endif /* _PROPERTY_COMMAND_H_ */

View File

@ -0,0 +1,225 @@
/*
* \brief Marshalling of mbox messages for property channel
* \author Norman Feske
* \date 2013-09-15
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* 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 _SRC__DRIVERS__PLATFORM__RPI__PROPERTY_MESSAGE_H_
#define _SRC__DRIVERS__PLATFORM__RPI__PROPERTY_MESSAGE_H_
/* Genode includes */
#include <util/misc_math.h>
#include <base/log.h>
/* board-specific includes */
#include <drivers/defs/rpi.h>
namespace Driver {
using namespace Genode;
struct Property_message;
}
/**
* Mailbox message buffer for the property channel
*
* This data structure is overlayed with memory shared with the VC. It
* contains a header, followed by a sequence of so-called command tags, wrapped
* up by a zero as an end marker.
*/
struct Driver::Property_message
{
uint32_t buf_size = 0;
enum Code { REQUEST = 0,
RESPONSE_SUCCESS = 0x80000000 };
Code code = REQUEST;
/*
* Start of the buffer that contains a sequence of tags
*/
char buffer[0];
/*
* There must be no member variables after this point
*/
/*
* Each tag consists of a header, a part with request arguments, and a
* part with responses.
*/
template <typename TAG>
struct Tag
{
uint32_t const opcode;
/**
* Size of tag buffer
*/
uint32_t const buf_size;
/**
* Size of request part of the tag
*
* The value is never changed locally. Therefore, it is declared as
* const. However, it will be updated by the VC. So we declare it
* volatile, too.
*/
uint32_t volatile const len;
char payload[0];
/**
* Utility for returning a response size of a tag type
*
* Depending on the presence of a 'TAG::Response' type, we need to
* return the actual size of the response (if the type is present) or
* 0. Both overloads are called with a compliant parameter 0. But only
* if 'T::Response' exists, the first overload is selected.
*
* SFINAE to the rescue!
*/
template <typename T>
static size_t response_size(typename T::Response *)
{
return sizeof(typename T::Response);
}
template <typename>
static size_t response_size(...)
{
return 0;
}
template <typename T>
static size_t request_size(typename T::Request *)
{
return sizeof(typename T::Request);
}
template <typename>
static size_t request_size(...)
{
return 0;
}
template <typename T>
struct Placeable : T
{
template <typename... ARGS>
Placeable(ARGS... args) : T(args...) { }
inline void *operator new (__SIZE_TYPE__, void *ptr) { return ptr; }
};
template <typename T, typename... ARGS>
void construct_request(typename T::Request *, ARGS... args)
{
new ((typename T::Request *)payload)
Placeable<typename T::Request>(args...);
}
template <typename>
void construct_request(...) { }
template <typename T>
void construct_response(typename T::Response *)
{
new (payload) Placeable<typename T::Response>;
}
template <typename>
void construct_response(...) { }
static constexpr size_t payload_size()
{
return max(request_size<TAG>(0), response_size<TAG>(0));
}
template <typename... REQUEST_ARGS>
Tag(REQUEST_ARGS... request_args)
:
opcode(TAG::opcode()),
buf_size(payload_size()),
len(request_size<TAG>(0))
{
/*
* The order is important. If we called 'construct_response' after
* 'construct_request', we would overwrite the request parameters
* with the default response.
*/
construct_response<TAG>(0);
construct_request<TAG>(0, request_args...);
}
inline void *operator new (__SIZE_TYPE__, void *ptr) { return ptr; }
};
void reset()
{
buf_size = 0;
code = REQUEST;
}
/**
* \return reference to tag in the message buffer
*/
template <typename POLICY, typename... REQUEST_ARGS>
typename POLICY::Response const &append(REQUEST_ARGS... request_args)
{
auto *tag = new (buffer + buf_size) Tag<POLICY>(request_args...);
buf_size += sizeof(Tag<POLICY>) + Tag<POLICY>::payload_size();
return *(typename POLICY::Response *)tag->payload;
}
template <typename POLICY, typename... REQUEST_ARGS>
void append_no_response(REQUEST_ARGS... request_args)
{
new (buffer + buf_size) Tag<POLICY>(request_args...);
buf_size += sizeof(Tag<POLICY>) + Tag<POLICY>::payload_size();
}
void finalize()
{
/* append end tag */
*(uint32_t *)(buffer + buf_size) = 0;
buf_size += sizeof(uint32_t);
}
static unsigned channel() { return 8; }
static Rpi::Videocore_cache_policy cache_policy()
{
return Rpi::NON_COHERENT; /* for channel 8 only */
}
void dump(char const *label)
{
unsigned const *buf = (unsigned *)this;
log(label, " message:");
for (unsigned i = 0;; i++) {
for (unsigned j = 0; j < 8; j++) {
unsigned const msg_word_idx = i*8 + j;
log(" ", Hex(buf[msg_word_idx]));
if (msg_word_idx*sizeof(unsigned) < buf_size)
continue;
return;
}
}
}
inline void *operator new (__SIZE_TYPE__, void *ptr) { return ptr; }
};
#endif /* _SRC__DRIVERS__PLATFORM__RPI__PROPERTY_MESSAGE_H_ */

View File

@ -0,0 +1,83 @@
/*
* \brief Platform driver - Device abstraction for rpi
* \author Stefan Kalkowski
* \date 2020-08-17
*/
/*
* Copyright (C) 2020 Genode Labs GmbH
*
* 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 <property_command.h>
#include <property_message.h>
#include <rpi_device.h>
#include <session_component.h>
unsigned Driver::Rpi_device::Power_domain::id()
{
if (name == "sdhci") { return 0; }
if (name == "uart_0") { return 1; }
if (name == "uart_1") { return 2; }
if (name == "usb") { return 3; }
if (name == "i2c_0") { return 4; }
if (name == "i2c_1") { return 5; }
if (name == "i2c_2") { return 6; }
if (name == "spi") { return 7; }
if (name == "ccp2tx") { return 8; }
warning("Invalid power-domain ", name);
return ~0U;
};
bool Driver::Rpi_device::acquire(Driver::Session_component & sc)
{
bool ret = Driver::Device::acquire(sc);
if (ret) {
_power_domain_list.for_each([&] (Power_domain & p) {
auto & msg = sc.env().mbox.message<Property_message>();
msg.append_no_response<Property_command::Set_power_state>(p.id(),
true,
true);
sc.env().mbox.call<Property_message>();
});
}
return ret;
}
void Driver::Rpi_device::release(Session_component & sc)
{
_power_domain_list.for_each([&] (Power_domain & p) {
auto & msg = sc.env().mbox.message<Property_message>();
msg.append_no_response<Property_command::Set_power_state>(p.id(),
true,
true);
sc.env().mbox.call<Property_message>();
});
return Driver::Device::release(sc);
}
void Driver::Rpi_device::_report_platform_specifics(Genode::Xml_generator & /*xml*/,
Driver::Session_component & /*sc*/)
{
//_clock_list.for_each([&] (Clock & c) {
// Avl_string_base * asb =
// sc.env().ccm.tree.first()->find_by_name(c.name.string());
// if (!asb || !c.driver_name.valid()) { return; }
// Driver::Clock & clock =
// static_cast<Driver::Clock::Clock_tree_element*>(asb)->object();
// xml.node("clock", [&] () {
// xml.attribute("rate", clock.get_rate());
// xml.attribute("name", c.driver_name);
// });
//});
}

View File

@ -0,0 +1,89 @@
/*
* \brief Platform driver - Device abstraction for rpi
* \author Stefan Kalkowski
* \date 2020-08-20
*/
/*
* Copyright (C) 2020 Genode Labs GmbH
*
* 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 _SRC__DRIVERS__PLATFORM__IMX8MQ__RPI_DEVICE_H_
#define _SRC__DRIVERS__PLATFORM__IMX8MQ__RPI_DEVICE_H_
#include <device.h>
namespace Driver {
using namespace Genode;
class Rpi_device;
struct Power_domain_update_policy;
}
class Driver::Rpi_device : public Driver::Device
{
public:
struct Power_domain : List_model<Power_domain>::Element
{
using Name = Genode::String<64>;
Name name;
Power_domain(Name name) : name(name) {}
unsigned id();
};
bool acquire(Session_component &) override;
void release(Session_component &) override;
Rpi_device(Device::Name name) : Device(name) {}
protected:
friend class Driver::Device_model;
friend class List_model<Device>;
void _report_platform_specifics(Xml_generator &,
Session_component &) override;
List_model<Power_domain> _power_domain_list {};
};
struct Driver::Power_domain_update_policy
: Genode::List_model<Rpi_device::Power_domain>::Update_policy
{
Genode::Allocator & alloc;
Power_domain_update_policy(Genode::Allocator & alloc) : alloc(alloc) {}
void destroy_element(Element & pd) {
Genode::destroy(alloc, &pd); }
Element & create_element(Genode::Xml_node node)
{
Element::Name name = node.attribute_value("name", Element::Name());
return *(new (alloc) Element(name));
}
void update_element(Element &, Genode::Xml_node) {}
static bool element_matches_xml_node(Element const & pd, Genode::Xml_node node)
{
Element::Name name = node.attribute_value("name", Element::Name());
return name == pd.name;
}
static bool node_is_element(Genode::Xml_node node)
{
return node.has_type("power-domain");
}
};
#endif /* _SRC__DRIVERS__PLATFORM__IMX8MQ__RPI_DEVICE_H_ */

View File

@ -0,0 +1,14 @@
TARGET = rpi_new_platform_drv
REQUIRES = arm_v6
SRC_CC = device.cc
SRC_CC += device_component.cc
SRC_CC += device_model_policy.cc
SRC_CC += rpi_device.cc
SRC_CC += main.cc
SRC_CC += session_component.cc
SRC_CC += root.cc
INC_DIR = $(PRG_DIR) $(REP_DIR)/src/drivers/platform/spec/arm
LIBS = base
vpath %.cc $(PRG_DIR)
vpath %.cc $(REP_DIR)/src/drivers/platform/spec/arm