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)