diff --git a/base/mk/spec-platform_rpi.mk b/base/mk/spec-platform_rpi.mk index c1712b6a53..aa1a2a9c29 100644 --- a/base/mk/spec-platform_rpi.mk +++ b/base/mk/spec-platform_rpi.mk @@ -5,7 +5,7 @@ # # denote wich specs are also fullfilled by this spec -SPECS += arm_v6 pl011 usb +SPECS += arm_v6 pl011 usb framebuffer # add repository relative include paths REP_INC_DIR += include/platform/rpi diff --git a/os/include/platform/rpi/platform/framebuffer_info.h b/os/include/platform/rpi/platform/framebuffer_info.h new file mode 100644 index 0000000000..a58c35f3bc --- /dev/null +++ b/os/include/platform/rpi/platform/framebuffer_info.h @@ -0,0 +1,59 @@ +/* + * \brief Framebuffer info structure + * \author Norman Feske + * \date 2013-09-15 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM__FRAMEBUFFER_INFO_H_ +#define _PLATFORM__FRAMEBUFFER_INFO_H_ + +#include + +namespace Platform { + using namespace Genode; + struct Framebuffer_info; +} + + +/** + * Structure used by the protocol between the Videocore GPU and the ARM CPU for + * setting up the framebuffer via the mbox. + */ +struct Platform::Framebuffer_info +{ + uint32_t phys_width; + uint32_t phys_height; + uint32_t virt_width; + uint32_t virt_height; + uint32_t pitch = 0; + uint32_t depth; + uint32_t x_offset = 0; + uint32_t y_offset = 0; + uint32_t addr = 0; + uint32_t size = 0; + + /** + * Default constructor needed to make the object transferable via RPC + */ + Framebuffer_info() + : + phys_width(0), phys_height(0), virt_width(0), virt_height(), + depth(0) + { } + + Framebuffer_info(uint32_t width, uint32_t height, uint32_t depth) + : + phys_width(width), phys_height(height), + virt_width(width), virt_height(height), + depth(depth) + { } +}; + +#endif /* _PLATFORM__FRAMEBUFFER_INFO_H_ */ diff --git a/os/include/platform/rpi/platform/property_message.h b/os/include/platform/rpi/platform/property_message.h new file mode 100644 index 0000000000..08e09437d4 --- /dev/null +++ b/os/include/platform/rpi/platform/property_message.h @@ -0,0 +1,226 @@ +/* + * \brief Marshalling of mbox messages for property channel + * \author Norman Feske + * \date 2013-09-15 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _PLATFORM__PROPERTY_MESSAGE_H_ +#define _PLATFORM__PROPERTY_MESSAGE_H_ + +/* Genode includes */ +#include +#include + +/* board-specific includes */ +#include + +namespace Platform { + 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 Platform::Property_message +{ + uint32_t buf_size; + + enum Code { REQUEST = 0, + RESPONSE_SUCCESS = 0x80000000 }; + + Code code = REQUEST; + + /* + * Start of the buffer that contains a sequence of tags + */ + char buffer[]; + + /* + * 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[]; + + /** + * 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_t, 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_t, 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 Board_base::Videocore_cache_policy cache_policy() + { + return Board_base::NON_COHERENT; /* for channel 8 only */ + } + + void dump(char const *label) + { + unsigned const *buf = (unsigned *)this; + printf("%s message:\n", label); + for (unsigned i = 0;; i++) { + for (unsigned j = 0; j < 8; j++) { + unsigned const msg_word_idx = i*8 + j; + printf(" %08x", buf[msg_word_idx]); + if (msg_word_idx*sizeof(unsigned) < buf_size) + continue; + printf("\n"); + return; + } + } + } + + inline void *operator new (size_t, void *ptr) { return ptr; } +}; + +#endif /* _PLATFORM__PROPERTY_MESSAGE_H_ */ diff --git a/os/include/platform/rpi/platform_session/client.h b/os/include/platform/rpi/platform_session/client.h new file mode 100644 index 0000000000..8971de4192 --- /dev/null +++ b/os/include/platform/rpi/platform_session/client.h @@ -0,0 +1,38 @@ +/* + * \brief Raspberry Pi specific platform session client side + * \author Norman Feske + * \date 2013-09-16 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PLATFORM_SESSION__CLIENT_H_ +#define _INCLUDE__PLATFORM_SESSION__CLIENT_H_ + +#include +#include +#include + +namespace Platform { struct Client; } + +struct Platform::Client : Genode::Rpc_client +{ + explicit Client(Capability session) + : Genode::Rpc_client(session) { } + + void setup_framebuffer(Framebuffer_info &info) { + call(info); } + + bool power_state(Power power) { + return call(power); } + + void power_state(Power power, bool enable) { + call(power, enable); } +}; + +#endif /* _INCLUDE__PLATFORM_SESSION__CLIENT_H_ */ diff --git a/os/include/platform/rpi/platform_session/platform_session.h b/os/include/platform/rpi/platform_session/platform_session.h new file mode 100644 index 0000000000..1a305b5564 --- /dev/null +++ b/os/include/platform/rpi/platform_session/platform_session.h @@ -0,0 +1,76 @@ +/* + * \brief Raspberry Pi specific platform session + * \author Norman Feske + * \date 2013-09-16 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__PLATFORM_SESSION__PLATFORM_SESSION_H_ +#define _INCLUDE__PLATFORM_SESSION__PLATFORM_SESSION_H_ + +#include +#include +#include +#include + +namespace Platform { + using namespace Genode; + struct Session; +} + + +struct Platform::Session : Genode::Session +{ + static const char *service_name() { return "Platform"; } + + /** + * Setup framebuffer + * + * The 'info' argument serves as both input and output parameter. As input, + * it describes the desired properties of the framebuffer. In return, the + * function delivers the values that were actually taken into effect. + */ + virtual void setup_framebuffer(Framebuffer_info &info) = 0; + + enum Power { + POWER_SDHCI = 0, + POWER_UART0 = 1, + POWER_UART1 = 2, + POWER_USB_HCD = 3, + POWER_I2C0 = 4, + POWER_I2C1 = 5, + POWER_I2C2 = 6, + POWER_SPI = 7, + POWER_CCP2TX = 8, + }; + + /** + * Request power state + */ + virtual bool power_state(Power) = 0; + + /** + * Set power state + */ + virtual void power_state(Power, bool enable) = 0; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_setup_framebuffer, void, setup_framebuffer, Framebuffer_info &); + GENODE_RPC(Rpc_get_power_state, bool, power_state, Power); + GENODE_RPC(Rpc_set_power_state, void, power_state, Power, bool); + + GENODE_RPC_INTERFACE(Rpc_setup_framebuffer, Rpc_set_power_state, + Rpc_get_power_state); +}; + +#endif /* _INCLUDE__PLATFORM_SESSION__PLATFORM_SESSION_H_ */ diff --git a/os/src/drivers/framebuffer/rpi/main.cc b/os/src/drivers/framebuffer/rpi/main.cc new file mode 100644 index 0000000000..7e593fefd3 --- /dev/null +++ b/os/src/drivers/framebuffer/rpi/main.cc @@ -0,0 +1,98 @@ +/* + * \brief Framebuffer driver for Raspberry Pi + * \author Norman Feske + * \date 2013-09-14 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include + +namespace Framebuffer { + using namespace Genode; + class Session_component; +}; + + +class Framebuffer::Session_component : public Genode::Rpc_object +{ + private: + + size_t const _width; + size_t const _height; + Attached_io_mem_dataspace _fb_mem; + + public: + + Session_component(addr_t phys_addr, size_t size, size_t width, size_t height) + : + _width(width), _height(height), _fb_mem(phys_addr, size) + { } + + /************************************ + ** Framebuffer::Session interface ** + ************************************/ + + Dataspace_capability dataspace() { return _fb_mem.cap(); } + + void release() { } + + Mode mode() const + { + return Mode(_width, _height, Mode::RGB565); + } + + void mode_sigh(Genode::Signal_context_capability) { } + + void refresh(int, int, int, int) { } +}; + + +int main(int, char **) +{ + using namespace Framebuffer; + using namespace Genode; + + printf("--- fb_drv started ---\n"); + + static Platform::Connection platform; + + Platform::Framebuffer_info fb_info(1024, 768, 16); + platform.setup_framebuffer(fb_info); + + /* + * Initialize server entry point + */ + enum { STACK_SIZE = 4096 }; + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "fb_ep"); + + /* + * Let the entry point serve the framebuffer session and root interfaces + */ + static Session_component fb_session(fb_info.addr, + fb_info.size, + fb_info.phys_width, + fb_info.phys_height); + static Static_root fb_root(ep.manage(&fb_session)); + + /* + * Announce service + */ + env()->parent()->announce(ep.manage(&fb_root)); + + sleep_forever(); + return 0; +} diff --git a/os/src/drivers/framebuffer/rpi/target.mk b/os/src/drivers/framebuffer/rpi/target.mk new file mode 100644 index 0000000000..12d1f93de1 --- /dev/null +++ b/os/src/drivers/framebuffer/rpi/target.mk @@ -0,0 +1,8 @@ +TARGET = fb_drv +REQUIRES = platform_rpi +SRC_CC = main.cc +LIBS = base +INC_DIR += $(PRG_DIR) + +# enable C++11 support +CC_CXX_OPT += -std=gnu++11 diff --git a/os/src/drivers/platform/rpi/framebuffer_message.h b/os/src/drivers/platform/rpi/framebuffer_message.h new file mode 100644 index 0000000000..62ab7843dc --- /dev/null +++ b/os/src/drivers/platform/rpi/framebuffer_message.h @@ -0,0 +1,64 @@ +/* + * \brief Marshalling of mbox messages for framebuffer channel + * \author Norman Feske + * \date 2013-09-15 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#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 Genode::Board_base::Videocore_cache_policy cache_policy() + { + return Genode::Board_base::COHERENT; + } + + void dump(char const *label) + { + using Genode::printf; + + printf("%s message:\n", label); + printf(" phys_width: %u\n", phys_width); + printf(" phys_height: %u\n", phys_height); + printf(" virt_width: %u\n", virt_width); + printf(" virt_height: %u\n", virt_height); + printf(" pitch: %u\n", pitch); + printf(" depth: %d\n", depth); + printf(" x_offset: %d\n", x_offset); + printf(" y_offset: %d\n", y_offset); + printf(" addr: 0x%08x\n", addr); + printf(" size: 0x%08x\n", size); + } + + inline void *operator new (Genode::size_t, void *ptr) { return ptr; } +}; + +#endif /* _FRAMEBUFFER_MESSAGE_H_ */ diff --git a/os/src/drivers/platform/rpi/main.cc b/os/src/drivers/platform/rpi/main.cc new file mode 100644 index 0000000000..e25da65027 --- /dev/null +++ b/os/src/drivers/platform/rpi/main.cc @@ -0,0 +1,110 @@ +/* + * \brief Driver for Raspberry Pi specific platform devices + * \author Norman Feske + * \date 2013-09-16 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* platform includes */ +#include +#include + +/* local includes */ +#include +#include +#include + + +namespace Platform { + class Session_component; + class Root; +} + + +class Platform::Session_component : public Genode::Rpc_object +{ + private: + + Mbox &_mbox; + + public: + + /** + * Constructor + */ + Session_component(Mbox &mbox) : _mbox(mbox) { } + + + /********************************** + ** Platform session interface ** + **********************************/ + + void setup_framebuffer(Framebuffer_info &info) + { + auto const &msg = _mbox.message(info); + _mbox.call(); + info = msg; + } + + bool power_state(Power id) + { + auto &msg = _mbox.message(); + auto const &res = msg.append(id); + _mbox.call(); + return res.state; + } + + void power_state(Power id, bool enable) + { + auto &msg = _mbox.message(); + msg.append_no_response(id, enable, true); + _mbox.call(); + } +}; + + +class Platform::Root : public Genode::Root_component +{ + private: + + Mbox _mbox; + + protected: + + Session_component *_create_session(const char *args) { + return new (md_alloc()) Session_component(_mbox); } + + public: + + Root(Rpc_entrypoint *session_ep, Allocator *md_alloc) + : Root_component(session_ep, md_alloc) { } +}; + + +int main(int, char **) +{ + using namespace Platform; + + PINF("--- Raspberry Pi platform driver ---\n"); + + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, 4096, "rpi_plat_ep"); + static Platform::Root plat_root(&ep, env()->heap()); + env()->parent()->announce(ep.manage(&plat_root)); + + sleep_forever(); + return 0; +} diff --git a/os/src/drivers/platform/rpi/mbox.h b/os/src/drivers/platform/rpi/mbox.h new file mode 100644 index 0000000000..d35f010a62 --- /dev/null +++ b/os/src/drivers/platform/rpi/mbox.h @@ -0,0 +1,118 @@ +/* + * \brief Mbox for communicating between Videocore and ARM + * \author Norman Feske + * \date 2013-09-14 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _MBOX_H_ +#define _MBOX_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include + +class Mbox : Genode::Attached_mmio +{ + private: + + 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 = { Genode::env()->ram_session(), + MSG_BUFFER_SIZE }; + + addr_t const _msg_phys = { Dataspace_client(_msg_buffer.cap()).phys_addr() }; + + struct Delayer : Mmio::Delayer + { + Timer::Connection timer; + void usleep(unsigned us) { timer.usleep(us); } + } _delayer;; + + template + MESSAGE &_message() + { + return *_msg_buffer.local_addr(); + } + + public: + + Mbox() : Genode::Attached_mmio(BASE, SIZE) { } + + /** + * 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(); + + if (!wait_for(0, _delayer, 500, 1)) { + PERR("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); + + if (!wait_for(0, _delayer, 500, 1)) { + PERR("Mbox: timeout waiting for response"); + return; + } + + if (verbose) + _message().dump("Output"); + } +}; + +#endif /* _MBOX_H_ */ diff --git a/os/src/drivers/platform/rpi/property_command.h b/os/src/drivers/platform/rpi/property_command.h new file mode 100644 index 0000000000..e9be42503a --- /dev/null +++ b/os/src/drivers/platform/rpi/property_command.h @@ -0,0 +1,115 @@ +/* + * \brief Command definitions for the property mbox channel + * \author Norman Feske + * \date 2013-09-15 + */ + +/* + * Copyright (C) 2013 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#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; + uint32_t const state; + }; + }; + + 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; + uint32_t const state; + }; + }; + + 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/os/src/drivers/platform/rpi/target.mk b/os/src/drivers/platform/rpi/target.mk new file mode 100644 index 0000000000..887e77fa19 --- /dev/null +++ b/os/src/drivers/platform/rpi/target.mk @@ -0,0 +1,8 @@ +TARGET = platform_drv +REQUIRES = platform_rpi +SRC_CC = main.cc +INC_DIR += ${PRG_DIR} +LIBS = base + +# enable C++11 support +CC_CXX_OPT += -std=gnu++11