From 73d3698e2fe516e48aa4cfc1c79fe9c4ed397268 Mon Sep 17 00:00:00 2001 From: Stefan Kalkowski Date: Fri, 21 Aug 2020 13:54:05 +0200 Subject: [PATCH] os: add new platform driver for Raspberry Pi Fix #3864 --- .../platform/rpi/device_model_policy.cc | 80 +++++++ repos/os/src/drivers/platform/rpi/env.h | 43 ++++ .../platform/rpi/framebuffer_message.h | 63 +++++ repos/os/src/drivers/platform/rpi/mbox.h | 127 ++++++++++ .../drivers/platform/rpi/property_command.h | 133 +++++++++++ .../drivers/platform/rpi/property_message.h | 225 ++++++++++++++++++ .../os/src/drivers/platform/rpi/rpi_device.cc | 83 +++++++ .../os/src/drivers/platform/rpi/rpi_device.h | 89 +++++++ repos/os/src/drivers/platform/rpi/target.mk | 14 ++ 9 files changed, 857 insertions(+) create mode 100644 repos/os/src/drivers/platform/rpi/device_model_policy.cc create mode 100644 repos/os/src/drivers/platform/rpi/env.h create mode 100644 repos/os/src/drivers/platform/rpi/framebuffer_message.h create mode 100644 repos/os/src/drivers/platform/rpi/mbox.h create mode 100644 repos/os/src/drivers/platform/rpi/property_command.h create mode 100644 repos/os/src/drivers/platform/rpi/property_message.h create mode 100644 repos/os/src/drivers/platform/rpi/rpi_device.cc create mode 100644 repos/os/src/drivers/platform/rpi/rpi_device.h create mode 100644 repos/os/src/drivers/platform/rpi/target.mk diff --git a/repos/os/src/drivers/platform/rpi/device_model_policy.cc b/repos/os/src/drivers/platform/rpi/device_model_policy.cc new file mode 100644 index 0000000000..3fcaa799fc --- /dev/null +++ b/repos/os/src/drivers/platform/rpi/device_model_policy.cc @@ -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 +#include + +using Driver::Device_model; +using Driver::Device; +using Driver::Rpi_device; + +void Device_model::destroy_element(Device & dev) +{ + Rpi_device & device = static_cast(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(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); + } +} diff --git a/repos/os/src/drivers/platform/rpi/env.h b/repos/os/src/drivers/platform/rpi/env.h new file mode 100644 index 0000000000..93ebabf018 --- /dev/null +++ b/repos/os/src/drivers/platform/rpi/env.h @@ -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 +#include +#include + +#include +#include + +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_ */ diff --git a/repos/os/src/drivers/platform/rpi/framebuffer_message.h b/repos/os/src/drivers/platform/rpi/framebuffer_message.h new file mode 100644 index 0000000000..3edb90a3e8 --- /dev/null +++ b/repos/os/src/drivers/platform/rpi/framebuffer_message.h @@ -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 +#include + +/* board-specific includes */ +#include +#include + +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_ */ diff --git a/repos/os/src/drivers/platform/rpi/mbox.h b/repos/os/src/drivers/platform/rpi/mbox.h new file mode 100644 index 0000000000..f341088750 --- /dev/null +++ b/repos/os/src/drivers/platform/rpi/mbox.h @@ -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 +#include +#include +#include +#include +#include +#include + +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 + MESSAGE &_message() + { + return *_msg_buffer.local_addr(); + } + + public: + + Mbox(Genode::Env &env) + : Genode::Attached_mmio(env, BASE, SIZE), _env(env) { } + + /** + * Return reference to typed message buffer + */ + template + MESSAGE &message(ARGS... args) + { + return *(new (_msg_buffer.local_addr()) MESSAGE(args...)); + } + + template + void call() + { + _message().finalize(); + + if (verbose) + _message().dump("Input"); + + /* flush pending data in the read buffer */ + while (!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(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().dump("Output"); + } +}; + +#endif /* _DRIVERS__PLATFORM__SPEC__RPI__MBOX_H_ */ diff --git a/repos/os/src/drivers/platform/rpi/property_command.h b/repos/os/src/drivers/platform/rpi/property_command.h new file mode 100644 index 0000000000..13bc481014 --- /dev/null +++ b/repos/os/src/drivers/platform/rpi/property_command.h @@ -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 + +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_ */ diff --git a/repos/os/src/drivers/platform/rpi/property_message.h b/repos/os/src/drivers/platform/rpi/property_message.h new file mode 100644 index 0000000000..ae4a9f5a01 --- /dev/null +++ b/repos/os/src/drivers/platform/rpi/property_message.h @@ -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 +#include + +/* board-specific includes */ +#include + +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 + 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 + static size_t response_size(typename T::Response *) + { + return sizeof(typename T::Response); + } + + template + static size_t response_size(...) + { + return 0; + } + + template + static size_t request_size(typename T::Request *) + { + return sizeof(typename T::Request); + } + + template + static size_t request_size(...) + { + return 0; + } + + template + struct Placeable : T + { + template + Placeable(ARGS... args) : T(args...) { } + + inline void *operator new (__SIZE_TYPE__, void *ptr) { return ptr; } + }; + + template + void construct_request(typename T::Request *, ARGS... args) + { + new ((typename T::Request *)payload) + Placeable(args...); + } + + template + void construct_request(...) { } + + template + void construct_response(typename T::Response *) + { + new (payload) Placeable; + } + + template + void construct_response(...) { } + + static constexpr size_t payload_size() + { + return max(request_size(0), response_size(0)); + } + + template + Tag(REQUEST_ARGS... request_args) + : + opcode(TAG::opcode()), + buf_size(payload_size()), + len(request_size(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(0); + construct_request(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::Response const &append(REQUEST_ARGS... request_args) + { + auto *tag = new (buffer + buf_size) Tag(request_args...); + + buf_size += sizeof(Tag) + Tag::payload_size(); + + return *(typename POLICY::Response *)tag->payload; + } + + template + void append_no_response(REQUEST_ARGS... request_args) + { + new (buffer + buf_size) Tag(request_args...); + + buf_size += sizeof(Tag) + Tag::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_ */ diff --git a/repos/os/src/drivers/platform/rpi/rpi_device.cc b/repos/os/src/drivers/platform/rpi/rpi_device.cc new file mode 100644 index 0000000000..9eddbefda3 --- /dev/null +++ b/repos/os/src/drivers/platform/rpi/rpi_device.cc @@ -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 +#include +#include +#include + + +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(); + msg.append_no_response(p.id(), + true, + true); + sc.env().mbox.call(); + }); + } + + return ret; +} + + +void Driver::Rpi_device::release(Session_component & sc) +{ + _power_domain_list.for_each([&] (Power_domain & p) { + auto & msg = sc.env().mbox.message(); + msg.append_no_response(p.id(), + true, + true); + sc.env().mbox.call(); + }); + + 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(asb)->object(); + // xml.node("clock", [&] () { + // xml.attribute("rate", clock.get_rate()); + // xml.attribute("name", c.driver_name); + // }); + //}); +} diff --git a/repos/os/src/drivers/platform/rpi/rpi_device.h b/repos/os/src/drivers/platform/rpi/rpi_device.h new file mode 100644 index 0000000000..92902eaedf --- /dev/null +++ b/repos/os/src/drivers/platform/rpi/rpi_device.h @@ -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 + +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::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; + + void _report_platform_specifics(Xml_generator &, + Session_component &) override; + + List_model _power_domain_list {}; +}; + + +struct Driver::Power_domain_update_policy +: Genode::List_model::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_ */ diff --git a/repos/os/src/drivers/platform/rpi/target.mk b/repos/os/src/drivers/platform/rpi/target.mk new file mode 100644 index 0000000000..ad6ee86253 --- /dev/null +++ b/repos/os/src/drivers/platform/rpi/target.mk @@ -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