diff --git a/repos/base/include/platform/imx53/drivers/board_base_support.h b/repos/base/include/platform/imx53/drivers/board_base_support.h
index 8289dee7c0..092ed8b948 100644
--- a/repos/base/include/platform/imx53/drivers/board_base_support.h
+++ b/repos/base/include/platform/imx53/drivers/board_base_support.h
@@ -26,6 +26,10 @@ struct Imx53::Board_base
MMIO_BASE = 0x0,
MMIO_SIZE = 0x70000000,
+ ESDHCV2_1_IRQ = 1,
+ ESDHCV2_1_MMIO_BASE = 0x50004000,
+ ESDHCV2_1_MMIO_SIZE = 0x00004000,
+
UART_1_IRQ = 31,
UART_1_MMIO_BASE = 0x53fbc000,
UART_1_MMIO_SIZE = 0x00004000,
diff --git a/repos/os/src/drivers/sd_card/imx53/bench/main.cc b/repos/os/src/drivers/sd_card/imx53/bench/main.cc
new file mode 100644
index 0000000000..43abb53536
--- /dev/null
+++ b/repos/os/src/drivers/sd_card/imx53/bench/main.cc
@@ -0,0 +1,166 @@
+/*
+ * \brief SD-card benchmark Imx53 platform
+ * \author Martin Stein
+ * \date 2015-03-04
+ */
+
+/*
+ * Copyright (C) 2012-2013 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU General Public License version 2.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+
+
+struct Operation
+{
+ virtual void operator () (Block::Driver &driver,
+ Genode::addr_t block_number,
+ Genode::size_t block_count,
+ Genode::addr_t buffer_phys,
+ char *buffer_virt) = 0;
+};
+
+
+/*
+ * \param total_size total number of bytes to read
+ * \param request_size number of bytes per request
+ */
+static void run_benchmark(Block::Driver &driver,
+ Timer::Session &timer,
+ char *buffer_virt,
+ Genode::addr_t buffer_phys,
+ Genode::size_t buffer_size,
+ Genode::size_t request_size,
+ Operation &operation)
+{
+ using namespace Genode;
+
+ PLOG("request_size=%zd bytes", request_size);
+
+ size_t const time_before_ms = timer.elapsed_ms();
+
+ size_t num_requests = buffer_size / request_size;
+
+ /*
+ * Trim number of requests if it would take to much time
+ */
+ if (num_requests > 1280) {
+ buffer_size = 1280 * request_size;
+ num_requests = buffer_size / request_size;
+ }
+
+ for (size_t i = 0; i < num_requests; i++)
+ {
+ size_t const block_count = request_size / driver.block_size();
+ addr_t const block_number = i*block_count;
+
+ operation(driver, block_number, block_count,
+ buffer_phys + i*request_size,
+ buffer_virt + i*request_size);
+ }
+
+ size_t const time_after_ms = timer.elapsed_ms();
+ size_t const duration_ms = time_after_ms - time_before_ms;
+
+ /*
+ * Convert bytes per milliseconds to kilobytes per seconds
+ *
+ * (total_size / 1024) / (duration_ms / 1000)
+ */
+ size_t const buffer_size_kb = buffer_size / 1024;
+ size_t const throughput_kb_per_sec = (1000*buffer_size_kb) / duration_ms;
+
+ PLOG(" duration: %zd ms", duration_ms);
+ PLOG(" amount: %zd KiB", buffer_size_kb);
+ PLOG(" throughput: %zd KiB/sec", throughput_kb_per_sec);
+}
+
+
+int main(int argc, char **argv)
+{
+ using namespace Genode;
+
+ printf("--- i.MX53 SD card benchmark ---\n");
+
+ bool const use_dma = true;
+
+ static Block::Imx53_driver driver(use_dma);
+
+ static Timer::Connection timer;
+
+ long const request_sizes[] = {
+ 512, 1024, 2048, 4096, 8192, 16384, 32768, 64*1024, 128*1024, 0 };
+
+ /* total size of communication buffer */
+ size_t const buffer_size = 10 * 1024 * 1024;
+
+ /* allocate read/write buffer */
+ static Attached_ram_dataspace buffer(env()->ram_session(), buffer_size, Genode::UNCACHED);
+ char * const buffer_virt = buffer.local_addr();
+ addr_t const buffer_phys = Dataspace_client(buffer.cap()).phys_addr();
+
+ /*
+ * Benchmark reading from SD card
+ */
+
+ printf("\n-- reading from SD card --\n");
+
+ struct Read : Operation
+ {
+ void operator () (Block::Driver &driver,
+ addr_t number, size_t count, addr_t phys, char *virt)
+ {
+ Block::Packet_descriptor p;
+ if (driver.dma_enabled())
+ driver.read_dma(number, count, phys, p);
+ else
+ driver.read(number, count, virt, p);
+ }
+ } read_operation;
+
+ for (unsigned i = 0; request_sizes[i]; i++) {
+ run_benchmark(driver, timer, buffer_virt, buffer_phys, buffer_size,
+ request_sizes[i], read_operation);
+ }
+
+ /*
+ * Benchmark writing to SD card
+ *
+ * We write back the content of the buffer, which we just filled during the
+ * read benchmark. If both read and write succeed, the SD card will retain
+ * its original content.
+ */
+
+ printf("\n-- writing to SD card --\n");
+
+ struct Write : Operation
+ {
+ void operator () (Block::Driver &driver,
+ addr_t number, size_t count, addr_t phys, char *virt)
+ {
+ Block::Packet_descriptor p;
+ if (driver.dma_enabled())
+ driver.write_dma(number, count, phys, p);
+ else
+ driver.write(number, count, virt, p);
+ }
+ } write_operation;
+
+ for (unsigned i = 0; request_sizes[i]; i++)
+ run_benchmark(driver, timer, buffer_virt, buffer_phys, buffer_size,
+ request_sizes[i], write_operation);
+
+ printf("\n--- i.MX53 SD card benchmark finished ---\n");
+ sleep_forever();
+ return 0;
+}
diff --git a/repos/os/src/drivers/sd_card/imx53/bench/target.mk b/repos/os/src/drivers/sd_card/imx53/bench/target.mk
new file mode 100644
index 0000000000..256953e6e7
--- /dev/null
+++ b/repos/os/src/drivers/sd_card/imx53/bench/target.mk
@@ -0,0 +1,5 @@
+TARGET = sd_card_bench
+REQUIRES = imx53
+SRC_CC = main.cc
+LIBS = base
+INC_DIR += $(PRG_DIR)/.. $(PRG_DIR)/../..
diff --git a/repos/os/src/drivers/sd_card/imx53/driver.h b/repos/os/src/drivers/sd_card/imx53/driver.h
new file mode 100644
index 0000000000..edbdaf31ce
--- /dev/null
+++ b/repos/os/src/drivers/sd_card/imx53/driver.h
@@ -0,0 +1,135 @@
+/*
+ * \brief Imx53-specific implementation of the Block::Driver interface
+ * \author Martin Stein
+ * \date 2015-02-04
+ */
+
+/*
+ * Copyright (C) 2012-2013 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU General Public License version 2.
+ */
+
+#ifndef _DRIVER_H_
+#define _DRIVER_H_
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+
+namespace Block {
+ using namespace Genode;
+ class Imx53_driver;
+}
+
+
+class Block::Imx53_driver : public Block::Driver
+{
+ private:
+
+ struct Timer_delayer : Timer::Connection, Mmio::Delayer
+ {
+ /**
+ * Implementation of 'Delayer' interface
+ */
+ void usleep(unsigned us) { Timer::Connection::usleep(us); }
+ } _delayer;
+
+ Attached_io_mem_dataspace _esdhcv2_1_mmio;
+ Esdhcv2_controller _controller;
+
+ bool const _use_dma;
+
+ public:
+
+ Imx53_driver(bool use_dma)
+ :
+ _esdhcv2_1_mmio(Genode::Board_base::ESDHCV2_1_MMIO_BASE,
+ Genode::Board_base::ESDHCV2_1_MMIO_SIZE),
+ _controller((addr_t)_esdhcv2_1_mmio.local_addr(),
+ Genode::Board_base::ESDHCV2_1_IRQ, _delayer, use_dma),
+ _use_dma(use_dma)
+ {
+ Sd_card::Card_info const card_info = _controller.card_info();
+
+ PLOG("SD card detected");
+ PLOG("capacity: %zd MiB", card_info.capacity_mb());
+ }
+
+
+ /*****************************
+ ** Block::Driver interface **
+ *****************************/
+
+ Genode::size_t block_size() { return 512; }
+
+ virtual Block::sector_t block_count()
+ {
+ return _controller.card_info().capacity_mb() * 1024 * 2;
+ }
+
+ Block::Session::Operations ops()
+ {
+ Block::Session::Operations o;
+ o.set_operation(Block::Packet_descriptor::READ);
+ o.set_operation(Block::Packet_descriptor::WRITE);
+ return o;
+ }
+
+ void read(Block::sector_t block_number,
+ Genode::size_t block_count,
+ char *out_buffer,
+ Packet_descriptor &packet)
+ {
+ if (!_controller.read_blocks(block_number, block_count, out_buffer))
+ throw Io_error();
+ ack_packet(packet);
+ }
+
+ void write(Block::sector_t block_number,
+ Genode::size_t block_count,
+ char const *buffer,
+ Packet_descriptor &packet)
+ {
+ if (!_controller.write_blocks(block_number, block_count, buffer))
+ throw Io_error();
+ ack_packet(packet);
+ }
+
+ void read_dma(Block::sector_t block_number,
+ Genode::size_t block_count,
+ Genode::addr_t phys,
+ Packet_descriptor &packet)
+ {
+ if (!_controller.read_blocks_dma(block_number, block_count, phys))
+ throw Io_error();
+ ack_packet(packet);
+ }
+
+ void write_dma(Block::sector_t block_number,
+ Genode::size_t block_count,
+ Genode::addr_t phys,
+ Packet_descriptor &packet)
+ {
+ if (!_controller.write_blocks_dma(block_number, block_count, phys))
+ throw Io_error();
+ ack_packet(packet);
+ }
+
+ bool dma_enabled() { return _use_dma; }
+
+ Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size) {
+ return Genode::env()->ram_session()->alloc(size, UNCACHED); }
+
+ void free_dma_buffer(Genode::Ram_dataspace_capability c) {
+ return Genode::env()->ram_session()->free(c); }
+};
+
+#endif /* _DRIVER_H_ */
diff --git a/repos/os/src/drivers/sd_card/imx53/esdhcv2.h b/repos/os/src/drivers/sd_card/imx53/esdhcv2.h
new file mode 100644
index 0000000000..74229d1808
--- /dev/null
+++ b/repos/os/src/drivers/sd_card/imx53/esdhcv2.h
@@ -0,0 +1,830 @@
+/*
+ * \brief ESDHCv2 host controller
+ * \author Martin Stein
+ * \date 2015-02-05
+ */
+
+/*
+ * Copyright (C) 2012-2015 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU General Public License version 2.
+ */
+
+#ifndef _ESDHCV2_H_
+#define _ESDHCV2_H_
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+
+namespace Adma2
+{
+ using namespace Genode;
+
+ /**
+ * Descriptor layout
+ */
+ struct Desc : Register<64>
+ {
+ static size_t constexpr max_size = 64 * 1024 - 4;
+
+ struct Valid : Bitfield<0, 1> { };
+ struct End : Bitfield<1, 1> { };
+ struct Int : Bitfield<2, 1> { };
+ struct Act1 : Bitfield<4, 1> { };
+ struct Act2 : Bitfield<5, 1> { };
+ struct Length : Bitfield<16, 16> { };
+ struct Address : Bitfield<32, 32> { };
+ };
+
+ /**
+ * Descriptor table
+ */
+ class Table
+ {
+ static size_t constexpr _max_desc = 1024;
+ static size_t constexpr _ds_size = _max_desc * sizeof(Desc::access_t);
+
+ Attached_ram_dataspace _ds;
+ Desc::access_t * const _base_virt;
+ addr_t const _base_phys;
+
+ public:
+
+ /**
+ * Constructor
+ */
+ Table() :
+ _ds(env()->ram_session(), _ds_size, UNCACHED),
+ _base_virt(_ds.local_addr()),
+ _base_phys(Dataspace_client(_ds.cap()).phys_addr())
+ { }
+
+ /**
+ * Marshal descriptors according to block request
+ *
+ * \param size request size in bytes
+ * \param buffer_phys physical base of transfer buffer
+ *
+ * \return true on success
+ */
+ bool setup_req(size_t const size, addr_t const buffer_phys)
+ {
+ /*
+ * Sanity check
+ *
+ * FIXME An alternative to this sanity check would be to
+ * expose the maximum transfer size to the driver and let
+ * the driver partition large requests into ones that are
+ * supported.
+ */
+ static size_t constexpr max_size = _max_desc * Desc::max_size;
+ if (size > max_size) {
+ PERR("Block request too large");
+ return false;
+ }
+ /* install new descriptors till they cover all requested bytes */
+ addr_t consumed = 0;
+ for (int index = 0; consumed < size; index++) {
+
+ /* clamp current request to maximum request size */
+ size_t const remaining = size - consumed;
+ size_t const curr = min(Desc::max_size, remaining);
+
+ /* assemble new descriptor */
+ Desc::access_t desc = 0;
+ Desc::Address::set(desc, buffer_phys + consumed);
+ Desc::Length::set(desc, curr);
+ Desc::Act1::set(desc, 0);
+ Desc::Act2::set(desc, 1);
+ Desc::Valid::set(desc, 1);
+
+ /* let last descriptor generate transfer-complete signal */
+ if (consumed + curr == size) { Desc::End::set(desc, 1); }
+
+ /* install and account descriptor */
+ _base_virt[index] = desc;
+ consumed += curr;
+ }
+ return true;
+ }
+
+ /*
+ * Accessors
+ */
+
+ addr_t base_phys() const { return _base_phys; }
+ };
+
+}
+
+/**
+ * Freescale eSDHCv2 host MMIO
+ */
+struct Esdhcv2 : Genode::Mmio
+{
+ typedef Genode::size_t size_t;
+ typedef Genode::addr_t addr_t;
+
+ /*
+ * MMIO structure
+ */
+
+ struct Blkattr : Register<0x4, 32>
+ {
+ struct Blksize : Bitfield<0, 13> { };
+ struct Blkcnt : Bitfield<16, 16> { };
+ };
+ template
+ struct Cmdrsp_tpl : Register
+ {
+ struct Rsp136_8_24 : Register::template Bitfield<0, 24> { };
+ struct Rsp136_0_8 : Register::template Bitfield<24, 8> { };
+ };
+ struct Cmdarg : Register<0x8, 32> { };
+ struct Cmdrsp0 : Cmdrsp_tpl<0x10> { };
+ struct Cmdrsp1 : Cmdrsp_tpl<0x14> { };
+ struct Cmdrsp2 : Cmdrsp_tpl<0x18> { };
+ struct Cmdrsp3 : Cmdrsp_tpl<0x1c> { };
+ struct Rsp136_0 : Genode::Bitset_2 { };
+ struct Rsp136_1 : Genode::Bitset_2 { };
+ struct Rsp136_2 : Genode::Bitset_2 { };
+ struct Rsp136_3 : Genode::Bitset_2 { };
+ struct Xfertyp : Register<0xc, 32>
+ {
+ struct Dmaen : Bitfield<0, 1> { };
+ struct Bcen : Bitfield<1, 1> { };
+ struct Ac12en : Bitfield<2, 1> { };
+ struct Dtdsel : Bitfield<4, 1>
+ {
+ enum { WRITE = 0, READ = 1, };
+ };
+ struct Msbsel : Bitfield<5, 1> { };
+ struct Rsptyp : Bitfield<16, 2>
+ {
+ enum {
+ _0BIT = 0,
+ _136BIT = 1,
+ _48BIT = 2,
+ _48BIT_BUSY = 3,
+ };
+ };
+ struct Dpsel : Bitfield<21, 1> { };
+ struct Cmdtyp : Bitfield<22, 2>
+ {
+ enum { ABORT_CMD12 = 3 };
+ };
+ struct Cmdinx : Bitfield<24, 6> { };
+ };
+ struct Datport : Register<0x20, 32> { };
+ struct Prsstat_lhw : Register<0x24, 16>
+ {
+ struct Sdstb : Bitfield<3, 1> { };
+
+ static constexpr access_t cmd_allowed() { return Sdstb::reg_mask(); }
+ };
+ struct Proctl : Register<0x28, 32>
+ {
+ struct Dtw : Bitfield<1, 2>
+ {
+ enum { _1BIT = 0, _4BIT = 1 };
+ };
+ struct Dmas : Bitfield<8, 2> { enum { ADMA2 = 2 }; };
+ };
+ struct Sysctl : Register<0x2c, 32>
+ {
+ struct Ipgen : Bitfield<0, 1> { };
+ struct Hcken : Bitfield<1, 1> { };
+ struct Peren : Bitfield<2, 1> { };
+ struct Dvs : Bitfield<4, 4>
+ {
+ enum { DIV1 = 0x0, DIV4 = 0x3, DIV16 = 0xf, };
+ };
+ struct Sdclkfs : Bitfield<8, 8>
+ {
+ enum { DIV1 = 0x00, DIV2 = 0x01, DIV32 = 0x10, };
+ };
+ struct Dtocv : Bitfield<16, 4>
+ {
+ enum { SDCLK_TIMES_2_POW_27 = 0xe };
+ };
+ struct Rsta : Bitfield<24, 1> { };
+ struct Rstc : Bitfield<25, 1> { };
+ struct Rstd : Bitfield<26, 1> { };
+ };
+
+ template
+ struct Irq_tpl : Register
+ {
+ struct Cc : Register::template Bitfield<0, 1> { };
+ struct Tc : Register::template Bitfield<1, 1> { };
+ struct Dint : Register::template Bitfield<03, 1> { };
+ struct Ctoe : Register::template Bitfield<16, 1> { };
+ struct Cce : Register::template Bitfield<17, 1> { };
+ struct Cebe : Register::template Bitfield<18, 1> { };
+ struct Cie : Register::template Bitfield<19, 1> { };
+ struct Dtoe : Register::template Bitfield<20, 1> { };
+ struct Dce : Register::template Bitfield<21, 1> { };
+ struct Debe : Register::template Bitfield<22, 1> { };
+ struct Ac12e : Register::template Bitfield<24, 1> { };
+ struct Dmae : Register::template Bitfield<28, 1> { };
+ };
+ struct Irq : Irq_tpl<0> { };
+ struct Irqstat : Irq_tpl<0x30> { };
+ struct Irqstaten : Irq_tpl<0x34> { };
+ struct Irqsigen : Irq_tpl<0x38> { };
+ struct Maxcurrent : Register<0x48, 32> { };
+ struct Adsaddr : Register<0x58, 32> { };
+ struct Hostver : Register<0xfc, 32>
+ {
+ struct Svn : Bitfield<0, 8> { };
+ struct Vvn : Bitfield<8, 8> { };
+ };
+ struct Wml : Register<0x44, 32>
+ {
+ struct Rd_wml : Bitfield<0, 8> { };
+ struct Rd_brst_len : Bitfield<8, 5> { };
+ struct Wr_wml : Bitfield<16, 8> { };
+ struct Wr_brst_len : Bitfield<24, 5> { };
+ };
+
+ /**
+ * Constructor
+ */
+ Esdhcv2(addr_t const mmio_base) : Genode::Mmio(mmio_base) { }
+
+ /**
+ * Reset command line circuit of host controller
+ */
+ bool reset_command(Delayer & delayer)
+ {
+ write(1);
+ if (!wait_for(0, delayer)) {
+ PERR("Reset of command circuit failed");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Reset data circuit of host controller
+ */
+ bool reset_data(Delayer & delayer)
+ {
+ write(1);
+ if (!wait_for(0, delayer)) {
+ PERR("Reset of data circuit failed");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Full host reset
+ */
+ bool reset_all(Delayer & delayer)
+ {
+ /* start reset */
+ write(1);
+
+ /*
+ * The SDHC specification says that a software reset shouldn't
+ * have an effect on the the card detection circuit. The ESDHC
+ * clears Sysctl::Ipgen, Sysctl::Hcken, and Sysctl::Peren
+ * nonetheless which disables clocks that card detection relies
+ * on.
+ */
+ Sysctl::access_t sysctl = read();
+ Sysctl::Ipgen::set(sysctl, 1);
+ Sysctl::Hcken::set(sysctl, 1);
+ Sysctl::Peren::set(sysctl, 1);
+ write(sysctl);
+
+ /* wait for reset completion */
+ return wait_for(0, delayer);
+ }
+
+ /**
+ * Disable all interrupts
+ */
+ void disable_irqs()
+ {
+ write(0);
+ write(0);
+ }
+
+ /**
+ * Enable desired interrupts
+ */
+ void enable_irqs()
+ {
+ Irq::access_t irq = 0;
+ Irq::Cc::set(irq, 1);
+ Irq::Tc::set(irq, 1);
+ Irq::Dint::set(irq, 1);
+ Irq::Ctoe::set(irq, 1);
+ Irq::Cce::set(irq, 1);
+ Irq::Cebe::set(irq, 1);
+ Irq::Cie::set(irq, 1);
+ Irq::Dtoe::set(irq, 1);
+ Irq::Dce::set(irq, 1);
+ Irq::Debe::set(irq, 1);
+ Irq::Ac12e::set(irq, 1);
+ Irq::Dmae::set(irq, 1);
+ write(irq);
+ write(irq);
+ }
+
+ enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 };
+
+ /**
+ * Set bus width
+ */
+ void bus_width(Bus_width bus_width)
+ {
+ switch (bus_width) {
+ case BUS_WIDTH_1: write(Proctl::Dtw::_1BIT); break;
+ case BUS_WIDTH_4: write(Proctl::Dtw::_4BIT); break;
+ }
+ }
+
+ /**
+ * Disable host clock
+ */
+ void disable_clock()
+ {
+ Sysctl::access_t sysctl = read();
+ Sysctl::Ipgen::set(sysctl, 0);
+ Sysctl::Hcken::set(sysctl, 0);
+ Sysctl::Peren::set(sysctl, 0);
+ Sysctl::Dvs::set(sysctl, Sysctl::Dvs::DIV1);
+ Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV1);
+ write(sysctl);
+ }
+
+ enum Clock_divider { CLOCK_DIV_8, CLOCK_DIV_512 };
+
+ /**
+ * Enable host clock and configure it to fullfill a given divider
+ */
+ void enable_clock(Clock_divider divider, Delayer &delayer)
+ {
+ Sysctl::access_t sysctl = read();
+ Sysctl::Ipgen::set(sysctl, 1);
+ Sysctl::Hcken::set(sysctl, 1);
+ Sysctl::Peren::set(sysctl, 1);
+ switch (divider) {
+ case CLOCK_DIV_8:
+ Sysctl::Dvs::set(sysctl, Sysctl::Dvs::DIV4);
+ Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV2);
+ break;
+ case CLOCK_DIV_512:
+ Sysctl::Dvs::set(sysctl, Sysctl::Dvs::DIV16);
+ Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV32);
+ break;
+ }
+ write(sysctl);
+ delayer.usleep(1000);
+ }
+
+ /**
+ * Set clock to fulfill a given divider
+ */
+ void clock(enum Clock_divider divider, Delayer &delayer)
+ {
+ disable_clock();
+ write(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
+ enable_clock(divider, delayer);
+ }
+};
+
+struct Esdhcv2_controller : private Esdhcv2, public Sd_card::Host_controller
+{
+ private:
+
+ enum {
+ BLOCK_SIZE = 512,
+ WATERMARK_WORDS = 16,
+ BURST_WORDS = 8,
+ };
+
+ Genode::Irq_connection _irq;
+ Delayer & _delayer;
+ Sd_card::Card_info _card_info;
+ bool const _use_dma;
+ Adma2::Table _adma2_table;
+
+ /**
+ * Print 'err' and throw detection-error exception
+ */
+ void _detect_err(char const * const err)
+ {
+ PERR("%s", err);
+ throw Detection_failed();
+ }
+
+ /**
+ * Initialize host and card and return basic card information
+ */
+ Sd_card::Card_info _init()
+ {
+ /* configure host for initialization stage */
+ using namespace Sd_card;
+ if (!reset_all(_delayer)) { _detect_err("Host reset failed"); }
+ disable_irqs();
+
+ /* check host version */
+ Hostver::access_t const hostver = read();
+ if (Hostver::Vvn::get(hostver) != 18) {
+ _detect_err("Unexpected Vendor Version Number"); }
+ if (Hostver::Svn::get(hostver) != 1) {
+ _detect_err("Unexpected Specification Version Number"); }
+
+ /*
+ * We should check host capabilities at this point if we want to
+ * support other versions of the ESDHC. For the i.MX53 ESDHCv2 we
+ * know that the capabilities fit our requirements.
+ */
+
+ enable_irqs();
+ bus_width(BUS_WIDTH_1);
+ _delayer.usleep(10000);
+ clock(CLOCK_DIV_512, _delayer);
+
+ /*
+ * Initialize card
+ */
+
+ /*
+ * At this point we should do an SDIO card reset if we later want
+ * to detect the unwanted case of an SDIO card beeing inserted.
+ * The reset would be done via 2 differently configured
+ * Io_rw_direct commands.
+ */
+
+ _delayer.usleep(1000);
+ if (!issue_command(Go_idle_state())) {
+ _detect_err("Go_idle_state command failed"); }
+
+ _delayer.usleep(2000);
+ if (!issue_command(Send_if_cond())) {
+ _detect_err("Send_if_cond command failed"); }
+
+ if (read() != 0x1aa) {
+ _detect_err("Unexpected response of Send_if_cond command"); }
+
+ /*
+ * At this point we could detect the unwanted case of an SDIO card
+ * beeing inserted by issuing 4 Io_send_op_cond commands at an
+ * interval of 10 ms (they should time out on SD).
+ */
+
+ if (!issue_command(Sd_send_op_cond(0, false))) {
+ _detect_err("Sd_send_op_cond command failed"); }
+
+ if (read() != 0xff8000) {
+ _detect_err("Unexpected response of Sd_send_op_cond command"); }
+
+ _delayer.usleep(1000);
+ if (!issue_command(Go_idle_state())) {
+ _detect_err("Go_idle_state command failed"); }
+
+ _delayer.usleep(2000);
+ if (!issue_command(Send_if_cond())) {
+ _detect_err("Send_if_cond failed"); }
+
+ if (read() != 0x1aa) {
+ _detect_err("Unexpected response of Send_if_cond command"); }
+
+ /*
+ * Power on card
+ *
+ * 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(0x200000, true))) {
+ _detect_err("Sd_send_op_cond command failed"); }
+
+ if (Ocr::Busy::get(read())) { break; }
+ _delayer.usleep(1000);
+ }
+ if (!i) { _detect_err("Could not power-on SD card"); }
+
+ /* get basic information about the card */
+ Card_info card_info = _detect();
+
+ /*
+ * Configure working clock of host
+ *
+ * FIXME Host and card may be driven with a higher clock rate but
+ * checks (maybe read SSR/SCR, read switch, try frequencies) are
+ * necessary for that.
+ */
+ clock(CLOCK_DIV_8, _delayer);
+
+ /*
+ * Configure card and host to use 4 data signals
+ *
+ * FIXME Host and card may be driven with a higher bus width but
+ * further checks (read SCR) are necessary for that.
+ */
+ if (!issue_command(
+ Set_bus_width(Set_bus_width::Arg::Bus_width::FOUR_BITS),
+ card_info.rca()))
+ { _detect_err("Set_bus_width(FOUR_BITS) command failed"); }
+
+ bus_width(BUS_WIDTH_4);
+ _delayer.usleep(10000);
+
+ /* configure card to use our block size */
+ if (!issue_command(Set_blocklen(BLOCK_SIZE))) {
+ _detect_err("Set_blocklen command failed"); }
+
+ /* configure host buffer */
+ Wml::access_t wml = read();
+ Wml::Rd_wml::set(wml, WATERMARK_WORDS);
+ Wml::Rd_brst_len::set(wml, BURST_WORDS);
+ Wml::Wr_wml::set(wml, WATERMARK_WORDS);
+ Wml::Wr_brst_len::set(wml, BURST_WORDS);
+ write(wml);
+
+ /* configure ADMA */
+ write(Proctl::Dmas::ADMA2);
+
+ /* configure interrupts for operational mode */
+ disable_irqs();
+ write(~0);
+ enable_irqs();
+ return card_info;
+ }
+
+ /*
+ * Wait till sending a new command is allowed
+ *
+ * \return true on success
+ */
+ bool _wait_for_cmd_allowed()
+ {
+ /*
+ * At least after multi-block writes with our
+ * "Broken Auto Command 12" fix, waiting only for Prsstat::Cihb
+ * isn't sufficient as Prsstat::Dla and Prsstat::Cdihb may also
+ * be active.
+ */
+ if (!wait_for(Prsstat_lhw::cmd_allowed(), _delayer)) {
+ PERR("wait till issuing a new command is allowed timed out");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Wait for the completion of a non-multiple-block command
+ *
+ * \return true on success
+ */
+ bool _wait_for_cmd_complete()
+ {
+ _irq.wait_for_irq();
+ if (read() != Irqstat::Cc::reg_mask()) {
+ PWRN("received unexpected host signal");
+ reset_command(_delayer);
+ reset_data(_delayer);
+ enable_irqs();
+ return false;
+ }
+ write(Irqstat::Cc::reg_mask());
+ return true;
+ }
+
+ /**
+ * Abort transmission by manually issuing stop command
+ *
+ * \return true on success
+ */
+ bool _abort_transmission()
+ {
+ write(0);
+ Xfertyp::access_t xfertyp = 0;
+ Xfertyp::Cmdinx::set(xfertyp, Sd_card::Stop_transmission::INDEX);
+ Xfertyp::Cmdtyp::set(xfertyp, Xfertyp::Cmdtyp::ABORT_CMD12);
+ Xfertyp::Rsptyp::set(xfertyp, Xfertyp::Rsptyp::_48BIT);
+ write(xfertyp);
+ return _wait_for_cmd_complete();
+ }
+
+ /**
+ * Wait for the completion of a multiple-block command
+ *
+ * \param r wether the transfer reads from card to host
+ *
+ * \return true on success
+ */
+ bool _wait_for_mbc_complete(bool const r)
+ {
+ /* wait for a signal */
+ _irq.wait_for_irq();
+ Irqstat::access_t const irq = read();
+
+ /*
+ * The ESDHC signals on multi-block transfers seem to be broken.
+ * Synchronizing to Irqstat::Tc before returning from transfer
+ * requests and to Prsstat::Cihb before sending the next command,
+ * as it is done with other SDHCs, isn't sufficient. Instead, both
+ * completion signals must be gathered while dealing with the fact
+ * that the interrupt triggers only on signal edges and we can
+ * therefore not wait for a second IRQ when we've received a
+ * single signal the first time.
+ */
+ Irqstat::access_t constexpr irq_cc_tc =
+ Irq::Cc::reg_mask() | Irq::Tc::reg_mask();
+ switch (irq) {
+ case Irq::Cc::reg_mask():
+ case Irq::Tc::reg_mask():
+
+ /* poll for the missing signal */
+ if (!wait_for(irq_cc_tc, _delayer)) {
+ PERR("completion host signal timed out");
+ return false;
+ }
+ case irq_cc_tc:
+
+ /* acknowledge both completion signals */
+ write(irq_cc_tc);
+
+ /*
+ * The "Auto Command 12" feature of the ESDHC seems to be
+ * broken for multi-block writes as it causes command-
+ * timeout errors sometimes. Thus, we end such transfers
+ * manually.
+ */
+ return r ? true : _abort_transmission();
+ default:
+ PERR("received unexpected host signal");
+ return false;
+ }
+ }
+
+ /**
+ * Prepare multi-block-transfer command with DMA
+ *
+ * \param blk_cnt number of transferred blocks block
+ * \param buf_phys physical base of transfer buffer
+ *
+ * \return true on success
+ */
+ bool _prepare_dma_mbc(size_t blk_cnt, addr_t buf_phys)
+ {
+ size_t const req_size = blk_cnt * BLOCK_SIZE;
+ if (!_adma2_table.setup_req(req_size, buf_phys)) {
+ PERR("Setup ADMA2 table failed");
+ return false;
+ }
+ write(_adma2_table.base_phys());
+ write(BLOCK_SIZE);
+ write(blk_cnt);
+ return true;
+ }
+
+
+ /****************************************
+ ** Sd_card::Host_controller interface **
+ ****************************************/
+
+ Sd_card::Cid _read_cid()
+ {
+ Sd_card::Cid cid;
+ cid.raw_0 = read();
+ cid.raw_1 = read();
+ cid.raw_2 = read();
+ cid.raw_3 = read();
+ return cid;
+ }
+
+ Sd_card::Csd _read_csd()
+ {
+ Sd_card::Csd csd;
+ csd.csd0 = read();
+ csd.csd1 = read();
+ csd.csd2 = read();
+ csd.csd3 = read();
+ return csd;
+ }
+
+ unsigned _read_rca()
+ {
+ Cmdrsp0::access_t const rsp0 = read();
+ return Sd_card::Send_relative_addr::Response::Rca::get(rsp0);
+ }
+
+ public:
+
+ /**
+ * Constructor
+ *
+ * \param mmio_base local base address of MMIO registers
+ * \param irq host-interrupt ID
+ * \param delayer delayer timing of MMIO accesses
+ * \param use_dma wether to use DMA or direct IO for transfers
+ */
+ Esdhcv2_controller(addr_t const mmio_base, unsigned const irq,
+ Delayer & delayer, bool const use_dma)
+ :
+ Esdhcv2(mmio_base), _irq(irq), _delayer(delayer),
+ _card_info(_init()), _use_dma(use_dma)
+ { }
+
+
+ /****************************************
+ ** Sd_card::Host_controller interface **
+ ****************************************/
+
+ bool _issue_command(Sd_card::Command_base const & command)
+ {
+ /* detect if command is a multi-block transfer and if it reads */
+ using namespace Sd_card;
+ bool const r = command.transfer == Sd_card::TRANSFER_READ;
+ bool const mb =
+ command.index == Sd_card::Read_multiple_block::INDEX ||
+ command.index == Sd_card::Write_multiple_block::INDEX;
+
+ /* assemble comsendcode */
+ Xfertyp::access_t cmd = 0;
+ Xfertyp::Cmdinx::set(cmd, command.index);
+ if (command.transfer != Sd_card::TRANSFER_NONE) {
+ Xfertyp::Dpsel::set(cmd);
+ Xfertyp::Bcen::set(cmd);
+ Xfertyp::Msbsel::set(cmd);
+ if (mb) {
+ /*
+ * The "Auto Command 12" feature of the ESDHC seems to be
+ * broken for multi-block writes as it causes command-
+ * timeout errors sometimes. Thus, we end such transfers
+ * manually.
+ */
+ if (r) { Xfertyp::Ac12en::set(cmd); }
+ if (_use_dma) { Xfertyp::Dmaen::set(cmd); }
+ }
+ Xfertyp::Dtdsel::set(cmd,
+ r ? Xfertyp::Dtdsel::READ : Xfertyp::Dtdsel::WRITE);
+ }
+ typedef Xfertyp::Rsptyp Rsptyp;
+ Xfertyp::access_t rt = 0;
+ switch (command.rsp_type) {
+ case RESPONSE_NONE: rt = Rsptyp::_0BIT; break;
+ case RESPONSE_136_BIT: rt = Rsptyp::_136BIT; break;
+ case RESPONSE_48_BIT: rt = Rsptyp::_48BIT; break;
+ case RESPONSE_48_BIT_WITH_BUSY: rt = Rsptyp::_48BIT_BUSY; break;
+ }
+ Xfertyp::Rsptyp::set(cmd, rt);
+
+ /* send command as soon as the host allows it */
+ if (!_wait_for_cmd_allowed()) { return false; }
+ write(command.arg);
+ write(cmd);
+
+ /* wait for completion */
+ return mb ? _wait_for_mbc_complete(r) : _wait_for_cmd_complete();
+ }
+
+ Sd_card::Card_info card_info() const { return _card_info; }
+
+ bool read_blocks(size_t const block_number, size_t const block_count,
+ char * out_buffer_phys)
+ {
+ PERR("block transfer without DMA not supported by now");
+ return false;
+ }
+
+ bool write_blocks(size_t block_number, size_t block_count,
+ char const *buffer_phys)
+ {
+ PERR("block transfer without DMA not supported by now");
+ return false;
+ }
+
+ bool read_blocks_dma(size_t blk_nr, size_t blk_cnt, addr_t buf_phys)
+ {
+ if (!_prepare_dma_mbc(blk_cnt, buf_phys)) { return false; }
+ return issue_command(Sd_card::Read_multiple_block(blk_nr));
+ }
+
+ bool write_blocks_dma(size_t blk_nr, size_t blk_cnt, addr_t buf_phys)
+ {
+ if (!_prepare_dma_mbc(blk_cnt, buf_phys)) { return false; }
+ return issue_command(Sd_card::Write_multiple_block(blk_nr));
+ }
+};
+
+#endif /* _ESDHCV2_H_ */
diff --git a/repos/os/src/drivers/sd_card/imx53/main.cc b/repos/os/src/drivers/sd_card/imx53/main.cc
new file mode 100644
index 0000000000..f9ed3cb501
--- /dev/null
+++ b/repos/os/src/drivers/sd_card/imx53/main.cc
@@ -0,0 +1,57 @@
+/*
+ * \brief SD-card driver
+ * \author Martin Stein
+ * \date 2015-02-04
+ */
+
+/*
+ * Copyright (C) 2012-2015 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU General Public License version 2.
+ */
+
+/* Genode includes */
+#include
+#include
+
+/* local includes */
+#include
+
+
+struct Main
+{
+ Server::Entrypoint &ep;
+
+ struct Factory : Block::Driver_factory
+ {
+ Block::Driver *create() {
+ return new (Genode::env()->heap()) Block::Imx53_driver(true); }
+
+ void destroy(Block::Driver *driver) {
+ Genode::destroy(Genode::env()->heap(),
+ static_cast(driver)); }
+ } factory;
+
+ Block::Root root;
+
+ Main(Server::Entrypoint &ep)
+ : ep(ep), root(ep, Genode::env()->heap(), factory)
+ {
+ Genode::printf("--- Imx53 SD card driver ---\n");
+
+ Genode::env()->parent()->announce(ep.manage(root));
+ }
+};
+
+
+/************
+ ** Server **
+ ************/
+
+namespace Server {
+ char const *name() { return "sd_card_ep"; }
+ size_t stack_size() { return 2*1024*sizeof(long); }
+ void construct(Entrypoint &ep) { static Main server(ep); }
+}
+
diff --git a/repos/os/src/drivers/sd_card/imx53/target.mk b/repos/os/src/drivers/sd_card/imx53/target.mk
new file mode 100644
index 0000000000..dba3e89a4b
--- /dev/null
+++ b/repos/os/src/drivers/sd_card/imx53/target.mk
@@ -0,0 +1,5 @@
+TARGET = sd_card_drv
+REQUIRES = imx53
+SRC_CC = main.cc
+LIBS = base server
+INC_DIR += $(PRG_DIR) $(PRG_DIR)/..
diff --git a/repos/os/src/drivers/sd_card/sd_card.h b/repos/os/src/drivers/sd_card/sd_card.h
index 92f926fc81..3ad6c5fe4a 100644
--- a/repos/os/src/drivers/sd_card/sd_card.h
+++ b/repos/os/src/drivers/sd_card/sd_card.h
@@ -195,6 +195,14 @@ namespace Sd_card {
};
};
+ struct Set_blocklen : Command<16, RESPONSE_48_BIT>
+ {
+ Set_blocklen(size_t blocklen)
+ {
+ arg = blocklen;
+ };
+ };
+
struct Read_multiple_block : Command<18, RESPONSE_48_BIT, TRANSFER_READ>
{
Read_multiple_block(unsigned long addr)