mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-20 11:39:14 +00:00
sd_card: Make OMAP4 'sd_card.h' default
This commit is contained in:
parent
e1d0839e19
commit
95a16adb6f
@ -2,4 +2,4 @@ TARGET = sd_card_bench
|
||||
REQUIRES = omap4
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
||||
INC_DIR += $(PRG_DIR)/..
|
||||
INC_DIR += $(PRG_DIR)/.. $(PRG_DIR)/../..
|
||||
|
@ -121,8 +121,7 @@ class Block::Omap4_driver : public Block::Driver
|
||||
bool dma_enabled() { return _use_dma; }
|
||||
|
||||
Ram_dataspace_capability alloc_dma_buffer(size_t size) {
|
||||
/* unused */
|
||||
return Ram_dataspace_capability(); }
|
||||
return Genode::env()->ram_session()->alloc(size, false); }
|
||||
};
|
||||
|
||||
#endif /* _DRIVER_H_ */
|
||||
|
@ -1,368 +0,0 @@
|
||||
/*
|
||||
* \brief SD card protocol definitions
|
||||
* \author Norman Feske
|
||||
* \date 2012-07-06
|
||||
*/
|
||||
|
||||
#ifndef _SD_CARD_H_
|
||||
#define _SD_CARD_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/register.h>
|
||||
|
||||
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 };
|
||||
|
||||
enum Transfer { TRANSFER_NONE, TRANSFER_READ, TRANSFER_WRITE };
|
||||
|
||||
struct Command_base
|
||||
{
|
||||
unsigned index; /* command opcode */
|
||||
Arg::access_t arg; /* argument */
|
||||
Response rsp_type; /* response type */
|
||||
Transfer transfer; /* data transfer type */
|
||||
|
||||
Command_base(unsigned op, Response rsp_type, Transfer transfer)
|
||||
:
|
||||
index(op), arg(0), rsp_type(rsp_type), transfer(transfer)
|
||||
{ }
|
||||
};
|
||||
|
||||
template <unsigned _INDEX, Response RSP_TYPE, Transfer TRANSFER = TRANSFER_NONE>
|
||||
struct Command : Command_base
|
||||
{
|
||||
enum { INDEX = _INDEX };
|
||||
Command() : Command_base(_INDEX, RSP_TYPE, TRANSFER) { }
|
||||
};
|
||||
|
||||
template <unsigned INDEX, Response RSP_TYPE, Transfer TRANSFER = TRANSFER_NONE>
|
||||
struct Prefixed_command : private Command_base
|
||||
{
|
||||
Prefixed_command() : Command_base(INDEX, RSP_TYPE, TRANSFER) { }
|
||||
|
||||
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, TRANSFER_READ>
|
||||
{
|
||||
Read_multiple_block(unsigned long addr)
|
||||
{
|
||||
arg = addr;
|
||||
}
|
||||
};
|
||||
|
||||
struct Write_multiple_block : Command<25, RESPONSE_48_BIT, TRANSFER_WRITE>
|
||||
{
|
||||
Write_multiple_block(unsigned long addr)
|
||||
{
|
||||
arg = addr;
|
||||
}
|
||||
};
|
||||
|
||||
struct Set_bus_width : Prefixed_command<6, RESPONSE_48_BIT>
|
||||
{
|
||||
struct Arg : Sd_card::Arg
|
||||
{
|
||||
struct Bus_width : Bitfield<0, 2>
|
||||
{
|
||||
enum Width { ONE_BIT = 0, FOUR_BITS = 2 };
|
||||
};
|
||||
};
|
||||
|
||||
Set_bus_width(Arg::Bus_width::Width width)
|
||||
{
|
||||
Arg::Bus_width::set(arg, width);
|
||||
}
|
||||
};
|
||||
|
||||
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>
|
||||
{
|
||||
struct Arg : Sd_card::Arg
|
||||
{
|
||||
struct Rca : Bitfield<16, 16> { };
|
||||
};
|
||||
|
||||
Acmd_prefix(unsigned rca)
|
||||
{
|
||||
Arg::Rca::set(arg, rca);
|
||||
}
|
||||
};
|
||||
|
||||
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.
|
||||
*
|
||||
* \param prefix_rca argument to CMD55 prefix command
|
||||
*/
|
||||
template <unsigned INDEX, Response RSP_TYPE, Transfer TRANSFER>
|
||||
bool issue_command(Prefixed_command<INDEX, RSP_TYPE, TRANSFER> const &command,
|
||||
unsigned prefix_rca = 0)
|
||||
{
|
||||
/* send CMD55 prefix */
|
||||
if (!_issue_command(Acmd_prefix(prefix_rca))) {
|
||||
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_ */
|
@ -2,4 +2,4 @@ TARGET = sd_card_drv
|
||||
REQUIRES = omap4
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
||||
INC_DIR += $(PRG_DIR)
|
||||
INC_DIR += $(PRG_DIR) $(PRG_DIR)/..
|
||||
|
140
os/src/drivers/sd_card/pl180/sd_card.h
Normal file
140
os/src/drivers/sd_card/pl180/sd_card.h
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* \brief SD-card protocol
|
||||
* \author Christian Helmuth
|
||||
* \date 2011-05-19
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-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 _SD_CARD_H_
|
||||
#define _SD_CARD_H_
|
||||
|
||||
#include <block/driver.h>
|
||||
|
||||
#include "host_driver.h"
|
||||
|
||||
|
||||
class Sd_card : public Block::Driver
|
||||
{
|
||||
private:
|
||||
|
||||
Host_driver &_hd;
|
||||
|
||||
enum { BLOCK_SIZE = 512 };
|
||||
|
||||
public:
|
||||
|
||||
Sd_card(Host_driver &host_driver) : _hd(host_driver)
|
||||
{
|
||||
unsigned resp;
|
||||
|
||||
/* CMD0: go idle state */
|
||||
_hd.request(0, 0);
|
||||
|
||||
/*
|
||||
* CMD8: send interface condition
|
||||
*
|
||||
* XXX only one hard-coded value currently.
|
||||
*/
|
||||
_hd.request(8, 0x1aa, &resp);
|
||||
|
||||
/*
|
||||
* ACMD41: card send operating condition
|
||||
*
|
||||
* This is an application-specific command and, therefore, consists
|
||||
* of prefix command CMD55 + CMD41.
|
||||
*/
|
||||
_hd.request(55, 0, &resp);
|
||||
_hd.request(41, 0x4000, &resp);
|
||||
|
||||
/* CMD2: all send card identification (CID) */
|
||||
_hd.request(2, &resp);
|
||||
|
||||
/* CMD3: send relative card address (RCA) */
|
||||
_hd.request(3, &resp);
|
||||
unsigned short rca = resp >> 16;
|
||||
|
||||
/*
|
||||
* Now, the card is in transfer mode...
|
||||
*/
|
||||
|
||||
/* CMD7: select card */
|
||||
_hd.request(7, rca << 16, &resp);
|
||||
}
|
||||
|
||||
Host_driver &host_driver() { return _hd; }
|
||||
|
||||
/****************************
|
||||
** Block-driver interface **
|
||||
****************************/
|
||||
|
||||
Genode::size_t block_size() { return BLOCK_SIZE; }
|
||||
/*
|
||||
* TODO report (and support) real capacity not just 512M
|
||||
*/
|
||||
Genode::size_t block_count() { return 0x20000000 / BLOCK_SIZE; }
|
||||
|
||||
void read(Genode::size_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char *out_buffer)
|
||||
{
|
||||
unsigned resp;
|
||||
unsigned length = BLOCK_SIZE;
|
||||
|
||||
for (Genode::size_t i = 0; i < block_count; ++i) {
|
||||
/*
|
||||
* CMD17: read single block
|
||||
*
|
||||
* SDSC cards use a byte address as argument while SDHC/SDSC uses a
|
||||
* block address here.
|
||||
*/
|
||||
_hd.read_request(17, (block_number + i) * BLOCK_SIZE,
|
||||
length, &resp);
|
||||
_hd.read_data(length, out_buffer + (i * BLOCK_SIZE));
|
||||
}
|
||||
}
|
||||
|
||||
void write(Genode::size_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char const *buffer)
|
||||
{
|
||||
unsigned resp;
|
||||
unsigned length = BLOCK_SIZE;
|
||||
|
||||
for (Genode::size_t i = 0; i < block_count; ++i) {
|
||||
/*
|
||||
* CMD24: write single block
|
||||
*
|
||||
* SDSC cards use a byte address as argument while SDHC/SDSC uses a
|
||||
* block address here.
|
||||
*/
|
||||
_hd.write_request(24, (block_number + i) * BLOCK_SIZE,
|
||||
length, &resp);
|
||||
_hd.write_data(length, buffer + (i * BLOCK_SIZE));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This driver does not support DMA operation, currently.
|
||||
*/
|
||||
|
||||
void read_dma(Genode::size_t, Genode::size_t, Genode::addr_t) {
|
||||
throw Io_error(); }
|
||||
|
||||
void write_dma(Genode::size_t, Genode::size_t, Genode::addr_t) {
|
||||
throw Io_error(); }
|
||||
|
||||
bool dma_enabled() { return false; }
|
||||
|
||||
Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size)
|
||||
{
|
||||
return Genode::env()->ram_session()->alloc(size, false);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _SD_CARD_H_ */
|
@ -3,4 +3,4 @@ REQUIRES = pl180
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
||||
|
||||
INC_DIR += $(PRG_DIR) $(PRG_DIR)/..
|
||||
INC_DIR += $(PRG_DIR)
|
||||
|
@ -1,140 +1,368 @@
|
||||
/*
|
||||
* \brief SD-card protocol
|
||||
* \author Christian Helmuth
|
||||
* \date 2011-05-19
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-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.
|
||||
* \brief SD card protocol definitions
|
||||
* \author Norman Feske
|
||||
* \date 2012-07-06
|
||||
*/
|
||||
|
||||
#ifndef _SD_CARD_H_
|
||||
#define _SD_CARD_H_
|
||||
|
||||
#include <block/driver.h>
|
||||
/* Genode includes */
|
||||
#include <util/register.h>
|
||||
|
||||
#include "host_driver.h"
|
||||
namespace Sd_card {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
class Sd_card : public Block::Driver
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Returned by 'Sd_send_op_cond'
|
||||
*/
|
||||
struct Ocr : Register<32>
|
||||
{
|
||||
struct Busy : Bitfield<31, 1> { };
|
||||
};
|
||||
|
||||
Host_driver &_hd;
|
||||
struct Cid {
|
||||
uint32_t raw_0;
|
||||
uint32_t raw_1;
|
||||
uint32_t raw_2;
|
||||
uint32_t raw_3;
|
||||
};
|
||||
|
||||
enum { BLOCK_SIZE = 512 };
|
||||
struct Csd0 : Register<32>
|
||||
{
|
||||
};
|
||||
|
||||
public:
|
||||
struct Csd1 : Register<32>
|
||||
{
|
||||
enum { BIT_BASE = 1*sizeof(access_t)*8 };
|
||||
|
||||
Sd_card(Host_driver &host_driver) : _hd(host_driver)
|
||||
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>
|
||||
{
|
||||
unsigned resp;
|
||||
enum { HIGH_CAPACITY = 1 };
|
||||
};
|
||||
};
|
||||
|
||||
/* CMD0: go idle state */
|
||||
_hd.request(0, 0);
|
||||
struct Csd
|
||||
{
|
||||
Csd0::access_t csd0;
|
||||
Csd1::access_t csd1;
|
||||
Csd2::access_t csd2;
|
||||
Csd3::access_t csd3;
|
||||
};
|
||||
|
||||
/*
|
||||
* CMD8: send interface condition
|
||||
*
|
||||
* XXX only one hard-coded value currently.
|
||||
*/
|
||||
_hd.request(8, 0x1aa, &resp);
|
||||
struct Arg : Register<32> { };
|
||||
|
||||
/*
|
||||
* ACMD41: card send operating condition
|
||||
*
|
||||
* This is an application-specific command and, therefore, consists
|
||||
* of prefix command CMD55 + CMD41.
|
||||
*/
|
||||
_hd.request(55, 0, &resp);
|
||||
_hd.request(41, 0x4000, &resp);
|
||||
enum Response { RESPONSE_NONE,
|
||||
RESPONSE_136_BIT,
|
||||
RESPONSE_48_BIT,
|
||||
RESPONSE_48_BIT_WITH_BUSY };
|
||||
|
||||
/* CMD2: all send card identification (CID) */
|
||||
_hd.request(2, &resp);
|
||||
enum Transfer { TRANSFER_NONE, TRANSFER_READ, TRANSFER_WRITE };
|
||||
|
||||
/* CMD3: send relative card address (RCA) */
|
||||
_hd.request(3, &resp);
|
||||
unsigned short rca = resp >> 16;
|
||||
struct Command_base
|
||||
{
|
||||
unsigned index; /* command opcode */
|
||||
Arg::access_t arg; /* argument */
|
||||
Response rsp_type; /* response type */
|
||||
Transfer transfer; /* data transfer type */
|
||||
|
||||
/*
|
||||
* Now, the card is in transfer mode...
|
||||
*/
|
||||
Command_base(unsigned op, Response rsp_type, Transfer transfer)
|
||||
:
|
||||
index(op), arg(0), rsp_type(rsp_type), transfer(transfer)
|
||||
{ }
|
||||
};
|
||||
|
||||
/* CMD7: select card */
|
||||
_hd.request(7, rca << 16, &resp);
|
||||
}
|
||||
template <unsigned _INDEX, Response RSP_TYPE, Transfer TRANSFER = TRANSFER_NONE>
|
||||
struct Command : Command_base
|
||||
{
|
||||
enum { INDEX = _INDEX };
|
||||
Command() : Command_base(_INDEX, RSP_TYPE, TRANSFER) { }
|
||||
};
|
||||
|
||||
Host_driver &host_driver() { return _hd; }
|
||||
template <unsigned INDEX, Response RSP_TYPE, Transfer TRANSFER = TRANSFER_NONE>
|
||||
struct Prefixed_command : private Command_base
|
||||
{
|
||||
Prefixed_command() : Command_base(INDEX, RSP_TYPE, TRANSFER) { }
|
||||
|
||||
/****************************
|
||||
** Block-driver interface **
|
||||
****************************/
|
||||
using Command_base::arg;
|
||||
|
||||
Genode::size_t block_size() { return BLOCK_SIZE; }
|
||||
/*
|
||||
* TODO report (and support) real capacity not just 512M
|
||||
/**
|
||||
* Used by ACMD overload of 'issue_command()'
|
||||
*/
|
||||
Genode::size_t block_count() { return 0x20000000 / BLOCK_SIZE; }
|
||||
Command_base const &base() const { return *this; }
|
||||
};
|
||||
|
||||
void read(Genode::size_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char *out_buffer)
|
||||
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
|
||||
{
|
||||
unsigned resp;
|
||||
unsigned length = BLOCK_SIZE;
|
||||
struct Rca : Bitfield<16, 16> { };
|
||||
};
|
||||
};
|
||||
|
||||
for (Genode::size_t i = 0; i < block_count; ++i) {
|
||||
/*
|
||||
* CMD17: read single block
|
||||
*
|
||||
* SDSC cards use a byte address as argument while SDHC/SDSC uses a
|
||||
* block address here.
|
||||
*/
|
||||
_hd.read_request(17, (block_number + i) * BLOCK_SIZE,
|
||||
length, &resp);
|
||||
_hd.read_data(length, out_buffer + (i * BLOCK_SIZE));
|
||||
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, TRANSFER_READ>
|
||||
{
|
||||
Read_multiple_block(unsigned long addr)
|
||||
{
|
||||
arg = addr;
|
||||
}
|
||||
};
|
||||
|
||||
struct Write_multiple_block : Command<25, RESPONSE_48_BIT, TRANSFER_WRITE>
|
||||
{
|
||||
Write_multiple_block(unsigned long addr)
|
||||
{
|
||||
arg = addr;
|
||||
}
|
||||
};
|
||||
|
||||
struct Set_bus_width : Prefixed_command<6, RESPONSE_48_BIT>
|
||||
{
|
||||
struct Arg : Sd_card::Arg
|
||||
{
|
||||
struct Bus_width : Bitfield<0, 2>
|
||||
{
|
||||
enum Width { ONE_BIT = 0, FOUR_BITS = 2 };
|
||||
};
|
||||
};
|
||||
|
||||
Set_bus_width(Arg::Bus_width::Width width)
|
||||
{
|
||||
Arg::Bus_width::set(arg, width);
|
||||
}
|
||||
};
|
||||
|
||||
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>
|
||||
{
|
||||
struct Arg : Sd_card::Arg
|
||||
{
|
||||
struct Rca : Bitfield<16, 16> { };
|
||||
};
|
||||
|
||||
Acmd_prefix(unsigned rca)
|
||||
{
|
||||
Arg::Rca::set(arg, rca);
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void write(Genode::size_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char const *buffer)
|
||||
{
|
||||
unsigned resp;
|
||||
unsigned length = BLOCK_SIZE;
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* \param prefix_rca argument to CMD55 prefix command
|
||||
*/
|
||||
template <unsigned INDEX, Response RSP_TYPE, Transfer TRANSFER>
|
||||
bool issue_command(Prefixed_command<INDEX, RSP_TYPE, TRANSFER> const &command,
|
||||
unsigned prefix_rca = 0)
|
||||
{
|
||||
/* send CMD55 prefix */
|
||||
if (!_issue_command(Acmd_prefix(prefix_rca))) {
|
||||
PERR("prefix command timed out");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Genode::size_t i = 0; i < block_count; ++i) {
|
||||
/*
|
||||
* CMD24: write single block
|
||||
*
|
||||
* SDSC cards use a byte address as argument while SDHC/SDSC uses a
|
||||
* block address here.
|
||||
*/
|
||||
_hd.write_request(24, (block_number + i) * BLOCK_SIZE,
|
||||
length, &resp);
|
||||
_hd.write_data(length, buffer + (i * BLOCK_SIZE));
|
||||
/* send actual command */
|
||||
return _issue_command(command.base());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This driver does not support DMA operation, currently.
|
||||
*/
|
||||
protected:
|
||||
|
||||
void read_dma(Genode::size_t, Genode::size_t, Genode::addr_t) {
|
||||
throw Io_error(); }
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
void write_dma(Genode::size_t, Genode::size_t, Genode::addr_t) {
|
||||
throw Io_error(); }
|
||||
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);
|
||||
|
||||
bool dma_enabled() { return false; }
|
||||
if (!issue_command(Send_relative_addr())) {
|
||||
PERR("Send_relative_addr timed out");
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size)
|
||||
{
|
||||
return Genode::env()->ram_session()->alloc(size, false);
|
||||
}
|
||||
};
|
||||
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_ */
|
||||
|
Loading…
Reference in New Issue
Block a user