diff --git a/os/src/drivers/sd_card/omap4/main.cc b/os/src/drivers/sd_card/omap4/main.cc
new file mode 100644
index 0000000000..a9323bfccd
--- /dev/null
+++ b/os/src/drivers/sd_card/omap4/main.cc
@@ -0,0 +1,160 @@
+/*
+ * \brief SD-card driver for OMAP4 platform
+ * \author Norman Feske
+ * \date 2012-07-03
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+
+
+namespace Block {
+ using namespace Genode;
+ class Omap4_driver;
+}
+
+
+class Block::Omap4_driver : public Block::Driver
+{
+ private:
+
+ struct Timer_delayer : Timer::Connection, Mmio::Delayer
+ {
+ /**
+ * Implementation of 'Delayer' interface
+ */
+ void usleep(unsigned us)
+ {
+ /* polling */
+ if (us == 0)
+ return;
+
+ unsigned ms = us / 1000;
+ if (ms == 0)
+ ms = 1;
+
+ Timer::Connection::msleep(ms);
+ }
+ } _delayer;
+
+ /* memory map */
+ enum {
+ MMCHS1_MMIO_BASE = 0x4809c000,
+ MMCHS1_MMIO_SIZE = 0x00001000,
+ };
+
+ /* display sub system registers */
+ Attached_io_mem_dataspace _mmchs1_mmio;
+
+ /* hsmmc controller instance */
+ Omap4_hsmmc_controller _controller;
+
+ public:
+
+ Omap4_driver();
+
+
+ /*****************************
+ ** Block::Driver interface **
+ *****************************/
+
+ Genode::size_t block_size() { return 512; }
+
+ virtual Genode::size_t block_count()
+ {
+ return _controller.card_info().capacity_mb() * 1024 * 2;
+ }
+
+ void read(Genode::size_t block_number,
+ Genode::size_t block_count,
+ char *out_buffer)
+ {
+ if (!_controller.read_blocks(block_number, block_count, out_buffer))
+ throw Io_error();
+ }
+
+ void write(Genode::size_t block_number,
+ Genode::size_t block_count,
+ char const *buffer)
+ {
+ if (!_controller.write_blocks(block_number, block_count, buffer))
+ throw Io_error();
+ }
+
+ void read_dma(Genode::size_t block_number,
+ Genode::size_t block_count,
+ Genode::addr_t phys)
+ {
+ throw Io_error();
+ }
+
+ void write_dma(Genode::size_t block_number,
+ Genode::size_t block_count,
+ Genode::addr_t phys)
+ {
+ throw Io_error();
+ }
+
+ bool dma_enabled() { return false; }
+};
+
+
+Block::Omap4_driver::Omap4_driver()
+:
+ _mmchs1_mmio(MMCHS1_MMIO_BASE, MMCHS1_MMIO_SIZE),
+ _controller((addr_t)_mmchs1_mmio.local_addr(), _delayer)
+{
+ Sd_card::Card_info const card_info = _controller.card_info();
+
+ PLOG("SD card detected");
+ PLOG("capacity: %zd MiB", card_info.capacity_mb());
+}
+
+
+/*
+ * MMC1: IRQ 83
+ */
+
+int main(int argc, char **argv)
+{
+ using namespace Genode;
+
+ printf("--- OMAP4 SD card driver ---\n");
+
+ /**
+ * Factory used by 'Block::Root' at session creation/destruction time
+ */
+ struct Driver_factory : Block::Driver_factory
+ {
+ Block::Driver *create()
+ {
+ return new (env()->heap()) Block::Omap4_driver();
+ }
+
+ void destroy(Block::Driver *driver)
+ {
+ Genode::destroy(env()->heap(),
+ static_cast(driver));
+ }
+
+ } driver_factory;
+
+ enum { STACK_SIZE = 4096 };
+ static Cap_connection cap;
+ static Rpc_entrypoint ep(&cap, STACK_SIZE, "block_ep");
+
+ static Block::Root block_root(&ep, env()->heap(), driver_factory);
+ env()->parent()->announce(ep.manage(&block_root));
+
+ sleep_forever();
+ return 0;
+}
diff --git a/os/src/drivers/sd_card/omap4/mmchs.h b/os/src/drivers/sd_card/omap4/mmchs.h
new file mode 100644
index 0000000000..91cc6faefa
--- /dev/null
+++ b/os/src/drivers/sd_card/omap4/mmchs.h
@@ -0,0 +1,739 @@
+/*
+ * \brief OMAP4 MMCHS controller registers
+ * \author Norman Feske
+ * \date 2012-07-03
+ */
+
+#ifndef _MMCHS_H_
+#define _MMCHS_H_
+
+/* Genode includes */
+#include
+
+/* local includes */
+#include
+
+struct Mmchs : Genode::Mmio
+{
+ enum { verbose = false };
+
+ typedef Genode::size_t size_t;
+
+ Mmchs(Genode::addr_t const mmio_base) : Genode::Mmio(mmio_base) { }
+
+ struct Sysconfig : Register<0x110, 32>
+ {
+ struct Autoidle : Bitfield<0, 1> { };
+ struct Softreset : Bitfield<1, 1> { };
+ struct Sidlemode : Bitfield<3, 2>
+ {
+ enum { NO_IDLE = 1 };
+ };
+ struct Clockactivity : Bitfield<8, 2>
+ {
+ enum { BOTH_ACTIVE = 3 };
+ };
+ };
+
+ struct Sysstatus : Register<0x114, 32>
+ {
+ struct Reset_done : Bitfield<0, 1> { };
+ };
+
+ struct Con : Register<0x12c, 32>
+ {
+ /**
+ * Open drain mode
+ */
+ struct Od : Bitfield<0, 1> { };
+
+ /**
+ * Start initialization stream
+ */
+ struct Init : Bitfield<1, 1> { };
+
+ struct Dw8 : Bitfield<5, 1> { };
+
+ };
+
+ /**
+ * Command register
+ */
+ struct Cmd : Register<0x20c, 32>
+ {
+ struct Index : Bitfield<24, 6> { };
+
+ /**
+ * Data present
+ */
+ 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 };
+ };
+
+ /**
+ * Data direction
+ */
+ struct Ddir : Bitfield<4, 1>
+ {
+ enum { WRITE = 0, READ = 1 };
+ };
+
+ /**
+ * Block count enable
+ */
+ struct Bce : Bitfield<1, 1> { };
+
+ /**
+ * Multiple block select
+ */
+ struct Msbs : Bitfield<5, 1> { };
+
+ /**
+ * Auto-CMD12 enable
+ */
+ struct Acen : Bitfield<2, 1> { };
+ };
+
+ /**
+ * Transfer length configuration
+ */
+ struct Blk : Register<0x204, 32>
+ {
+ struct Blen : Bitfield<0, 12> { };
+ struct Nblk : Bitfield<16, 16> { };
+ };
+
+ /**
+ * Command argument
+ */
+ struct Arg : Register<0x208, 32> { };
+
+ /**
+ * Response bits 0..31
+ */
+ struct Rsp10 : Register<0x210, 32> { };
+
+ /**
+ * Response bits 32..63
+ */
+ struct Rsp32 : Register<0x214, 32> { };
+
+ /**
+ * Response bits 64..95
+ */
+ struct Rsp54 : Register<0x218, 32> { };
+
+ /**
+ * Response bits 96..127
+ */
+ struct Rsp76 : Register<0x21c, 32> { };
+
+ struct Data : Register<0x220, 32> { };
+
+ struct Pstate : Register<0x224, 32>
+ {
+ /**
+ * Command inhibit
+ */
+ struct Cmdi : Bitfield<0, 1> { };
+
+ /**
+ * Buffer write enable status
+ */
+ struct Bwe : Bitfield<10, 1> { };
+
+ /**
+ * Buffer read enable status
+ */
+ struct Bre : Bitfield<11, 1> { };
+ };
+
+ struct Hctl : Register<0x228, 32>
+ {
+ /**
+ * Wakeup event enable
+ */
+ struct Iwe : Bitfield<24, 1> { };
+
+ /**
+ * SD bus power
+ */
+ struct Sdbp : Bitfield<8, 1>
+ {
+ enum { POWER_OFF = 0, POWER_ON = 1 };
+ };
+
+ /**
+ * SD voltage select
+ */
+ struct Sdvs : Bitfield<9, 3>
+ {
+ enum Voltage { VOLTAGE_1_8 = 5,
+ VOLTAGE_3_0 = 6,
+ VOLTAGE_3_3 = 7 };
+ };
+
+ /**
+ * Data transfer width
+ */
+ struct Dtw : Bitfield<1, 1>
+ {
+ enum { ONE_BIT = 0, FOUR_BITS = 1 };
+ };
+ };
+
+ struct Sysctl : Register<0x22c, 32>
+ {
+ /**
+ * Internal clock enable
+ */
+ struct Ice : Bitfield<0, 1> { };
+
+ /**
+ * Internal clock stable
+ */
+ struct Ics : Bitfield<1, 1> { };
+
+ /**
+ * Clock enable
+ */
+ struct Ce : Bitfield<2, 1> { };
+
+ /**
+ * Clock divider
+ */
+ struct Clkd : Bitfield<6, 10> { };
+
+ /**
+ * Software reset all
+ */
+ struct Sra : Bitfield<24, 1> { };
+
+ /**
+ * Software reset for command line
+ */
+ struct Src : Bitfield<25, 1> { };
+
+ /**
+ * Data timeout counter
+ */
+ struct Dto : Bitfield<16, 4>
+ {
+ enum { TCF_2_POW_27 = 0xe };
+ };
+ };
+
+ struct Stat : Register<0x230, 32>
+ {
+ /**
+ * Transfer completed
+ */
+ struct Tc : Bitfield<1, 1> { };
+
+ /**
+ * Command completed
+ */
+ struct Cc : Bitfield<0, 1> { };
+
+ /**
+ * Error
+ */
+ struct Erri : Bitfield<15, 1> { };
+
+ /**
+ * Command timed out
+ */
+ struct Cto : Bitfield<16, 1> { };
+ };
+
+ /**
+ * Interrupt enable
+ */
+ struct Ie : Register<0x234, 32>
+ {
+ /**
+ * Command completed
+ */
+ struct Cc_enable : Bitfield<0, 1> { };
+
+ /**
+ * Card interrupt
+ */
+ struct Cirq_enable : Bitfield<8, 1> { };
+
+ /**
+ * Command timeout error
+ */
+ struct Cto_enable : Bitfield<16, 1> { };
+ };
+
+ struct Ise : Register<0x238, 32> { };
+
+ /**
+ * Capabilities
+ */
+ struct Capa : Register<0x240, 32>
+ {
+ struct Vs30 : Bitfield<25, 1> { };
+ struct Vs18 : Bitfield<26, 1> { };
+ };
+
+ bool reset_cmd_line(Delayer &delayer)
+ {
+ 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.
+ */
+ if (!wait_for(1, delayer, 1000, 0)) {
+ PERR("reset of cmd line timed out (src != 1)");
+ return false;
+ }
+
+ if (!wait_for(0, delayer, 1000, 0)) {
+ PERR("reset of cmd line timed out (src != 0)");
+ return false;
+ }
+
+ return true;
+ }
+
+ /* XXX unused */
+ bool soft_reset_all(Delayer &delayer)
+ {
+ write(1);
+
+ if (!wait_for(1, delayer, 1000, 0)) {
+ PERR("soft reset all timed out (src != 1)");
+ return false;
+ }
+
+ return true;
+ }
+
+ void disable_irq()
+ {
+ write(0);
+ write(0);
+ write(~0);
+ }
+
+ enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_8 };
+
+ void bus_width(Bus_width bus_width)
+ {
+ switch (bus_width) {
+ case BUS_WIDTH_1:
+ write(0);
+ write(Hctl::Dtw::ONE_BIT);
+ break;
+
+ case BUS_WIDTH_8:
+ write(1);
+ break;
+ }
+ }
+
+ bool sd_bus_power_on(Delayer &delayer)
+ {
+ write(Hctl::Sdbp::POWER_ON);
+
+ if (!wait_for(1, delayer)) {
+ PERR("setting Hctl::Sdbp timed out");
+ return false;
+ }
+ return true;
+ }
+
+ void stop_clock()
+ {
+ write(0);
+ }
+
+ enum Clock_divider { CLOCK_DIV_0, CLOCK_DIV_240 };
+
+ bool set_and_enable_clock(enum Clock_divider divider, Delayer &delayer)
+ {
+ write(Sysctl::Dto::TCF_2_POW_27);
+
+ switch (divider) {
+ case CLOCK_DIV_0: write(0); break;
+ case CLOCK_DIV_240: write(240); break;
+ }
+
+ write(1);
+
+ /* wait for clock to become stable */
+ if (!wait_for(1, delayer)) {
+ PERR("clock enable timed out");
+ return false;
+ }
+
+ /* enable clock */
+ write(1);
+
+ return true;
+ }
+
+ enum Voltage { VOLTAGE_3_0, VOLTAGE_1_8 };
+
+ void set_bus_power(Voltage voltage)
+ {
+ switch (voltage) {
+ case VOLTAGE_3_0:
+ write(Hctl::Sdvs::VOLTAGE_3_0);
+ break;
+
+ case VOLTAGE_1_8:
+ write(Hctl::Sdvs::VOLTAGE_1_8);
+ break;
+ }
+
+ write(1);
+
+ if (voltage == VOLTAGE_3_0)
+ write(1);
+ }
+
+ bool init_stream(Delayer &delayer)
+ {
+ write(0x307f0033);
+
+ /* start initialization sequence */
+ write(1);
+
+ write(0);
+
+ if (!wait_for(1, delayer, 1000*1000, 0)) {
+ PERR("init stream timed out");
+ return false;
+ }
+
+ /* stop initialization sequence */
+ write(0);
+
+ write(~0);
+ read();
+
+ return true;
+ }
+
+ struct Hl_sysconfig : Register<0x10, 32> { };
+};
+
+
+struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
+{
+ private:
+
+ Delayer &_delayer;
+ Sd_card::Card_info _card_info;
+
+ Sd_card::Card_info _init()
+ {
+ using namespace Sd_card;
+
+ write(0x2015);
+ write(0x0);
+
+ set_bus_power(VOLTAGE_3_0);
+
+ if (!sd_bus_power_on(_delayer)) {
+ PERR("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, _delayer)) {
+ PERR("set_clock failed");
+ throw Detection_failed();
+ }
+
+ if (!init_stream(_delayer)) {
+ PERR("sending the initialization stream failed");
+ throw Detection_failed();
+ }
+
+ write(0);
+
+ _delayer.usleep(1000);
+
+ if (!issue_command(Go_idle_state())) {
+ PWRN("Go_idle_state command failed");
+ throw Detection_failed();
+ }
+
+ _delayer.usleep(2000);
+
+ if (!issue_command(Send_if_cond())) {
+ PWRN("Send_if_cond command failed");
+ throw Detection_failed();
+ }
+
+ if (read() != 0x1aa) {
+ PERR("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))) {
+ PWRN("Sd_send_op_cond command failed");
+ throw Detection_failed();
+ }
+
+ if (Ocr::Busy::get(read()))
+ break;
+
+ _delayer.usleep(1000);
+ }
+
+ if (i == 0) {
+ PERR("Sd_send_op_cond timed out, could no power on SD card");
+ throw Detection_failed();
+ }
+
+ Card_info card_info = _detect();
+
+ write(0);
+
+ return card_info;
+ }
+
+ public:
+
+ /**
+ * Constructor
+ *
+ * \param mmio_base local base address of MMIO registers
+ */
+ Omap4_hsmmc_controller(Genode::addr_t const mmio_base, Delayer &delayer)
+ :
+ Mmchs(mmio_base), _delayer(delayer), _card_info(_init())
+ { }
+
+
+ /****************************************
+ ** Sd_card::Host_controller interface **
+ ****************************************/
+
+ bool _issue_command(Sd_card::Command_base const &command)
+ {
+ if (verbose)
+ PLOG("-> index=0x%08x, arg=0x%08x, rsp_type=%d",
+ command.index, command.arg, command.rsp_type);
+
+ if (!wait_for(0, _delayer)) {
+ PERR("wait for Pstate::Cmdi timed out");
+ return false;
+ }
+
+ /* write command argument */
+ write(command.arg);
+
+ /* assemble command register */
+ Cmd::access_t cmd = 0;
+ Cmd::Index::set(cmd, command.index);
+ if (command.index == Sd_card::Read_multiple_block::INDEX
+ || command.index == Sd_card::Write_multiple_block::INDEX) {
+
+ Cmd::Dp::set(cmd);
+ Cmd::Bce::set(cmd);
+ Cmd::Msbs::set(cmd);
+ Cmd::Acen::set(cmd);
+
+ /* set data-direction bit depending on the command */
+ bool const read = command.index == Sd_card::Read_multiple_block::INDEX;
+ Cmd::Ddir::set(cmd, read ? Cmd::Ddir::READ : Cmd::Ddir::WRITE);
+ }
+
+ Cmd::access_t rsp_type = 0;
+ switch (command.rsp_type) {
+ case Sd_card::RESPONSE_NONE: rsp_type = Cmd::Rsp_type::RESPONSE_NONE; break;
+ case Sd_card::RESPONSE_136_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_136_BIT; break;
+ case Sd_card::RESPONSE_48_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_48_BIT; break;
+ case Sd_card::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 */
+ write(cmd);
+
+ bool result = false;
+
+ /* wait until command is completed, return false on timeout */
+ for (unsigned long i; i < 1000*1000; i++) {
+ Stat::access_t const stat = read();
+ if (Stat::Erri::get(stat)) {
+ PWRN("SD command error");
+ if (Stat::Cto::get(stat))
+ PWRN("timeout");
+
+ reset_cmd_line(_delayer);
+ write(~0);
+ read();
+ result = false;
+ break;
+ }
+ if (Stat::Cc::get(stat) == 1) {
+ result = true;
+ break;
+ }
+ }
+
+ if (verbose)
+ PLOG("<- %s", result ? "succeeded" : "timed out");
+
+ /* clear status of command-completed bit */
+ write(1);
+ read();
+
+ return result;
+ }
+
+ Sd_card::Card_info card_info() const
+ {
+ return _card_info;
+ }
+
+ 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()
+ {
+ return Sd_card::Send_relative_addr::Response::Rca::get(read());
+ }
+
+ bool _wait_for_transfer_complete()
+ {
+ if (!wait_for(1, _delayer, 1000*1000, 0)
+ && !wait_for(1, _delayer)) {
+ PERR("Stat::Tc timed out");
+ return false;
+ }
+
+ /* clear transfer-completed bit */
+ write(1);
+ return true;
+ }
+
+ bool _wait_for_bre()
+ {
+ if (!wait_for(1, _delayer, 1000*1000, 0)
+ && !wait_for(1, _delayer)) {
+ PERR("Pstate::Bre timed out");
+ return false;
+ }
+ return true;
+ }
+
+ bool _wait_for_bwe()
+ {
+ if (!wait_for(1, _delayer, 1000*1000, 0)
+ && !wait_for(1, _delayer)) {
+ PERR("Pstate::Bwe timed out");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Read data blocks from SD card
+ *
+ * \return true on success
+ */
+ bool read_blocks(size_t block_number, size_t block_count, char *out_buffer)
+ {
+ using namespace Sd_card;
+
+ write(0x200);
+ write(block_count);
+
+ if (!issue_command(Read_multiple_block(block_number))) {
+ PERR("Read_multiple_block failed");
+ return false;
+ }
+
+ size_t const num_accesses = block_count*512/sizeof(Data::access_t);
+ Data::access_t *dst = (Data::access_t *)(out_buffer);
+
+ for (size_t i = 0; i < num_accesses; i++) {
+ if (!_wait_for_bre())
+ return false;
+
+ *dst++ = read();
+ }
+
+ return _wait_for_transfer_complete();
+ }
+
+ /**
+ * Write data blocks to SD card
+ *
+ * \return true on success
+ */
+ bool write_blocks(size_t block_number, size_t block_count, char const *buffer)
+ {
+ using namespace Sd_card;
+
+ write(0x200);
+ write(block_count);
+
+ if (!issue_command(Write_multiple_block(block_number))) {
+ PERR("Write_multiple_block failed");
+ return false;
+ }
+
+ size_t const num_accesses = block_count*512/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())
+ return false;
+
+ write(*src++);
+ }
+
+ return _wait_for_transfer_complete();
+ }
+};
+
+#endif /* _MMCHS_H_ */
diff --git a/os/src/drivers/sd_card/omap4/sd_card.h b/os/src/drivers/sd_card/omap4/sd_card.h
new file mode 100644
index 0000000000..b370f34ec4
--- /dev/null
+++ b/os/src/drivers/sd_card/omap4/sd_card.h
@@ -0,0 +1,336 @@
+/*
+ * \brief SD card protocol definitions
+ * \author Norman Feske
+ * \date 2012-07-06
+ */
+
+#ifndef _SD_CARD_H_
+#define _SD_CARD_H_
+
+/* Genode includes */
+#include
+
+namespace Sd_card {
+
+ using namespace Genode;
+
+ /**
+ * Returned by 'Sd_send_op_cond'
+ */
+ struct Ocr : Register<32>
+ {
+ struct Busy : Bitfield<31, 1> { };
+ };
+
+ struct Cid {
+ uint32_t raw_0;
+ uint32_t raw_1;
+ uint32_t raw_2;
+ uint32_t raw_3;
+ };
+
+ struct Csd0 : Register<32>
+ {
+ };
+
+ struct Csd1 : Register<32>
+ {
+ enum { BIT_BASE = 1*sizeof(access_t)*8 };
+
+ struct Device_size_lo : Bitfield<48 - BIT_BASE, 16> { };
+ };
+
+ struct Csd2 : Register<32>
+ {
+ enum { BIT_BASE = 2*sizeof(access_t)*8 };
+
+ struct Device_size_hi : Bitfield<64 - BIT_BASE, 6> { };
+ };
+
+ struct Csd3 : Register<32>
+ {
+ enum { BIT_BASE = 3*sizeof(access_t)*8 };
+
+ struct Version : Bitfield<126 - BIT_BASE, 2>
+ {
+ enum { HIGH_CAPACITY = 1 };
+ };
+ };
+
+ struct Csd
+ {
+ Csd0::access_t csd0;
+ Csd1::access_t csd1;
+ Csd2::access_t csd2;
+ Csd3::access_t csd3;
+ };
+
+ struct Arg : Register<32> { };
+
+ enum Response { RESPONSE_NONE,
+ RESPONSE_136_BIT,
+ RESPONSE_48_BIT,
+ RESPONSE_48_BIT_WITH_BUSY };
+
+ struct Command_base
+ {
+ unsigned index; /* command opcode */
+ Arg::access_t arg; /* argument */
+ Response rsp_type; /* response type */
+
+ Command_base(unsigned op, Response rsp_type)
+ :
+ index(op), arg(0), rsp_type(rsp_type)
+ { }
+ };
+
+ template
+ struct Command : Command_base
+ {
+ enum { INDEX = _INDEX };
+ Command() : Command_base(_INDEX, RSP_TYPE) { }
+ };
+
+ template
+ struct Prefixed_command : private Command_base
+ {
+ Prefixed_command() : Command_base(INDEX, RSP_TYPE) { }
+
+ using Command_base::arg;
+
+ /**
+ * Used by ACMD overload of 'issue_command()'
+ */
+ Command_base const &base() const { return *this; }
+ };
+
+ struct Go_idle_state : Command<0, RESPONSE_NONE> { };
+
+ struct All_send_cid : Command<2, RESPONSE_136_BIT> { };
+
+ struct Send_relative_addr : Command<3, RESPONSE_48_BIT>
+ {
+ struct Response : Sd_card::Arg
+ {
+ struct Rca : Bitfield<16, 16> { };
+ };
+ };
+
+ struct Select_card : Command<7, RESPONSE_48_BIT>
+ {
+ struct Arg : Sd_card::Arg
+ {
+ struct Rca : Bitfield<16, 16> { };
+ };
+
+ Select_card(unsigned rca)
+ {
+ Arg::Rca::set(arg, rca);
+ }
+ };
+
+ struct Send_if_cond : Command<8, RESPONSE_48_BIT>
+ {
+ struct Arg : Sd_card::Arg
+ {
+ struct Check_pattern : Bitfield<0, 8> { };
+ struct Supply_voltage : Bitfield<8, 4> { };
+ };
+
+ Send_if_cond()
+ {
+ Arg::Check_pattern::set(arg, 0xaa);
+ Arg::Supply_voltage::set(arg, 1);
+ }
+ };
+
+ struct Send_csd : Command<9, RESPONSE_136_BIT>
+ {
+ struct Arg : Sd_card::Arg
+ {
+ struct Rca : Bitfield<16, 16> { };
+ };
+
+ Send_csd(unsigned rca)
+ {
+ Arg::Rca::set(arg, rca);
+ }
+ };
+
+ struct Set_block_count : Command<23, RESPONSE_48_BIT>
+ {
+ Set_block_count(size_t count)
+ {
+ arg = count;
+ };
+ };
+
+ struct Read_multiple_block : Command<18, RESPONSE_48_BIT>
+ {
+ Read_multiple_block(unsigned long addr)
+ {
+ arg = addr;
+ }
+ };
+
+ struct Write_multiple_block : Command<25, RESPONSE_48_BIT>
+ {
+ Write_multiple_block(unsigned long addr)
+ {
+ arg = addr;
+ }
+ };
+
+ struct Sd_send_op_cond : Prefixed_command<41, RESPONSE_48_BIT>
+ {
+ struct Arg : Sd_card::Arg
+ {
+ /**
+ * Operating condition register
+ */
+ struct Ocr : Bitfield<0, 24> { };
+
+ /**
+ * Host capacity support
+ */
+ struct Hcs : Bitfield<30, 1> { };
+ };
+
+ Sd_send_op_cond(unsigned ocr, bool hcs)
+ {
+ Arg::Ocr::set(arg, ocr);
+ Arg::Hcs::set(arg, hcs);
+ }
+ };
+
+ struct Acmd_prefix : Command<55, RESPONSE_48_BIT> { };
+
+
+ class Card_info
+ {
+ private:
+
+ unsigned _rca;
+ size_t _capacity_mb;
+
+ public:
+
+ Card_info(unsigned rca, size_t capacity_mb)
+ : _rca(rca), _capacity_mb(capacity_mb)
+ { }
+
+ /**
+ * Return capacity in megabytes
+ */
+ size_t capacity_mb() const { return _capacity_mb; }
+
+ /**
+ * Return relative card address
+ */
+ unsigned rca() const { return _rca; }
+ };
+
+
+ /**
+ * SD card host controller
+ */
+ class Host_controller
+ {
+ public:
+
+ /**
+ * Exception type
+ */
+ struct Detection_failed { };
+
+ protected:
+
+ virtual bool _issue_command(Command_base const &command) = 0;
+
+ virtual Cid _read_cid() = 0;
+
+ virtual Csd _read_csd() = 0;
+
+ virtual unsigned _read_rca() = 0;
+
+ public:
+
+ virtual Card_info card_info() const = 0;
+
+ bool issue_command(Command_base const &command)
+ {
+ return _issue_command(command);
+ }
+
+ /**
+ * Issue application-specific command
+ *
+ * This overload is selected if the supplied command type has
+ * 'Prefixed_command' as its base class. In this case, we need to
+ * issue a CMD55 as command prefix followed by the actual command.
+ */
+ template
+ bool issue_command(Prefixed_command const &command)
+ {
+ /* send CMD55 prefix */
+ if (!_issue_command(Acmd_prefix())) {
+ PERR("prefix command timed out");
+ return false;
+ }
+
+ /* send actual command */
+ return _issue_command(command.base());
+ }
+
+ protected:
+
+ /**
+ * Perform SD card detection sequence
+ *
+ * \throw Detection_failed
+ */
+ Card_info _detect()
+ {
+ if (!issue_command(All_send_cid())) {
+ PWRN("All_send_cid command failed");
+ throw Detection_failed();
+ }
+
+ Cid const cid = _read_cid();
+ PLOG("CID: 0x%08x 0x%08x 0x%08x 0x%08x",
+ cid.raw_0, cid.raw_1, cid.raw_2, cid.raw_3);
+
+ if (!issue_command(Send_relative_addr())) {
+ PERR("Send_relative_addr timed out");
+ throw Detection_failed();
+ }
+
+ unsigned const rca = _read_rca();
+ PLOG("RCA: 0x%04x", rca);
+
+ if (!issue_command(Send_csd(rca))) {
+ PERR("Send_csd failed");
+ throw Detection_failed();
+ }
+
+ Csd const csd = _read_csd();
+
+ if (Csd3::Version::get(csd.csd3) != Csd3::Version::HIGH_CAPACITY) {
+ PERR("Could not detect high-capacity card");
+ throw Detection_failed();
+ }
+
+ size_t const device_size = ((Csd2::Device_size_hi::get(csd.csd2) << 16)
+ | Csd1::Device_size_lo::get(csd.csd1)) + 1;
+
+ if (!issue_command(Select_card(rca))) {
+ PERR("Select_card failed");
+ throw Detection_failed();
+ }
+
+ return Card_info(rca, device_size / 2);
+ }
+ };
+}
+
+#endif /* _SD_CARD_H_ */
diff --git a/os/src/drivers/sd_card/omap4/target.mk b/os/src/drivers/sd_card/omap4/target.mk
new file mode 100644
index 0000000000..2bc25cc626
--- /dev/null
+++ b/os/src/drivers/sd_card/omap4/target.mk
@@ -0,0 +1,5 @@
+TARGET = sd_card_drv
+REQUIRES = omap4
+SRC_CC = main.cc
+LIBS = cxx env server signal
+INC_DIR += $(PRG_DIR)