diff --git a/lib/mk/spec/arm_v7/bootstrap-hw-panda.mk b/lib/mk/spec/arm_v7/bootstrap-hw-panda.mk new file mode 100644 index 0000000..21edb7f --- /dev/null +++ b/lib/mk/spec/arm_v7/bootstrap-hw-panda.mk @@ -0,0 +1,17 @@ +NR_OF_CPUS = 2 + +INC_DIR += $(REP_DIR)/src/bootstrap/spec/panda +INC_DIR += $(REP_DIR)/src/include +SRC_CC += bootstrap/spec/arm/cpu.cc +SRC_CC += bootstrap/spec/arm/cortex_a9_mmu.cc +SRC_CC += bootstrap/spec/arm/gicv2.cc +SRC_CC += bootstrap/spec/panda/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 + +CC_MARCH = -mcpu=cortex-a9 -mfpu=vfpv3 -mfloat-abi=softfp + +include $(BASE_DIR)/../base-hw/lib/mk/bootstrap-hw.inc + +vpath bootstrap/spec/panda/platform.cc $(REP_DIR)/src diff --git a/lib/mk/spec/arm_v7/core-hw-panda.mk b/lib/mk/spec/arm_v7/core-hw-panda.mk new file mode 100644 index 0000000..d5920a6 --- /dev/null +++ b/lib/mk/spec/arm_v7/core-hw-panda.mk @@ -0,0 +1,20 @@ +# +# \brief Build config for Genodes core process +# \author Stefan Kalkowski +# \author Martin Stein +# \date 2012-10-04 +# + +# add include paths +INC_DIR += $(REP_DIR)/src/core/spec/panda +INC_DIR += $(REP_DIR)/src/include + +# add C++ sources +SRC_CC += platform_services.cc + +NR_OF_CPUS += 2 + +CC_MARCH = -mcpu=cortex-a9 -mfpu=vfpv3 -mfloat-abi=softfp + +# include less specific configuration +include $(BASE_DIR)/../base-hw/lib/mk/spec/cortex_a9/core-hw.inc diff --git a/mk/spec/panda.mk b/mk/spec/panda.mk new file mode 100644 index 0000000..e696e96 --- /dev/null +++ b/mk/spec/panda.mk @@ -0,0 +1,11 @@ +# +# Enable peripherals of the platform +# +SPECS += omap4 usb panda gpio framebuffer + +# +# Pull in CPU specifics +# +SPECS += arm_v7a + +include $(BASE_DIR)/mk/spec/arm_v7a.mk diff --git a/recipes/src/base-hw-panda/content.mk b/recipes/src/base-hw-panda/content.mk new file mode 100644 index 0000000..db8aa59 --- /dev/null +++ b/recipes/src/base-hw-panda/content.mk @@ -0,0 +1,41 @@ +BOARD = panda + +content: src/include src/core src/lib src/timer lib/mk LICENSE + +src/include src/core src/lib src/timer lib/mk: + mkdir -p $@ + cp -r $(GENODE_DIR)/repos/base/$@/* $@ + cp -r $(GENODE_DIR)/repos/base-hw/$@/* $@ + +LICENSE: + cp $(GENODE_DIR)/LICENSE $@ + +content: lib/mk/spec/arm_v7/bootstrap-hw-panda.mk lib/mk/spec/arm_v7/core-hw-panda.mk + +lib/mk/spec/arm_v7/bootstrap-hw-panda.mk lib/mk/spec/arm_v7/core-hw-panda.mk: lib/mk + cp $(REP_DIR)/$@ $@ + +content: etc/specs.conf src/bootstrap + +etc/specs.conf src/bootstrap: + mkdir -p etc + mkdir -p src + cp -r $(GENODE_DIR)/repos/base-hw/$@ $@ + +content: generalize_target_names remove_other_board_libs + +generalize_target_names: lib/mk src/lib src/timer + for spec in arm riscv x86_64; do \ + mv lib/mk/spec/$$spec/ld-hw.mk lib/mk/spec/$$spec/ld.mk; \ + done; + sed -i "s/ld-hw/ld/" src/lib/ld/hw/target.mk + sed -i "s/hw_timer_drv/timer/" src/timer/hw/target.mk + +remove_other_board_libs: lib/mk + find lib/mk/spec -name core-hw-*.mk -o -name bootstrap-hw-*.mk |\ + grep -v "hw-$(BOARD).mk" | xargs rm -rf + +content: enable_board_spec + +enable_board_spec: etc/specs.conf + echo "SPECS += panda" >> etc/specs.conf diff --git a/recipes/src/base-hw-panda/hash b/recipes/src/base-hw-panda/hash new file mode 100644 index 0000000..4c29903 --- /dev/null +++ b/recipes/src/base-hw-panda/hash @@ -0,0 +1 @@ +2020-03-23-b 841ce3bb426e5685d1c08612acdd78b70eb5e7b8 diff --git a/recipes/src/base-hw-panda/used_apis b/recipes/src/base-hw-panda/used_apis new file mode 100644 index 0000000..ed9b772 --- /dev/null +++ b/recipes/src/base-hw-panda/used_apis @@ -0,0 +1,2 @@ +base-hw +base diff --git a/src/bootstrap/spec/panda/board.h b/src/bootstrap/spec/panda/board.h new file mode 100644 index 0000000..a2e233b --- /dev/null +++ b/src/bootstrap/spec/panda/board.h @@ -0,0 +1,81 @@ +/* + * \brief Pandaboard specific definitions + * \author Stefan Kalkowski + * \date 2017-02-20 + */ + +/* + * 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__PANDA__BOARD_H_ +#define _SRC__BOOTSTRAP__SPEC__PANDA__BOARD_H_ + +#include +#include +#include +#include + +namespace Board { + using namespace Hw::Panda_board; + using Pic = Hw::Gicv2; + static constexpr bool NON_SECURE = false; + + class L2_cache; +} + +namespace Bootstrap { struct Actlr; } + + +struct Bootstrap::Actlr +{ + static void enable_smp() + { + using namespace Board; + call_panda_firmware(CPU_ACTLR_SMP_BIT_RAISE, 0); + } + + static void disable_smp() { /* not implemented */ } +}; + + +class Board::L2_cache : Hw::Pl310 +{ + private: + + unsigned long _init_value() + { + Aux::access_t v = 0; + Aux::Associativity::set(v, Aux::Associativity::WAY_16); + Aux::Way_size::set(v, Aux::Way_size::KB_64); + Aux::Share_override::set(v, true); + Aux::Replacement_policy::set(v, Aux::Replacement_policy::PRAND); + Aux::Ns_lockdown::set(v, true); + Aux::Ns_irq_ctrl::set(v, true); + Aux::Data_prefetch::set(v, true); + Aux::Inst_prefetch::set(v, true); + Aux::Early_bresp::set(v, true); + return v; + } + + public: + + L2_cache(Genode::addr_t mmio) : Hw::Pl310(mmio) { + call_panda_firmware(L2_CACHE_AUX_REG, _init_value()); } + + using Hw::Pl310::invalidate; + + void enable() + { + call_panda_firmware(L2_CACHE_ENABLE_REG, 1); + Pl310::mask_interrupts(); + } + + void disable() { + call_panda_firmware(L2_CACHE_ENABLE_REG, 0); } +}; + +#endif /* _SRC__BOOTSTRAP__SPEC__PANDA__BOARD_H_ */ diff --git a/src/bootstrap/spec/panda/platform.cc b/src/bootstrap/spec/panda/platform.cc new file mode 100644 index 0000000..188847b --- /dev/null +++ b/src/bootstrap/spec/panda/platform.cc @@ -0,0 +1,50 @@ +/* + * \brief Parts of platform that are specific to Pandaboard + * \author Stefan Kalkowski + * \date 2017-01-30 + */ + +/* + * 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 + +using namespace Board; + +Bootstrap::Platform::Board::Board() +: early_ram_regions(Memory_region { RAM_0_BASE, RAM_0_SIZE }), + core_mmio(Memory_region { CORTEX_A9_PRIVATE_MEM_BASE, + CORTEX_A9_PRIVATE_MEM_SIZE }, + Memory_region { TL16C750_3_MMIO_BASE, + TL16C750_MMIO_SIZE }, + Memory_region { PL310_MMIO_BASE, + PL310_MMIO_SIZE }) { } + + +bool Board::Cpu::errata(Board::Cpu::Errata) { return false; } + + +void Board::Cpu::wake_up_all_cpus(void * const ip) +{ + struct Wakeup_generator : Genode::Mmio + { + struct Aux_core_boot_0 : Register<0x800, 32> { + struct Cpu1_status : Bitfield<2, 2> { }; }; + + struct Aux_core_boot_1 : Register<0x804, 32> { }; + + Wakeup_generator(void * const ip) : Mmio(CORTEX_A9_WUGEN_MMIO_BASE) + { + write((Genode::addr_t)ip); + write(1); + } + }; + + Wakeup_generator wgen(ip); + asm volatile("dsb\n" + "sev\n"); +} diff --git a/src/core/spec/panda/board.h b/src/core/spec/panda/board.h new file mode 100644 index 0000000..e8da9ef --- /dev/null +++ b/src/core/spec/panda/board.h @@ -0,0 +1,54 @@ +/* + * \brief Board driver for core on pandaboard + * \author Stefan Kalkowski + * \author Martin Stein + * \date 2014-06-02 + */ + +/* + * Copyright (C) 2014-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__PANDA__BOARD_H_ +#define _CORE__SPEC__PANDA__BOARD_H_ + +#include +#include +#include + +namespace Board { + using namespace Hw::Panda_board; + using Pic = Hw::Gicv2; + + class L2_cache : public Hw::Pl310 + { + private: + + unsigned long _debug_value() + { + Debug::access_t v = 0; + Debug::Dwb::set(v, 1); + Debug::Dcl::set(v, 1); + return v; + } + + public: + + L2_cache(Genode::addr_t mmio) : Hw::Pl310(mmio) { } + + void clean_invalidate() + { + using namespace Hw; + call_panda_firmware(L2_CACHE_SET_DEBUG_REG, _debug_value()); + Pl310::clean_invalidate(); + call_panda_firmware(L2_CACHE_SET_DEBUG_REG, 0); + } + }; + + L2_cache & l2_cache(); +} + +#endif /* _CORE__SPEC__PANDA__BOARD_H_ */ diff --git a/src/drivers/framebuffer/spec/omap4/dispc.h b/src/drivers/framebuffer/spec/omap4/dispc.h new file mode 100644 index 0000000..18f55ae --- /dev/null +++ b/src/drivers/framebuffer/spec/omap4/dispc.h @@ -0,0 +1,129 @@ +/* + * \brief Display controller + * \author Martin Stein + * \author Norman Feske + * \date 2012-06-11 + */ + +/* + * Copyright (C) 2009-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 _DISPC_H_ +#define _DISPC_H_ + +/* Genode includes */ +#include + +struct Dispc : Genode::Mmio +{ + /** + * Configures the display controller module for outputs LCD 1 and TV + */ + struct Control1 : Register<0x40, 32> + { + struct Lcd_enable : Bitfield<0, 1> { }; + struct Tv_enable : Bitfield<1, 1> { }; + struct Go_lcd : Bitfield<5, 1> + { + enum { HW_UPDATE_DONE = 0x0, /* set by HW after updating */ + REQUEST_HW_UPDATE = 0x1 }; /* must be set by user */ + }; + struct Go_tv : Bitfield<6, 1> + { + enum { HW_UPDATE_DONE = 0x0, /* set by HW after updating */ + REQUEST_HW_UPDATE = 0x1 }; /* must be set by user */ + }; + }; + + /** + * Configures the display controller module for outputs LCD 1 and TV + */ + struct Config1 : Register<0x44, 32> + { + /** + * Loading of palette/gamma table + */ + struct Load_mode : Bitfield<1, 2> + { + enum { DATA_EVERY_FRAME = 0x2, }; + }; + }; + + struct Size_tv : Register<0x78, 32> + { + struct Width : Bitfield<0, 11> { }; + struct Height : Bitfield<16, 11> { }; + }; + struct Size_lcd : Register<0x7c, 32> + { + struct Width : Bitfield<0, 11> { }; + struct Height : Bitfield<16, 11> { }; + }; + + /** + * Configures base address of the graphics buffer + */ + struct Gfx_ba0 : Register<0x80, 32> { }; + struct Gfx_ba1 : Register<0x84, 32> { }; + + /** + * Configures the size of the graphics window + */ + struct Gfx_size : Register<0x8c, 32> + { + struct Sizex : Bitfield<0,11> { }; + struct Sizey : Bitfield<16,11> { }; + }; + + /** + * Configures the graphics attributes + */ + struct Gfx_attributes : Register<0xa0, 32> + { + struct Enable : Bitfield<0, 1> { }; + + struct Format : Bitfield<1, 5> + { + enum { RGB16 = 0x6, + ARGB32 = 0xc, + RGBA32 = 0xd }; + }; + + /** + * Select GFX channel output + */ + struct Channelout : Bitfield<8, 1> + { + enum { TV = 0x1 }; + }; + + struct Channelout2 : Bitfield<30, 2> + { + enum { PRIMARY_LCD = 0 }; + }; + }; + + struct Global_buffer : Register<0x800, 32> { }; + + struct Divisor : Register<0x804, 32> + { + struct Enable : Bitfield<0, 1> { }; + struct Lcd : Bitfield<16, 8> { }; + }; + + /** + * Constructor + * + * \param mmio_base base address of DISPC MMIO + */ + Dispc(Genode::addr_t const mmio_base) + : + Mmio(mmio_base) + { } +}; + +#endif /* _DISPC_H_ */ diff --git a/src/drivers/framebuffer/spec/omap4/driver.h b/src/drivers/framebuffer/spec/omap4/driver.h new file mode 100644 index 0000000..93604de --- /dev/null +++ b/src/drivers/framebuffer/spec/omap4/driver.h @@ -0,0 +1,293 @@ +/* + * \brief Frame-buffer driver for the OMAP4430 display-subsystem (HDMI) + * \author Norman Feske + * \author Martin Stein + * \date 2012-06-01 + */ + +/* + * 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. + */ + +/* Genode includes */ +#include +#include +#include + +/* local includes */ +#include +#include +#include + + +namespace Framebuffer { + using namespace Genode; + class Driver; +} + + +class Framebuffer::Driver +{ + public: + + enum Format { FORMAT_RGB565 }; + enum Output { OUTPUT_LCD, OUTPUT_HDMI }; + + private: + + enum { + DSS_MMIO_BASE = 0x58000000, + DSS_MMIO_SIZE = 0x00001000, + DISPC_MMIO_BASE = 0x58001000, + DISPC_MMIO_SIZE = 0x00001000, + HDMI_MMIO_BASE = 0x58006000, + HDMI_MMIO_SIZE = 0x00001000, + }; + + Genode::Env &_env; + + bool _init_lcd(addr_t phys_base); + + bool _init_hdmi(addr_t phys_base); + + struct Timer_delayer : Timer::Connection, Mmio::Delayer + { + Timer_delayer(Genode::Env &env) : Timer::Connection(env) { } + + /** + * Implementation of 'Delayer' interface + */ + void usleep(uint64_t us) + { + Timer::Connection::usleep(us); + } + } _delayer { _env }; + + /* display sub system registers */ + Attached_io_mem_dataspace _dss_mmio; + Dss _dss; + + /* display controller registers */ + Attached_io_mem_dataspace _dispc_mmio; + Dispc _dispc; + + /* HDMI controller registers */ + Attached_io_mem_dataspace _hdmi_mmio; + Hdmi _hdmi; + + size_t _fb_width; + size_t _fb_height; + Format _fb_format; + + public: + + Driver(Genode::Env &env); + + static size_t bytes_per_pixel(Format format) + { + switch (format) { + case FORMAT_RGB565: return 2; + } + return 0; + } + + size_t buffer_size(size_t width, size_t height, Format format) + { + return bytes_per_pixel(format)*width*height; + } + + bool init(size_t width, size_t height, Format format, + Output output, addr_t phys_base); +}; + + +Framebuffer::Driver::Driver(Genode::Env &env) +: + _env(env), + _dss_mmio(_env, DSS_MMIO_BASE, DSS_MMIO_SIZE), + _dss((addr_t)_dss_mmio.local_addr()), + + _dispc_mmio(_env, DISPC_MMIO_BASE, DISPC_MMIO_SIZE), + _dispc((addr_t)_dispc_mmio.local_addr()), + + _hdmi_mmio(_env, HDMI_MMIO_BASE, HDMI_MMIO_SIZE), + _hdmi((addr_t)_hdmi_mmio.local_addr()), + + _fb_width(0), + _fb_height(0), + _fb_format(FORMAT_RGB565) +{ } + + +bool Framebuffer::Driver::_init_lcd(Framebuffer::addr_t phys_base) +{ + /* disable LCD to allow editing configuration */ + _dispc.write(0); + + /* set load mode */ + _dispc.write(Dispc::Config1::Load_mode::DATA_EVERY_FRAME); + + _dispc.write(_fb_width - 1); + _dispc.write(_fb_height - 1); + + Dispc::Gfx_attributes::access_t pixel_format = 0; + switch (_fb_format) { + case FORMAT_RGB565: pixel_format = Dispc::Gfx_attributes::Format::RGB16; break; + } + _dispc.write(pixel_format); + + _dispc.write(phys_base); + _dispc.write(phys_base); + + _dispc.write(_fb_width - 1); + _dispc.write(_fb_height - 1); + + _dispc.write(0x6d2240); + _dispc.write(1); + + _dispc.write(1); + _dispc.write(1); + + return true; +} + +bool Framebuffer::Driver::_init_hdmi(Framebuffer::addr_t phys_base) +{ + /* enable display core clock and set divider to 1 */ + _dispc.write(1); + _dispc.write(1); + + /* set load mode */ + _dispc.write(Dispc::Config1::Load_mode::DATA_EVERY_FRAME); + + _hdmi.write(0); + + if (!_hdmi.issue_pwr_pll_command(Hdmi::Pwr_ctrl::ALL_OFF, _delayer)) { + error("powering off HDMI timed out"); + return false; + } + + if (!_hdmi.issue_pwr_pll_command(Hdmi::Pwr_ctrl::BOTH_ON_ALL_CLKS, _delayer)) { + error("powering on HDMI timed out"); + return false; + } + + if (!_hdmi.reset_pll(_delayer)) { + error("resetting HDMI PLL timed out"); + return false; + } + + _hdmi.write(Hdmi::Pll_control::Mode::MANUAL); + + _hdmi.write(270); + _hdmi.write(15); + + _hdmi.write(0); + _hdmi.write(1); + _hdmi.write(0); + _hdmi.write(3); + _hdmi.write(2); + + _hdmi.write(1); + _hdmi.write(0x35555); + + if (!_hdmi.pll_go(_delayer)) { + error("HDMI PLL GO timed out"); + return false; + } + + if (!_hdmi.issue_pwr_phy_command(Hdmi::Pwr_ctrl::LDOON, _delayer)) { + error("HDMI Phy power on timed out"); + return false; + } + + _hdmi.write(1); + _hdmi.write(0xf0000000); + + if (!_hdmi.issue_pwr_phy_command(Hdmi::Pwr_ctrl::TXON, _delayer)) { + error("HDMI Txphy power on timed out"); + return false; + } + + _hdmi.write(160); + _hdmi.write(24); + _hdmi.write(136); + + _hdmi.write(29); + _hdmi.write(3); + _hdmi.write(6); + + _hdmi.write(Hdmi::Video_cfg::Packing_mode::PACK_24B); + + _hdmi.write(_fb_width); + _hdmi.write(_fb_height); + + _hdmi.write(0); + _hdmi.write(0); + _hdmi.write(0); + _hdmi.write(1); + + _dss.write(Dss::Ctrl::Venc_hdmi_switch::HDMI); + + _dispc.write(_fb_width - 1); + _dispc.write(_fb_height - 1); + + _hdmi.write(1); + + Dispc::Gfx_attributes::access_t pixel_format = 0; + switch (_fb_format) { + case FORMAT_RGB565: pixel_format = Dispc::Gfx_attributes::Format::RGB16; break; + } + _dispc.write(pixel_format); + + _dispc.write(phys_base); + _dispc.write(phys_base); + + _dispc.write(_fb_width - 1); + _dispc.write(_fb_height - 1); + + _dispc.write(0x6d2240); + _dispc.write(1); + + _dispc.write(Dispc::Gfx_attributes::Channelout::TV); + _dispc.write(Dispc::Gfx_attributes::Channelout2::PRIMARY_LCD); + + _dispc.write(1); + _dispc.write(1); + + try { + _dispc.wait_for(_delayer, Dispc::Control1::Go_tv::Equal(Dispc::Control1::Go_tv::HW_UPDATE_DONE)); + } + catch (Dispc::Polling_timeout) { + error("Go_tv timed out"); + return false; + } + return true; +} + + +bool Framebuffer::Driver::init(size_t width, size_t height, + Framebuffer::Driver::Format format, + Output output, + Framebuffer::addr_t phys_base) +{ + _fb_width = width; + _fb_height = height; + _fb_format = format; + + bool ret = false; + switch (output) { + case OUTPUT_LCD: + ret = _init_lcd(phys_base); + break; + case OUTPUT_HDMI: + ret = _init_hdmi(phys_base); + break; + default: + error("unknown output ", (int)output, " specified"); + } + return ret; +} diff --git a/src/drivers/framebuffer/spec/omap4/dss.h b/src/drivers/framebuffer/spec/omap4/dss.h new file mode 100644 index 0000000..ea664b3 --- /dev/null +++ b/src/drivers/framebuffer/spec/omap4/dss.h @@ -0,0 +1,29 @@ +/* + * \brief General display subsystem registers + * \author Norman Feske + * \date 2012-06-11 + */ + +#ifndef _DSS_H_ +#define _DSS_H_ + +/* Genode includes */ +#include + +struct Dss : Genode::Mmio +{ + Dss(Genode::addr_t const mmio_base) : Genode::Mmio(mmio_base) { } + + struct Sysstatus : Register<0x14, 32> { }; + + struct Ctrl : Register<0x40, 32> + { + struct Venc_hdmi_switch : Bitfield<15, 1> + { + enum { HDMI = 1 }; + }; + }; + struct Status : Register<0x5c, 32> { }; +}; + +#endif /* _DSS_H_ */ diff --git a/src/drivers/framebuffer/spec/omap4/hdmi.h b/src/drivers/framebuffer/spec/omap4/hdmi.h new file mode 100644 index 0000000..1b2f401 --- /dev/null +++ b/src/drivers/framebuffer/spec/omap4/hdmi.h @@ -0,0 +1,197 @@ +/* + * \brief HDMI subsystem registers + * \author Norman Feske + * \date 2012-06-11 + */ + +/* + * Copyright (C) 2009-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__FRAMEBUFFER__SPEC__OMAP4__HDMI_H_ +#define _DRIVERS__FRAMEBUFFER__SPEC__OMAP4__HDMI_H_ + +/* Genode includes */ +#include + +struct Hdmi : Genode::Mmio +{ + struct Pwr_ctrl : Register<0x40, 32> + { + enum Pll_cmd_type { ALL_OFF = 0, + BOTH_ON_ALL_CLKS = 2, }; + + struct Pll_cmd : Bitfield<2, 2> { }; + struct Pll_status : Bitfield<0, 2> { }; + + enum Phy_cmd_type { LDOON = 1, + TXON = 2 }; + + struct Phy_cmd : Bitfield<6, 2> { }; + struct Phy_status : Bitfield<4, 2> { }; + + }; + + struct Video_cfg : Register<0x50, 32> + { + struct Start : Bitfield<31, 1> { }; + + struct Packing_mode : Bitfield<8, 3> + { + enum { PACK_24B = 1 }; + }; + + struct Vsp : Bitfield<7, 1> { }; + struct Hsp : Bitfield<6, 1> { }; + struct Interlacing : Bitfield<3, 1> { }; + struct Tm : Bitfield<0, 2> { }; + }; + + struct Video_size : Register<0x60, 32> + { + struct X : Bitfield<0, 16> { }; + struct Y : Bitfield<16, 16> { }; + }; + + struct Video_timing_h : Register<0x68, 32> + { + struct Bp : Bitfield<20, 12> { }; + struct Fp : Bitfield<8, 12> { }; + struct Sw : Bitfield<0, 8> { }; + }; + + struct Video_timing_v : Register<0x6c, 32> + { + struct Bp : Bitfield<20, 12> { }; + struct Fp : Bitfield<8, 12> { }; + struct Sw : Bitfield<0, 8> { }; + }; + + /** + * \return true on success + */ + bool issue_pwr_pll_command(Pwr_ctrl::Pll_cmd_type cmd, Delayer &delayer) + { + write(cmd); + + try { + wait_for(delayer, Pwr_ctrl::Pll_status::Equal(cmd)); + } + catch (Polling_timeout) { + Genode::error("Pwr_ctrl::Pll_cmd failed"); + return false; + } + return true; + } + + bool issue_pwr_phy_command(Pwr_ctrl::Phy_cmd_type cmd, Delayer &delayer) + { + write(cmd); + + try { + wait_for(delayer, Pwr_ctrl::Phy_status::Equal(cmd)); + } + catch (Polling_timeout) { + Genode::error("unexpected Pwr_ctrl::Phy_status"); + return false; + } + return true; + } + + struct Pll_control : Register<0x200, 32> + { + struct Mode : Bitfield<0, 1> + { + enum { MANUAL = 0 }; + }; + + struct Reset : Bitfield<3, 1> { }; + }; + + struct Pll_status : Register<0x204, 32> + { + struct Reset_done : Bitfield<0, 1> { }; + struct Pll_locked : Bitfield<1, 1> { }; + }; + + bool wait_until_pll_locked(Delayer &delayer) + { + try { + wait_for(delayer, Pll_status::Pll_locked::Equal(1)); + } + catch (Polling_timeout) { + Genode::error("Pll_locked::Pll_locked unexpectedly not set"); + return false; + } + return true; + } + + struct Pll_go : Register<0x208, 32> + { + struct Go : Bitfield<0, 1> { }; + }; + + bool pll_go(Delayer &delayer) + { + write(1); + + /* wait for PLL_GO bit change and the PLL reaching locked state */ + try { + wait_for(delayer, Pll_go::Go::Equal(1)); + } + catch (Polling_timeout) { + Genode::error("Pll_go::Go unexpectedly not set"); + return false; + } + return wait_until_pll_locked(delayer); + } + + struct Cfg1 : Register<0x20c, 32> + { + struct Regm : Bitfield<9, 12> { }; + struct Regn : Bitfield<1, 8> { }; + }; + + struct Cfg2 : Register<0x210, 32> + { + struct Highfreq_div_by_2 : Bitfield<12, 1> { }; + struct Refen : Bitfield<13, 1> { }; + struct Clkinen : Bitfield<14, 1> { }; + struct Refsel : Bitfield<21, 2> { }; + struct Freq_divider : Bitfield<1, 3> { }; + }; + + struct Cfg4 : Register<0x220, 32> + { + struct Regm2 : Bitfield<18, 7> { }; + struct Regmf : Bitfield<0, 18> { }; + }; + + bool reset_pll(Delayer &delayer) + { + write(0); + + try { + wait_for(delayer, Pll_status::Reset_done::Equal(1)); + } + catch (Polling_timeout) { + Genode::error("Pll_status::Reset_done unexpectedly not set"); + return false; + } + return true; + }; + + struct Txphy_tx_ctrl : Register<0x300, 32> + { + struct Freqout : Bitfield<30, 2> { }; + }; + + struct Txphy_digital_ctrl : Register<0x304, 32> { }; + + Hdmi(Genode::addr_t const mmio_base) : Mmio(mmio_base) { } +}; + +#endif /* _DRIVERS__FRAMEBUFFER__SPEC__OMAP4__HDMI_H_ */ diff --git a/src/drivers/framebuffer/spec/omap4/main.cc b/src/drivers/framebuffer/spec/omap4/main.cc new file mode 100644 index 0000000..ccd571c --- /dev/null +++ b/src/drivers/framebuffer/spec/omap4/main.cc @@ -0,0 +1,207 @@ +/* + * \brief Frame-buffer driver for the OMAP4430 display-subsystem (HDMI) + * \author Norman Feske + * \date 2012-06-21 + */ + +/* + * 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. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* local includes */ +#include + + +namespace Framebuffer { + using namespace Genode; + class Session_component; +}; + + +class Framebuffer::Session_component : public Genode::Rpc_object +{ + private: + + /* + * Noncopyable + */ + Session_component(Session_component const &); + Session_component &operator = (Session_component const &); + + size_t _width; + size_t _height; + bool _buffered; + Mode _mode { }; + Driver::Format _format; + size_t _size; + + /* dataspace uses a back buffer (if '_buffered' is true) */ + Genode::Dataspace_capability _bb_ds; + void *_bb_addr; + + /* dataspace of physical frame buffer */ + Genode::Dataspace_capability _fb_ds; + void *_fb_addr; + + Signal_context_capability _sync_sigh { }; + + Timer::Connection _timer; + + /** + * 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; + } + + void _refresh_buffered(int x, int y, int w, int h) + { + Mode _mode = mode(); + + /* clip specified coordinates against screen boundaries */ + int x2 = min(x + w - 1, (int)_mode.width() - 1), + y2 = min(y + h - 1, (int)_mode.height() - 1); + int x1 = max(x, 0), + y1 = max(y, 0); + if (x1 > x2 || y1 > y2) return; + + int bypp = _mode.bytes_per_pixel(); + + /* copy pixels from back buffer to physical frame buffer */ + char *src = (char *)_bb_addr + bypp*(_mode.width()*y1 + x1), + *dst = (char *)_fb_addr + bypp*(_mode.width()*y1 + x1); + + blit(src, bypp*_mode.width(), dst, bypp*_mode.width(), + bypp*(x2 - x1 + 1), y2 - y1 + 1); + } + + public: + + Session_component(Genode::Env &env, Driver &driver, size_t width, size_t height, + Driver::Output output, bool buffered) + : _width(width), + _height(height), + _buffered(buffered), + _format(Driver::FORMAT_RGB565), + _size(driver.buffer_size(width, height, _format)), + _bb_ds(buffered ? env.ram().alloc(_size) + : Genode::Ram_dataspace_capability()), + _bb_addr(buffered ? (void*)env.rm().attach(_bb_ds) : 0), + _fb_ds(env.ram().alloc(_size, WRITE_COMBINED)), + _fb_addr((void*)env.rm().attach(_fb_ds)), + _timer(env) + { + if (!driver.init(width, height, _format, output, + Dataspace_client(_fb_ds).phys_addr())) { + error("Could not initialize display"); + struct Could_not_initialize_display : Exception { }; + throw Could_not_initialize_display(); + } + + Genode::log("using ", width, "x", height, + output == Driver::OUTPUT_HDMI ? " HDMI" : " LCD"); + } + + /************************************ + ** Framebuffer::Session interface ** + ************************************/ + + Dataspace_capability dataspace() override + { + return _buffered ? _bb_ds : _fb_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 + { + _sync_sigh = sigh; + + _timer.sigh(_sync_sigh); + _timer.trigger_periodic(10*1000); + } + + void refresh(int x, int y, int w, int h) override + { + if (_buffered) + _refresh_buffered(x, y, w, h); + + if (_sync_sigh.valid()) + Signal_transmitter(_sync_sigh).submit(); + } +}; + + +template +static T config_attribute(Genode::Xml_node node, char const *attr_name, T const &default_value) +{ + return node.attribute_value(attr_name, default_value); +} + +static Framebuffer::Driver::Output config_output(Genode::Xml_node node, + Framebuffer::Driver::Output default_value) +{ + Framebuffer::Driver::Output value = default_value; + + try { + Genode::String<8> output; + node.attribute("output").value(&output); + + if (output == "LCD") { value = Framebuffer::Driver::OUTPUT_LCD; } + } catch (...) { } + + return 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_attribute(_config.xml(), "width", 1024u), + config_attribute(_config.xml(), "height", 768u), + config_output(_config.xml(), Framebuffer::Driver::OUTPUT_HDMI), + config_attribute(_config.xml(), "buffered", false), + }; + + Genode::Static_root _fb_root { _ep.manage(_fb_session) }; + + Main(Genode::Env &env) : _env(env), _ep(_env.ep()) + { + /* announce service */ + _env.parent().announce(_ep.manage(_fb_root)); + } +}; + + +void Component::construct(Genode::Env &env) { static Main main(env); } diff --git a/src/drivers/framebuffer/spec/omap4/target.mk b/src/drivers/framebuffer/spec/omap4/target.mk new file mode 100644 index 0000000..3ce825f --- /dev/null +++ b/src/drivers/framebuffer/spec/omap4/target.mk @@ -0,0 +1,15 @@ +# +# \brief Framebuffer driver specific for OMAP44xx systems +# \author Martin Stein +# \date 2012-05-02 +# + +TARGET = omap4_fb_drv +REQUIRES = arm_v7 +SRC_CC = main.cc +LIBS = base blit +INC_DIR += $(PRG_DIR) + +vpath main.cc $(PRG_DIR) + +CC_CXX_WARN_STRICT = diff --git a/src/drivers/gpio/spec/omap4/driver.h b/src/drivers/gpio/spec/omap4/driver.h new file mode 100644 index 0000000..44c42b5 --- /dev/null +++ b/src/drivers/gpio/spec/omap4/driver.h @@ -0,0 +1,272 @@ +/* + * \brief Gpio driver for the OMAP4 + * \author Ivan Loskutov + * \author Stefan Kalkowski + * \date 2012-06-23 + */ + +/* + * Copyright (C) 2012 Ksys Labs LLC + * 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. + */ + +#ifndef _DRIVERS__GPIO__SPEC__OMAP4__DRIVER_H_ +#define _DRIVERS__GPIO__SPEC__OMAP4__DRIVER_H_ + +/* Genode includes */ +#include +#include +#include + +/* local includes */ +#include "gpio.h" + + +class Omap4_driver : public Gpio::Driver +{ + private: + + enum { + PIN_SHIFT = 5, + MAX_BANKS = 6, + MAX_PINS = 32 + }; + + enum { + GPIO1_MMIO_BASE = 0x4a310000, + GPIO1_MMIO_SIZE = 0x1000, + GPIO1_IRQ = 29 + 32, + GPIO2_MMIO_BASE = 0x48055000, + GPIO2_MMIO_SIZE = 0x1000, + GPIO2_IRQ = 30 + 32, + GPIO3_MMIO_BASE = 0x48057000, + GPIO3_MMIO_SIZE = 0x1000, + GPIO3_IRQ = 31 + 32, + GPIO4_MMIO_BASE = 0x48059000, + GPIO4_MMIO_SIZE = 0x1000, + GPIO4_IRQ = 32 + 32, + GPIO5_MMIO_BASE = 0x4805b000, + GPIO5_MMIO_SIZE = 0x1000, + GPIO5_IRQ = 33 + 32, + GPIO6_MMIO_BASE = 0x4805d000, + GPIO6_MMIO_SIZE = 0x1000, + GPIO6_IRQ = 34 + 32, + }; + + class Gpio_bank + { + private: + + Gpio_reg _reg; + Genode::Irq_connection _irq; + Genode::Signal_handler _dispatcher; + Genode::Signal_context_capability _sig_cap[MAX_PINS]; + bool _irq_enabled[MAX_PINS]; + + void _handle() + { + _reg.write(0xffffffff); + + unsigned long status = _reg.read(); + + for(unsigned i = 0; i < MAX_PINS; i++) { + if ((status & (1 << i)) && _irq_enabled[i] && + _sig_cap[i].valid()) + Genode::Signal_transmitter(_sig_cap[i]).submit(); + } + + _irq.ack_irq(); + } + + + public: + + Gpio_bank(Genode::Env &env, + Genode::addr_t base, Genode::size_t size, + unsigned irq) + : _reg(env, base, size), _irq(env, irq), + _dispatcher(env.ep(), *this, &Gpio_bank::_handle) + { + for (unsigned i = 0; i < MAX_PINS; i++) + _irq_enabled[i] = false; + + _irq.sigh(_dispatcher); + _irq.ack_irq(); + } + + Gpio_reg* regs() { return &_reg; } + + void irq(int pin, bool enable) + { + if (enable) { + _reg.write(1 << pin); + _reg.write(1 << pin); + } + else + _reg.write(1 << pin); + _irq_enabled[pin] = enable; + } + + void ack_irq(int) { Genode::warning(__func__, " not implemented"); } + + void sigh(int pin, Genode::Signal_context_capability cap) { + _sig_cap[pin] = cap; } + }; + + Gpio_bank _gpio_bank_0; + Gpio_bank _gpio_bank_1; + Gpio_bank _gpio_bank_2; + Gpio_bank _gpio_bank_3; + Gpio_bank _gpio_bank_4; + Gpio_bank _gpio_bank_5; + + Gpio_bank *_gpio_bank(int gpio) + { + switch (gpio >> PIN_SHIFT) { + case 0: + return &_gpio_bank_0; + case 1: + return &_gpio_bank_1; + case 2: + return &_gpio_bank_2; + case 3: + return &_gpio_bank_3; + case 4: + return &_gpio_bank_4; + case 5: + return &_gpio_bank_5; + } + + Genode::error("no Gpio_bank for pin ", gpio, " available"); + return 0; + } + + int _gpio_index(int gpio) { return gpio & 0x1f; } + + Omap4_driver(Genode::Env &env) + : + _gpio_bank_0(env, GPIO1_MMIO_BASE, GPIO1_MMIO_SIZE, GPIO1_IRQ), + _gpio_bank_1(env, GPIO2_MMIO_BASE, GPIO2_MMIO_SIZE, GPIO2_IRQ), + _gpio_bank_2(env, GPIO3_MMIO_BASE, GPIO3_MMIO_SIZE, GPIO3_IRQ), + _gpio_bank_3(env, GPIO4_MMIO_BASE, GPIO4_MMIO_SIZE, GPIO4_IRQ), + _gpio_bank_4(env, GPIO5_MMIO_BASE, GPIO5_MMIO_SIZE, GPIO5_IRQ), + _gpio_bank_5(env, GPIO6_MMIO_BASE, GPIO6_MMIO_SIZE, GPIO6_IRQ) + { } + + public: + + static Omap4_driver& factory(Genode::Env &env); + + + /****************************** + ** Gpio::Driver interface ** + ******************************/ + + void direction(unsigned gpio, bool input) override + { + Gpio_reg *gpio_reg = _gpio_bank(gpio)->regs(); + gpio_reg->write(input ? 1 : 0, _gpio_index(gpio)); + } + + void write(unsigned gpio, bool level) override + { + Gpio_reg *gpio_reg = _gpio_bank(gpio)->regs(); + + if (level) + gpio_reg->write(1 << _gpio_index(gpio)); + else + gpio_reg->write(1 << _gpio_index(gpio)); + } + + bool read(unsigned gpio) override + { + Gpio_reg *gpio_reg = _gpio_bank(gpio)->regs(); + return gpio_reg->read(_gpio_index(gpio)); + } + + void debounce_enable(unsigned gpio, bool enable) override + { + Gpio_reg *gpio_reg = _gpio_bank(gpio)->regs(); + gpio_reg->write(enable ? 1 : 0, + _gpio_index(gpio)); + } + + void debounce_time(unsigned gpio, unsigned long us) override + { + unsigned char debounce; + + if (us < 32) + debounce = 0x01; + else if (us > 7936) + debounce = 0xff; + else + debounce = (us / 0x1f) - 1; + + Gpio_reg *gpio_reg = _gpio_bank(gpio)->regs(); + gpio_reg->write(debounce); + } + + void falling_detect(unsigned gpio) override + { + Gpio_reg *gpio_reg = _gpio_bank(gpio)->regs(); + gpio_reg->write (0, _gpio_index(gpio)); + gpio_reg->write (0, _gpio_index(gpio)); + gpio_reg->write(1, _gpio_index(gpio)); + gpio_reg->write (0, _gpio_index(gpio)); + } + + void rising_detect(unsigned gpio) override + { + Gpio_reg *gpio_reg = _gpio_bank(gpio)->regs(); + gpio_reg->write (0, _gpio_index(gpio)); + gpio_reg->write (0, _gpio_index(gpio)); + gpio_reg->write(0, _gpio_index(gpio)); + gpio_reg->write (1, _gpio_index(gpio)); + } + + void high_detect(unsigned gpio) override + { + Gpio_reg *gpio_reg = _gpio_bank(gpio)->regs(); + gpio_reg->write (0, _gpio_index(gpio)); + gpio_reg->write (1, _gpio_index(gpio)); + gpio_reg->write(0, _gpio_index(gpio)); + gpio_reg->write (0, _gpio_index(gpio)); + } + + void low_detect(unsigned gpio) override + { + Gpio_reg *gpio_reg = _gpio_bank(gpio)->regs(); + gpio_reg->write (1, _gpio_index(gpio)); + gpio_reg->write (0, _gpio_index(gpio)); + gpio_reg->write(0, _gpio_index(gpio)); + gpio_reg->write (0, _gpio_index(gpio)); + } + + void irq_enable(unsigned gpio, bool enable) override + { + _gpio_bank(gpio)->irq(_gpio_index(gpio), enable); + } + + void ack_irq(unsigned gpio) override + { + _gpio_bank(gpio)->ack_irq(_gpio_index(gpio)); + } + + void register_signal(unsigned gpio, + Genode::Signal_context_capability cap) override + { + _gpio_bank(gpio)->sigh(_gpio_index(gpio), cap); } + + void unregister_signal(unsigned gpio) override + { + Genode::Signal_context_capability cap; + _gpio_bank(gpio)->sigh(_gpio_index(gpio), cap); + } + + bool gpio_valid(unsigned gpio) override { return gpio < (MAX_PINS*MAX_BANKS); } +}; + +#endif /* _DRIVERS__GPIO__SPEC__OMAP4__DRIVER_H_ */ diff --git a/src/drivers/gpio/spec/omap4/gpio.h b/src/drivers/gpio/spec/omap4/gpio.h new file mode 100644 index 0000000..3b1b219 --- /dev/null +++ b/src/drivers/gpio/spec/omap4/gpio.h @@ -0,0 +1,50 @@ +/* + * \brief OMAP4 GPIO definitions + * \author Ivan Loskutov + * \author Stefan Kalkowski + * \date 2012-06-23 + */ + +/* + * Copyright (C) 2012 Ksys Labs LLC + * 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. + */ + +#ifndef _DRIVERS__GPIO__SPEC__OMAP4__GPIO_H_ +#define _DRIVERS__GPIO__SPEC__OMAP4__GPIO_H_ + +/* Genode includes */ +#include +#include + +struct Gpio_reg : Genode::Attached_io_mem_dataspace, Genode::Mmio +{ + Gpio_reg(Genode::Env &env, + Genode::addr_t const mmio_base, + Genode::size_t const mmio_size) + : Genode::Attached_io_mem_dataspace(env, mmio_base, mmio_size), + Genode::Mmio((Genode::addr_t)local_addr()) { } + + struct Oe : Register_array<0x134, 32, 32, 1> {}; + struct Irqstatus_0 : Register<0x02c, 32> {}; + struct Irqstatus_set_0 : Register<0x034, 32> {}; + struct Irqstatus_clr_0 : Register<0x03c, 32> {}; + struct Ctrl : Register<0x130, 32> {}; + struct Leveldetect0 : Register_array<0x140, 32, 32, 1> {}; + struct Leveldetect1 : Register_array<0x144, 32, 32, 1> {}; + struct Risingdetect : Register_array<0x148, 32, 32, 1> {}; + struct Fallingdetect : Register_array<0x14c, 32, 32, 1> {}; + struct Debounceenable : Register_array<0x150, 32, 32, 1> {}; + struct Debouncingtime : Register<0x154, 32> + { + struct Time : Bitfield<0, 8> {}; + }; + struct Cleardataout : Register<0x190, 32> {}; + struct Setdataout : Register<0x194, 32> {}; + struct Datain : Register_array<0x138, 32, 32, 1> {}; +}; + +#endif /* _DRIVERS__GPIO__SPEC__OMAP4__GPIO_H_ */ diff --git a/src/drivers/gpio/spec/omap4/main.cc b/src/drivers/gpio/spec/omap4/main.cc new file mode 100644 index 0000000..db87024 --- /dev/null +++ b/src/drivers/gpio/spec/omap4/main.cc @@ -0,0 +1,64 @@ +/* + * \brief Gpio driver for the OMAP4 + * \author Ivan Loskutov + * \author Stefan Kalkowski + * \date 2012-06-23 + */ + +/* + * Copyright (C) 2012 Ksys Labs LLC + * 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. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + +/* local includes */ +#include + +Omap4_driver& Omap4_driver::factory(Genode::Env &env) +{ + static Omap4_driver driver(env); + return driver; +} + + +struct Main +{ + Genode::Env &env; + Genode::Sliced_heap sliced_heap; + Omap4_driver &driver; + Gpio::Root root; + + Genode::Attached_rom_dataspace config_rom { env, "config" }; + + Main(Genode::Env &env) + : + env(env), + sliced_heap(env.ram(), env.rm()), + driver(Omap4_driver::factory(env)), + root(&env.ep().rpc_ep(), &sliced_heap, driver) + { + using namespace Genode; + + log("--- omap4 gpio driver ---"); + + Gpio::process_config(config_rom.xml(), driver); + + /* + * Announce service + */ + env.parent().announce(env.ep().manage(root)); + } +}; + + +void Component::construct(Genode::Env &env) { static Main main(env); } diff --git a/src/drivers/gpio/spec/omap4/target.mk b/src/drivers/gpio/spec/omap4/target.mk new file mode 100644 index 0000000..2a253dd --- /dev/null +++ b/src/drivers/gpio/spec/omap4/target.mk @@ -0,0 +1,8 @@ +TARGET = omap4_gpio_drv +REQUIRES = arm_v7 +SRC_CC = main.cc +LIBS = base +INC_DIR += $(PRG_DIR) + +vpath main.cc $(PRG_DIR) + diff --git a/src/drivers/sd_card/spec/omap4/driver.cc b/src/drivers/sd_card/spec/omap4/driver.cc new file mode 100644 index 0000000..1d969c0 --- /dev/null +++ b/src/drivers/sd_card/spec/omap4/driver.cc @@ -0,0 +1,458 @@ +/* + * \brief OMAP4-specific implementation of the Block::Driver interface + * \author Norman Feske + * \date 2012-07-19 + */ + +/* + * 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. + */ + +/* local includes */ +#include + +using namespace Genode; +using namespace Sd_card; + + +Card_info Driver::_init() +{ + Mmio::write(0x2015); + Mmio::write(0x0); + + _set_bus_power(VOLTAGE_3_0); + + if (!_sd_bus_power_on()) { + error("sd_bus_power failed"); + } + _disable_irq(); + + _bus_width(BUS_WIDTH_1); + + _delayer.usleep(10*1000); + + _stop_clock(); + if (!_set_and_enable_clock(CLOCK_DIV_240)) { + error("set_clock failed"); + throw Detection_failed(); + } + if (!_init_stream()) { + error("sending the initialization stream failed"); + throw Detection_failed(); + } + Mmio::write(0); + _delayer.usleep(1000); + + if (!issue_command(Go_idle_state())) { + error("Go_idle_state command failed"); + throw Detection_failed(); + } + _delayer.usleep(2000); + + if (!issue_command(Send_if_cond())) { + error("Send_if_cond command failed"); + throw Detection_failed(); + } + if (Mmio::read() != 0x1aa) { + error("unexpected response of Send_if_cond command"); + throw Detection_failed(); + } + + /* + * We need to issue the same Sd_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. + */ + + int i = 1000; + for (; i > 0; --i) { + if (!issue_command(Sd_send_op_cond(0x18000, true))) { + warning("Sd_send_op_cond command failed"); + throw Detection_failed(); + } + + if (Ocr::Busy::get(Mmio::read())) + break; + + _delayer.usleep(1000); + } + if (i == 0) { + error("Sd_send_op_cond timed out, could no power-on SD card"); + throw Detection_failed(); + } + Card_info card_info = _detect(); + + /* + * Switch card to use 4 data signals + */ + if (!issue_command(Set_bus_width(Set_bus_width::Arg::Bus_width::FOUR_BITS), + card_info.rca())) { + warning("Set_bus_width(FOUR_BITS) command failed"); + throw Detection_failed(); + } + _bus_width(BUS_WIDTH_4); + _delayer.usleep(10*1000); + + _stop_clock(); + if (!_set_and_enable_clock(CLOCK_DIV_0)) { + error("set_clock failed"); + throw Detection_failed(); + } + + /* enable IRQs */ + Mmio::write(1); + Mmio::write(1); + Mmio::write(1); + Mmio::write(1); + + return card_info; +} + + +bool Driver::_wait_for_bre() +{ + try { wait_for(Attempts(1000000), Microseconds(0), _delayer, + Pstate::Bre::Equal(1)); } + catch (Polling_timeout) { + try { wait_for(_delayer, Pstate::Bre::Equal(1)); } + catch (Polling_timeout) { + error("Pstate::Bre timed out"); + return false; + } + } + return true; +} + + +bool Driver::_wait_for_bwe() +{ + try { wait_for(Attempts(1000000), Microseconds(0), _delayer, + Pstate::Bwe::Equal(1)); } + catch (Polling_timeout) { + try { wait_for(_delayer, Pstate::Bwe::Equal(1)); } + catch (Polling_timeout) { + error("Pstate::Bwe timed out"); + return false; + } + } + return true; +} + + +void Driver::_handle_irq() +{ + _irq.ack_irq(); + + if (!_block_transfer.pending) { + return; } + + if (Mmio::read() != 1) { + warning("unexpected interrupt, Stat: ", Hex(Mmio::read())); + return; + } + + Mmio::write(1); + + if (Mmio::read() != 0) { + warning("unexpected state (" + "Stat: ", Hex(Mmio::read()), " " + "Blen: ", Hex(Mmio::read()), " " + "Nblk: ", Mmio::read()); + return; + } + _block_transfer.pending = false; + ack_packet(_block_transfer.packet, true); +} + + +bool Driver::_reset_cmd_line() +{ + Mmio::write(1); + + /* + * We must poll quickly. If we waited too long until checking the + * bit, the polling would be infinite. Apparently the hardware + * depends on the timing here. + */ + try { wait_for(Attempts(1000), Microseconds(0), _delayer, + Sysctl::Src::Equal(1)); } + catch (Polling_timeout) { + error("reset of cmd line timed out (src != 1)"); + return false; + } + try { wait_for(Attempts(1000), Microseconds(0), _delayer, + Sysctl::Src::Equal(0)); } + catch (Polling_timeout) { + error("reset of cmd line timed out (src != 0)"); + return false; + } + return true; +} + + +void Driver::_disable_irq() +{ + Mmio::write(0); + Mmio::write(0); + Mmio::write(~0); +} + + +void Driver::_bus_width(Bus_width bus_width) +{ + switch (bus_width) { + case BUS_WIDTH_1: + Mmio::write(0); + Mmio::write(Hctl::Dtw::ONE_BIT); + break; + + case BUS_WIDTH_4: + Mmio::write(0); + Mmio::write(Hctl::Dtw::FOUR_BITS); + break; + } +} + + +bool Driver::_sd_bus_power_on() +{ + Mmio::write(Hctl::Sdbp::POWER_ON); + + try { wait_for(_delayer, Hctl::Sdbp::Equal(1)); } + catch (Polling_timeout) { + error("setting Hctl::Sdbp timed out"); + return false; + } + return true; +} + + +bool Driver::_set_and_enable_clock(enum Clock_divider divider) +{ + Mmio::write(Sysctl::Dto::TCF_2_POW_27); + + switch (divider) { + case CLOCK_DIV_0: Mmio::write(0); break; + case CLOCK_DIV_240: Mmio::write(240); break; + } + + Mmio::write(1); + + /* wait for clock to become stable */ + try { wait_for(_delayer, Sysctl::Ics::Equal(1)); } + catch (Polling_timeout) { + error("clock enable timed out"); + return false; + } + /* enable clock */ + Mmio::write(1); + + return true; +} + + +void Driver::_set_bus_power(Voltage voltage) +{ + switch (voltage) { + case VOLTAGE_3_0: + Mmio::write(Hctl::Sdvs::VOLTAGE_3_0); + break; + case VOLTAGE_1_8: + Mmio::write(Hctl::Sdvs::VOLTAGE_1_8); + break; + } + Mmio::write(1); + + if (voltage == VOLTAGE_3_0) + Mmio::write(1); +} + + +bool Driver::_init_stream() +{ + Mmio::write(0x307f0033); + + /* start initialization sequence */ + Mmio::write(1); + Mmio::write(0); + + try { wait_for(Attempts(1000000), Microseconds(0), _delayer, + Stat::Cc::Equal(1)); } + catch (Polling_timeout) { + error("init stream timed out"); + return false; + } + /* stop initialization sequence */ + Mmio::write(0); + Mmio::write(~0); + Mmio::read(); + return true; +} + + +bool Driver::_issue_command(Command_base const &command) +{ + try { wait_for(_delayer, Pstate::Cmdi::Equal(0)); } + catch (Polling_timeout) { + error("wait for Pstate::Cmdi timed out"); + return false; + } + /* write command argument */ + Mmio::write(command.arg); + + /* assemble command register */ + Cmd::access_t cmd = 0; + Cmd::Index::set(cmd, command.index); + if (command.transfer != TRANSFER_NONE) { + + Cmd::Dp::set(cmd); + Cmd::Bce::set(cmd); + Cmd::Msbs::set(cmd); + + if (command.index == Read_multiple_block::INDEX || + command.index == Write_multiple_block::INDEX) + { + Cmd::Acen::set(cmd); + } + /* set data-direction bit depending on the command */ + bool const read = command.transfer == TRANSFER_READ; + Cmd::Ddir::set(cmd, read ? Cmd::Ddir::READ : Cmd::Ddir::WRITE); + } + 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); + + /* write command */ + Mmio::write(cmd); + bool result = false; + + /* wait until command is completed, return false on timeout */ + for (unsigned long i = 0; i < 1000*1000; i++) { + Stat::access_t const stat = Mmio::read(); + if (Stat::Erri::get(stat)) { + warning("SD command error"); + if (Stat::Cto::get(stat)) + warning("timeout"); + + _reset_cmd_line(); + Mmio::write(~0); + Mmio::read(); + result = false; + break; + } + if (Stat::Cc::get(stat) == 1) { + result = true; + break; + } + } + /* clear status of command-completed bit */ + Mmio::write(1); + Mmio::read(); + return result; +} + + +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; +} + + +Driver::Driver(Env &env) +: + Driver_base(env.ram()), + Attached_mmio(env, MMCHS1_MMIO_BASE, MMCHS1_MMIO_SIZE), _env(env) +{ + _irq.sigh(_irq_handler); + _irq.ack_irq(); + log("SD card detected"); + log("capacity: ", _card_info.capacity_mb(), " MiB"); +} + + +void Driver::read(Block::sector_t block_number, + size_t block_count, + char *buffer, + Block::Packet_descriptor &pkt) +{ + if (_block_transfer.pending) { + throw Request_congestion(); } + + Mmio::write(_block_size()); + Mmio::write(block_count); + + _block_transfer.packet = pkt; + _block_transfer.pending = true; + + if (!issue_command(Read_multiple_block(block_number))) { + error("Read_multiple_block failed"); + throw Io_error(); + } + size_t const num_accesses = block_count * _block_size() / + sizeof(Data::access_t); + Data::access_t *dst = (Data::access_t *)(buffer); + + for (size_t i = 0; i < num_accesses; i++) { + if (!_wait_for_bre()) + throw Io_error(); + + *dst++ = Mmio::read(); + } +} + + +void Driver::write(Block::sector_t block_number, + size_t block_count, + char const *buffer, + Block::Packet_descriptor &pkt) +{ + if (_block_transfer.pending) { + throw Request_congestion(); } + + Mmio::write(_block_size()); + Mmio::write(block_count); + + _block_transfer.packet = pkt; + _block_transfer.pending = true; + + if (!issue_command(Write_multiple_block(block_number))) { + error("Write_multiple_block failed"); + throw Io_error(); + } + size_t const num_accesses = block_count * _block_size() / + sizeof(Data::access_t); + Data::access_t const *src = (Data::access_t const *)(buffer); + + for (size_t i = 0; i < num_accesses; i++) { + if (!_wait_for_bwe()) { + throw Io_error(); } + + Mmio::write(*src++); + } +} diff --git a/src/drivers/sd_card/spec/omap4/driver.h b/src/drivers/sd_card/spec/omap4/driver.h new file mode 100644 index 0000000..a5af67e --- /dev/null +++ b/src/drivers/sd_card/spec/omap4/driver.h @@ -0,0 +1,223 @@ +/* + * \brief OMAP4-specific implementation of the Block::Driver interface + * \author Norman Feske + * \date 2012-07-19 + */ + +/* + * 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. + */ + +#ifndef _DRIVER_H_ +#define _DRIVER_H_ + +/* Genode includes */ +#include +#include +#include + +/* local includes */ +#include + +namespace Sd_card { class Driver; } + + +class Sd_card::Driver : public Driver_base, + private Attached_mmio +{ + private: + + enum { + MMCHS1_MMIO_BASE = 0x4809c000, + MMCHS1_MMIO_SIZE = 0x00001000, + HSMMC_IRQ = 115, + }; + + enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 }; + + enum Clock_divider { CLOCK_DIV_0, CLOCK_DIV_240 }; + + enum Voltage { VOLTAGE_3_0, VOLTAGE_1_8 }; + + struct Sysconfig : Register<0x110, 32> { }; + + struct Con : Register<0x12c, 32> + { + struct Init : Bitfield<1, 1> { }; + struct Dw8 : Bitfield<5, 1> { }; + }; + + struct Cmd : Register<0x20c, 32> + { + struct Bce : Bitfield<1, 1> { }; + struct Acen : Bitfield<2, 1> { }; + struct Msbs : Bitfield<5, 1> { }; + struct Index : Bitfield<24, 6> { }; + struct Dp : Bitfield<21, 1> { }; + + struct Rsp_type : Bitfield<16, 2> + { + enum Response { RESPONSE_NONE = 0, + RESPONSE_136_BIT = 1, + RESPONSE_48_BIT = 2, + RESPONSE_48_BIT_WITH_BUSY = 3 }; + }; + + struct Ddir : Bitfield<4, 1> + { + enum { WRITE = 0, READ = 1 }; + }; + }; + + struct Blk : Register<0x204, 32> + { + struct Blen : Bitfield<0, 12> { }; + struct Nblk : Bitfield<16, 16> { }; + }; + + struct Arg : Register<0x208, 32> { }; + struct Rsp10 : Register<0x210, 32> { }; + struct Rsp32 : Register<0x214, 32> { }; + struct Rsp54 : Register<0x218, 32> { }; + struct Rsp76 : Register<0x21c, 32> { }; + struct Data : Register<0x220, 32> { }; + + struct Pstate : Register<0x224, 32> + { + struct Cmdi : Bitfield<0, 1> { }; + struct Bwe : Bitfield<10, 1> { }; + struct Bre : Bitfield<11, 1> { }; + }; + + struct Hctl : Register<0x228, 32> + { + struct Sdbp : Bitfield<8, 1> + { + enum { POWER_OFF = 0, POWER_ON = 1 }; + }; + + struct Sdvs : Bitfield<9, 3> + { + enum Voltage { VOLTAGE_1_8 = 5, + VOLTAGE_3_0 = 6, + VOLTAGE_3_3 = 7 }; + }; + + struct Dtw : Bitfield<1, 1> + { + enum { ONE_BIT = 0, FOUR_BITS = 1 }; + }; + }; + + struct Sysctl : Register<0x22c, 32> + { + struct Ice : Bitfield<0, 1> { }; + struct Ics : Bitfield<1, 1> { }; + struct Ce : Bitfield<2, 1> { }; + struct Clkd : Bitfield<6, 10> { }; + struct Src : Bitfield<25, 1> { }; + + struct Dto : Bitfield<16, 4> + { + enum { TCF_2_POW_27 = 0xe }; + }; + }; + + struct Stat : Register<0x230, 32> + { + struct Tc : Bitfield<1, 1> { }; + struct Cc : Bitfield<0, 1> { }; + struct Erri : Bitfield<15, 1> { }; + struct Cto : Bitfield<16, 1> { }; + }; + + struct Ie : Register<0x234, 32> + { + struct Tc_enable : Bitfield<1, 1> { }; + struct Cto_enable : Bitfield<16, 1> { }; + }; + + struct Ise : Register<0x238, 32> + { + struct Tc_sigen : Bitfield<1, 1> { }; + struct Cto_sigen : Bitfield<16, 1> { }; + }; + + struct Capa : Register<0x240, 32> + { + struct Vs30 : Bitfield<25, 1> { }; + struct Vs18 : Bitfield<26, 1> { }; + }; + + struct Block_transfer + { + Block::Packet_descriptor packet { }; + bool pending = false; + }; + + 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); } + }; + + Env &_env; + Block_transfer _block_transfer { }; + Timer_delayer _delayer { _env }; + Signal_handler _irq_handler { _env.ep(), *this, &Driver::_handle_irq }; + Irq_connection _irq { _env, HSMMC_IRQ }; + Card_info _card_info { _init() }; + + Card_info _init(); + bool _wait_for_bre(); + bool _wait_for_bwe(); + void _handle_irq(); + bool _reset_cmd_line(); + void _disable_irq(); + void _bus_width(Bus_width bus_width); + bool _sd_bus_power_on(); + bool _set_and_enable_clock(enum Clock_divider divider); + void _set_bus_power(Voltage voltage); + bool _init_stream(); + + void _stop_clock() { Mmio::write(0); } + + + /********************* + ** Host_controller ** + *********************/ + + bool _issue_command(Command_base const &command) override; + Cid _read_cid() override; + Csd _read_csd() override; + + Card_info card_info() const override { return _card_info; } + + unsigned _read_rca() override { + return Send_relative_addr::Response::Rca::get(Mmio::read()); } + + public: + + Driver(Env &env); + + + /******************* + ** Block::Driver ** + *******************/ + + void read(Block::sector_t block_number, + size_t block_count, + char *buffer, + Block::Packet_descriptor &pkt) override; + + void write(Block::sector_t block_number, + size_t block_count, + char const *buffer, + Block::Packet_descriptor &pkt) override; +}; + +#endif /* _DRIVER_H_ */ diff --git a/src/drivers/sd_card/spec/omap4/target.mk b/src/drivers/sd_card/spec/omap4/target.mk new file mode 100644 index 0000000..1fad406 --- /dev/null +++ b/src/drivers/sd_card/spec/omap4/target.mk @@ -0,0 +1,9 @@ +TARGET = omap4_sd_card_drv +REQUIRES = arm_v7 +SRC_CC += main.cc driver.cc +LIBS += base + +INC_DIR += $(PRG_DIR) $(BASE_DIR)/../os/src/drivers/sd_card + +vpath %.cc $(BASE_DIR)/../os/src/drivers/sd_card +vpath %.cc $(PRG_DIR) diff --git a/src/drivers/usb_host/spec/panda/platform.cc b/src/drivers/usb_host/spec/panda/platform.cc new file mode 100644 index 0000000..1001bf6 --- /dev/null +++ b/src/drivers/usb_host/spec/panda/platform.cc @@ -0,0 +1,301 @@ +/* + * \brief EHCI for OMAP4 + * \author Sebastian Sumpf + * \date 2012-06-20 + */ + +/* + * Copyright (C) 2012-2017 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#include + +#include +#include +#include + +#include + +#include + +using namespace Genode; + +/** + * Base addresses + */ +enum { + EHCI_BASE = 0x4a064c00, + UHH_BASE = 0x4a064000, + TLL_BASE = 0x4a062000, + SCRM_BASE = 0x4a30a000, + CAM_BASE = 0x4a009000, /* used for L3INIT_CM2 */ +}; + + +/** + * Inerrupt numbers + */ +enum { IRQ_EHCI = 109 }; + + +/** + * Resources for platform device + */ +static resource _ehci[] = +{ + { EHCI_BASE, EHCI_BASE + 0x400 - 1, "ehci", IORESOURCE_MEM }, + { IRQ_EHCI, IRQ_EHCI, "ehci-irq", IORESOURCE_IRQ }, +}; + + +/** + * Port informations for platform device + */ +static struct ehci_hcd_omap_platform_data _ehci_data; + + +/** + * Enables USB clocks + */ +struct Clocks : Genode::Mmio +{ + Clocks(Genode::addr_t const mmio_base) : Mmio(mmio_base) + { + write(0x101); + write(0x1008002); + write(0x1); + } + + struct Usb_host_clk : Register<0x358, 32> { }; + struct Usb_tll_clk : Register<0x368, 32> { }; + struct Usb_phy_clk : Register<0x3e0, 32> { }; + + template void update(unsigned val) + { + typename T::access_t access = read(); + access |= val; + write(access); + }; +}; + + +/** + * Panda board reference USB clock + */ +struct Aux3 : Genode::Mmio +{ + Aux3(addr_t const mmio_base) : Mmio(mmio_base) + { + enable(); + } + + /* the clock register */ + struct Aux3_clk : Register<0x31c, 32> + { + struct Src_select : Bitfield<1, 2> { }; + struct Div : Bitfield<16, 4> { enum { DIV_2 = 1 }; }; + struct Enable : Bitfield<8, 1> { enum { ON = 1 }; }; + }; + + /* clock source register */ + struct Aux_src : Register<0x110, 32, true> { }; + + void enable() + { + /* select system clock */ + write(0); + + /* set to 19.2 Mhz */ + write(Aux3_clk::Div::DIV_2); + + /* enable clock */ + write(Aux3_clk::Enable::ON); + + /* enable_ext = 1 | enable_int = 1| mode = 0x01 */ + write(0xd); + } +}; + + +/** + * ULPI transceiverless link + */ +struct Tll : Genode::Mmio +{ + Tll(addr_t const mmio_base) : Mmio(mmio_base) + { + reset(); + } + + struct Sys_config : Register<0x10, 32> + { + struct Soft_reset : Bitfield<1, 1> { }; + struct Cactivity : Bitfield<8, 1> { }; + struct Sidle_mode : Bitfield<3, 2> { }; + struct Ena_wakeup : Bitfield<2, 1> { }; + }; + + struct Sys_status : Register<0x14, 32> { }; + + void reset() + { + write(0x0); + + /* reset */ + write(0x1); + + while(!read()) + msleep(1); + + /* disable IDLE, enable wake up, enable auto gating */ + write(1); + write(1); + write(1); + } +}; + + +/** + * USB high-speed host + */ +struct Uhh : Genode::Mmio +{ + Uhh(addr_t const mmio_base) : Mmio(mmio_base) + { + /* diable idle and standby */ + write(1); + write(1); + + /* set ports to external phy */ + write(0); + write(0); + } + + struct Sys_config : Register<0x10, 32> + { + struct Idle : Bitfield<2, 2> { }; + struct Standby : Bitfield<4, 2> { }; + }; + + struct Host_config : Register<0x40, 32> + { + struct P1_mode : Bitfield<16, 2> { }; + struct P2_mode : Bitfield<18, 2> { }; + }; +}; + + +/** + * 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> { }; + }; +}; + + +/** + * Initialize the USB controller from scratch, since the boot loader might not + * do it or even disable USB. + */ +static void omap_ehci_init(Genode::Env &env) +{ + /* taken from the Panda board manual */ + enum { HUB_POWER = 1, HUB_NRESET = 62, ULPI_PHY_TYPE = 182 }; + + /* SCRM */ + Io_mem_connection io_scrm(env, SCRM_BASE, 0x1000); + addr_t scrm_base = (addr_t)env.rm().attach(io_scrm.dataspace()); + + /* enable reference clock */ + Aux3 aux3(scrm_base); + + /* init GPIO */ + Gpio::Connection gpio_power(env, HUB_POWER); + Gpio::Connection gpio_reset(env, HUB_NRESET); + + /* disable the hub power and reset before init */ + gpio_power.direction(Gpio::Session::OUT); + gpio_reset.direction(Gpio::Session::OUT); + gpio_power.write(false); + gpio_reset.write(true); + + /* enable clocks */ + Io_mem_connection io_clock(env, CAM_BASE, 0x1000); + addr_t clock_base = (addr_t)env.rm().attach(io_clock.dataspace()); + Clocks c(clock_base); + + /* reset TLL */ + Io_mem_connection io_tll(env, TLL_BASE, 0x1000); + addr_t tll_base = (addr_t)env.rm().attach(io_tll.dataspace()); + Tll t(tll_base); + + /* reset host */ + Io_mem_connection io_uhh(env, UHH_BASE, 0x1000); + addr_t uhh_base = (addr_t)env.rm().attach(io_uhh.dataspace()); + Uhh uhh(uhh_base); + + /* enable hub power */ + gpio_power.write(true); + + /* reset EHCI */ + addr_t ehci_base = uhh_base + 0xc00; + Ehci ehci(ehci_base); + + addr_t base[] = { scrm_base, clock_base, tll_base, uhh_base, 0 }; + for (int i = 0; base[i]; i++) + env.rm().detach(base[i]); +} + + +extern "C" void module_ehci_omap_init(); +extern "C" int module_usbnet_init(); +extern "C" int module_smsc95xx_driver_init(); + +void platform_hcd_init(Genode::Env &, Services *services) +{ + /* register EHCI controller */ + module_ehci_omap_init(); + + /* initialize EHCI */ + omap_ehci_init(services->env); + + /* setup EHCI-controller platform device */ + platform_device *pdev = (platform_device *)kzalloc(sizeof(platform_device), 0); + pdev->name = (char *)"ehci-omap"; + pdev->id = 0; + pdev->num_resources = 2; + pdev->resource = _ehci; + + + _ehci_data.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY; + _ehci_data.port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED; + _ehci_data.phy_reset = 0; + pdev->dev.platform_data = &_ehci_data; + + /* + * 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); +} diff --git a/src/drivers/usb_host/spec/panda/target.mk b/src/drivers/usb_host/spec/panda/target.mk new file mode 100644 index 0000000..a4d3ddd --- /dev/null +++ b/src/drivers/usb_host/spec/panda/target.mk @@ -0,0 +1,23 @@ +DDE_LINUX_DIR = $(BASE_DIR)/../dde_linux + +include $(DDE_LINUX_DIR)/src/drivers/usb_host/target.inc + +TARGET = panda_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/panda/platform.cc +SRC_C += usb/host/ehci-omap.c + +CC_OPT += -DCONFIG_USB_EHCI_HCD_OMAP=1 +CC_OPT += -DCONFIG_USB_EHCI_TT_NEWSCHED=1 +CC_OPT += -DCONFIG_EXTCON=1 + +vpath %.cc $(DDE_LINUX_DIR)/src +vpath % $(DDE_LINUX_DIR)/src/drivers/usb_host diff --git a/src/include/hw/spec/arm/panda_board.h b/src/include/hw/spec/arm/panda_board.h new file mode 100644 index 0000000..c82641f --- /dev/null +++ b/src/include/hw/spec/arm/panda_board.h @@ -0,0 +1,78 @@ +/* + * \brief Pandaboard specific 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__PANDA_BOARD_H_ +#define _SRC__INCLUDE__HW__SPEC__ARM__PANDA_BOARD_H_ + +#include +#include +#include +#include +#include + +namespace Hw::Panda_board { + + enum { + /* device IO memory */ + MMIO_0_BASE = 0x48000000, + + /* normal RAM */ + RAM_0_BASE = 0x80000000, + RAM_0_SIZE = 0x40000000, + + /* L2 cache */ + PL310_MMIO_BASE = 0x48242000, + PL310_MMIO_SIZE = 0x00001000, + + /* CPU */ + CORTEX_A9_PRIVATE_MEM_BASE = 0x48240000, + CORTEX_A9_PRIVATE_MEM_SIZE = 0x00002000, + CORTEX_A9_PRIVATE_TIMER_CLK = 400000000, + CORTEX_A9_PRIVATE_TIMER_DIV = 200, + CORTEX_A9_WUGEN_MMIO_BASE = 0x48281000, + CORTEX_A9_SCU_MMIO_BASE = 0x48240000, + + TL16C750_3_MMIO_BASE = MMIO_0_BASE + 0x20000, + TL16C750_MMIO_SIZE = 0x2000, + TL16C750_CLOCK = 48*1000*1000, + }; + + using Cpu_mmio = Hw::Cortex_a9_mmio; + using Serial = Genode::Tl16c750_uart; + + enum { + UART_BASE = TL16C750_3_MMIO_BASE, + UART_CLOCK = TL16C750_CLOCK, + }; + + enum Panda_firmware_opcodes { + CPU_ACTLR_SMP_BIT_RAISE = 0x25, + L2_CACHE_SET_DEBUG_REG = 0x100, + L2_CACHE_ENABLE_REG = 0x102, + L2_CACHE_AUX_REG = 0x109, + }; + + static inline void call_panda_firmware(Genode::addr_t func, + Genode::addr_t val) + { + register Genode::addr_t _func asm("r12") = func; + register Genode::addr_t _val asm("r0") = val; + asm volatile("dsb \n" + "push {r1-r11} \n" + "smc #0 \n" + "pop {r1-r11} \n" + :: "r" (_func), "r" (_val) : "memory", "cc"); + } +} + +#endif /* _SRC__INCLUDE__HW__SPEC__ARM__PANDA_BOARD_H_ */ diff --git a/src/include/hw/uart/tl16c750.h b/src/include/hw/uart/tl16c750.h new file mode 100644 index 0000000..9c7dbf5 --- /dev/null +++ b/src/include/hw/uart/tl16c750.h @@ -0,0 +1,246 @@ +/* + * \brief UART driver for the Texas instruments TL16C750 module + * \author Martin stein + * \date 2011-10-17 + */ + +/* + * Copyright (C) 2011-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__TL16C750_H_ +#define _INCLUDE__DRIVERS__UART__TL16C750_H_ + +/* Genode includes */ +#include + +namespace Genode { class Tl16c750_uart; } + + +/** + * Base driver Texas instruments TL16C750 UART module + * + * In contrast to the abilities of the TL16C750, this driver targets only + * the basic UART functionalities. + */ +class Genode::Tl16c750_uart : public Mmio +{ + protected: + /** + * Least significant divisor part + */ + struct Uart_dll : Register<0x0, 32> + { + struct Clock_lsb : Bitfield<0, 8> { }; + }; + + /** + * Transmit holding register + */ + struct Uart_thr : Register<0x0, 32> + { + struct Thr : Bitfield<0, 8> { }; + }; + + /** + * Receiver holding register + */ + struct Uart_rhr : Register<0x0, 32> + { + struct Rhr : Bitfield<0, 8> { }; + }; + + /** + * Most significant divisor part + */ + struct Uart_dlh : Register<0x4, 32> + { + struct Clock_msb : Bitfield<0, 6> { }; + }; + + /** + * Interrupt enable register + */ + struct Uart_ier : Register<0x4, 32> + { + struct Rhr_it : Bitfield<0, 1> { }; + struct Thr_it : Bitfield<1, 1> { }; + struct Line_sts_it : Bitfield<2, 1> { }; + struct Modem_sts_it : Bitfield<3, 1> { }; + struct Sleep_mode : Bitfield<4, 1> { }; + struct Xoff_it : Bitfield<5, 1> { }; + struct Rts_it : Bitfield<6, 1> { }; + struct Cts_it : Bitfield<7, 1> { }; + }; + + /** + * Interrupt identification register + */ + struct Uart_iir : Register<0x8, 32> + { + struct It_pending : Bitfield<0, 1> { }; + }; + + /** + * FIFO control register + */ + struct Uart_fcr : Register<0x8, 32> + { + struct Fifo_enable : Bitfield<0, 1> { }; + }; + + /** + * Line control register + */ + struct Uart_lcr : Register<0xc, 32> + { + struct Char_length : Bitfield<0, 2> + { + enum { _8_BIT = 3 }; + }; + struct Nb_stop : Bitfield<2, 1> + { + enum { _1_STOP_BIT = 0 }; + }; + struct Parity_en : Bitfield<3, 1> { }; + struct Break_en : Bitfield<6, 1> { }; + struct Div_en : Bitfield<7, 1> { }; + struct Reg_mode : Bitfield<0, 8> + { + enum { OPERATIONAL = 0, CONFIG_A = 0x80, CONFIG_B = 0xbf }; + }; + }; + + /** + * Modem control register + */ + struct Uart_mcr : Register<0x10, 32> + { + struct Tcr_tlr : Bitfield<6, 1> { }; + }; + + /** + * Line status register + */ + struct Uart_lsr : Register<0x14, 32> + { + struct Rx_fifo_empty : Bitfield<0, 1> { }; + struct Tx_fifo_empty : Bitfield<5, 1> { }; + }; + + /** + * Mode definition register 1 + */ + struct Uart_mdr1 : Register<0x20, 32> + { + struct Mode_select : Bitfield<0, 3> + { + enum { UART_16X = 0, DISABLED = 7 }; + }; + }; + + /** + * System control register + */ + struct Uart_sysc : Register<0x54, 32> + { + struct Softreset : Bitfield<1, 1> { }; + }; + + /** + * System status register + */ + struct Uart_syss : Register<0x58, 32> + { + struct Resetdone : Bitfield<0, 1> { }; + }; + + void _init(unsigned long const clock, unsigned long const baud_rate) + { + /* disable UART */ + write(Uart_mdr1::Mode_select::DISABLED); + + /* enable access to 'Uart_fcr' and 'Uart_ier' */ + write(Uart_lcr::Reg_mode::OPERATIONAL); + + /* + * Configure FIFOs, we don't use any interrupts or DMA, + * thus FIFO trigger and DMA configurations are dispensable. + */ + write(1); + + /* disable interrupts and sleep mode */ + write(Uart_ier::Rhr_it::bits(0) + | Uart_ier::Thr_it::bits(0) + | Uart_ier::Line_sts_it::bits(0) + | Uart_ier::Modem_sts_it::bits(0) + | Uart_ier::Sleep_mode::bits(0) + | Uart_ier::Xoff_it::bits(0) + | Uart_ier::Rts_it::bits(0) + | Uart_ier::Cts_it::bits(0)); + + /* enable access to 'Uart_dlh' and 'Uart_dll' */ + write(Uart_lcr::Reg_mode::CONFIG_B); + + /* + * Load the new divisor value (this driver solely uses + * 'UART_16X' mode) + */ + enum { UART_16X_DIVIDER_LOG2 = 4 }; + unsigned long const adjusted_br = baud_rate << UART_16X_DIVIDER_LOG2; + double const divisor = (double)clock / adjusted_br; + unsigned long const divisor_uint = (unsigned long)divisor; + write(divisor_uint); + write(divisor_uint>>Uart_dll::Clock_lsb::WIDTH); + + /* + * Configure protocol formatting and thereby return to + * operational mode. + */ + write(Uart_lcr::Char_length::bits(Uart_lcr::Char_length::_8_BIT) + | Uart_lcr::Nb_stop::bits(Uart_lcr::Nb_stop::_1_STOP_BIT) + | Uart_lcr::Parity_en::bits(0) + | Uart_lcr::Break_en::bits(0) + | Uart_lcr::Div_en::bits(0)); + + /* + * Switch to UART mode, we don't use hardware or software flow + * control, thus according configurations are dispensable + */ + write(Uart_mdr1::Mode_select::UART_16X); + } + + public: + /** + * Constructor + * + * \param base MMIO base address + * \param clock reference clock + * \param baud_rate targeted baud rate + */ + Tl16c750_uart(addr_t const base, unsigned long const clock, + unsigned long const baud_rate) : Mmio(base) + { + /* reset and init UART */ + write(1); + while (!read()) ; + _init(clock, baud_rate); + } + + /** + * Transmit ASCII char 'c' + */ + void put_char(char const c) + { + /* wait as long as the transmission buffer is full */ + while (!read()) ; + + /* transmit character */ + write(c); + } +}; + +#endif /* _INCLUDE__DRIVERS__UART__TL16C750_H_ */ diff --git a/tool/run/boot_dir/hw b/tool/run/boot_dir/hw new file mode 100644 index 0000000..e896355 --- /dev/null +++ b/tool/run/boot_dir/hw @@ -0,0 +1,24 @@ +source [genode_dir]/tool/run/boot_dir/hw + + +proc bootstrap_link_address { } { + if {[have_spec "panda"]} { return "0x88000000" } + + puts "unknown platform no linker address known" + exit -1 +} + + +## +# Base source archive within depot +# +proc base_src { } { + + if {[have_spec panda]} { return base-hw-panda } + + global specs + + puts stderr "Test requires base-hw kernel archive, which is missing for this build configuration" + puts stderr " SPECS=\"$specs\"" + exit 0 +}