sd_card: Make OMAP4 'sd_card.h' default

This commit is contained in:
Sebastian Sumpf 2013-03-22 15:16:34 +01:00 committed by Norman Feske
parent e1d0839e19
commit 95a16adb6f
8 changed files with 473 additions and 474 deletions

View File

@ -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)/../..

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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)/..

View 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_ */

View File

@ -3,4 +3,4 @@ REQUIRES = pl180
SRC_CC = main.cc
LIBS = base
INC_DIR += $(PRG_DIR) $(PRG_DIR)/..
INC_DIR += $(PRG_DIR)

View File

@ -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_ */