diff --git a/include/regulator/component.h b/include/regulator/component.h new file mode 100644 index 0000000..1ef81e6 --- /dev/null +++ b/include/regulator/component.h @@ -0,0 +1,104 @@ +/* + * \brief Regulator-session component + * \author Stefan Kalkowski + * \date 2013-06-13 + */ + +/* + * 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 _INCLUDE__REGULATOR__COMPONENT_H_ +#define _INCLUDE__REGULATOR__COMPONENT_H_ + +#include +#include +#include + + +namespace Regulator { + class Session_component; + class Root; +} + + +class Regulator::Session_component : public Regulator::Session_rpc_object +{ + private: + + Driver_factory & _driver_factory; + Driver & _driver; + + public: + + Session_component(Regulator_id regulator_id, + Driver_factory & driver_factory) + : Session_rpc_object(regulator_id), + _driver_factory(driver_factory), + _driver(_driver_factory.create(regulator_id)) { } + + ~Session_component() + { + _driver.state(_id, false); + _driver_factory.destroy(_driver); + } + + + /*********************************** + ** Regulator session interface ** + ***********************************/ + + void level(unsigned long level) override { _driver.level(_id, level); } + unsigned long level() override { return _driver.level(_id); } + void state(bool enable) override { _driver.state(_id, enable); } + bool state() override { return _driver.state(_id); } +}; + + +class Regulator::Root : + public Genode::Root_component +{ + private: + + Regulator::Driver_factory & _driver_factory; + + protected: + + Session_component *_create_session(const char *args) override + { + using namespace Genode; + + char reg_name[64]; + Arg_string::find_arg(args, "regulator").string(reg_name, + sizeof(reg_name), 0); + size_t ram_quota = + Arg_string::find_arg(args, "ram_quota").ulong_value(0); + + /* delete ram quota by the memory needed for the session */ + size_t session_size = max((size_t)4096, + sizeof(Session_component)); + if (ram_quota < session_size) + throw Insufficient_ram_quota(); + + if (!strlen(reg_name)) + throw Service_denied(); + + return new (md_alloc()) + Session_component(regulator_id_by_name(reg_name), + _driver_factory); + } + + public: + + Root(Genode::Env & env, + Genode::Allocator & md_alloc, + Regulator::Driver_factory & driver_factory) + : Genode::Root_component(env.ep(), + md_alloc), + _driver_factory(driver_factory) { } +}; + +#endif /* _INCLUDE__REGULATOR__COMPONENT_H_ */ diff --git a/include/regulator/driver.h b/include/regulator/driver.h new file mode 100644 index 0000000..d653454 --- /dev/null +++ b/include/regulator/driver.h @@ -0,0 +1,54 @@ +/* + * \brief Regulator-driver interface + * \author Stefan Kalkowski + * \date 2013-06-13 + */ + +/* + * 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 _INCLUDE__REGULATOR__DRIVER_H_ +#define _INCLUDE__REGULATOR__DRIVER_H_ + +#include + +namespace Regulator { + + struct Driver; + struct Driver_factory; +} + + +/** + * Interface to be implemented by the device-specific driver code + */ +struct Regulator::Driver : Genode::Interface +{ + virtual void level(Regulator_id id, unsigned long level) = 0; + virtual unsigned long level(Regulator_id id) = 0; + virtual void state(Regulator_id id, bool enable) = 0; + virtual bool state(Regulator_id id) = 0; +}; + + +/** + * Interface for constructing the driver object + */ +struct Regulator::Driver_factory : Genode::Interface +{ + /** + * Construct new driver + */ + virtual Driver &create(Regulator_id regulator) = 0; + + /** + * Destroy driver + */ + virtual void destroy(Driver &driver) = 0; +}; + +#endif /* _REGULATOR__DRIVER_H_ */ diff --git a/include/regulator_session/capability.h b/include/regulator_session/capability.h new file mode 100644 index 0000000..17845fe --- /dev/null +++ b/include/regulator_session/capability.h @@ -0,0 +1,22 @@ +/* + * \brief Regulator session capability type + * \author Stefan Kalkowski + * \date 2013-06-13 + */ + +/* + * 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 _INCLUDE__REGULATOR_SESSION__CAPABILITY_H_ +#define _INCLUDE__REGULATOR_SESSION__CAPABILITY_H_ + +#include +#include + +namespace Regulator { typedef Genode::Capability Session_capability; } + +#endif /* _INCLUDE__REGULATOR_SESSION__CAPABILITY_H_ */ diff --git a/include/regulator_session/client.h b/include/regulator_session/client.h new file mode 100644 index 0000000..5d14492 --- /dev/null +++ b/include/regulator_session/client.h @@ -0,0 +1,44 @@ +/* + * \brief Client-side regulator session interface + * \author Stefan Kalkowski + * \date 2013-06-13 + */ + +/* + * 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 _INCLUDE__REGULATOR_SESSION__CLIENT_H_ +#define _INCLUDE__REGULATOR_SESSION__CLIENT_H_ + +#include +#include + +namespace Regulator { struct Session_client; } + + +struct Regulator::Session_client : public Genode::Rpc_client +{ + /** + * Constructor + * + * \param session session capability + */ + Session_client(Session_capability session) + : Genode::Rpc_client(session) { } + + + /********************************* + ** Regulator session interface ** + *********************************/ + + void level(unsigned long level) override { call(level); } + unsigned long level() override { return call(); } + void state(bool enable) override { call(enable); } + bool state() override { return call(); } +}; + +#endif /* _INCLUDE__REGULATOR_SESSION__CLIENT_H_ */ diff --git a/include/regulator_session/connection.h b/include/regulator_session/connection.h new file mode 100644 index 0000000..ef9c7fa --- /dev/null +++ b/include/regulator_session/connection.h @@ -0,0 +1,42 @@ +/* + * \brief Connection to regulator service + * \author Stefan Kalkowski + * \date 2013-06-13 + */ + +/* + * 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 _INCLUDE__REGULATOR_SESSION__CONNECTION_H_ +#define _INCLUDE__REGULATOR_SESSION__CONNECTION_H_ + +#include +#include +#include + +namespace Regulator { struct Connection; } + + +struct Regulator::Connection : Genode::Connection, Session_client +{ + /** + * Constructor + * + * \param regulator identifier for the specific regulator + * \param label string identifier of the client + */ + Connection(Genode::Env &env, Regulator_id regulator, const char * label = "") + : + Genode::Connection(env, + session(env.parent(), + "ram_quota=8K, cap_quota=%ld, regulator=\"%s\", label=\"%s\"", + CAP_QUOTA, regulator_name_by_id(regulator), label)), + Session_client(cap()) + { } +}; + +#endif /* _INCLUDE__REGULATOR_SESSION__CONNECTION_H_ */ diff --git a/include/regulator_session/regulator_session.h b/include/regulator_session/regulator_session.h new file mode 100644 index 0000000..72fa4ca --- /dev/null +++ b/include/regulator_session/regulator_session.h @@ -0,0 +1,65 @@ +/* + * \brief Abstract regulator session interface. + * \author Stefan Kalkowski + * \date 2013-06-13 + */ + +/* + * 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 _INCLUDE__REGULATOR_SESSION__REGULATOR_SESSION_H_ +#define _INCLUDE__REGULATOR_SESSION__REGULATOR_SESSION_H_ + +#include + +namespace Regulator { struct Session; } + + +struct Regulator::Session : public Genode::Session +{ + /** + * \noapi + */ + static const char *service_name() { return "Regulator"; } + + enum { CAP_QUOTA = 2 }; + + virtual ~Session() { } + + /** + * Set regulator specific level + */ + virtual void level(unsigned long level) = 0; + + /** + * Returns current regulator level + */ + virtual unsigned long level() = 0; + + /** + * Enable/disable regulator + */ + virtual void state(bool enable) = 0; + + /** + * Returns whether regulator is enabled or not + */ + virtual bool state() = 0; + + + /******************* + ** RPC interface ** + *******************/ + + GENODE_RPC(Rpc_set_level, void, level, unsigned long); + GENODE_RPC(Rpc_level, unsigned long, level); + GENODE_RPC(Rpc_set_state, void, state, bool); + GENODE_RPC(Rpc_state, bool, state); + GENODE_RPC_INTERFACE(Rpc_set_level, Rpc_level, Rpc_set_state, Rpc_state); +}; + +#endif /* _INCLUDE__REGULATOR_SESSION__REGULATOR_SESSION_H_ */ diff --git a/include/regulator_session/rpc_object.h b/include/regulator_session/rpc_object.h new file mode 100644 index 0000000..4dcf5a8 --- /dev/null +++ b/include/regulator_session/rpc_object.h @@ -0,0 +1,40 @@ +/* + * \brief Server-side block regulator interface + * \author Stefan Kalkowski + * \date 2013-06-13 + */ + +/* + * 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 _INCLUDE__REGULATOR_SESSION__SERVER_H_ +#define _INCLUDE__REGULATOR_SESSION__SERVER_H_ + +#include +#include +#include + +namespace Regulator { class Session_rpc_object; } + + +class Regulator::Session_rpc_object : public Genode::Rpc_object +{ + protected: + + Regulator_id _id; /* regulator identifier */ + + public: + + /** + * Constructor + * + * \param id identifies the specific regulator + */ + Session_rpc_object(Regulator_id id) : _id(id) { } +}; + +#endif /* _INCLUDE__REGULATOR_SESSION__SERVER_H_ */ diff --git a/include/spec/exynos5/regulator/consts.h b/include/spec/exynos5/regulator/consts.h new file mode 100644 index 0000000..29df21f --- /dev/null +++ b/include/spec/exynos5/regulator/consts.h @@ -0,0 +1,86 @@ +/* + * \brief Regulator definitions for Exynos5 + * \author Stefan Kalkowski + * \date 2013-06-13 + */ + +/* + * 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 _INCLUDE__SPEC__EXYNOS5__REGULATOR__CONSTS_H_ +#define _INCLUDE__SPEC__EXYNOS5__REGULATOR__CONSTS_H_ + +#include + +namespace Regulator { + + enum Regulator_id { + CLK_CPU, + CLK_SATA, + CLK_USB30, + CLK_USB20, + CLK_MMC0, + CLK_HDMI, + PWR_SATA, + PWR_USB30, + PWR_USB20, + PWR_HDMI, + MAX, + INVALID + }; + + struct Regulator_name { + Regulator_id id; + const char * name; + }; + + static constexpr Regulator_name names[] = { + { CLK_CPU, "clock-cpu" }, + { CLK_SATA, "clock-sata" }, + { CLK_USB30, "clock-usb3.0" }, + { CLK_USB20, "clock-usb2.0" }, + { CLK_MMC0, "clock-mmc0" }, + { CLK_HDMI, "clock-hdmi" }, + { PWR_SATA, "power-sata" }, + { PWR_USB30, "power-usb3.0" }, + { PWR_USB20, "power-usb2.0" }, + { PWR_HDMI, "power-hdmi"}, + }; + + inline Regulator_id regulator_id_by_name(const char * name) + { + for (unsigned i = 0; i < sizeof(names)/sizeof(names[0]); i++) + if (Genode::strcmp(names[i].name, name) == 0) + return names[i].id; + return INVALID; + } + + inline const char * regulator_name_by_id(Regulator_id id) { + return (id < sizeof(names)/sizeof(names[0])) ? names[id].name : 0; } + + + /*************************************** + ** Device specific level definitions ** + ***************************************/ + + enum Cpu_clock_freq { + CPU_FREQ_200 = 200000000, + CPU_FREQ_400 = 400000000, + CPU_FREQ_600 = 600000000, + CPU_FREQ_800 = 800000000, + CPU_FREQ_1000 = 1000000000, + CPU_FREQ_1200 = 1200000000, + CPU_FREQ_1400 = 1400000000, + CPU_FREQ_1600 = 1600000000, + CPU_FREQ_1700 = 1700000000, + /* warning: 1700 not recommended by the reference manual + we just insert this for performance measurement against + Linux, which uses this overclocking */ + }; +} + +#endif /* _INCLUDE__SPEC__EXYNOS5__REGULATOR__CONSTS_H_ */ diff --git a/lib/mk/spec/arm_v7/bootstrap-hw-arndale.mk b/lib/mk/spec/arm_v7/bootstrap-hw-arndale.mk new file mode 100644 index 0000000..2348f36 --- /dev/null +++ b/lib/mk/spec/arm_v7/bootstrap-hw-arndale.mk @@ -0,0 +1,21 @@ +INC_DIR += $(REP_DIR)/src/bootstrap/spec/arndale +INC_DIR += $(REP_DIR)/src/include + +SRC_CC += bootstrap/spec/arm/cortex_a15_cpu.cc +SRC_CC += bootstrap/spec/arm/gicv2.cc +SRC_CC += bootstrap/spec/arndale/platform.cc +SRC_CC += bootstrap/spec/arm/arm_v7_cpu.cc +SRC_CC += hw/spec/32bit/memory_map.cc +SRC_S += bootstrap/spec/arm/crt0.s + +NR_OF_CPUS = 2 + +# +# we need more specific compiler hints for some 'special' assembly code +# override -march=armv7-a because it conflicts with -mcpu=cortex-a15 +# +CC_MARCH = -mcpu=cortex-a15 -mfpu=vfpv3 -mfloat-abi=softfp + +include $(call select_from_repositories,lib/mk/bootstrap-hw.inc) + +vpath bootstrap/spec/arndale/platform.cc $(REP_DIR)/src diff --git a/lib/mk/spec/arm_v7/bootstrap-hw-odroid_xu.mk b/lib/mk/spec/arm_v7/bootstrap-hw-odroid_xu.mk new file mode 100644 index 0000000..df7eac7 --- /dev/null +++ b/lib/mk/spec/arm_v7/bootstrap-hw-odroid_xu.mk @@ -0,0 +1,10 @@ +INC_DIR += $(REP_DIR)/src/bootstrap/spec/odroid_xu + +SRC_CC += bootstrap/spec/arm/cortex_a15_cpu.cc +SRC_CC += bootstrap/spec/arm/gicv2.cc +SRC_CC += bootstrap/spec/odroid_xu/platform.cc +SRC_CC += bootstrap/spec/arm/arm_v7_cpu.cc +SRC_CC += hw/spec/32bit/memory_map.cc +SRC_S += bootstrap/spec/arm/crt0.s + +include $(call select_from_repositories,lib/mk/bootstrap-hw.inc) diff --git a/lib/mk/spec/arm_v7/core-hw-arndale.mk b/lib/mk/spec/arm_v7/core-hw-arndale.mk new file mode 100644 index 0000000..0ca1d09 --- /dev/null +++ b/lib/mk/spec/arm_v7/core-hw-arndale.mk @@ -0,0 +1,34 @@ +# +# \brief Build config for Genodes core process +# \author Stefan Kalkowski +# \date 2015-02-09 +# + +# add include paths +INC_DIR += $(REP_DIR)/src/core/spec/arndale +INC_DIR += $(REP_DIR)/src/core/spec/arm/virtualization +INC_DIR += $(REP_DIR)/src/include + +# add C++ sources +SRC_CC += kernel/vm_thread_on.cc +SRC_CC += spec/arm/gicv2.cc +SRC_CC += spec/arm/virtualization/gicv2.cc +SRC_CC += spec/arm_v7/virtualization/kernel/vm.cc +SRC_CC += spec/arm/virtualization/platform_services.cc +SRC_CC += spec/arm/virtualization/vm_session_component.cc +SRC_CC += vm_session_common.cc +SRC_CC += vm_session_component.cc + +# add assembly sources +SRC_S += spec/arm_v7/virtualization/exception_vector.s + +NR_OF_CPUS = 2 + +# +# we need more specific compiler hints for some 'special' assembly code +# override -march=armv7-a because it conflicts with -mcpu=cortex-a15 +# +CC_MARCH = -mcpu=cortex-a15 -mfpu=vfpv3 -mfloat-abi=softfp + +# include less specific configuration +include $(REP_DIR)/lib/mk/spec/arm_v7/core-hw-exynos5.inc diff --git a/lib/mk/spec/arm_v7/core-hw-exynos5.inc b/lib/mk/spec/arm_v7/core-hw-exynos5.inc new file mode 100644 index 0000000..15fca18 --- /dev/null +++ b/lib/mk/spec/arm_v7/core-hw-exynos5.inc @@ -0,0 +1,20 @@ +# +# \brief Build config for Genodes core process +# \author Martin Stein +# \date 2011-12-16 +# + +TMP := $(call select_from_repositories,lib/mk/core-hw.inc) +BASE_HW_DIR := $(TMP:%lib/mk/core-hw.inc=%) + +# add include paths +INC_DIR += $(REP_DIR)/src/core +INC_DIR += $(BASE_HW_DIR)/src/core/spec/exynos5 + +# add C++ sources +SRC_CC += spec/arm/exynos_mct.cc + +# include less specific configuration +include $(BASE_HW_DIR)/lib/mk/spec/cortex_a15/core-hw.inc + +vpath spec/arm/exynos_mct.cc ${REP_DIR}/src/core diff --git a/lib/mk/spec/arm_v7/core-hw-odroid_xu.mk b/lib/mk/spec/arm_v7/core-hw-odroid_xu.mk new file mode 100644 index 0000000..75d23dd --- /dev/null +++ b/lib/mk/spec/arm_v7/core-hw-odroid_xu.mk @@ -0,0 +1,17 @@ +# +# \brief Build config for Genodes core process +# \author Stefan Kalkowski +# \date 2015-02-09 +# + +# add include paths +INC_DIR += $(REP_DIR)/src/core/spec/odroid_xu +INC_DIR += $(REP_DIR)/src/include + +# add C++ sources +SRC_CC += spec/arm/gicv2.cc +SRC_CC += kernel/vm_thread_off.cc +SRC_CC += platform_services.cc + +# include less specific configuration +include $(REP_DIR)/lib/mk/spec/arm_v7/core-hw-exynos5.inc diff --git a/mk/spec/arndale.mk b/mk/spec/arndale.mk new file mode 100644 index 0000000..62a836a --- /dev/null +++ b/mk/spec/arndale.mk @@ -0,0 +1,3 @@ +SPECS += arm_v7a framebuffer usb + +include $(call select_from_repositories,mk/spec/arm_v7a.mk) diff --git a/mk/spec/exynos5.mk b/mk/spec/exynos5.mk new file mode 100644 index 0000000..c21c62c --- /dev/null +++ b/mk/spec/exynos5.mk @@ -0,0 +1,5 @@ +SPECS += arm_v7a framebuffer usb + +REP_INC_DIR += include/spec/exynos5 + +include $(BASE_DIR)/mk/spec/arm_v7a.mk diff --git a/mk/spec/odroid_xu.mk b/mk/spec/odroid_xu.mk new file mode 100644 index 0000000..e84ff3a --- /dev/null +++ b/mk/spec/odroid_xu.mk @@ -0,0 +1,4 @@ +SPECS += arm_v7a + +include $(call select_from_repositories,mk/spec/arm_v7a.mk) + diff --git a/recipes/src/base-hw-arndale/content.mk b/recipes/src/base-hw-arndale/content.mk new file mode 100644 index 0000000..20a8afd --- /dev/null +++ b/recipes/src/base-hw-arndale/content.mk @@ -0,0 +1,9 @@ +ARCH := arm_v7 +BOARD := arndale + +content: lib/mk/spec/arm_v7/core-hw-exynos5.inc + +lib/mk/spec/arm_v7/core-hw-exynos5.inc: lib/mk + cp -r $(REP_DIR)/$@ $@ + +include $(REP_DIR)/recipes/src/base-hw_content.inc diff --git a/recipes/src/base-hw-arndale/hash b/recipes/src/base-hw-arndale/hash new file mode 100644 index 0000000..78cf7b4 --- /dev/null +++ b/recipes/src/base-hw-arndale/hash @@ -0,0 +1 @@ +2020-04-08 2390004bdcae04c78423af03c463c3a5a9d5fc4f diff --git a/recipes/src/base-hw-arndale/used_apis b/recipes/src/base-hw-arndale/used_apis new file mode 100644 index 0000000..ed9b772 --- /dev/null +++ b/recipes/src/base-hw-arndale/used_apis @@ -0,0 +1,2 @@ +base-hw +base diff --git a/recipes/src/base-hw-odroid_xu/content.mk b/recipes/src/base-hw-odroid_xu/content.mk new file mode 100644 index 0000000..5a1a718 --- /dev/null +++ b/recipes/src/base-hw-odroid_xu/content.mk @@ -0,0 +1,9 @@ +ARCH := arm_v7 +BOARD := odroid_xu + +content: lib/mk/spec/arm_v7/core-hw-exynos5.inc + +lib/mk/spec/arm_v7/core-hw-exynos5.inc: lib/mk + cp -r $(REP_DIR)/$@ $@ + +include $(REP_DIR)/recipes/src/base-hw_content.inc diff --git a/recipes/src/base-hw-odroid_xu/hash b/recipes/src/base-hw-odroid_xu/hash new file mode 100644 index 0000000..26163e3 --- /dev/null +++ b/recipes/src/base-hw-odroid_xu/hash @@ -0,0 +1 @@ +2020-04-08 a6f0c5c419530587e8b5c8c0b1e8e815ea82bde6 diff --git a/recipes/src/base-hw-odroid_xu/used_apis b/recipes/src/base-hw-odroid_xu/used_apis new file mode 100644 index 0000000..ed9b772 --- /dev/null +++ b/recipes/src/base-hw-odroid_xu/used_apis @@ -0,0 +1,2 @@ +base-hw +base diff --git a/src/bootstrap/spec/arndale/board.h b/src/bootstrap/spec/arndale/board.h new file mode 100644 index 0000000..12f8b46 --- /dev/null +++ b/src/bootstrap/spec/arndale/board.h @@ -0,0 +1,28 @@ +/* + * \brief Arndale specific board definitions + * \author Stefan Kalkowski + * \date 2017-04-03 + */ + +/* + * Copyright (C) 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__BOOTSTRAP__SPEC__ARNDALE__BOARD_H_ +#define _SRC__BOOTSTRAP__SPEC__ARNDALE__BOARD_H_ + +#include +#include +#include +#include + +namespace Board { + using namespace Hw::Arndale_board; + using Pic = Hw::Gicv2; + static constexpr bool NON_SECURE = true; +} + +#endif /* _SRC__BOOTSTRAP__SPEC__ARNDALE__BOARD_H_ */ diff --git a/src/bootstrap/spec/arndale/cpu.cc b/src/bootstrap/spec/arndale/cpu.cc new file mode 100644 index 0000000..2f1c69c --- /dev/null +++ b/src/bootstrap/spec/arndale/cpu.cc @@ -0,0 +1,25 @@ +/* + * \brief CPU-specific initialization code for Arndale + * \author Stefan Kalkowski + * \date 2016-01-07 + */ + +/* + * Copyright (C) 2016-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. + */ + +/* core includes */ +#include +#include + + + +void Genode::Cpu::init(Genode::Translation_table & table) +{ + prepare_nonsecure_world(); + prepare_hypervisor(table); + switch_to_supervisor_mode(); +} diff --git a/src/bootstrap/spec/arndale/platform.cc b/src/bootstrap/spec/arndale/platform.cc new file mode 100644 index 0000000..669bc65 --- /dev/null +++ b/src/bootstrap/spec/arndale/platform.cc @@ -0,0 +1,84 @@ +/* + * \brief Parts of platform that are specific to Arndale + * \author Martin Stein + * \date 2012-04-27 + */ + +/* + * Copyright (C) 2012-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. + */ + +#include +#include + +extern "C" void * _start_setup_stack; /* entrypoint for non-boot CPUs */ +static unsigned char hyp_mode_stack[1024]; /* hypervisor mode's kernel stack */ + +using namespace Board; + +Bootstrap::Platform::Board::Board() +: early_ram_regions(Memory_region { RAM_0_BASE, RAM_0_SIZE }), + core_mmio(Memory_region { IRQ_CONTROLLER_BASE, IRQ_CONTROLLER_SIZE }, + Memory_region { MCT_MMIO_BASE, MCT_MMIO_SIZE }, + Memory_region { UART_2_MMIO_BASE, UART_2_MMIO_SIZE }) { } + + +static inline void switch_to_supervisor_mode() +{ + using Cpsr = Hw::Arm_cpu::Psr; + + Cpsr::access_t cpsr = 0; + Cpsr::M::set(cpsr, Cpsr::M::SVC); + Cpsr::F::set(cpsr, 1); + Cpsr::I::set(cpsr, 1); + + asm volatile ( + "msr sp_svc, sp \n" /* copy current mode's sp */ + "msr lr_svc, lr \n" /* copy current mode's lr */ + "msr elr_hyp, lr \n" /* copy current mode's lr to hyp lr */ + "msr sp_hyp, %[stack] \n" /* copy to hyp stack pointer */ + "msr spsr_cxfs, %[cpsr] \n" /* set psr for supervisor mode */ + "adr lr, 1f \n" /* load exception return address */ + "eret \n" /* exception return */ + "1:":: [cpsr] "r" (cpsr), [stack] "r" (&hyp_mode_stack)); +} + + +unsigned Bootstrap::Platform::enable_mmu() +{ + static volatile bool primary_cpu = true; + static unsigned long timer_freq = 24000000; + + /* locally initialize interrupt controller */ + ::Board::Pic pic { }; + + volatile unsigned long * mct_control = (unsigned long*) 0x101C0240; + *mct_control = 0x100; + prepare_nonsecure_world(timer_freq); + prepare_hypervisor((addr_t)core_pd->table_base); + switch_to_supervisor_mode(); + + Cpu::Sctlr::init(); + Cpu::Cpsr::init(); + + /* primary cpu wakes up all others */ + if (primary_cpu && NR_OF_CPUS > 1) { + Cpu::invalidate_data_cache(); + primary_cpu = false; + Cpu::wake_up_all_cpus(&_start_setup_stack); + } + + Cpu::enable_mmu_and_caches((Genode::addr_t)core_pd->table_base); + + return Cpu::Mpidr::Aff_0::get(Cpu::Mpidr::read()); +} + + +void Board::Cpu::wake_up_all_cpus(void * const ip) +{ + *(void * volatile *)Board::IRAM_BASE = ip; + asm volatile("dsb; sev;"); +} diff --git a/src/bootstrap/spec/odroid_xu/board.h b/src/bootstrap/spec/odroid_xu/board.h new file mode 100644 index 0000000..6e87c82 --- /dev/null +++ b/src/bootstrap/spec/odroid_xu/board.h @@ -0,0 +1,28 @@ +/* + * \brief Odroid XU specific board definitions + * \author Stefan Kalkowski + * \date 2017-04-03 + */ + +/* + * Copyright (C) 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__BOOTSTRAP__SPEC__ODROID_XU__BOARD_H_ +#define _SRC__BOOTSTRAP__SPEC__ODROID_XU__BOARD_H_ + +#include +#include +#include +#include + +namespace Board { + using namespace Hw::Odroid_xu_board; + using Pic = Hw::Gicv2; + static constexpr bool NON_SECURE = false; +} + +#endif /* _SRC__BOOTSTRAP__SPEC__ODROID_XU__BOARD_H_ */ diff --git a/src/bootstrap/spec/odroid_xu/platform.cc b/src/bootstrap/spec/odroid_xu/platform.cc new file mode 100644 index 0000000..2537ec8 --- /dev/null +++ b/src/bootstrap/spec/odroid_xu/platform.cc @@ -0,0 +1,36 @@ +/* + * \brief Parts of platform that are specific to Odroid XU + * \author Martin Stein + * \date 2012-04-27 + */ + +/* + * Copyright (C) 2012-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. + */ + +#include + +using namespace Board; + +Bootstrap::Platform::Board::Board() +: early_ram_regions(Memory_region { RAM_0_BASE, RAM_0_SIZE }), + core_mmio(Memory_region { IRQ_CONTROLLER_BASE, IRQ_CONTROLLER_SIZE }, + Memory_region { IRQ_CONTROLLER_VT_CTRL_BASE, IRQ_CONTROLLER_VT_CTRL_SIZE }, + Memory_region { MCT_MMIO_BASE, MCT_MMIO_SIZE }, + Memory_region { UART_2_MMIO_BASE, UART_2_MMIO_SIZE }) { } + + +unsigned Bootstrap::Platform::enable_mmu() +{ + /* locally initialize interrupt controller */ + ::Board::Pic pic { }; + + Cpu::Sctlr::init(); + Cpu::Cpsr::init(); + Cpu::invalidate_data_cache(); + Cpu::enable_mmu_and_caches((Genode::addr_t)core_pd->table_base); + return 0; +} diff --git a/src/core/spec/arm/exynos_mct.cc b/src/core/spec/arm/exynos_mct.cc new file mode 100644 index 0000000..5955ce5 --- /dev/null +++ b/src/core/spec/arm/exynos_mct.cc @@ -0,0 +1,97 @@ +/* + * \brief Timer driver for core + * \author Stefan Kalkowski + * \author Martin stein + * \date 2013-01-10 + */ + +/* + * 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. + */ + +/* core include */ +#include +#include +#include + +#include + +using namespace Genode; +using namespace Kernel; + + +unsigned Timer::interrupt_id() const +{ + switch (_device.cpu_id) { + case 0: return Board::MCT_IRQ_L0; + case 1: return Board::MCT_IRQ_L1; + default: return 0; + } +} + + +Board::Timer::Timer(unsigned cpu_id) +: + Mmio(Platform::mmio_to_virt(Board::MCT_MMIO_BASE)), + local(Platform::mmio_to_virt(Board::MCT_MMIO_BASE) + + (cpu_id ? L1 : L0)), + ticks_per_ms(calc_ticks_per_ms(Board::MCT_CLOCK)), + cpu_id(cpu_id) +{ + static unsigned initialized = 0; + if (initialized++) return; + + Mct_cfg::access_t mct_cfg = 0; + Mct_cfg::Prescaler::set(mct_cfg, PRESCALER); + Mct_cfg::Div_mux::set(mct_cfg, DIV_MUX); + write(mct_cfg); +} + + +Board::Timer::Local::Local(Genode::addr_t base) +: Mmio(base) +{ + write(Int_enb::Frceie::bits(1)); + + acked_write(0xffffffff); + acked_write(0xffffffff); + + Tcon::access_t tcon = 0; + Tcon::Frc_start::set(tcon, 1); + Tcon::Timer_start::set(tcon, 1); + acked_write(tcon); +} + + +void Timer::_start_one_shot(time_t const ticks) +{ + using Device = Board::Timer; + _device.local.cnt = _device.local.read(); + _device.local.write(1); + _device.local.acked_write(ticks); +} + + +time_t Timer::_duration() const +{ + using Tcnto = Board::Timer::Local::Tcnto; + unsigned long ret = _device.local.cnt - _device.local.read(); + return ret; +} + + + +time_t Timer::ticks_to_us(time_t const ticks) const { + return timer_ticks_to_us(ticks, _device.ticks_per_ms); } + + +time_t Timer::us_to_ticks(time_t const us) const { + return (us / 1000) * _device.ticks_per_ms; } + + +time_t Timer::_max_value() const { + return 0xffffffff; } diff --git a/src/core/spec/arm/exynos_mct.h b/src/core/spec/arm/exynos_mct.h new file mode 100644 index 0000000..f31c69d --- /dev/null +++ b/src/core/spec/arm/exynos_mct.h @@ -0,0 +1,121 @@ +/* + * \brief Timer driver for core + * \author Martin stein + * \date 2013-01-10 + */ + +/* + * Copyright (C) 2013-2017 Genode Labs GmbH + * + * This file is part of the Kernel OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _SRC__CORE__SPEC__ARM__EXYNOS_MCT_H_ +#define _SRC__CORE__SPEC__ARM__EXYNOS_MCT_H_ + +/* Kernel includes */ +#include + +/* base-hw includes */ +#include + +namespace Board { class Timer; } + + +struct Board::Timer : Genode::Mmio +{ + enum { + PRESCALER = 1, + DIV_MUX = 0, + }; + + /** + * MCT configuration + */ + struct Mct_cfg : Register<0x0, 32> + { + struct Prescaler : Bitfield<0, 8> { }; + struct Div_mux : Bitfield<8, 3> { }; + }; + + + /***************** + ** Local timer ** + *****************/ + + enum Local_timer_offset { L0 = 0x300, L1 = 0x400 }; + + struct Local : Genode::Mmio { + + struct Tcntb : Register<0x0, 32> { }; + struct Tcnto : Register<0x4, 32> { }; + struct Icntb : Register<0x8, 32> { }; + struct Icnto : Register<0xc, 32> { }; + struct Frcntb : Register<0x10, 32> { }; + struct Frcnto : Register<0x14, 32> { }; + + struct Tcon : Register<0x20, 32> + { + struct Timer_start : Bitfield<0, 1> { }; + struct Irq_start : Bitfield<1, 1> { }; + struct Irq_type : Bitfield<2, 1> { }; + struct Frc_start : Bitfield<3, 1> { }; + }; + + struct Int_cstat : Register<0x30, 32, true> + { + struct Intcnt : Bitfield<0, 1> { }; + struct Frccnt : Bitfield<1, 1> { }; + }; + + struct Int_enb : Register<0x34, 32> + { + struct Inteie : Bitfield<0, 1> { }; + struct Frceie : Bitfield<1, 1> { }; + }; + + struct Wstat : Register<0x40, 32, true> + { + struct Tcntb : Bitfield<0, 1> { }; + struct Icntb : Bitfield<1, 1> { }; + struct Frcntb : Bitfield<2, 1> { }; + struct Tcon : Bitfield<3, 1> { }; + }; + + Tcnto::access_t cnt = { 0 }; + + /** + * Write to reg that replies via ack bit and clear ack bit + */ + template + void acked_write(typename DEST::Register_base::access_t const v) + { + typedef typename DEST::Register_base Dest; + typedef typename ACK::Bitfield_base Ack; + write(v); + while (!read()); + write(1); + } + + void update_cnt() { cnt = read(); } + + Local(Genode::addr_t base); + }; + + /** + * Calculate amount of ticks per ms for specific input clock + * + * \param clock input clock + */ + Kernel::time_t static calc_ticks_per_ms(unsigned const clock) { + return clock / (PRESCALER + 1) / (1 << DIV_MUX) / 1000; } + + Local local; + unsigned const ticks_per_ms; + unsigned const cpu_id; + + Timer(unsigned cpu_id); +}; + +#endif /* _SRC__CORE__SPEC__ARM__EXYNOS_MCT_H_ */ diff --git a/src/core/spec/arndale/board.h b/src/core/spec/arndale/board.h new file mode 100644 index 0000000..65e8778 --- /dev/null +++ b/src/core/spec/arndale/board.h @@ -0,0 +1,33 @@ +/* + * \brief Board driver for core + * \author Stefan Kalkowski + * \date 2017-04-27 + */ + +/* + * Copyright (C) 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 _CORE__SPEC__ARNDALE__BOARD_H_ +#define _CORE__SPEC__ARNDALE__BOARD_H_ + +#include +#include +#include +#include +#include + +namespace Kernel { class Cpu; } + +namespace Board { + using namespace Hw::Arndale_board; + + struct Virtual_local_pic {}; + + enum { VCPU_MAX = 1 }; +} + +#endif /* _CORE__SPEC__ARNDALE__BOARD_H_ */ diff --git a/src/core/spec/odroid_xu/board.h b/src/core/spec/odroid_xu/board.h new file mode 100644 index 0000000..6dfaf9d --- /dev/null +++ b/src/core/spec/odroid_xu/board.h @@ -0,0 +1,27 @@ +/* + * \brief Board driver for core + * \author Stefan Kalkowski + * \date 2017-04-27 + */ + +/* + * Copyright (C) 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 _CORE__SPEC__ODROID_XU__BOARD_H_ +#define _CORE__SPEC__ODROID_XU__BOARD_H_ + +#include +#include +#include + +namespace Board { + using namespace Hw::Odroid_xu_board; + + using Pic = Hw::Gicv2; +} + +#endif /* _CORE__SPEC__ODROID_XU__BOARD_H_ */ diff --git a/src/drivers/framebuffer/spec/exynos5/driver.cc b/src/drivers/framebuffer/spec/exynos5/driver.cc new file mode 100644 index 0000000..69ac970 --- /dev/null +++ b/src/drivers/framebuffer/spec/exynos5/driver.cc @@ -0,0 +1,1113 @@ +/* + * \brief Framebuffer driver for Exynos5 HDMI + * \author Martin Stein + * \date 2013-08-09 + */ + +/* + * 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. + */ + +/* local includes */ +#include + +/* Genode includes */ +#include +#include +#include +#include + +using namespace Genode; + +enum { + /* Mixer base */ + MIXER_BASE = 0x14450000, + + /* HDMI base */ + HDMI_BASE = 0x14530000, + + /* I2C BASE */ + I2C_BASE = 0x12ce0000, + + /* I2C */ + I2C_HDMI_IRQ = 96, +}; + +/** + * Delayer with timer backend + */ +class Timer_delayer : public Mmio::Delayer, public Timer::Connection +{ + public: + + /************* + ** Delayer ** + *************/ + + Timer_delayer(Genode::Env &env) : Timer::Connection(env) { } + + void usleep(uint64_t us) { Timer::Connection::usleep(us); } +}; + + +/** + * Sends and receives data via I2C protocol as master or slave + */ +class I2c_interface : public Attached_mmio +{ + private: + + /******************** + ** MMIO structure ** + ********************/ + + struct Start_msg : Genode::Register<8> + { + struct Rx : Bitfield<0, 1> { }; + struct Addr : Bitfield<1, 7> { }; + }; + struct Con : Register<0x0, 8> + { + struct Tx_prescaler : Bitfield<0, 4> { }; + struct Irq_pending : Bitfield<4, 1> { }; + struct Irq_en : Bitfield<5, 1> { }; + struct Clk_sel : Bitfield<6, 1> { }; + struct Ack_en : Bitfield<7, 1> { }; + }; + struct Stat : Register<0x4, 8> + { + struct Last_bit : Bitfield<0, 1> { }; + struct Arbitr : Bitfield<3, 1> { }; + struct Txrx_en : Bitfield<4, 1> { }; + struct Busy : Bitfield<5, 1> { }; + struct Mode : Bitfield<6, 2> { }; + }; + struct Add : Register<0x8, 8> + { + struct Slave_addr : Bitfield<1, 7> { }; + }; + struct Ds : Register<0xc, 8> { }; + struct Lc : Register<0x10, 8> + { + struct Sda_out_delay : Bitfield<0, 2> { }; + struct Filter_en : Bitfield<2, 1> { }; + }; + + enum { + MASTER_RX = 2, + MASTER_TX = 3, + TX_DELAY_US = 1, + }; + + Irq_connection _irq; + + Mmio::Delayer &_delayer; + + /** + * Wait until the IRQ signal was received + */ + void _wait_for_irq() + { + /* + * Instead of using the signal from the IRQ session we + * busy wait and poll at max 2048 times. + */ + try { + wait_for(Attempts(2048), Microseconds(500), _delayer, + Con::Irq_pending::Equal(1)); + _irq.ack_irq(); + } + catch (Polling_timeout) { + error("Con::Irq_pending unexpectedly not set"); + } + } + + /** + * Stop a running transfer as master + */ + void _stop_m_transfer() + { + write(0); + write(0); + write(0); + if (read()) { + warning("I2C got stuck after transfer, forcely terminate"); + write(0); + } + } + + /** + * Start transfer of a message as master + * + * \param slave I2C address of targeted slave + * \param tx wether to send or receive + * + * \retval 0 succeeded + * \retval -1 failed + */ + int _start_m_transfer(uint8_t slave, bool tx) + { + /* create start op and get bus */ + Start_msg::access_t start = 0; + Start_msg::Addr::set(start, slave); + Start_msg::Rx::set(start, !tx); + + try { wait_for(_delayer, Stat::Busy::Equal(0)); } + catch (Polling_timeout) { + error("I2C too busy to do transfer"); + return -1; + } + + /* enable signal receipt */ + Con::access_t con = read(); + Con::Irq_en::set(con, 1); + Con::Ack_en::set(con, 1); + write(con); + + /* send start op */ + Stat::access_t stat = 0; + Stat::Txrx_en::set(stat, 1); + Stat::Mode::set(stat, tx ? MASTER_TX : MASTER_RX); + write(stat); + write(start); + _delayer.usleep(TX_DELAY_US); + + /* end start-op transfer */ + write(con); + Stat::Busy::set(stat, 1); + write(stat); + _wait_for_irq(); + if (_arbitration_error()) return -1; + return 0; + } + + /** + * Wether acknowledgment for last transaction can be received + */ + bool _ack_received() + { + for (unsigned i = 0; i < 3; i++) { + if (read() && !read()) return 1; + _delayer.usleep(TX_DELAY_US); + } + error("I2C ack not received"); + return 0; + } + + /** + * Wether arbitration errors occured during the last transaction + */ + bool _arbitration_error() + { + if (read()) { + error("I2C arbitration failed"); + return 1; + } + return 0; + } + + + public: + + /** + * Constructor + * + * \param base physical MMIO base + * \param irq interrupt name + */ + I2c_interface(Genode::Env &env, addr_t base, unsigned irq, + Mmio::Delayer &delayer) + : + Attached_mmio(env, base, 0x10000), _irq(env, irq), + _delayer(delayer) + { + /* FIXME: is this a correct slave address? */ + write(0); + + /* FIXME: do prescaler/clk generic (Lx: s3c24xx_i2c_clockrate) */ + Con::access_t con = 0; + Con::Irq_en::set(con, 1); + Con::Ack_en::set(con, 1); + Con::Tx_prescaler::set(con, 1); + Con::Clk_sel::set(con, 1); + write(con); + + /* FIXME: do delay/filter generic (Lx: s3c24xx_i2c_clockrate) */ + Lc::access_t lc = 0; + Lc::Sda_out_delay::set(lc, 2); + Lc::Filter_en::set(lc, 1); + write(lc); + + _irq.ack_irq(); + } + + ~I2c_interface() { } + + /** + * Transmit an I2C message as master + * + * \param slave I2C address of targeted slave + * \param msg base of message + * \param msg_size size of message + * + * \retval 0 succeeded + * \retval -1 failed + */ + int m_transmit(uint8_t slave, uint8_t * msg, size_t msg_size) + { + /* initialize message transfer */ + if (_start_m_transfer(slave, 1)) return -1; + size_t off = 0; + while (1) + { + /* send next message byte or leave if message end reached */ + if (!_ack_received()) return -1; + if (off == msg_size) break; + write(msg[off]); + _delayer.usleep(TX_DELAY_US); + + /* finish last byte and prepare for next one */ + off++; + write(0); + _wait_for_irq(); + if (_arbitration_error()) return -1; + } + /* end message transfer */ + if (!_ack_received()) return -1; + _stop_m_transfer(); + return 0; + } +}; + +/** + * Mixes several video and graphic inputs to get a single output stream + */ +class Video_mixer : public Attached_mmio +{ + private: + + /******************** + ** MMIO structure ** + ********************/ + + struct Status : Register<0x0, 32> + { + struct Reg_run : Bitfield<0, 1> { }; + struct Sync_enable : Bitfield<2, 1> { }; + struct Dma_16_burst : Bitfield<7, 1> { }; + struct Soft_reset : Bitfield<8, 1> { }; + }; + struct Cfg : Register<0x4, 32> + { + struct Hd_sd : Bitfield<0, 1> { }; + struct Scan_mode : Bitfield<2, 1> { }; + struct M0_video_en : Bitfield<3, 1> { }; + struct M0_g0_en : Bitfield<4, 1> { }; + struct M0_g1_en : Bitfield<5, 1> { }; + struct Dst_sel : Bitfield<7, 1> { }; + struct Hd_mode : Bitfield<6, 1> { }; + struct Out_type : Bitfield<8, 1> { }; + struct Rgb_format : Bitfield<9, 2> { }; + struct M1_video_en : Bitfield<13, 1> { }; + struct M1_g0_en : Bitfield<14, 1> { }; + struct M1_g1_en : Bitfield<15, 1> { }; + struct Layer_update : Bitfield<31, 1> { }; + }; + struct Irq_en : Register<0x8, 32> { }; + + template + struct Grp_cfg : Register + { + struct Color_format : Register::template Bitfield<8, 4> { }; + struct Pixel_blend_en : Register::template Bitfield<16, 1> { }; + struct Win_blend_en : Register::template Bitfield<17, 1> { }; + struct Pre_mul_mode : Register::template Bitfield<20, 1> { }; + struct Blank_change : Register::template Bitfield<21, 1> { }; + struct Rtqos : Register::template Bitfield<23, 9> { }; + }; + struct M0_g0_cfg : Grp_cfg<0x20> { }; + struct M0_g0_base : Register<0x24, 32> { }; + + template + struct Bg_color : Register + { + struct Ycbcr : Register::template Bitfield<0, 24> { }; + }; + struct M0_bg_color0 : Bg_color<0x64> { }; + struct M0_bg_color1 : Bg_color<0x68> { }; + struct M0_bg_color2 : Bg_color<0x6c> { }; + + struct M0_layer_cfg : Register<0x10, 32> + { + struct Video_prio : Bitfield<0, 4> { }; + struct G0_prio : Bitfield<4, 4> { }; + struct G1_prio : Bitfield<8, 4> { }; + }; + struct M0_g0_span : Register<0x28, 32> + { + struct Span : Bitfield<0, 15> { }; + }; + template + struct Xy : Register + { + struct Y : Register::template Bitfield<0, 11> { }; + struct X : Register::template Bitfield<16, 11> { }; + }; + struct M0_g0_sxy : Xy<0x2c> { }; + struct M0_g0_dxy : Xy<0x34> { }; + + struct M0_g0_wh : Register<0x30, 32> + { + struct Height : Bitfield<0, 11> { }; + struct V_scale : Bitfield<12, 2> { }; + struct Width : Bitfield<16, 11> { }; + struct H_scale : Bitfield<28, 2> { }; + }; + struct M0_cm_coeff_y : Register<0x80, 32> { }; + struct M0_cm_coeff_cb : Register<0x84, 32> { }; + struct M0_cm_coeff_cr : Register<0x88, 32> { }; + + public: + + typedef Framebuffer::Driver::Format Format; + + /** + * Constructor + */ + Video_mixer(Genode::Env &env) + : Attached_mmio(env, MIXER_BASE, 0x10000) { } + + /** + * Initialize mixer for displaying one graphical input fullscreen + * + * \param fb_phys physical base of framebuffer + * \param fb_width width of framebuffer in pixel + * \param fb_height height of framebuffer in pixel + * \param fb_format pixel format of framebuffer + * + * \retval 0 succeeded + * \retval -1 failed + */ + int init(addr_t const fb_phys, size_t const fb_width, + size_t const fb_height, Format const fb_format) + { + using namespace Framebuffer; + + /* reset and disable */ + write(1); + write(0); + write(0); + + /* global layer switches and output config */ + Cfg::access_t cfg = read(); + Cfg::M0_video_en::set(cfg, 0); + Cfg::M0_g0_en::set(cfg, 0); + Cfg::M0_g1_en::set(cfg, 0); + Cfg::Dst_sel::set(cfg, 1); /* HDMI */ + Cfg::Out_type::set(cfg, 1); /* RGB888 */ + Cfg::M1_video_en::set(cfg, 0); + Cfg::M1_g0_en::set(cfg, 0); + Cfg::M1_g1_en::set(cfg, 0); + write(cfg); + + /* global input config */ + write(1); + + /* layer arrangement of mixer 0 */ + M0_layer_cfg::access_t lcfg = read(); + M0_layer_cfg::Video_prio::set(lcfg, 0); /* hidden */ + M0_layer_cfg::G0_prio::set(lcfg, 1); /* framebuffer */ + M0_layer_cfg::G1_prio::set(lcfg, 0); /* hidden */ + write(lcfg); + + /* background colors of mixer 0 */ + enum { BLACK = 0x8080 }; + write(BLACK); + write(BLACK); + write(BLACK); + + /* common config of graphic input 0 of mixer 0 */ + M0_g0_cfg::access_t gcfg = read(); + M0_g0_cfg::Rtqos::set(gcfg, 0); + M0_g0_cfg::Pre_mul_mode::set(gcfg, 0); + M0_g0_cfg::Blank_change::set(gcfg, 1); /* no blank key */ + M0_g0_cfg::Win_blend_en::set(gcfg, 0); + M0_g0_cfg::Pixel_blend_en::set(gcfg, 0); + write(gcfg); + + /* input pixel format */ + switch (fb_format) { + case Driver::FORMAT_RGB565: + write(4); + break; + default: + error("framebuffer format not supported"); + return -1; + } + /* window measurements */ + write(fb_width); + M0_g0_wh::access_t wh = read(); + M0_g0_wh::Height::set (wh, fb_height); + M0_g0_wh::V_scale::set(wh, 0); /* don't scale */ + M0_g0_wh::Width::set (wh, fb_width); + M0_g0_wh::H_scale::set(wh, 0); /* don't scale */ + write(wh); + + /* window location at input */ + M0_g0_sxy::access_t sxy = read(); + M0_g0_sxy::Y::set(sxy, 0); + M0_g0_sxy::X::set(sxy, 0); + write(sxy); + + /* window location at output */ + M0_g0_dxy::access_t dxy = read(); + M0_g0_dxy::Y::set(dxy, 0); + M0_g0_dxy::X::set(dxy, 0); + write(dxy); + + /* set-up input DMA */ + write(fb_phys); + + /* + * FIXME: For FB heights greater than 576 Linaro uses RGB709 16-235, + * wich implies reconfiguration of regs 0x80, 0x84, and 0x88. + * As we always use RGB601 0-255 we can live with reset values. + */ + + /* adjust global output config and enable layer */ + cfg = read(); + switch (fb_height) { + case 480: + Cfg::Hd_sd::set(cfg, 1); + Cfg::Hd_mode::set(cfg, 1); + break; + case 576: + Cfg::Hd_sd::set(cfg, 1); + Cfg::Hd_mode::set(cfg, 1); + break; + case 720: + Cfg::Hd_sd::set(cfg, 1); + Cfg::Hd_mode::set(cfg, 1); + break; + case 1080: + Cfg::Hd_sd::set(cfg, 1); + Cfg::Hd_mode::set(cfg, 1); + break; + default: + error("framebuffer height not supported"); + return -1; + } + Cfg::Scan_mode::set(cfg, 1); /* progressive */ + Cfg::M0_g0_en::set(cfg, 1); + Cfg::Rgb_format::set(cfg, 0); /* RGB601, 0-255 */ + Cfg::Layer_update::set(cfg, 1); + write(cfg); + + /* start mixer */ + write(1); + write(1); + return 0; + } +}; + + +/** + * Dedicated I2C interface for communicating with HDMI PHY controller + */ +class I2c_hdmi : public I2c_interface +{ + private: + + enum { + SLAVE_ADDR = 0x08, + HDMI_PHY_SLAVE = 0x38, + }; + + Mmio::Delayer &_delayer; + + public: + + /** + * Constructor + */ + I2c_hdmi(Genode::Env &env, Mmio::Delayer &delayer) + : I2c_interface(env, I2C_BASE, I2C_HDMI_IRQ, delayer), + _delayer(delayer) + { } + + /** + * Stop HDMI PHY from operating + * + * \retval 0 succeeded + * \retval -1 failed + */ + int stop_hdmi_phy() + { + uint8_t stop[] = { 0x1f, 0x00 }; + enum { STOP_SIZE = sizeof(stop)/sizeof(stop[0]) }; + if (m_transmit(HDMI_PHY_SLAVE, stop, STOP_SIZE)) { return -1; } + return 0; + } + + /** + * Configure HDMI PHY for the given parameters and start it + * + * \param pixel_clk frequency of pixel processing + * + * \retval 0 succeeded + * \retval -1 failed + */ + int setup_and_start_hdmi_phy(unsigned const pixel_clk) + { + static uint8_t cfg_148_5[] = { 0x01, + 0xd1,0x1f,0x00,0x40,0x40, + 0xf8,0x08,0x81,0xa0,0xba, + 0xd8,0x45,0xa0,0xac,0x80, + 0x3c,0x80,0x11,0x04,0x02, + 0x22,0x44,0x86,0x54,0x4b, + 0x25,0x03,0x00,0x00,0x01, + 0x00 }; + + /* select and write config */ + uint8_t * cfg; + size_t cfg_size; + switch (pixel_clk) { + case 148500000: + cfg = cfg_148_5; + cfg_size = sizeof(cfg_148_5)/sizeof(cfg_148_5[0]); + break; + default: + error("pixel clock not supported"); + return -1; + } + if (m_transmit(HDMI_PHY_SLAVE, cfg, cfg_size)) { return -1; } + + /* ensure that configuration is applied */ + _delayer.usleep(10000); + + /* start hdmi phy */ + static uint8_t start[] = { 0x1f, 0x80 }; + enum { START_SIZE = sizeof(start)/sizeof(start[0]) }; + if (m_transmit(HDMI_PHY_SLAVE, start, START_SIZE)) { return -1; } + return 0; + } +}; + + +/** + * Converts input stream from video mixer into HDMI packet stream for HDMI PHY + */ +class Hdmi : public Attached_mmio +{ + private: + + /********************************** + ** Utilities for MMIO structure ** + **********************************/ + + template + struct B13o8_0 : Register + { + struct Bits_0_8 : Register::template Bitfield<0, 8> { }; + }; + template + struct B13o8_1 : Register + { + struct Bits_8_5 : Register::template Bitfield<0, 5> { }; + }; + template + struct B12o8_0 : Register + { + struct Bits_0_8 : Register::template Bitfield<0, 8> { }; + }; + template + struct B12o8_1 : Register + { + struct Bits_8_4 : Register::template Bitfield<0, 5> { }; + }; + template + struct B11o8_0 : Register + { + struct Bits_0_8 : Register::template Bitfield<0, 8> { }; + }; + template + struct B11o8_1 : Register + { + struct Bits_8_3 : Register::template Bitfield<0, 3> { }; + }; + + template + void b13o8(uint16_t v) + { + write(v); + write(v >> 8); + } + + template + void b12o8(uint16_t v) + { + write(v); + write(v >> 8); + } + + template + void b11o8(uint16_t v) + { + write(v); + write(v >> 8); + } + + + /************************************************** + ** MMIO structure: Control registers 0x1453xxxx ** + **************************************************/ + + struct Intc_con_0 : Register<0x0, 8> + { + struct En_global : Bitfield<6, 1> { }; + }; + struct Phy_status_0 : Register<0x20, 32> + { + struct Phy_ready : Bitfield<0, 1> { }; + }; + struct Phy_con_0 : Register<0x30, 8> + { + struct Pwr_off : Bitfield<0, 1> { }; + }; + template + struct Rstout : Register + { + struct Reset : Register::template Bitfield<0, 1> { }; + }; + struct Phy_rstout : Rstout<0x74> { }; + struct Core_rstout : Rstout<0x80> { }; + + + /*********************************************** + ** MMIO structure: Core registers 0x1454xxxx ** + ***********************************************/ + + enum { CORE = 0x10000 }; + + template + struct Sync_pol : Register + { + struct Pol : Register::template Bitfield<0, 1> { }; + }; + struct Con_0 : Register + { + struct System_en : Bitfield<0, 1> { }; + struct Blue_scr_en : Bitfield<5, 1> { }; + }; + struct Mode_sel : Register + { + struct Mode : Bitfield<0, 2> { }; + }; + struct H_blank : Bitset_2< B13o8_0, + B13o8_1 > { }; + struct V2_blank : Bitset_2< B13o8_0, + B13o8_1 > { }; + struct V1_blank : Bitset_2< B13o8_0, + B13o8_1 > { }; + struct V_line : Bitset_2< B13o8_0, + B13o8_1 > { }; + struct H_line : Bitset_2< B13o8_0, + B13o8_1 > { }; + struct Hsync_pol : Sync_pol { }; + struct Vsync_pol : Sync_pol { }; + struct Int_pro_mode : Register + { + struct Mode : Bitfield<0, 1> { }; + }; + struct V_blank_f0 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_blank_f1 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct H_sync_start : Bitset_2< B11o8_0 , + B11o8_1 > { }; + struct H_sync_end : Bitset_2< B11o8_0 , + B11o8_1 > { }; + struct V_sync_line_bef_2 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_sync_line_bef_1 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_sync_line_aft_2 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_sync_line_aft_1 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_sync_line_aft_pxl_2 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_sync_line_aft_pxl_1 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_blank_f2 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_blank_f3 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_blank_f4 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_blank_f5 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_sync_line_aft_3 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_sync_line_aft_4 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_sync_line_aft_5 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_sync_line_aft_6 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_sync_line_aft_pxl_3 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_sync_line_aft_pxl_4 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_sync_line_aft_pxl_5 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct V_sync_line_aft_pxl_6 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct Vact_space_1 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct Vact_space_2 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct Vact_space_3 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct Vact_space_4 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct Vact_space_5 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct Vact_space_6 : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct Avi_con : Register + { + struct Tx_con : Bitfield<0, 2> { }; + }; + struct Avi_header_0 : Register { }; + struct Avi_header_1 : Register { }; + struct Avi_header_2 : Register { }; + struct Avi_check_sum : Register { }; + struct Avi_data_1 : Register { }; + struct Avi_data_2 : Register { }; + struct Avi_data_3 : Register { }; + struct Avi_data_4 : Register { }; + struct Avi_data_5 : Register { }; + struct Avi_data_6 : Register { }; + struct Avi_data_7 : Register { }; + struct Avi_data_8 : Register { }; + struct Avi_data_9 : Register { }; + struct Avi_data_10 : Register { }; + struct Avi_data_11 : Register { }; + struct Avi_data_12 : Register { }; + struct Avi_data_13 : Register { }; + + + /*********************************************************** + ** MMIO structure: Timing-generator registers 0x1458xxxx ** + ***********************************************************/ + + enum { TG = 0x50000 }; + + struct Cmd : Register + { + struct Tg_en : Bitfield<0, 1> { }; + }; + struct H_fsz : Bitset_2< B13o8_0 , + B13o8_1 > { }; + struct Hact_st : Bitset_2< B12o8_0 , + B12o8_1 > { }; + struct Hact_sz : Bitset_2< B12o8_0 , + B12o8_1 > { }; + struct V_fsz : Bitset_2< B11o8_0 , + B11o8_1 > { }; + struct Vsync : Bitset_2< B11o8_0 , + B11o8_1 > { }; + struct Vsync2 : Bitset_2< B11o8_0 , + B11o8_1 > { }; + struct Vact_st : Bitset_2< B11o8_0 , + B11o8_1 > { }; + struct Vact_sz : Bitset_2< B11o8_0 , + B11o8_1 > { }; + struct Field_chg : Bitset_2< B11o8_0 , + B11o8_1 > { }; + struct Vact_st2 : Bitset_2< B11o8_0 , + B11o8_1 > { }; + struct Vact_st3 : Bitset_2< B11o8_0 , + B11o8_1 > { }; + struct Vact_st4 : Bitset_2< B11o8_0 , + B11o8_1 > { }; + struct Vsync_top_hdmi : Bitset_2< B11o8_0 , + B11o8_1 > { }; + struct Vsync_bot_hdmi : Bitset_2< B11o8_0 , + B11o8_1 > { }; + struct Field_top_hdmi : Bitset_2< B11o8_0 , + B11o8_1 > { }; + struct Field_bot_hdmi : Bitset_2< B11o8_0 , + B11o8_1 > { }; + struct Fp_3d : Register + { + struct Value : Bitfield<0, 1> { }; + }; + + /** + * Configure core and timing generator for CEA video mode 16 + */ + void _setup_mode_16() + { + /* core config */ + write(280); + write(1125); + write(45); + write(1125); + write(2200); + write(0); + write(0); + write(0); + write(~0); + write(~0); + write(86); + write(130); + write(9); + write(4); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + write(~0); + + /* timing generator config */ + write(2200); + write(280); + write(1920); + write(1125); + write(1); + write(563); + write(45); + write(1080); + write(563); + write(584); + write(1147); + write(1710); + write(1); + write(563); + write(1); + write(563); + write(0); + } + + I2c_hdmi _i2c_hdmi; + + Mmio::Delayer &_delayer; + + public: + + /** + * Constructor + */ + Hdmi(Genode::Env &env, Mmio::Delayer &delayer) + : Attached_mmio(env, HDMI_BASE, 0xa0000), + _i2c_hdmi(env, delayer), _delayer(delayer) + { } + + /** + * Initialize HDMI controller for video output only + * + * \param scr_width screen width in pixel + * \param scr_height screen height in pixel + * + * \retval 0 succeeded + * \retval -1 failed + */ + int init(unsigned scr_width, unsigned scr_height) + { + /* choose appropriate output mode and parameters */ + enum Aspect_ratio { _16_9 }; + enum { VREFRESH_HZ = 60 }; + Aspect_ratio aspect_ratio; + unsigned pixel_clk; + uint8_t cea_video_mode; + + /* FIXME: see edid_cea_modes in drm_edid.c in Linaro */ + if (scr_width == 1920 && scr_height == 1080 && VREFRESH_HZ == 60) { + pixel_clk = 148500000; + aspect_ratio = _16_9; + cea_video_mode = 16; + } else { + error("resolution not supported"); + return -1; + } + /* set-up HDMI PHY */ + write(0); + if (_i2c_hdmi.stop_hdmi_phy()) return -1; + write(1); + _delayer.usleep(10000); + write(0); + _delayer.usleep(10000); + if (_i2c_hdmi.setup_and_start_hdmi_phy(pixel_clk)) return -1; + + /* reset HDMI CORE */ + write(0); + _delayer.usleep(10000); + write(1); + _delayer.usleep(10000); + + /* common config */ + write(0); + write(2); /* HDMI mode */ + write(0); + write(2); /* transmit on every VSYNC */ + + /* AVI packet config: header */ + enum { + INFOFRAME = 0x80, + AVI = 0x02, + TYPE = INFOFRAME | AVI, + VERSION = 2, + LENGTH = 13, + HDR_CHK_SUM = TYPE + VERSION + LENGTH, + }; + write(TYPE); + write(VERSION); + write(LENGTH); + + /* AVI packet config: data byte 1 */ + enum { + UNDERSCANNED_DISPL = 1 << 1, + ACTIVE_FORMAT = 1 << 4, + RGB = 0 << 5, + OUT_FORMAT = UNDERSCANNED_DISPL | ACTIVE_FORMAT | RGB, + }; + write(OUT_FORMAT); + + /* AVI packet config: data byte 2 */ + enum { + PIC_RATIO_16_9 = 0x20, + AVI_RATIO_SAME_AS_PIC = 0x08, + }; + switch (aspect_ratio) { + case _16_9: + write(PIC_RATIO_16_9 | AVI_RATIO_SAME_AS_PIC); + break; + default: + error("aspect ratio not supported"); + return -1; + } + write(cea_video_mode); + + /* AVI packet config: checksum */ + Avi_check_sum::access_t acs = HDR_CHK_SUM; + acs += read(); + acs += read(); + acs += read(); + acs += read(); + acs += read(); + acs += read(); + acs += read(); + acs += read(); + acs += read(); + acs += read(); + acs += read(); + acs += read(); + acs += read(); + acs = 0x100 - (acs & 0xff); + write(acs); + + /** + * FIXME: At this point Linaro writes AUI infoframe. For more + * info see hdmi_conf_init in exynos_hdmi.c. + */ + + /** + * FIXME: At this point Linaro attempts to limit pixel values + * wich fails due to a SW bug and seems to be not necessary + * anyways (see hdmi_conf_init in exynos_hdmi.c). + */ + + /** + * FIXME: At this point Linux configures audio. For more + * info see hdmi_audio_init in exynos_hdmi.c. + */ + + /* do video and timing-generator config */ + switch (cea_video_mode) { + case 16: + _setup_mode_16(); + break; + default: + error("mode not supported"); + return -1; + } + + /* wait for PHY PLLs to get steady */ + try { + wait_for(Attempts(10), Microseconds(500), _delayer, + Phy_status_0::Phy_ready::Equal(1)); + } + catch (Polling_timeout) { + error("HDMI PHY not ready"); + return -1; + } + + /* turn on core and timing generator */ + write(1); + write(1); + + /** + * FIXME: At this point Linaro turns Audio on. + * See hdmi_audio_control in exynos_hdmi.c. + */ + + return 0; + } +}; + + +/************************* + ** Framebuffer::Driver ** + *************************/ + +int Framebuffer::Driver::init(size_t width, size_t height, Format format, + addr_t fb_phys) +{ + _fb_width = width; + _fb_height = height; + _fb_format = format; + + static Timer_delayer delayer(_env); + + /* feed in power and clocks */ + static Regulator::Connection hdmi_clock(_env, Regulator::CLK_HDMI); + hdmi_clock.state(1); + static Regulator::Connection hdmi_power(_env, Regulator::PWR_HDMI); + hdmi_power.state(1); + + int err; + + /* set-up video mixer to feed HDMI */ + static Video_mixer video_mixer(_env); + err = video_mixer.init(fb_phys, _fb_width, _fb_height, _fb_format); + if (err) { return -1; } + + /* set-up HDMI to feed connected device */ + static Hdmi hdmi(_env, delayer); + err = hdmi.init(_fb_width, _fb_height); + if (err) { return -1; } + + return 0; +} diff --git a/src/drivers/framebuffer/spec/exynos5/driver.h b/src/drivers/framebuffer/spec/exynos5/driver.h new file mode 100644 index 0000000..3c0308b --- /dev/null +++ b/src/drivers/framebuffer/spec/exynos5/driver.h @@ -0,0 +1,108 @@ +/* + * \brief Framebuffer driver for Exynos5 HDMI + * \author Martin Stein + * \date 2013-08-09 + */ + +/* + * 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 _DRIVER_H_ +#define _DRIVER_H_ + +/* Genode includes */ +#include +#include +#include + +namespace Framebuffer +{ + using namespace Genode; + + /** + * Framebuffer driver + */ + class Driver; +} + +class Framebuffer::Driver +{ + public: + + enum Format { FORMAT_RGB565 }; + enum Output { OUTPUT_LCD, OUTPUT_HDMI }; + + private: + + Genode::Env &_env; + + size_t _fb_width; + size_t _fb_height; + Format _fb_format; + + public: + + /** + * Constructor + */ + Driver(Genode::Env &env) + : + _env(env), + _fb_width(0), + _fb_height(0), + _fb_format(FORMAT_RGB565) + { } + + /** + * Return amount of bytes that is used for one pixel descriptor + * + * \param format pixel format + * + * \retval 0 failed + * \retval >0 succeeded + */ + static size_t bytes_per_pixel(Format format) + { + switch (format) { + case FORMAT_RGB565: + return 2; + default: + error("unknown pixel format"); + return 0; + } + } + + /** + * Return size of framebuffer in bytes + * + * \param width width of framebuffer in pixel + * \param height height of framebuffer in pixel + * \param format pixel format of framebuffer + * + * \retval 0 failed + * \retval >0 succeeded + */ + size_t buffer_size(size_t width, size_t height, Format format) + { + return bytes_per_pixel(format) * width * height; + } + + /** + * Initialize driver for HDMI output + * + * \param width width of screen and framebuffer in pixel + * \param height height of screen and framebuffer in pixel + * \param format pixel format of framebuffer + * \param fb_phys physical base of framebuffer + * + * \retval -1 failed + * \retval 0 succeeded + */ + int init(size_t width, size_t height, Format format, addr_t fb_phys); +}; + +#endif /* _DRIVER_H_ */ diff --git a/src/drivers/framebuffer/spec/exynos5/main.cc b/src/drivers/framebuffer/spec/exynos5/main.cc new file mode 100644 index 0000000..db547e6 --- /dev/null +++ b/src/drivers/framebuffer/spec/exynos5/main.cc @@ -0,0 +1,147 @@ +/* + * \brief Framebuffer driver for Exynos5 HDMI + * \author Martin Stein + * \date 2013-08-09 + */ + +/* + * 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. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* local includes */ +#include + +namespace Framebuffer +{ + using namespace Genode; + + /** + * Framebuffer session backend + */ + class Session_component; + struct Main; +}; + +class Framebuffer::Session_component +: + public Genode::Rpc_object +{ + private: + + Genode::Env &_env; + + unsigned _width; + unsigned _height; + Driver::Format _format; + size_t _size; + Dataspace_capability _ds; + addr_t _phys_base; + Timer::Connection _timer { _env }; + + /** + * Convert Driver::Format to Framebuffer::Mode::Format + */ + static Mode::Format _convert_format(Driver::Format driver_format) + { + switch (driver_format) { + case Driver::FORMAT_RGB565: return Mode::RGB565; + } + return Mode::INVALID; + } + + public: + + /** + * Constructor + * + * \param driver driver backend that communicates with hardware + * \param width width of framebuffer in pixel + * \param height height of framebuffer in pixel + * \param output targeted output device + */ + Session_component(Genode::Env &env, Driver &driver, + unsigned width, unsigned height) + : + _env(env), + _width(width), + _height(height), + _format(Driver::FORMAT_RGB565), + _size(driver.buffer_size(width, height, _format)), + _ds(_env.ram().alloc(_size, WRITE_COMBINED)), + _phys_base(Dataspace_client(_ds).phys_addr()) + { + if (driver.init(width, height, _format, _phys_base)) { + error("could not initialize display"); + struct Could_not_initialize_display : Exception { }; + throw Could_not_initialize_display(); + } + } + + /************************************ + ** Framebuffer::Session interface ** + ************************************/ + + Dataspace_capability dataspace() override { return _ds; } + + Mode mode() const override + { + return Mode(_width, _height, _convert_format(_format)); + } + + void mode_sigh(Genode::Signal_context_capability) override { } + + void sync_sigh(Genode::Signal_context_capability sigh) override + { + _timer.sigh(sigh); + _timer.trigger_periodic(10*1000); + } + + void refresh(int, int, int, int) override { } +}; + + +static unsigned config_dimension(Genode::Xml_node node, char const *attr, + unsigned default_value) +{ + return node.attribute_value(attr, default_value); +} + + +struct Main +{ + Genode::Env &_env; + Genode::Entrypoint &_ep; + + Genode::Attached_rom_dataspace _config { _env, "config" }; + + Framebuffer::Driver _driver { _env }; + + Framebuffer::Session_component _fb_session { _env, _driver, + config_dimension(_config.xml(), "width", 1920), + config_dimension(_config.xml(), "height", 1080) + }; + + Genode::Static_root _fb_root { _ep.manage(_fb_session) }; + + Main(Genode::Env &env) : _env(env), _ep(_env.ep()) + { + /* announce service and relax */ + _env.parent().announce(_ep.manage(_fb_root)); + } +}; + + +void Component::construct(Genode::Env &env) { static Main main(env); } diff --git a/src/drivers/framebuffer/spec/exynos5/target.mk b/src/drivers/framebuffer/spec/exynos5/target.mk new file mode 100644 index 0000000..b9ee1ae --- /dev/null +++ b/src/drivers/framebuffer/spec/exynos5/target.mk @@ -0,0 +1,8 @@ +TARGET = exynos5_fb_drv +REQUIRES = arm_v7 +SRC_CC += main.cc driver.cc +LIBS += base +INC_DIR += $(PRG_DIR) +INC_DIR += $(call select_from_repositories,include/spec/exynos5) + +CC_CXX_WARN_STRICT := diff --git a/src/drivers/platform/spec/arndale/cmu.h b/src/drivers/platform/spec/arndale/cmu.h new file mode 100644 index 0000000..a519057 --- /dev/null +++ b/src/drivers/platform/spec/arndale/cmu.h @@ -0,0 +1,545 @@ +/* + * \brief Regulator driver for clock management unit of Exynos5250 SoC + * \author Stefan Kalkowski + * \date 2013-06-13 + */ + +/* + * Copyright (C) 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__ARNDALE__CMU_H_ +#define _DRIVERS__PLATFORM__SPEC__ARNDALE__CMU_H_ + +#include +#include +#include +#include + +using namespace Regulator; +using Genode::warning; + +class Cmu : public Regulator::Driver, + private Genode::Attached_mmio +{ + private: + + enum { + CMU_MMIO_BASE = 0x10010000, + CMU_MMIO_SIZE = 0x24000, + }; + + static const Genode::uint16_t m_values[]; /* M values for frequencies */ + static const Genode::uint8_t p_values[]; /* P values for frequencies */ + static const Genode::uint8_t s_values[]; /* S values for frequencies */ + + template + struct Pll_lock : Register + { + struct Pll_locktime : Register::template Bitfield<0, 20> { }; + + static Genode::uint32_t max_lock_time(Genode::uint8_t pdiv) { + return pdiv * 250; }; + }; + + template + struct Pll_con0 : Register + { + struct S : Register::template Bitfield < 0, 3> { }; + struct P : Register::template Bitfield < 8, 6> { }; + struct M : Register::template Bitfield <16, 10> { }; + struct Locked : Register::template Bitfield <29, 1> { }; + struct Enable : Register::template Bitfield <31, 1> { }; + }; + + + /*********************** + ** CMU CPU registers ** + ***********************/ + + typedef Pll_lock<0> Apll_lock; + typedef Pll_con0<0x100> Apll_con0; + + struct Clk_src_cpu : Register<0x200, 32> + { + struct Mux_cpu_sel : Bitfield<16, 1> + { + enum { MOUT_APLL, SCLK_MPLL}; + }; + }; + + struct Clk_mux_stat_cpu : Register<0x400, 32> + { + struct Cpu_sel : Bitfield<16, 3> + { + enum { MOUT_APLL = 0b1, SCLK_MPLL = 0b10 }; + }; + }; + + struct Clk_div_cpu0 : Register<0x500, 32> + { + /* Cpu0 divider values for frequencies 200 - 1700 */ + static const Genode::uint32_t values[]; + }; + + struct Clk_div_cpu1 : Register<0x504, 32> + { + /* Divider for cpu1 doesn't change */ + enum { FIX_VALUE = 32 }; + }; + + struct Clk_div_stat_cpu0 : Register<0x600, 32> + { + struct Div_arm : Bitfield< 0, 1> {}; + struct Div_cpud : Bitfield< 4, 1> {}; + struct Div_acp : Bitfield< 8, 1> {}; + struct Div_pheriph : Bitfield<12, 1> {}; + struct Div_atb : Bitfield<16, 1> {}; + struct Div_pclk_dbg : Bitfield<20, 1> {}; + struct Div_apll : Bitfield<24, 1> {}; + struct Div_arm2 : Bitfield<28, 1> {}; + + static bool in_progress(access_t stat_word) + { + return stat_word & (Div_arm::bits(1) | + Div_cpud::bits(1) | + Div_acp::bits(1) | + Div_pheriph::bits(1) | + Div_atb::bits(1) | + Div_pclk_dbg::bits(1) | + Div_apll::bits(1) | + Div_arm2::bits(1)); + } + }; + + struct Clk_div_stat_cpu1 : Register<0x604, 32> + { + struct Div_copy : Bitfield<0, 1> { }; + struct Div_hpm : Bitfield<4, 1> { }; + + static bool in_progress(access_t stat_word) + { + return stat_word & (Div_copy::bits(1) | + Div_hpm::bits(1)); + } + }; + + + /************************ + ** CMU CORE registers ** + ************************/ + + typedef Pll_lock<0x4000> Mpll_lock; + typedef Pll_con0<0x4100> Mpll_con0; + + struct Clk_src_core1 : Register<0x4204, 32> + { + struct Mux_mpll_sel : Bitfield<8, 1> { enum { XXTI, MPLL_FOUT_RGT }; }; + }; + + struct Clk_gate_ip_acp : Register<0x8800, 32> { }; + struct Clk_gate_ip_isp0 : Register<0xc800, 32> { }; + struct Clk_gate_ip_isp1 : Register<0xc804, 32> { }; + struct Clk_gate_sclk_isp : Register<0xc900, 32> { }; + + + /*********************** + ** CMU TOP registers ** + ***********************/ + + typedef Pll_lock<0x10020> Cpll_lock; + typedef Pll_lock<0x10030> Epll_lock; + typedef Pll_lock<0x10040> Vpll_lock; + typedef Pll_lock<0x10050> Gpll_lock; + typedef Pll_con0<0x10120> Cpll_con0; + typedef Pll_con0<0x10130> Epll_con0; + typedef Pll_con0<0x10140> Vpll_con0; + typedef Pll_con0<0x10150> Gpll_con0; + + struct Clk_src_top2 : Register<0x10218, 32> + { + struct Mux_mpll_user_sel : Bitfield<20, 1> { enum { XXTI, MOUT_MPLL}; }; + }; + + struct Clk_src_fsys : Register<0x10244, 32> + { + struct Sata_sel : Bitfield<24, 1> { + enum { SCLK_MPLL_USER, SCLK_BPLL_USER }; }; + struct Usbdrd30_sel : Bitfield<28, 1> { + enum { SCLK_MPLL_USER, SCLK_CPLL }; }; + }; + + struct Clk_src_mask_fsys : Register<0x10340, 32> + { + struct Mmc0_mask : Bitfield<0, 1> { enum { MASK, UNMASK }; }; + struct Sata_mask : Bitfield<24, 1> { enum { MASK, UNMASK }; }; + struct Usbdrd30_mask : Bitfield<28, 1> { enum { MASK, UNMASK }; }; + }; + + struct Clk_div_fsys0 : Register<0x10548, 32> + { + struct Sata_ratio : Bitfield<20, 4> { }; + struct Usbdrd30_ratio : Bitfield<24, 4> { }; + }; + + struct Clk_div_stat_fsys0 : Register<0x10648, 32> + { + struct Div_sata : Bitfield<20, 1> {}; + struct Div_usbdrd30 : Bitfield<24, 1> {}; + }; + + struct Clk_gate_ip_gscl : Register<0x10920, 32> { }; + struct Clk_gate_ip_disp1 : Register<0x10928, 32> + { + struct Clk_mixer : Bitfield<5, 1> { }; + struct Clk_hdmi : Bitfield<6, 1> { }; + }; + struct Clk_gate_ip_mfc : Register<0x1092c, 32> { }; + struct Clk_gate_ip_g3d : Register<0x10930, 32> { }; + struct Clk_gate_ip_gen : Register<0x10934, 32> { }; + + struct Clk_gate_ip_fsys : Register<0x10944, 32> + { + struct Pdma0 : Bitfield<1, 1> { }; + struct Pdma1 : Bitfield<2, 1> { }; + struct Sata : Bitfield<6, 1> { }; + struct Sdmmc0 : Bitfield<12, 1> { }; + struct Usbhost20 : Bitfield<18, 1> { }; + struct Usbdrd30 : Bitfield<19, 1> { }; + struct Sata_phy_ctrl : Bitfield<24, 1> { }; + struct Sata_phy_i2c : Bitfield<25, 1> { }; + }; + + struct Clk_src_disp1_0 : Register<0x1022c, 32> + { + struct Hdmi_sel : Bitfield<20, 1> { }; + }; + + struct Clk_src_mask_disp1_0 : Register<0x1032c, 32> + { + struct Hdmi_mask : Bitfield<20, 1> { }; + }; + + struct Clk_gate_ip_peric : Register<0x10950, 32> + { + struct Clk_uart2 : Bitfield<2, 1> { }; + struct Clk_i2chdmi : Bitfield<14, 1> { }; + struct Clk_pwm : Bitfield<24, 1> { }; + }; + + struct Clk_gate_block : Register<0x10980, 32> + { + struct Clk_disp1 : Bitfield<5, 1> { }; + struct Clk_gen : Bitfield<2, 1> { }; + }; + + + /************************* + ** CMU CDREX registers ** + *************************/ + + typedef Pll_lock<0x20010> Bpll_lock; + typedef Pll_con0<0x20110> Bpll_con0; + + struct Pll_div2_sel : Register<0x20a24, 32> + { + struct Mpll_fout_sel : Bitfield<4, 1> { + enum { MPLL_FOUT_HALF, MPLL_FOUT }; }; + }; + + + /******************* + ** CPU functions ** + *******************/ + + Cpu_clock_freq _cpu_freq; + + void _cpu_clk_freq(unsigned long level) + { + unsigned freq; + switch (level) { + case CPU_FREQ_200: + freq = 0; + break; + case CPU_FREQ_400: + freq = 1; + break; + case CPU_FREQ_600: + freq = 2; + break; + case CPU_FREQ_800: + freq = 3; + break; + case CPU_FREQ_1000: + freq = 4; + break; + case CPU_FREQ_1200: + freq = 5; + break; + case CPU_FREQ_1400: + freq = 6; + break; + case CPU_FREQ_1600: + freq = 7; + break; + case CPU_FREQ_1700: + freq = 8; + break; + default: + warning("Unsupported CPU frequency level ", level); + warning("Supported values are 200, 400, 600, 800 MHz"); + warning("and 1, 1.2, 1.4, 1.6, 1.7 GHz"); + return; + }; + + /** + * change clock divider values + */ + + /* cpu0 divider */ + write(Clk_div_cpu0::values[freq]); + while (Clk_div_stat_cpu0::in_progress(read())) ; + + /* cpu1 divider */ + write(Clk_div_cpu1::FIX_VALUE); + while (Clk_div_stat_cpu1::in_progress(read())) ; + + + /** + * change APLL frequency + */ + + /* change reference clock to MPLL */ + write(Clk_src_cpu::Mux_cpu_sel::SCLK_MPLL); + while (read() + != Clk_mux_stat_cpu::Cpu_sel::SCLK_MPLL) ; + + /* set lock time */ + unsigned pdiv = p_values[freq]; + write(Apll_lock::max_lock_time(pdiv)); + + /* change P, M, S values of APLL */ + write(p_values[freq]); + write(m_values[freq]); + write(s_values[freq]); + + while (!read()) ; + + /* change reference clock back to APLL */ + write(Clk_src_cpu::Mux_cpu_sel::MOUT_APLL); + while (read() + != Clk_mux_stat_cpu::Cpu_sel::MOUT_APLL) ; + + _cpu_freq = static_cast(level); + } + + + /********************** + ** Device functions ** + **********************/ + + void _hdmi_enable() + { + write(1); + Clk_gate_ip_disp1::access_t gd1 = read(); + Clk_gate_ip_disp1::Clk_mixer::set(gd1, 1); + Clk_gate_ip_disp1::Clk_hdmi::set(gd1, 1); + write(gd1); + write(1); + write(1); + write(1); + } + + void _sata_enable() + { + /* enable I2C for SATA */ + write(1); + + /** + * set SATA clock to 66 MHz (nothing else supported) + * assuming 800 MHz from sclk_mpll_user, formula: sclk / (divider + 1) + */ + write(11); /* */ + while (read()) ; + + /* enable SATA and SATA Phy */ + write(1); + write(1); + write(1); + } + + void _usb30_enable() + { + /** + * set USBDRD30 clock to 66 MHz + * assuming 800 MHz from sclk_mpll_user, formula: sclk / (divider + 1) + */ + write(11); + while (read()) ; + + /* enable USBDRD30 clock */ + write(1); + write(1); + } + + void _enable(Regulator_id id) + { + switch (id) { + case CLK_SATA: + _sata_enable(); + break; + case CLK_HDMI: + _hdmi_enable(); + break; + case CLK_USB30: + _usb30_enable(); + break; + case CLK_USB20: + return write(1); + case CLK_MMC0: + write(1); + write(1); + break; + default: + warning("Unsupported for ", names[id].name); + } + } + + void _disable(Regulator_id id) + { + switch (id) { + case CLK_SATA: + write(0); + write(0); + write(0); + write(0); + break; + case CLK_USB30: + write(0); + write(0); + break; + case CLK_USB20: + return write(0); + case CLK_MMC0: + write(0); + write(0); + break; + default: + warning("Unsupported for ", names[id].name); + } + } + + public: + + /** + * Constructor + */ + Cmu(Genode::Env &env) + : Genode::Attached_mmio(env, CMU_MMIO_BASE, CMU_MMIO_SIZE), + _cpu_freq(CPU_FREQ_1600) + { + /** + * Close certain clock gates by default (~ 0.7 Watt reduction) + */ + write(0); + write(0); + write(0); + write(0); + write(0); + write(0); + write(0); + write(0); + write(0); + write(0); + write(Clk_gate_ip_peric::Clk_uart2::bits(1) | + Clk_gate_ip_peric::Clk_pwm::bits(1)); + write(Clk_gate_block::Clk_gen::bits(1)); + + + /** + * Set default CPU frequency + */ + _cpu_clk_freq(_cpu_freq); + + /** + * Hard wiring of certain reference clocks + */ + write(Pll_div2_sel::Mpll_fout_sel::MPLL_FOUT_HALF); + write(Clk_src_core1::Mux_mpll_sel::MPLL_FOUT_RGT); + write(Clk_src_top2::Mux_mpll_user_sel::MOUT_MPLL); + write(Clk_src_fsys::Sata_sel::SCLK_MPLL_USER); + write(Clk_src_fsys::Usbdrd30_sel::SCLK_MPLL_USER); + } + + + /******************************** + ** Regulator driver interface ** + ********************************/ + + void level(Regulator_id id, unsigned long level) override + { + switch (id) { + case CLK_CPU: + _cpu_clk_freq(level); + break; + default: + warning("Unsupported for ", names[id].name); + } + } + + unsigned long level(Regulator_id id) override + { + switch (id) { + case CLK_CPU: + return _cpu_freq; + case CLK_USB30: + case CLK_SATA: + return 66666666; /* 66 MHz */ + default: + warning("Unsupported for ", names[id].name); + } + return 0; + } + + void state(Regulator_id id, bool enable) override + { + if (enable) + _enable(id); + else + _disable(id); + } + + bool state(Regulator_id id) override + { + switch (id) { + case CLK_SATA: + return read() && + read() && + read(); + case CLK_USB30: + return read() && + read(); + case CLK_USB20: + return read(); + case CLK_MMC0: + return read() && + read(); + default: + warning("Unsupported for ", names[id].name); + } + return true; + } +}; + + +const Genode::uint8_t Cmu::s_values[] = { 2, 1, 1, 0, 0, 0, 0, 0, 0 }; +const Genode::uint16_t Cmu::m_values[] = { 100, 100, 200, 100, 125, + 150, 175, 200, 425 }; +const Genode::uint8_t Cmu::p_values[] = { 3, 3, 4, 3, 3, 3, 3, 3, 6 }; +const Genode::uint32_t Cmu::Clk_div_cpu0::values[] = { 0x1117710, 0x1127710, 0x1137710, + 0x2147710, 0x2147710, 0x3157720, + 0x4167720, 0x4177730, 0x5377730 }; +#endif /* _DRIVERS__PLATFORM__SPEC__ARNDALE__CMU_H_ */ diff --git a/src/drivers/platform/spec/arndale/main.cc b/src/drivers/platform/spec/arndale/main.cc new file mode 100644 index 0000000..b1eeee2 --- /dev/null +++ b/src/drivers/platform/spec/arndale/main.cc @@ -0,0 +1,72 @@ +/* + * \brief Driver for Arndale specific platform devices (clocks, power, etc.) + * \author Stefan Kalkowski + * \date 2013-06-13 + */ + +/* + * Copyright (C) 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. + */ + +#include +#include +#include +#include +#include + +#include +#include + + +struct Driver_factory : Regulator::Driver_factory +{ + Cmu _cmu; + Pmu _pmu; + + Driver_factory(Genode::Env &env) : _cmu(env), _pmu(env) { } + + Regulator::Driver &create(Regulator::Regulator_id id) override + { + switch (id) { + case Regulator::CLK_CPU: + case Regulator::CLK_SATA: + case Regulator::CLK_USB30: + case Regulator::CLK_USB20: + case Regulator::CLK_MMC0: + case Regulator::CLK_HDMI: + return _cmu; + case Regulator::PWR_SATA: + case Regulator::PWR_USB30: + case Regulator::PWR_USB20: + case Regulator::PWR_HDMI: + return _pmu; + default: + throw Genode::Service_denied(); /* invalid regulator */ + }; + } + + void destroy(Regulator::Driver &) override { } +}; + + +struct Main +{ + Genode::Env & env; + Genode::Heap heap { env.ram(), env.rm() }; + ::Driver_factory factory { env }; + Regulator::Root root { env, heap, factory }; + + Main(Genode::Env & env) : env(env) { + env.parent().announce(env.ep().manage(root)); } +}; + + +void Component::construct(Genode::Env &env) +{ + Genode::log("--- Arndale platform driver ---"); + + static Main main(env); +} diff --git a/src/drivers/platform/spec/arndale/pmu.h b/src/drivers/platform/spec/arndale/pmu.h new file mode 100644 index 0000000..fe2fcad --- /dev/null +++ b/src/drivers/platform/spec/arndale/pmu.h @@ -0,0 +1,237 @@ +/* + * \brief Regulator driver for power management unit of Exynos5250 SoC + * \author Stefan Kalkowski + * \date 2013-06-18 + */ + +/* + * Copyright (C) 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__ARNDALE__PMU_H_ +#define _DRIVERS__PLATFORM__SPEC__ARNDALE__PMU_H_ + +#include +#include +#include +#include + +using namespace Regulator; +using Genode::warning; + +class Pmu : public Regulator::Driver, + private Genode::Attached_mmio +{ + private: + + enum { + PMU_MMIO_BASE = 0x10040000, + PMU_MMIO_SIZE = 0x5000, + }; + + template + struct Control : Register + { + struct Enable : Register::template Bitfield<0, 1> { }; + }; + + template + struct Configuration : Register + { + struct Local_pwr_cfg : Register::template Bitfield<0, 3> { }; + }; + + template + struct Status : Register + { + struct Stat : Register::template Bitfield<0, 3> { }; + }; + + template + struct Sysclk_configuration : Register + { + struct Local_pwr_cfg : Register::template Bitfield<0, 1> { }; + }; + + template + struct Sysclk_status : Register + { + struct Stat : Register::template Bitfield<0, 1> { }; + }; + + struct Hdmi_phy_control : Register<0x700, 32> + { + struct Enable : Bitfield<0, 1> { }; + struct Div_ratio : Bitfield<16, 10> { }; + }; + + typedef Control<0x704> Usbdrd_phy_control; + typedef Control<0x708> Usbhost_phy_control; + typedef Control<0x70c> Efnand_phy_control; + typedef Control<0x718> Adc_phy_control; + typedef Control<0x71c> Mtcadc_phy_control; + typedef Control<0x720> Dptx_phy_control; + typedef Control<0x724> Sata_phy_control; + + typedef Sysclk_configuration<0x2a40> Vpll_sysclk_configuration; + typedef Sysclk_status<0x2a44> Vpll_sysclk_status; + typedef Sysclk_configuration<0x2a60> Epll_sysclk_configuration; + typedef Sysclk_status<0x2a64> Epll_sysclk_status; + typedef Sysclk_configuration<0x2aa0> Cpll_sysclk_configuration; + typedef Sysclk_status<0x2aa4> Cpll_sysclk_status; + typedef Sysclk_configuration<0x2ac0> Gpll_sysclk_configuration; + typedef Sysclk_status<0x2ac4> Gpll_sysclk_status; + + typedef Configuration<0x4000> Gscl_configuration; + typedef Status<0x4004> Gscl_status; + typedef Configuration<0x4020> Isp_configuration; + typedef Status<0x4024> Isp_status; + typedef Configuration<0x4040> Mfc_configuration; + typedef Status<0x4044> Mfc_status; + typedef Configuration<0x4060> G3d_configuration; + typedef Status<0x4064> G3d_status; + typedef Configuration<0x40A0> Disp1_configuration; + typedef Status<0x40A4> Disp1_status; + typedef Configuration<0x40C0> Mau_configuration; + typedef Status<0x40C4> Mau_status; + + + template + void _disable_domain() + { + if (read() == 0) + return; + write(0); + while (read() != 0) ; + } + + template + void _enable_domain() + { + if (read() == 7) + return; + write(7); + while (read() != 7) ; + } + + void _enable(unsigned long id) + { + switch (id) { + case PWR_USB30: + write(1); + break; + case PWR_USB20: + write(1); + break; + case PWR_SATA : + write(1); + break; + case PWR_HDMI: { + _enable_domain(); + Hdmi_phy_control::access_t hpc = read(); + Hdmi_phy_control::Div_ratio::set(hpc, 150); + Hdmi_phy_control::Enable::set(hpc, 1); + write(hpc); + break; } + default: + warning("Unsupported for ", names[id].name); + } + } + + void _disable(unsigned long id) + { + switch (id) { + case PWR_USB30: + write(0); + break; + case PWR_USB20: + write(0); + break; + case PWR_SATA : + write(0); + break; + default: + warning("Unsupported for ", names[id].name); + } + } + + public: + + /** + * Constructor + */ + Pmu(Genode::Env &env) + : Genode::Attached_mmio(env, PMU_MMIO_BASE, PMU_MMIO_SIZE) + { + write(0); + write(0); + write(0); + write(0); + write(0); + write(0); + write(0); + write(0); + + _disable_domain(); + _disable_domain(); + _disable_domain(); + _disable_domain(); + _disable_domain(); + _disable_domain(); + + _disable_domain(); + _disable_domain(); + _disable_domain(); + _disable_domain(); + } + + + /******************************** + ** Regulator driver interface ** + ********************************/ + + void level(Regulator_id id, unsigned long /* level */) override + { + switch (id) { + default: + warning("Unsupported for ", names[id].name); + } + } + + unsigned long level(Regulator_id id) override + { + switch (id) { + default: + warning("Unsupported for ", names[id].name); + } + return 0; + } + + void state(Regulator_id id, bool enable) override + { + if (enable) + _enable(id); + else + _disable(id); + } + + bool state(Regulator_id id) override + { + switch (id) { + case PWR_USB30: + return read(); + case PWR_USB20: + return read(); + case PWR_SATA: + return read(); + default: + warning("Unsupported for ", names[id].name); + } + return true; + } +}; + +#endif /* _DRIVERS__PLATFORM__SPEC__ARNDALE__PMU_H_ */ diff --git a/src/drivers/platform/spec/arndale/target.mk b/src/drivers/platform/spec/arndale/target.mk new file mode 100644 index 0000000..1ffc89e --- /dev/null +++ b/src/drivers/platform/spec/arndale/target.mk @@ -0,0 +1,5 @@ +TARGET = arndale_platform_drv +REQUIRES = arm_v7 +SRC_CC = main.cc +INC_DIR += ${PRG_DIR} ${REP_DIR}/include/spec/exynos5 +LIBS = base diff --git a/src/drivers/sd_card/spec/exynos5/driver.cc b/src/drivers/sd_card/spec/exynos5/driver.cc new file mode 100644 index 0000000..51ee066 --- /dev/null +++ b/src/drivers/sd_card/spec/exynos5/driver.cc @@ -0,0 +1,443 @@ +/* + * \brief Exynos5-specific implementation of the Block::Driver interface + * \author Sebastian Sumpf + * \author Martin Stein + * \date 2013-03-22 + */ + +/* + * Copyright (C) 2015-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. + */ + +/* local includes */ +#include + +using namespace Genode; +using namespace Sd_card; + + +Driver::Driver(Env &env) +: + Driver_base(env.ram()), + Attached_mmio(env, MSH_BASE, MSH_SIZE), _env(env) +{ + _irq.sigh(_irq_handler); + _irq.ack_irq(); + + log("SD/MMC card detected"); + log("capacity: ", _card_info.capacity_mb(), " MiB"); +} + + +void Driver::read_dma(Block::sector_t block_number, + size_t block_count, + addr_t buf_phys, + Block::Packet_descriptor &pkt) +{ + if (_block_transfer.pending) { + throw Request_congestion(); } + + if (!_setup_idmac_descriptor_table(block_count, buf_phys)) + throw Io_error(); + + _block_transfer.packet = pkt; + _block_transfer.pending = true; + + if (!_issue_command(Read_multiple_block(block_number))) { + error("Read_multiple_block failed"); + throw Io_error(); + } +} + + +void Driver::write_dma(Block::sector_t block_number, + size_t block_count, + addr_t buf_phys, + Block::Packet_descriptor &pkt) +{ + if (_block_transfer.pending) { + throw Request_congestion(); } + + if (!_setup_idmac_descriptor_table(block_count, buf_phys)) + throw Io_error(); + + _block_transfer.packet = pkt; + _block_transfer.pending = true; + + if (!_issue_command(Write_multiple_block(block_number))) { + error("Read_multiple_block failed"); + throw Io_error(); + } +} + + +bool Driver::_reset() +{ + Mmio::write(0x7); + try { wait_for(Attempts(100), Microseconds(1000), _delayer, + Ctrl::Reset::Equal(0)); } + catch (Polling_timeout) { + error("Could not reset host contoller"); + return false; + } + return true; +} + + +void Driver::_reset_fifo() +{ + Mmio::write(0x2); + try { wait_for(Attempts(100), Microseconds(1000), _delayer, + Ctrl::Reset::Equal(0)); } + catch (Polling_timeout) { + error("Could not reset fifo"); } +} + + +void Driver::_disable_irq() +{ + Mmio::write(~0U); + Mmio::write(0); +} + + +bool Driver::_update_clock_registers() +{ + Cmd::access_t cmd = 0; + Cmd::Wait_prvdata_complete::set(cmd, 1); + Cmd::Update_clock_registers_only::set(cmd, 1); + Cmd::Start_cmd::set(cmd, 1); + Mmio::write(cmd); + + try { wait_for(_delayer, Cmd::Start_cmd::Equal(0)); } + catch (Polling_timeout) { + error("Update clock registers failed"); + return false; + } + return true; +} + + +bool Driver::_setup_bus(unsigned clock_div) +{ + /* set host clock divider */ + Mmio::write(clock_div); + + if (!_update_clock_registers()) + return false; + + /* enable clock for card 1 */ + Mmio::write(0x1); + + if (!_update_clock_registers()) + return false; + + _delayer.usleep(10 * 1000); + return true; +} + + +Card_info Driver::_init() +{ + Mmio::write(1); + if (!_reset()) + throw Detection_failed(); + + Mmio::write(0x1); + + _disable_irq(); + + Mmio::write(~0U); + Mmio::write(0); + Mmio::write(1); + Mmio::write(0); + Mmio::write(0x203f0040); + + /* set to one bit transfer Bit */ + if (!_setup_bus(CLK_DIV_400Khz)) + throw Detection_failed(); + + Mmio::write(BUS_WIDTH_1); + + if (!issue_command(Go_idle_state())) { + warning("Go_idle_state command failed"); + throw Detection_failed(); + } + _delayer.usleep(2000); + + if (!issue_command(Send_if_cond())) { + warning("Send_if_cond command failed"); + throw Detection_failed(); + } + /* if this succeeds it is an SD card */ + if ((Mmio::read() & 0xff) == 0xaa) + log("Found SD card"); + + /* + * We need to issue the same Mmc_send_op_cond command multiple + * times. The first time, we receive the status information. On + * subsequent attempts, the response tells us that the card is + * busy. Usually, the command is issued twice. We give up if the + * card is not reaching busy state after one second. + */ + unsigned i = 1000; + unsigned voltages = 0x300080; + unsigned arg = 0; + for (; i > 0; --i) { + if (!issue_command(Mmc_send_op_cond(arg, true))) { + warning("Sd_send_op_cond command failed"); + throw Detection_failed(); + } + arg = Mmio::read(); + arg = (voltages & (arg & 0x007FFF80)) | (arg & 0x60000000); + + _delayer.usleep(1000); + + if (Ocr::Busy::get(Mmio::read())) + break; + } + if (i == 0) { + error("Send_op_cond timed out, could no power-on SD/MMC card"); + throw Detection_failed(); + } + Card_info card_info = _detect_mmc(); + + /* switch frequency to high speed */ + enum { EXT_CSD_HS_TIMING = 185 }; + if (!issue_command(Mmc_switch(EXT_CSD_HS_TIMING, 1))) { + error("Error setting high speed frequency"); + throw Detection_failed(); + } + enum { EXT_CSD_BUS_WIDTH = 183 }; + /* set card to 8 bit */ + if (!issue_command(Mmc_switch(EXT_CSD_BUS_WIDTH, 2))) { + error("Error setting card bus width"); + throw Detection_failed(); + } + Mmio::write(BUS_WIDTH_8); + + /* set to eight bit transfer Bit */ + if (!_setup_bus(CLK_DIV_52Mhz)) { + error("Error setting bus to high speed"); + throw Detection_failed(); + } + /* Enable IRQs data read timeout, data transfer done, resp error */ + Mmio::write(0x28a); + Mmio::write(1); + return card_info; +} + + +bool Driver::_setup_idmac_descriptor_table(size_t block_count, + addr_t phys_addr) +{ + size_t const max_idmac_block_count = IDMAC_DESC_MAX_ENTRIES * 8; + + if (block_count > max_idmac_block_count) { + error("Block request too large"); + return false; + } + _reset_fifo(); + + Idmac_desc::Flags flags = Idmac_desc::FS; + size_t b = block_count; + int index = 0; + for (index = 0; b; index++, phys_addr += 0x1000, flags = Idmac_desc::NONE) { + b = _idmac_desc[index].set(b, _block_size(), phys_addr, flags); + _idmac_desc[index].next = + _idmac_desc_phys + ((index + 1) * sizeof(Idmac_desc)); + } + _idmac_desc[index].next = (unsigned)_idmac_desc; + _idmac_desc[index].flags |= Idmac_desc::ER; + Mmio::write(_idmac_desc_phys); + + Mmio::write(1); + Mmio::write(1); + + Mmio::write(1); + Mmio::write(1); + + Mmio::write(_block_size()); + Mmio::write(_block_size() * block_count); + + Mmio::write(1); + + return true; +} + + +void Driver::_handle_irq() +{ + _irq.ack_irq(); + + if (!_block_transfer.pending) { + return; } + + bool success = false; + if (Mmio::read()) { + error("Response error"); + } + if (Mmio::read()) { + error("Data read timeout"); + } + if (Mmio::read()) { + error("CRC error"); + } + if (Mmio::read()) { + Mmio::write(~0U); + if (!_issue_command(Stop_transmission())) { + error("unable to stop transmission"); + } else { + success = true; + } + } + _block_transfer.pending = false; + ack_packet(_block_transfer.packet, success); +} + + +bool Driver::_issue_command(Command_base const &command) +{ + try { wait_for(Attempts(10000), Microseconds(100), _delayer, + Status::Data_busy::Equal(0)); } + catch (Polling_timeout) { + error("wait for State::Data_busy timed out ", + Hex(Mmio::read())); + return false; + } + + Mmio::write(~0UL); + + /* write command argument */ + Mmio::write(command.arg); + + Cmd::access_t cmd = 0; + Cmd::Index::set(cmd, command.index); + + if (command.transfer != TRANSFER_NONE) { + /* set data-direction bit depending on the command */ + bool const write = command.transfer == TRANSFER_WRITE; + Cmd::Data_expected::set(cmd, 1); + Cmd::Write::set(cmd, write ? 1 : 0); + } + + Cmd::access_t rsp_type = 0; + switch (command.rsp_type) { + case RESPONSE_NONE: rsp_type = Cmd::Rsp_type::RESPONSE_NONE; break; + case RESPONSE_136_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_136_BIT; break; + case RESPONSE_48_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_48_BIT; break; + case RESPONSE_48_BIT_WITH_BUSY: rsp_type = Cmd::Rsp_type::RESPONSE_48_BIT_WITH_BUSY; break; + } + + Cmd::Rsp_type::set(cmd, rsp_type); + Cmd::Start_cmd::set(cmd, 1); + Cmd::Use_hold_reg::set(cmd ,1); + Cmd::Wait_prvdata_complete::set(cmd, 1); + + if (command.index == 0) + Cmd::Init_sequence::set(cmd, 1); + + /* issue command */ + Mmio::write(cmd); + + try { wait_for(Attempts(10000), Microseconds(100), _delayer, + Rintsts::Command_done::Equal(1)); } + catch (Polling_timeout) { + error("command failed " + "Rintst: ", Mmio::read(), " " + "Mintst: ", Mmio::read(), " " + "Status: ", Mmio::read()); + + if (Mmio::read()) + warning("timeout"); + + if (Mmio::read()) + warning("repsonse error"); + + return false; + } + + /* acknowledge interrupt */ + Mmio::write(1); + + _delayer.usleep(100); + + return true; +} + + +Cid Driver::_read_cid() +{ + Cid cid; + cid.raw_0 = Mmio::read(); + cid.raw_1 = Mmio::read(); + cid.raw_2 = Mmio::read(); + cid.raw_3 = Mmio::read(); + return cid; +} + + +Csd Driver::_read_csd() +{ + Csd csd; + csd.csd0 = Mmio::read(); + csd.csd1 = Mmio::read(); + csd.csd2 = Mmio::read(); + csd.csd3 = Mmio::read(); + return csd; +} + + +size_t Driver::_read_ext_csd() +{ + Attached_ram_dataspace ds(_env.ram(), _env.rm(), 0x1000, UNCACHED); + + addr_t phys = Dataspace_client(ds.cap()).phys_addr(); + _setup_idmac_descriptor_table(1, phys); + + if (!issue_command(Mmc_send_ext_csd())) + throw Detection_failed(); + + try { wait_for(_delayer, Rintsts::Data_transfer_over::Equal(1)); } + catch (Polling_timeout) { + error("cannot retrieve extented CSD"); + throw Detection_failed(); + } + /* clear IRQ */ + Mmio::write(1); + + /* contruct extented CSD */ + Ext_csd csd((addr_t)ds.local_addr()); + + /* read revision */ + if (csd.Mmio::read() < 2) { + error("extented CSD revision is < 2"); + throw Detection_failed(); + } + + /* return sector count */ + uint64_t capacity = csd.Mmio::read() * _block_size(); + + /* to MB */ + return capacity / (1024 * 1024); +} + + +size_t Driver::Idmac_desc::set(size_t block_count, + size_t block_size, + addr_t phys_addr, + Flags flag) +{ + constexpr size_t MAX_BLOCKS = 8; + flags = OWN | flag | + (block_count <= MAX_BLOCKS ? LD : (CH | DIC)); + bytes = ((block_count < MAX_BLOCKS) ? block_count : MAX_BLOCKS) * + block_size; + addr = phys_addr; + + return block_count < MAX_BLOCKS ? + 0 : block_count - MAX_BLOCKS; +} diff --git a/src/drivers/sd_card/spec/exynos5/driver.h b/src/drivers/sd_card/spec/exynos5/driver.h new file mode 100644 index 0000000..5a85662 --- /dev/null +++ b/src/drivers/sd_card/spec/exynos5/driver.h @@ -0,0 +1,246 @@ +/* + * \brief Exynos5-specific implementation of the Block::Driver interface + * \author Sebastian Sumpf + * \author Martin Stein + * \date 2013-03-22 + */ + +/* + * Copyright (C) 2015-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 _DRIVER_H_ +#define _DRIVER_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* local includes */ +#include + +namespace Sd_card { class Driver; } + + +class Sd_card::Driver : public Driver_base, + private Attached_mmio +{ + private: + + /* + * Noncopyable + */ + Driver(Driver const &); + Driver &operator = (Driver const &); + + enum { + HOST_FREQ = 52000000, + CLK_FREQ = 400000000, + CLK_DIV_52Mhz = 4, + CLK_DIV_400Khz = 0xff, + MSH_BASE = 0x12200000, + MSH_SIZE = 0x10000, + IDMAC_DESC_MAX_ENTRIES = 1024, + SDMMC0_IRQ = 107, + }; + + enum Bus_width { + BUS_WIDTH_1 = 0, + BUS_WIDTH_4 = 1, + BUS_WIDTH_8 = 1 << 16, + }; + + template + struct Register : Mmio::Register { }; + + struct Ctrl : Register<0x0> + { + struct Reset : Bitfield<0, 3> { }; + struct Global_interrupt : Bitfield<4, 1> { }; + struct Dma_enable : Bitfield<5, 1> { }; + struct Use_internal_dmac : Bitfield<25, 1> { }; + }; + + struct Pwren : Register<0x4> { }; + struct Clkdiv : Register<0x8> { }; + struct Clkena : Register<0x10> { }; + struct Tmout : Register<0x14> { }; + struct Ctype : Register<0x18, true> { }; + struct Blksize : Register<0x1c> { }; + struct Bytcnt : Register<0x20> { }; + struct Intmask : Register<0x24> { }; + struct Cmdarg : Register<0x28> { }; + + struct Cmd : Register<0x2c> + { + struct Index : Bitfield<0, 6> { }; + struct Rsp_type : Bitfield<6, 3> + { + enum Response { RESPONSE_NONE = 0, + RESPONSE_48_BIT = 1, + RESPONSE_48_BIT_WITH_BUSY = 5, + RESPONSE_136_BIT = 7, + }; + }; + struct Data_expected : Bitfield<9, 1> { }; + struct Write : Bitfield<10, 1> { }; + struct Wait_prvdata_complete : Bitfield<13, 1> { }; + struct Init_sequence : Bitfield<15, 1> { }; + struct Update_clock_registers_only : Bitfield<21, 1> { }; + struct Use_hold_reg : Bitfield<29, 1> { }; + struct Start_cmd : Bitfield<31, 1> { }; + }; + + struct Rsp0 : Register<0x30> { }; + struct Rsp1 : Register<0x34> { }; + struct Rsp2 : Register<0x38> { }; + struct Rsp3 : Register<0x3c> { }; + + struct Mintsts : Register<0x40> { }; + struct Rintsts : Register<0x44, true> + { + struct Response_error : Bitfield<1, 1> { }; + struct Data_transfer_over : Bitfield<3, 1> { }; + struct Command_done : Bitfield<2, 1> { }; + struct Data_crc_error : Bitfield<7, 1> { }; + struct Response_timeout : Bitfield<8, 1> { }; + struct Data_read_timeout : Bitfield<9, 1> { }; + }; + + struct Status : Register<0x48> + { + struct Data_busy : Bitfield<9, 1> { }; + }; + + struct Fifoth : Register<0x4c> { }; + + struct Bmod : Register<0x80, true> + { + struct Fixed_burst : Bitfield<1, 1> { }; + struct Idmac_enable : Bitfield<7, 1> { }; + }; + + struct Pldmnd : Register<0x84> { }; + struct Idsts : Register<0x8c> { }; + struct Idinten : Register<0x90, true> { }; + struct Dbaddr : Register<0x88> { }; + struct Clksel : Register<0x9c> { }; + struct Emmc_ddr_req : Register<0x10c, true> { }; + + struct Idmac_desc + { + enum Flags { + NONE = 0, + DIC = 1 << 1, + LD = 1 << 2, + FS = 1 << 3, + CH = 1 << 4, + ER = 1 << 5, + OWN = 1 << 31, + }; + + unsigned flags; + unsigned bytes; + unsigned addr; + unsigned next; + + size_t set(size_t block_count, + size_t block_size, + addr_t phys_addr, + Flags flag); + }; + + struct Clock_regulator + { + Regulator::Connection regulator; + + Clock_regulator(Env &env) : regulator(env, Regulator::CLK_MMC0) { + regulator.state(true); } + }; + + struct Timer_delayer : Timer::Connection, Mmio::Delayer + { + Timer_delayer(Genode::Env &env) : Timer::Connection(env) { } + + void usleep(uint64_t us) override { Timer::Connection::usleep(us); } + }; + + struct Block_transfer + { + Block::Packet_descriptor packet { }; + bool pending = false; + }; + + Env &_env; + Timer_delayer _delayer { _env }; + Block_transfer _block_transfer { }; + Clock_regulator _clock_regulator { _env }; + Signal_handler _irq_handler { _env.ep(), *this, &Driver::_handle_irq }; + Irq_connection _irq { _env, SDMMC0_IRQ }; + Attached_ram_dataspace _idmac_desc_ds { _env.ram(), _env.rm(), + IDMAC_DESC_MAX_ENTRIES * sizeof(Idmac_desc), + UNCACHED }; + Idmac_desc *const _idmac_desc { _idmac_desc_ds.local_addr() }; + addr_t const _idmac_desc_phys { Dataspace_client(_idmac_desc_ds.cap()) + .phys_addr() }; + Card_info _card_info { _init() }; + + bool _reset(); + void _reset_fifo(); + void _disable_irq(); + bool _update_clock_registers(); + bool _setup_bus(unsigned clock_div); + void _handle_irq(); + Card_info _init(); + + bool _setup_idmac_descriptor_table(size_t block_count, + addr_t phys_addr); + + + /********************* + ** Host_controller ** + *********************/ + + bool _issue_command(Command_base const &command) override; + Cid _read_cid() override ; + Csd _read_csd() override ; + size_t _read_ext_csd() override; + unsigned _read_rca() override { return 0; } + + Card_info card_info() const override { return _card_info; } + + public: + + using Block::Driver::read; + using Block::Driver::write; + + Driver(Env &env); + + + /******************* + ** Block::Driver ** + *******************/ + + void read_dma(Block::sector_t block_number, + size_t block_count, + addr_t buf_phys, + Block::Packet_descriptor &pkt) override; + + void write_dma(Block::sector_t block_number, + size_t block_count, + addr_t buf_phys, + Block::Packet_descriptor &pkt) override; + + bool dma_enabled() override { return true; } + + Ram_dataspace_capability alloc_dma_buffer(size_t size) override { + return _env.ram().alloc(size, UNCACHED); } +}; + +#endif /* _DRIVER_H_ */ diff --git a/src/drivers/sd_card/spec/exynos5/target.mk b/src/drivers/sd_card/spec/exynos5/target.mk new file mode 100644 index 0000000..0508af9 --- /dev/null +++ b/src/drivers/sd_card/spec/exynos5/target.mk @@ -0,0 +1,10 @@ +TARGET = exynos5_sd_card_drv +REQUIRES = arm_v7 +SRC_CC += main.cc driver.cc +LIBS += base +INC_DIR += $(PRG_DIR) +INC_DIR += $(BASE_DIR)/../os/src/drivers/sd_card +INC_DIR += $(REP_DIR)/include/spec/exynos5 + +vpath %.cc $(BASE_DIR)/../os/src/drivers/sd_card +vpath %.cc $(PRG_DIR) diff --git a/src/drivers/usb_host/spec/arndale/platform.cc b/src/drivers/usb_host/spec/arndale/platform.cc new file mode 100644 index 0000000..0726c33 --- /dev/null +++ b/src/drivers/usb_host/spec/arndale/platform.cc @@ -0,0 +1,339 @@ +/* + * \brief EHCI for Arndale initialization code + * \author Sebastian Sumpf + * \date 2013-02-20 + */ + +/* + * Copyright (C) 2013-2017 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +/* Genode */ +#include +#include +#include +#include +#include +#include + +/* Emulation */ +#include +#include + + +using namespace Genode; + +enum { + EHCI_BASE = 0x12110000, + DWC3_BASE = 0x12000000, + DWC3_PHY_BASE = 0x12100000, + GPIO_BASE = 0x11400000, + EHCI_IRQ = 103, + DWC3_IRQ = 104, +}; + +static resource _ehci[] = +{ + { EHCI_BASE, EHCI_BASE + 0xfff, "ehci", IORESOURCE_MEM }, + { EHCI_IRQ, EHCI_IRQ, "ehci-irq", IORESOURCE_IRQ }, +}; + +static resource _dwc3[] = +{ + { DWC3_BASE, DWC3_BASE + 0xcfff, "dwc3", IORESOURCE_MEM }, + { DWC3_IRQ, DWC3_IRQ, "dwc3-irq", IORESOURCE_IRQ }, +}; + + +/** + * EHCI controller + */ +struct Ehci : Genode::Mmio +{ + Ehci(addr_t const mmio_base) : Mmio(mmio_base) + { + write(0); + + /* reset */ + write(1); + + while(read()) + msleep(1); + } + + struct Cmd : Register<0x10, 32> + { + struct Reset : Bitfield<1, 1> { }; + }; +}; + + +/** + * Gpio handling + */ +struct Gpio_bank { + unsigned con; + unsigned dat; +}; + + +static inline +unsigned con_mask(unsigned val) { return 0xf << ((val) << 2); } + + +static inline +unsigned con_sfr(unsigned x, unsigned v) { return (v) << ((x) << 2); } + + +static void gpio_cfg_pin(Gpio_bank *bank, int gpio, int cfg) +{ + unsigned int value; + + value = readl(&bank->con); + value &= ~con_mask(gpio); + value |= con_sfr(gpio, cfg); + writel(value, &bank->con); +} + + +static void gpio_direction_output(Gpio_bank *bank, int gpio, int en) +{ + unsigned int value; + enum { GPIO_OUTPUT = 0x1 }; + + gpio_cfg_pin(bank, gpio, GPIO_OUTPUT); + + value = readl(&bank->dat); + value &= ~(0x1 << gpio); + if (en) + value |= 0x1 << gpio; + writel(value, &bank->dat); +} + + +static void arndale_ehci_init(Genode::Env &env) +{ + enum Gpio_offset { D1 = 0x180, X3 = 0xc60 }; + + /* enable USB3 clock and power up */ + static Regulator::Connection reg_clk(env, Regulator::CLK_USB20); + reg_clk.state(true); + + static Regulator::Connection reg_pwr(env, Regulator::PWR_USB20); + reg_pwr.state(true); + + /* reset hub via GPIO */ + Io_mem_connection io_gpio(env, GPIO_BASE, 0x1000); + addr_t gpio_base = (addr_t)env.rm().attach(io_gpio.dataspace()); + + Gpio_bank *d1 = reinterpret_cast(gpio_base + D1); + Gpio_bank *x3 = reinterpret_cast(gpio_base + X3); + + /* hub reset */ + gpio_direction_output(x3, 5, 0); + /* hub connect */ + gpio_direction_output(d1, 7, 0); + + gpio_direction_output(x3, 5, 1); + gpio_direction_output(d1, 7, 1); + + env.rm().detach(gpio_base); + + /* reset ehci controller */ + Io_mem_connection io_ehci(env, EHCI_BASE, 0x1000); + addr_t ehci_base = (addr_t)env.rm().attach(io_ehci.dataspace()); + + Ehci ehci(ehci_base); + env.rm().detach(ehci_base); +} + + +struct Phy_usb3 : Genode::Mmio +{ + struct Link_system : Register<0x4, 32> + { + struct Fladj : Bitfield<1, 6> { }; + struct Ehci_version_control : Bitfield<27, 1> { }; + }; + + struct Phy_utmi : Register<0x8, 32> { }; + + struct Phy_clk_rst : Register<0x10, 32> + { + struct Common_onn : Bitfield<0, 1> { }; + struct Port_reset : Bitfield<1, 1> { }; + struct Ref_clk_sel : Bitfield<2, 2> { }; + struct Retenablen : Bitfield<4, 1> { }; + struct Fsel : Bitfield<5, 6> { }; + struct Mpll_mult : Bitfield<11, 7> { }; + struct Ref_ssp_en : Bitfield<19, 1> { }; + struct Ssc_en : Bitfield<20, 1> { }; + struct Ssc_ref_clk_sel : Bitfield<23, 8> { }; + }; + + struct Phy_reg0 : Register<0x14, 32> { }; + + struct Phy_param0 : Register<0x1c, 32> + { + struct Loss_level : Bitfield<26, 5> { }; + struct Ref_use_pad : Bitfield<31, 1> { }; + }; + + struct Phy_param1 : Register<0x20, 32> + { + struct Pcs_txdeemph : Bitfield<0, 5> { }; + }; + + struct Phy_test : Register<0x28, 32> + { + struct Power_down_ssb_hsb : Bitfield<2, 2> { }; + }; + + struct Phy_batchg : Register<0x30, 32> + { + struct Utmi_clksel : Bitfield<2, 1> { }; + }; + + struct Phy_resume : Register<0x34, 32> { }; + + Phy_usb3 (Genode::Env & env, addr_t const base) : Mmio(base) + { + Timer::Connection timer(env); + + /* reset */ + write(0); + + /* clock source */ + write(0); + + /* set Loss-of-Signal Detector sensitivity */ + write(0x9); + write(0); + + /* + * Setting the Frame length Adj value[6:1] to default 0x20 + * See xHCI 1.0 spec, 5.2.4 + */ + write(1); + write(0x20); + + /* set Tx De-Emphasis level */ + write(0x1c); + + /* set clock */ + write(1); + + /* PHYTEST POWERDOWN Control */ + write(0); + + /* UTMI power */ + enum { OTG_DISABLE = (1 << 6) }; + write(OTG_DISABLE); + + /* setup clock */ + Phy_clk_rst::access_t clk = 0; + + /* + * Use same reference clock for high speed + * as for super speed + */ + Phy_clk_rst::Ref_clk_sel::set(clk, 0x2); + /* 24 MHz */ + Phy_clk_rst::Fsel::set(clk, 0x2a); + Phy_clk_rst::Mpll_mult::set(clk, 0x68); + Phy_clk_rst::Ssc_ref_clk_sel::set(clk, 0x88); + + /* port reset */ + Phy_clk_rst::Port_reset::set(clk, 1); + + /* digital power supply in normal operating mode */ + Phy_clk_rst::Retenablen::set(clk, 1); + /* enable ref clock for SS function */ + Phy_clk_rst::Ref_ssp_en::set(clk, 1); + /* enable spread spectrum */ + Phy_clk_rst::Ssc_en::set(clk, 1); + /* power down HS Bias and PLL blocks in suspend mode */ + Phy_clk_rst::Common_onn::set(clk, 1); + + write(clk); + timer.usleep(10); + write(0); + } +}; + + +static void arndale_xhci_init(Genode::Env &env) +{ + /* enable USB3 clock and power up */ + static Regulator::Connection reg_clk(env, Regulator::CLK_USB30); + reg_clk.state(true); + + static Regulator::Connection reg_pwr(env, Regulator::PWR_USB30); + reg_pwr.state(true); + + /* setup PHY */ + Attached_io_mem_dataspace io_phy(env, DWC3_PHY_BASE, 0x1000); + Phy_usb3 phy(env, (addr_t)io_phy.local_addr()); +} + + +extern "C" void module_ehci_exynos_init(); +extern "C" void module_dwc3_driver_init(); +extern "C" void module_xhci_plat_init(); + + +void ehci_setup(Services *services) +{ + /* register EHCI controller */ + module_ehci_exynos_init(); + + /* setup controller */ + arndale_ehci_init(services->env); + + /* setup EHCI-controller platform device */ + platform_device *pdev = (platform_device *)kzalloc(sizeof(platform_device), 0); + pdev->name = (char *)"exynos-ehci"; + pdev->id = 0; + pdev->num_resources = 2; + pdev->resource = _ehci; + + /*needed for DMA buffer allocation. See 'hcd_buffer_alloc' in 'buffer.c' */ + static u64 dma_mask = ~(u64)0; + pdev->dev.dma_mask = &dma_mask; + pdev->dev.coherent_dma_mask = ~0; + + platform_device_register(pdev); +} + + +void xhci_setup(Services *services) +{ + module_dwc3_driver_init(); + module_xhci_plat_init(); + + arndale_xhci_init(services->env); + + /* setup DWC3-controller platform device */ + platform_device *pdev = (platform_device *)kzalloc(sizeof(platform_device), 0); + pdev->name = (char *)"dwc3"; + pdev->id = 0; + pdev->num_resources = 2; + pdev->resource = _dwc3; + + /*needed for DMA buffer allocation. See 'hcd_buffer_alloc' in 'buffer.c' */ + static u64 dma_mask = ~(u64)0; + pdev->dev.dma_mask = &dma_mask; + pdev->dev.coherent_dma_mask = ~0; + + platform_device_register(pdev); +} + + +void platform_hcd_init(Genode::Env &, Services *services) +{ + ehci_setup(services); + xhci_setup(services); +} diff --git a/src/drivers/usb_host/spec/arndale/regulator/consts.h b/src/drivers/usb_host/spec/arndale/regulator/consts.h new file mode 100644 index 0000000..1eb9fc8 --- /dev/null +++ b/src/drivers/usb_host/spec/arndale/regulator/consts.h @@ -0,0 +1 @@ +#include diff --git a/src/drivers/usb_host/spec/arndale/target.mk b/src/drivers/usb_host/spec/arndale/target.mk new file mode 100644 index 0000000..550699a --- /dev/null +++ b/src/drivers/usb_host/spec/arndale/target.mk @@ -0,0 +1,30 @@ +DDE_LINUX_DIR = $(BASE_DIR)/../dde_linux + +include $(DDE_LINUX_DIR)/src/drivers/usb_host/target.inc + +TARGET = arndale_usb_host_drv +REQUIRES = arm_v7 + +INC_DIR += $(DDE_LINUX_DIR)/src/drivers/usb_host +INC_DIR += $(DDE_LINUX_DIR)/src/include +INC_DIR += $(DDE_LINUX_DIR)/src/drivers/usb_host/spec/arm +INC_DIR += $(DDE_LINUX_DIR)/src/include/spec/arm +INC_DIR += $(DDE_LINUX_DIR)/src/include/spec/arm_v7 + +SRC_CC += spec/arm/platform.cc +SRC_CC += spec/arndale/platform.cc + +SRC_C += usb/dwc3/core.c +SRC_C += usb/dwc3/dwc3-exynos.c +SRC_C += usb/dwc3/host.c +SRC_C += usb/host/ehci-exynos.c +SRC_C += usb/host/xhci-plat.c + +CC_OPT += -DCONFIG_USB_EHCI_TT_NEWSCHED=1 +CC_OPT += -DCONFIG_USB_DWC3_HOST=1 +CC_OPT += -DCONFIG_USB_OTG_UTILS=1 +CC_OPT += -DCONFIG_USB_XHCI_PLATFORM=1 +CC_OPT += -DDWC3_QUIRK + +vpath %.cc $(DDE_LINUX_DIR)/src +vpath % $(DDE_LINUX_DIR)/src/drivers/usb_host diff --git a/src/include/hw/spec/arm/arndale_board.h b/src/include/hw/spec/arm/arndale_board.h new file mode 100644 index 0000000..e820017 --- /dev/null +++ b/src/include/hw/spec/arm/arndale_board.h @@ -0,0 +1,35 @@ +/* + * \brief Arndale specific board definitions + * \author Stefan Kalkowski + * \date 2019-05-15 + */ + +/* + * Copyright (C) 2019 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__INCLUDE__HW__SPEC__ARM__ARNDALE_BOARD_H_ +#define _SRC__INCLUDE__HW__SPEC__ARM__ARNDALE_BOARD_H_ + +#include +#include +#include +#include + +namespace Hw::Arndale_board { + + using namespace Exynos5; + + using Cpu_mmio = Hw::Cortex_a15_mmio; + using Serial = Genode::Exynos_uart; + + enum { + UART_BASE = UART_2_MMIO_BASE, + UART_CLOCK = 100000000, + }; +} + +#endif /* _SRC__INCLUDE__HW__SPEC__ARM__ARNDALE_BOARD_H_ */ diff --git a/src/include/hw/spec/arm/exynos5.h b/src/include/hw/spec/arm/exynos5.h new file mode 100644 index 0000000..cc9bc95 --- /dev/null +++ b/src/include/hw/spec/arm/exynos5.h @@ -0,0 +1,66 @@ +/* + * \brief MMIO and IRQ definitions common to Exynos5 SoC + * \author Stefan Kalkowski + * \date 2013-11-25 + */ + +/* + * 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. + */ + +#include + +#ifndef _INCLUDE__DRIVERS__DEFS__EXYNOS5_H_ +#define _INCLUDE__DRIVERS__DEFS__EXYNOS5_H_ + +namespace Exynos5 { + + using namespace Arm_v7; + + enum { + /* normal RAM */ + RAM_0_BASE = 0x40000000, + RAM_0_SIZE = 0x80000000, + + /* device IO memory */ + MMIO_0_BASE = 0x10000000, + MMIO_0_SIZE = 0x10000000, + + /* interrupt controller */ + IRQ_CONTROLLER_BASE = 0x10480000, + IRQ_CONTROLLER_SIZE = 0x00010000, + IRQ_CONTROLLER_VT_CTRL_BASE = 0x10484000, + IRQ_CONTROLLER_VT_CTRL_SIZE = 0x1000, + IRQ_CONTROLLER_VT_CPU_BASE = 0x10486000, + IRQ_CONTROLLER_VT_CPU_SIZE = 0x1000, + + /* UART */ + UART_2_MMIO_BASE = 0x12C20000, + UART_2_MMIO_SIZE = 0x1000, + UART_2_IRQ = 85, + + /* pulse-width-modulation timer */ + PWM_MMIO_BASE = 0x12dd0000, + PWM_MMIO_SIZE = 0x1000, + PWM_CLOCK = 66000000, + PWM_IRQ_0 = 68, + + /* multicore timer */ + MCT_MMIO_BASE = 0x101c0000, + MCT_MMIO_SIZE = 0x1000, + MCT_CLOCK = 24000000, + MCT_IRQ_L0 = 152, + MCT_IRQ_L1 = 153, + + /* IRAM */ + IRAM_BASE = 0x02020000, + + /* SATA/AHCI */ + SATA_IRQ = 147, + }; +}; + +#endif /* _INCLUDE__DRIVERS__DEFS__EXYNOS5_H_ */ diff --git a/src/include/hw/spec/arm/odroid_xu_board.h b/src/include/hw/spec/arm/odroid_xu_board.h new file mode 100644 index 0000000..3615822 --- /dev/null +++ b/src/include/hw/spec/arm/odroid_xu_board.h @@ -0,0 +1,33 @@ +/* + * \brief Odroid XU specific board definitions + * \author Stefan Kalkowski + * \date 2019-05-16 + */ + +/* + * Copyright (C) 2019 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__INCLUDE__HW__SPEC__ARM__ODROID_XU_BOARD_H_ +#define _SRC__INCLUDE__HW__SPEC__ARM__ODROID_XU_BOARD_H_ + +#include +#include +#include +#include + +namespace Hw::Odroid_xu_board { + using namespace Exynos5; + using Cpu_mmio = Hw::Cortex_a15_mmio; + using Serial = Genode::Exynos_uart; + + enum { + UART_BASE = UART_2_MMIO_BASE, + UART_CLOCK = 62668800, + }; +} + +#endif /* _SRC__INCLUDE__HW__SPEC__ARM__ODROID_XU_BOARD_H_ */ diff --git a/src/include/hw/uart/exynos.h b/src/include/hw/uart/exynos.h new file mode 100644 index 0000000..1fbb078 --- /dev/null +++ b/src/include/hw/uart/exynos.h @@ -0,0 +1,245 @@ +/* + * \brief Driver for the Exynos UART + * \author Martin stein + * \date 2013-01-09 + */ + +/* + * 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 _INCLUDE__DRIVERS__UART__EXYNOS_H_ +#define _INCLUDE__DRIVERS__UART__EXYNOS_H_ + +/* Genode includes */ +#include + +namespace Genode { class Exynos_uart; } + + +/** + * Exynos UART driver base + */ +class Genode::Exynos_uart: Mmio +{ + protected: + + /** + * Line control + */ + struct Ulcon : Register<0x0, 32> + { + struct Word_length : Bitfield<0, 2> { enum { _8_BIT = 3 }; }; + struct Stop_bits : Bitfield<2, 1> { enum { _1_BIT = 0 }; }; + struct Parity_mode : Bitfield<3, 3> { enum { NONE = 0 }; }; + struct Infrared_mode : Bitfield<6, 1> { }; + + /** + * Initialization value + */ + static access_t init_value() + { + return Word_length::bits(Word_length::_8_BIT) | + Stop_bits::bits(Stop_bits::_1_BIT) | + Parity_mode::bits(Parity_mode::NONE) | + Infrared_mode::bits(0); + } + }; + + /** + * Control + */ + struct Ucon : Register<0x4, 32> + { + struct Receive_mode : Bitfield<0, 2> { enum { IRQ_POLL = 1 }; }; + struct Transmit_mode : Bitfield<2, 2> { enum { IRQ_POLL = 1 }; }; + struct Send_brk_signal : Bitfield<4, 1> { }; + struct Loop_back_mode : Bitfield<5, 1> { }; + struct Rx_err_irq : Bitfield<6, 1> { }; + struct Rx_timeout : Bitfield<7, 1> { }; + struct Rx_irq_type : Bitfield<8, 1> { enum { LEVEL = 1 }; }; + struct Tx_irq_type : Bitfield<9, 1> { enum { LEVEL = 1 }; }; + struct Rx_to_dma_susp : Bitfield<10, 1> { }; + struct Rx_to_empty_rx : Bitfield<11, 1> { }; + struct Rx_to_interval : Bitfield<12, 4> { }; + struct Rx_dma_bst_size : Bitfield<16, 3> { }; + struct Tx_dma_bst_size : Bitfield<20, 3> { }; + + /** + * Initialization value + */ + static access_t init_value() + { + return Receive_mode::bits(Receive_mode::IRQ_POLL) | + Transmit_mode::bits(Transmit_mode::IRQ_POLL) | + Rx_timeout::bits(1); + } + }; + + /** + * FIFO control + */ + struct Ufcon : Register<0x8, 32> + { + struct Fifo_en : Bitfield<0, 1> { }; + struct Rx_fifo_rst : Bitfield<1, 1> { }; + struct Tx_fifo_rst : Bitfield<2, 1> { }; + }; + + /** + * Modem control + */ + struct Umcon : Register<0xc, 32> + { + struct Send_request : Bitfield<0, 1> { }; + struct Modem_irq : Bitfield<3, 1> { }; + struct Auto_flow_ctl : Bitfield<4, 1> { }; + struct Rts_trigger : Bitfield<5, 3> { }; + + /** + * Initialization value + */ + static access_t init_value() + { + return Send_request::bits(0) | + Modem_irq::bits(0) | + Auto_flow_ctl::bits(0) | + Rts_trigger::bits(0); + } + }; + + /** + * FIFO status + */ + struct Ufstat : Register<0x18, 32> + { + struct Rx_fifo_count : Bitfield<0, 8> { }; + struct Rx_fifo_full : Bitfield<8, 1> { }; + struct Tx_fifo_full : Bitfield<24, 1> { }; + }; + + /** + * Transmit buffer + */ + struct Utxh : Register<0x20, 32> + { + struct Transmit_data : Bitfield<0, 8> { }; + }; + + /** + * Receive buffer + */ + struct Urxh : Register<0x24, 32> + { + struct Receive_data : Bitfield<0, 8> { }; + }; + + /** + * Baud Rate Divisor + */ + struct Ubrdiv : Register<0x28, 32> + { + struct Baud_rate_div : Bitfield<0, 16> { }; + }; + + /** + * Fractional part of Baud Rate Divisor + */ + struct Ufracval : Register<0x2c, 32> + { + struct Baud_rate_frac : Bitfield<0, 4> { }; + }; + + /** + * Interrupt mask register + */ + template + struct Uintx : Register + { + struct Rxd : Register::template Bitfield<0, 1> { }; + struct Error : Register::template Bitfield<1, 1> { }; + struct Txd : Register::template Bitfield<2, 1> { }; + struct Modem : Register::template Bitfield<3, 1> { }; + }; + + using Uintp = Uintx<0x30>; + using Uintm = Uintx<0x38>; + + void _rx_enable() + { + write(1); + + /* mask all IRQs except receive IRQ */ + write(Uintm::Error::bits(1) | + Uintm::Txd::bits(1) | + Uintm::Modem::bits(1)); + + /* clear pending IRQs */ + write(Uintp::Rxd::bits(1) | + Uintp::Error::bits(1) | + Uintp::Txd::bits(1) | + Uintp::Modem::bits(1)); + } + + bool _rx_avail() { + return (read() & (Ufstat::Rx_fifo_count::bits(0xff) + | Ufstat::Rx_fifo_full::bits(1))); } + + /** + * Return character received via UART + */ + char _rx_char() + { + (void)read(); + char c = read(); + + /* clear pending RX IRQ */ + write(Uintp::Rxd::bits(1)); + return c; + } + + public: + + /** + * Constructor + * + * \param base MMIO base address + * \param clock reference clock + * \param baud_rate targeted baud rate + */ + Exynos_uart(addr_t const base, unsigned const clock, + unsigned const baud_rate) : Mmio(base) + { + /* RX and TX FIFO reset */ + write(1); + write(1); + while (read() || read()) ; + + /* init control registers */ + write(Ulcon::init_value()); + write(Ucon::init_value()); + write(Umcon::init_value()); + + /* apply baud rate */ + float const div_val = ((float)clock / (baud_rate * 16)) - 1; + Ubrdiv::access_t const ubrdiv = div_val; + Ufracval::access_t const ufracval = + ((float)div_val - ubrdiv) * 16; + write(ubrdiv); + write(ufracval); + } + + /** + * Print character 'c' through the UART + */ + void put_char(char const c) + { + while (read()) ; + write(c); + } +}; + +#endif /* _INCLUDE__DRIVERS__UART__EXYNOS_H_ */ diff --git a/tool/run/boot_dir/hw b/tool/run/boot_dir/hw index e896355..5041e3e 100644 --- a/tool/run/boot_dir/hw +++ b/tool/run/boot_dir/hw @@ -2,7 +2,9 @@ source [genode_dir]/tool/run/boot_dir/hw proc bootstrap_link_address { } { - if {[have_spec "panda"]} { return "0x88000000" } + if {[have_spec "arndale"]} { return "0x88000000" } + if {[have_spec "odroid_xu"]} { return "0x88000000" } + if {[have_spec "panda"]} { return "0x88000000" } puts "unknown platform no linker address known" exit -1 @@ -14,7 +16,9 @@ proc bootstrap_link_address { } { # proc base_src { } { - if {[have_spec panda]} { return base-hw-panda } + if {[have_spec arndale]} { return base-hw-arndale } + if {[have_spec odroid_xu]} { return base-hw-odroid_xu } + if {[have_spec panda]} { return base-hw-panda } global specs