sd_card: Exynos5/MMC support

Added MMC specific parts to Sd-card implemntation. Read/Write takes advantage of
DMA. Currently we leave card in one bit mode.
This commit is contained in:
Sebastian Sumpf 2013-03-22 15:19:09 +01:00 committed by Norman Feske
parent 95a16adb6f
commit a3afb3dae4
6 changed files with 931 additions and 2 deletions

View File

@ -5,7 +5,7 @@
#
# denote specs that are fullfilled by this spec
SPECS += cortex_a15
SPECS += exynos5 cortex_a15
# add repository relative paths
REP_INC_DIR += include/platform/arndale \

View File

@ -0,0 +1,123 @@
/*
* \brief Exynos5-specific implementation of the Block::Driver interface
* \author Sebastian Sumpf
* \date 2013-03-22
*/
#ifndef _DRIVER_H_
#define _DRIVER_H_
#include <util/mmio.h>
#include <os/attached_io_mem_dataspace.h>
#include <base/printf.h>
#include <timer_session/connection.h>
#include <block/component.h>
/* local includes */
#include <dwmmc.h>
namespace Block {
using namespace Genode;
class Exynos5_driver;
}
class Block::Exynos5_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;
Timer::Connection::usleep(us);
}
} _delayer;
enum {
MSH_BASE = 0x12200000, /* host controller base for eMMC */
MSH_SIZE = 0x10000,
};
/* display sub system registers */
Attached_io_mem_dataspace _mmio;
/* mobile storage host controller instance */
Exynos5_msh_controller _controller;
bool const _use_dma;
public:
Exynos5_driver(bool use_dma)
:
_mmio(MSH_BASE, MSH_SIZE),
_controller((addr_t)_mmio.local_addr<void>(),
_delayer, use_dma),
_use_dma(use_dma)
{
Sd_card::Card_info const card_info = _controller.card_info();
PLOG("SD/MMC card detected");
PLOG("capacity: %zd MiB", card_info.capacity_mb());
}
/*****************************
** 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)
{
if (!_controller.read_blocks_dma(block_number, block_count, phys))
throw Io_error();
}
void write_dma(Genode::size_t block_number,
Genode::size_t block_count,
Genode::addr_t phys)
{
if (!_controller.write_blocks_dma(block_number, block_count, phys))
throw Io_error();
}
bool dma_enabled() { return _use_dma; }
Ram_dataspace_capability alloc_dma_buffer(size_t size) {
return Genode::env()->ram_session()->alloc(size, false); }
};
#endif /* _DRIVER_H_ */

View File

@ -0,0 +1,660 @@
/*
* \brief DesignWare Multimedia Card interface
* \author Sebastian Sumpf
* \date 2013-03-06
*/
#ifndef _DWMMC_H_
#define _DWMMC_H_
#include <irq_session/connection.h>
#include <os/attached_ram_dataspace.h>
#include <util/mmio.h>
#include <sd_card.h>
struct Dwmmc : Genode::Mmio
{
enum { verbose = false };
/*
* These apply to card controller 0 and 1 only
*/
enum {
HOST_FREQ = 52000000, /* Hz */
CLK_FREQ = 400000000, /* Hz */
/* CLK_FREQ / (2 * CLK_DIV <= HOST_FREQ) */
CLK_DIV_52Mhz = 4,
CLK_DIV_400Khz = 0xff,
};
Dwmmc(Genode::addr_t base) : Genode::Mmio(base) { }
template <Genode::off_t OFFSET, bool STRICT_WRITE = false>
struct Register : Genode::Mmio::Register<OFFSET, 32, STRICT_WRITE> { };
/**
* Control register
*/
struct Ctrl : Register<0x0>
{
/* Controller/FIFO/DMA reset */
struct Reset : Bitfield<0, 3> { };
struct Global_interrupt : Bitfield<4, 1> { };
struct Dma_enable : Bitfield<5, 1> { };
struct Use_internal_dmac : Bitfield<25, 1> { };
};
/**
* Power-enable register
*/
struct Pwren : Register<0x4> { };
/**
* Clock-devider register
*/
struct Clkdiv : Register<0x8> { };
/**
* Clock-enable register
*/
struct Clkena : Register<0x10> { };
/**
* Timeout register
*/
struct Tmout : Register<0x14> { };
/**
* Card-type register
*/
struct Ctype : Register<0x18, true> { };
/**
* Block-size register
*/
struct Blksize : Register<0x1c> { };
/**
* Byte-count register
*/
struct Bytcnt : Register<0x20> { };
/**
* Interrupt-mask register
*/
struct Intmask : Register<0x24> { };
/**
* Command-argument register
*/
struct Cmdarg : Register<0x28> { };
/**
* Command register
*/
struct Cmd : Register<0x2c>
{
struct Index : Bitfield<0, 6> { };
struct Rsp_type : Bitfield<6, 3>
{
enum Response { RESPONSE_NONE = 0,
RESPONSE_48_BIT = 1,
RESPONSE_48_BIT_WITH_BUSY = 5,
RESPONSE_136_BIT = 7,
};
};
struct Data_expected : Bitfield<9, 1> { };
struct Write : Bitfield<10, 1> { };
struct Wait_prvdata_complete : Bitfield<13, 1> { };
struct Init_sequence : Bitfield<15, 1> { };
struct Update_clock_registers_only : Bitfield<21, 1> { };
struct Use_hold_reg : Bitfield<29, 1> { };
struct Start_cmd : Bitfield<31, 1> { };
};
/**
* Response bits 0..127
*/
struct Rsp0 : Register<0x30> { };
struct Rsp1 : Register<0x34> { };
struct Rsp2 : Register<0x38> { };
struct Rsp3 : Register<0x3c> { };
/**
* Interrupt-status register
*/
struct Mintsts : Register<0x40> { };
struct Rintsts : Register<0x44, true>
{
struct Response_error : Bitfield<1, 1> { };
struct Data_transfer_over : Bitfield<3, 1> { };
struct Command_done : Bitfield<2, 1> { };
struct Data_crc_error : Bitfield<7, 1> { };
struct Response_timeout : Bitfield<8, 1> { };
struct Data_read_timeout : Bitfield<9, 1> { };
};
/**
* Status register
*/
struct Status : Register<0x48>
{
struct Data_busy : Bitfield<9, 1> { };
};
/**
* Fifo-threshold register
*/
struct Fifoth : Register<0x4c> { };
/**
* Bus-mode register
*/
struct Bmod : Register<0x80, true>
{
struct Fixed_burst : Bitfield<1, 1> { };
struct Idmac_enable : Bitfield<7, 1> { };
};
struct Idsts : Register<0x8c> { };
struct Idinten : Register<0x90, true> { };
/**
* Descriptor list base-address register
*/
struct Dbaddr : Register<0x88> { };
/**
* Clock selector
*/
struct Clksel : Register<0x9c> { };
struct Emmc_ddr_req : Register<0x10c, true>
{
};
typedef Genode::size_t size_t;
void powerup()
{
write<Pwren>(1);
}
bool reset(Delayer &delayer)
{
/* set all three bits */
write<Ctrl::Reset>(0x7);
if (!wait_for<Ctrl::Reset>(0, delayer, 100, 1000)) {
PERR("Could not reset host contoller");
return false;
}
return true;
}
void reset_fifo(Delayer &delayer)
{
write<Ctrl::Reset>(0x2);
if (!wait_for<Ctrl::Reset>(0, delayer, 100, 1000))
PERR("Could not reset fifo");
}
void disable_irq()
{
write<Rintsts>(~0U);
write<Intmask>(0);
}
enum Bus_width {
BUS_WIDTH_1 = 0,
BUS_WIDTH_4 = 1,
BUS_WIDTH_8 = 1 << 16,
};
void bus_width(Bus_width bus_width)
{
write<Ctype>(bus_width);
}
bool update_clock_registers(Delayer &delayer)
{
Cmd::access_t cmd = 0;
Cmd::Wait_prvdata_complete::set(cmd, 1);
Cmd::Update_clock_registers_only::set(cmd, 1);
Cmd::Start_cmd::set(cmd, 1);
write<Cmd>(cmd);
if (!wait_for<Cmd::Start_cmd>(0, delayer)) {
PERR("Update clock registers failed");
return false;
}
return true;
}
bool setup_bus(unsigned clock_div, Delayer &delayer)
{
/* set host clock divider */
write<Clkdiv>(clock_div);
if (!update_clock_registers(delayer))
return false;
/* enable clock for card 1 */
write<Clkena>(0x1);
if (!update_clock_registers(delayer))
return false;
delayer.usleep(10 * 1000);
return true;
}
};
struct Exynos5_msh_controller : private Dwmmc, Sd_card::Host_controller
{
private:
enum { BLOCK_SIZE = 512 };
/**
* DMA descriptpor
*/
struct Idmac_desc
{
enum Flags {
NONE = 0,
LD = 1 << 2,
FS = 1 << 3,
CH = 1 << 4,
OWN = 1 << 31,
};
unsigned flags;
unsigned bytes;
unsigned addr;
unsigned next;
size_t set(size_t block_count, Genode::addr_t phys_addr, Flags flag)
{
flags = OWN | CH | flag | (block_count <= 8 ? LD : 0);
bytes = ((block_count < 8) ? block_count : 8) * BLOCK_SIZE;
addr = phys_addr;
next = (unsigned)&next + sizeof(unsigned);
return block_count < 8 ? 0 : block_count - 8;
}
};
/*
* DMA descriptors
*/
enum { IDMAC_DESC_MAX_ENTRIES = 1024 /* can be up to 65536 */};
Genode::Attached_ram_dataspace _idmac_desc_ds;
Idmac_desc * const _idmac_desc;
Genode::addr_t const _idmac_desc_phys;
Delayer &_delayer;
Sd_card::Card_info _card_info;
Genode::Irq_connection _irq;
Sd_card::Card_info _init()
{
using namespace Sd_card;
powerup();
if (!reset(_delayer))
throw Detection_failed();
write<Emmc_ddr_req>(0x1);
disable_irq();
write<Tmout>(~0U);
write<Idinten>(0);
write<Bmod>(1);
write<Bytcnt>(0);
write<Fifoth>(0x203f0040);
/* set to one bit transfer Bit */
if (!setup_bus(CLK_DIV_400Khz, _delayer))
throw Detection_failed();
bus_width(BUS_WIDTH_1);
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 this succeeds it is an SD card */
if ((read<Rsp0>() & 0xff) == 0xaa)
PINF("Found SD card");
/*
* We need to issue the same Mmc_send_op_cond command multiple
* times. The first time, we receive the status information. On
* subsequent attempts, the response tells us that the card is
* busy. Usually, the command is issued twice. We give up if the
* card is not reaching busy state after one second.
*/
unsigned i = 1000;
unsigned voltages = 0x300080;
unsigned arg = 0;
for (; i > 0; --i) {
if (!issue_command(Mmc_send_op_cond(arg, true))) {
PWRN("Sd_send_op_cond command failed");
throw Detection_failed();
}
arg = read<Rsp0>();
arg = (voltages & (arg & 0x007FFF80)) | (arg & 0x60000000);
_delayer.usleep(1000);
if (Ocr::Busy::get(read<Rsp0>()))
break;
}
if (i == 0) {
PERR("Send_op_cond timed out, could no power-on SD/MMC card");
throw Detection_failed();
}
Card_info card_info = _detect_mmc();
#if 0
/* set to eight bit transfer Bit */
if (!setup_bus(CLK_DIV_52Mhz, _delayer))
throw Detection_failed();
bus_width(BUS_WIDTH_8);
/*
* TODO SD card: set bus width (on card) - 4 bit
*/
#endif
/*
* Enable Interrupts data read timeout | data transfer done | response
* error
*/
write<Intmask>(0x28a);
write<Ctrl::Global_interrupt>(1);
return card_info;
}
bool _setup_idmac_descriptor_table(size_t block_count,
Genode::addr_t phys_addr)
{
size_t const max_idmac_block_count = IDMAC_DESC_MAX_ENTRIES * 8;
if (block_count > max_idmac_block_count) {
PERR("Block request too large");
return false;
}
reset_fifo(_delayer);
Idmac_desc::Flags flags = Idmac_desc::FS;
size_t b = block_count;
for (int index = 0; b;
index++, phys_addr += 0x1000, flags = Idmac_desc::NONE)
b = _idmac_desc[index].set(b, phys_addr, flags);
write<Dbaddr>(_idmac_desc_phys);
write<Ctrl::Dma_enable>(1);
write<Ctrl::Use_internal_dmac>(1);
write<Bmod::Fixed_burst>(1);
write<Bmod::Idmac_enable>(1);
write<Blksize>(BLOCK_SIZE);
write<Bytcnt>(BLOCK_SIZE * block_count);
return true;
}
bool _wait_for_transfer_complete()
{
while (1) {
_irq.wait_for_irq();
if (read<Rintsts::Data_transfer_over>()) {
write<Rintsts>(~0U);
return true;
}
if (read<Rintsts::Response_error>()) {
PERR("Response error");
return false;
}
if (read<Rintsts::Data_read_timeout>()) {
PERR("Data read timeout");
return false;
}
if (read<Rintsts::Data_crc_error>()) {
PERR("CRC error");
return false;
}
}
}
public:
enum { IRQ_NUMBER = 107 };
Exynos5_msh_controller(Genode::addr_t const mmio_base, Delayer &delayer,
bool use_dma)
: Dwmmc(mmio_base),
_idmac_desc_ds(Genode::env()->ram_session(),
IDMAC_DESC_MAX_ENTRIES*sizeof(Idmac_desc),
false),
_idmac_desc(_idmac_desc_ds.local_addr<Idmac_desc>()),
_idmac_desc_phys(Genode::Dataspace_client(_idmac_desc_ds.cap()).phys_addr()),
_delayer(delayer), _card_info(_init()), _irq(IRQ_NUMBER)
{
}
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<Status::Data_busy>(0, _delayer, 10000, 100)) {
PERR("wait for State::Data_busy timed out %x", read<Status>());
return false;
}
write<Rintsts>(~0UL);
/* write command argument */
write<Cmdarg>(command.arg);
Cmd::access_t cmd = 0;
Cmd::Index::set(cmd, command.index);
if (command.transfer != Sd_card::TRANSFER_NONE) {
/* set data-direction bit depending on the command */
bool const write = command.transfer == Sd_card::TRANSFER_WRITE;
Cmd::Data_expected::set(cmd, 1);
Cmd::Write::set(cmd, write ? 1 : 0);
}
Cmd::access_t rsp_type = 0;
switch (command.rsp_type) {
case 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);
Cmd::Start_cmd::set(cmd, 1);
Cmd::Use_hold_reg::set(cmd ,1);
Cmd::Wait_prvdata_complete::set(cmd, 1);
if (command.index == 0)
Cmd::Init_sequence::set(cmd, 1);
/* issue command */
write<Cmd>(cmd);
if (!wait_for<Rintsts::Command_done>(1, _delayer, 10000, 100)) {
PERR("Command failed Rintst: %x Mintst: %x Status: %x", read<Rintsts>(), read<Mintsts>(), read<Status>());
if (read<Rintsts::Response_timeout>())
PWRN("timeout");
if (read<Rintsts::Response_error>())
PWRN("Repsonse error");
return false;
}
if (verbose)
PDBG("IRQ: Rintsts %x transfer %s", read<Rintsts>(),
command.transfer == Sd_card::TRANSFER_NONE ? "none" : "data");
/* acknowledge interrupt */
write<Rintsts::Command_done>(1);
_delayer.usleep(100);
return true;
}
Sd_card::Cid _read_cid()
{
Sd_card::Cid cid;
cid.raw_0 = read<Rsp0>();
cid.raw_1 = read<Rsp1>();
cid.raw_2 = read<Rsp2>();
cid.raw_3 = read<Rsp3>();
return cid;
}
Sd_card::Csd _read_csd()
{
Sd_card::Csd csd;
csd.csd0 = read<Rsp0>();
csd.csd1 = read<Rsp1>();
csd.csd2 = read<Rsp2>();
csd.csd3 = read<Rsp3>();
return csd;
}
unsigned _read_rca() { return 0; }
size_t _read_ext_csd()
{
using namespace Genode;
Attached_ram_dataspace ds(env()->ram_session(), 0x1000, false);
addr_t phys = Genode::Dataspace_client(ds.cap()).phys_addr();
_setup_idmac_descriptor_table(1, phys);
if (!issue_command(Sd_card::Mmc_send_ext_csd()))
throw Detection_failed();
if (!wait_for<Rintsts::Data_transfer_over>(1, _delayer)) {
PERR("Error retrieving extented CSD");
throw Detection_failed();
}
/* clear IRQ */
write<Rintsts::Data_transfer_over>(1);
/* contruct extented CSD */
Sd_card::Ext_csd csd((addr_t)ds.local_addr<addr_t>());
/* read revision */
if (csd.read<Sd_card::Ext_csd::Revision>() < 2) {
PERR("Extented CSD revision is < 2");
throw Detection_failed();
}
/* return sector count */
uint64_t capacity = csd.read<Sd_card::Ext_csd::Sector_count>() * BLOCK_SIZE;
/* to MB */
return capacity / (1024 * 1024);
}
Sd_card::Card_info card_info() const
{
return _card_info;
}
bool read_blocks(size_t block_number, size_t block_count, char *out_buffer)
{
PWRN("'read_blocks' not implemented");
return true;
}
bool write_blocks(size_t block_number, size_t block_count, char const *buffer)
{
PWRN("'write_blocks' not implemented");
return true;
}
bool read_blocks_dma(size_t block_number, size_t block_count,
Genode::addr_t buffer_phys)
{
if (!_setup_idmac_descriptor_table(block_count, buffer_phys))
return false;
if (!_issue_command(Sd_card::Read_multiple_block(block_number))) {
PERR("Read_multiple_block failed, Status: 0x%08x", read<Status>());
return false;
}
bool complete = _wait_for_transfer_complete();
if (!_issue_command(Sd_card::Stop_transmission())) {
PERR("Unable to stop transmission");
return false;
}
return complete;
}
bool write_blocks_dma(size_t block_number, size_t block_count,
Genode::addr_t buffer_phys)
{
if (!_setup_idmac_descriptor_table(block_count, buffer_phys))
return false;
if (!_issue_command(Sd_card::Write_multiple_block(block_number))) {
PERR("Read_multiple_block failed, Status: 0x%08x", read<Status>());
return false;
}
bool complete = _wait_for_transfer_complete();
if (!_issue_command(Sd_card::Stop_transmission())) {
PERR("Unable to stop transmission");
return false;
}
return complete;
}
};
#endif /* _DWMMC_H_ */

View File

@ -0,0 +1,50 @@
/*
* \brief eMMC driver for Arndale/Exynos5 platform
* \author Sebastian Sumpf
* \date 2013-03-06
*/
/* Genode includes */
#include <base/sleep.h>
#include <base/printf.h>
#include <cap_session/connection.h>
/* local includes */
#include <driver.h>
int main(int argc, char **argv)
{
using namespace Genode;
printf("--- Arndale eMMC card driver ---\n");
/**
* Factory used by 'Block::Root' at session creation/destruction time
*/
struct Driver_factory : Block::Driver_factory
{
Block::Driver *create()
{
bool use_dma = true;
return new (env()->heap()) Block::Exynos5_driver(use_dma);
}
void destroy(Block::Driver *driver)
{
Genode::destroy(env()->heap(),
static_cast<Block::Exynos5_driver *>(driver));
}
} driver_factory;
enum { STACK_SIZE = 8192 };
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;
}

View File

@ -0,0 +1,5 @@
TARGET = sd_card_drv
REQUIRES = exynos5
SRC_CC = main.cc
LIBS = base
INC_DIR += $(PRG_DIR) $(PRG_DIR)/..

View File

@ -1,6 +1,7 @@
/*
* \brief SD card protocol definitions
* \author Norman Feske
* \author Sebastian Sumpf
* \date 2012-07-06
*/
@ -53,8 +54,10 @@ namespace Sd_card {
struct Version : Bitfield<126 - BIT_BASE, 2>
{
enum { HIGH_CAPACITY = 1 };
enum { HIGH_CAPACITY = 1, EXT_CSD = 3 };
};
struct Mmc_spec_vers : Bitfield<122 - BIT_BASE, 4> { };
};
struct Csd
@ -65,6 +68,15 @@ namespace Sd_card {
Csd3::access_t csd3;
};
struct Ext_csd : Mmio
{
Ext_csd(addr_t base) : Mmio(base) { }
struct Revision : Register<0xc0, 8> { };
struct Sector_count : Register<0xd4, 32> { };
};
struct Arg : Register<32> { };
enum Response { RESPONSE_NONE,
@ -117,6 +129,11 @@ namespace Sd_card {
{
struct Rca : Bitfield<16, 16> { };
};
Send_relative_addr(unsigned rca = 0)
{
Response::Rca::set(arg, rca);
}
};
struct Select_card : Command<7, RESPONSE_48_BIT>
@ -160,6 +177,9 @@ namespace Sd_card {
}
};
struct Mmc_send_ext_csd : Command<8, RESPONSE_48_BIT_WITH_BUSY, TRANSFER_READ>
{ };
struct Set_block_count : Command<23, RESPONSE_48_BIT>
{
Set_block_count(size_t count)
@ -222,6 +242,30 @@ namespace Sd_card {
}
};
struct Mmc_send_op_cond : Command<1, RESPONSE_48_BIT>
{
struct Arg : Sd_card::Arg
{
/**
* Operating condition register
*/
struct Ocr : Bitfield<0, 24> { };
/**
* Host capacity support
*/
struct Hcs : Bitfield<30, 1> { };
};
Mmc_send_op_cond(unsigned ocr, bool hcs)
{
Arg::Ocr::set(arg, ocr);
Arg::Hcs::set(arg, hcs);
}
};
struct Stop_transmission : Command<12, RESPONSE_48_BIT> { };
struct Acmd_prefix : Command<55, RESPONSE_48_BIT>
{
struct Arg : Sd_card::Arg
@ -282,6 +326,8 @@ namespace Sd_card {
virtual unsigned _read_rca() = 0;
virtual size_t _read_ext_csd() { return 0; }
public:
virtual Card_info card_info() const = 0;
@ -362,6 +408,51 @@ namespace Sd_card {
return Card_info(rca, device_size / 2);
}
Card_info _detect_mmc()
{
if (!issue_command(All_send_cid())) {
PWRN("All_send_cid command failed");
throw Detection_failed();
}
unsigned const rca = 1;
if (!issue_command(Send_relative_addr(rca))) {
PERR("Send_relative_addr timed out");
throw Detection_failed();
}
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::EXT_CSD) {
PERR("Csd version is not extented CSD");
throw Detection_failed();
}
if (Csd3::Mmc_spec_vers::get(csd.csd3) < 4) {
PERR("Csd specific version is less than 4");
throw Detection_failed();
}
if (!issue_command(Select_card(rca))) {
PERR("Select_card failed");
throw Detection_failed();
}
size_t device_size;
if(!(device_size = _read_ext_csd())) {
PERR("Could not read extented CSD");
throw Detection_failed();
}
return Card_info(rca, device_size);
}
};
}