mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-25 00:11:07 +00:00
OMAP4 SD card driver
The new SD card driver at 'os/src/drivers/sd_card/omap4' allows the use of an SD card with the Pandaboard as block service. Currently, the driver is using PIO, no DMA, and no IRQs. The driver can be tested using the 'os/run/sd_card.run' script.
This commit is contained in:
parent
cfda8ac4ae
commit
5cd099ed90
160
os/src/drivers/sd_card/omap4/main.cc
Normal file
160
os/src/drivers/sd_card/omap4/main.cc
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* \brief SD-card driver for OMAP4 platform
|
||||
* \author Norman Feske
|
||||
* \date 2012-07-03
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/sleep.h>
|
||||
#include <util/mmio.h>
|
||||
#include <os/attached_io_mem_dataspace.h>
|
||||
#include <base/printf.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <cap_session/connection.h>
|
||||
#include <block/component.h>
|
||||
|
||||
/* local includes */
|
||||
#include <mmchs.h>
|
||||
|
||||
|
||||
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<void>(), _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<Block::Omap4_driver *>(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;
|
||||
}
|
739
os/src/drivers/sd_card/omap4/mmchs.h
Normal file
739
os/src/drivers/sd_card/omap4/mmchs.h
Normal file
@ -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 <util/mmio.h>
|
||||
|
||||
/* local includes */
|
||||
#include <sd_card.h>
|
||||
|
||||
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<Sysctl::Src>(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<Sysctl::Src>(1, delayer, 1000, 0)) {
|
||||
PERR("reset of cmd line timed out (src != 1)");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!wait_for<Sysctl::Src>(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<Sysctl::Sra>(1);
|
||||
|
||||
if (!wait_for<Sysctl::Sra>(1, delayer, 1000, 0)) {
|
||||
PERR("soft reset all timed out (src != 1)");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void disable_irq()
|
||||
{
|
||||
write<Ise>(0);
|
||||
write<Ie>(0);
|
||||
write<Stat>(~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<Con::Dw8>(0);
|
||||
write<Hctl::Dtw>(Hctl::Dtw::ONE_BIT);
|
||||
break;
|
||||
|
||||
case BUS_WIDTH_8:
|
||||
write<Con::Dw8>(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool sd_bus_power_on(Delayer &delayer)
|
||||
{
|
||||
write<Hctl::Sdbp>(Hctl::Sdbp::POWER_ON);
|
||||
|
||||
if (!wait_for<Hctl::Sdbp>(1, delayer)) {
|
||||
PERR("setting Hctl::Sdbp timed out");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void stop_clock()
|
||||
{
|
||||
write<Sysctl::Ce>(0);
|
||||
}
|
||||
|
||||
enum Clock_divider { CLOCK_DIV_0, CLOCK_DIV_240 };
|
||||
|
||||
bool set_and_enable_clock(enum Clock_divider divider, Delayer &delayer)
|
||||
{
|
||||
write<Sysctl::Dto>(Sysctl::Dto::TCF_2_POW_27);
|
||||
|
||||
switch (divider) {
|
||||
case CLOCK_DIV_0: write<Sysctl::Clkd>(0); break;
|
||||
case CLOCK_DIV_240: write<Sysctl::Clkd>(240); break;
|
||||
}
|
||||
|
||||
write<Sysctl::Ice>(1);
|
||||
|
||||
/* wait for clock to become stable */
|
||||
if (!wait_for<Sysctl::Ics>(1, delayer)) {
|
||||
PERR("clock enable timed out");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* enable clock */
|
||||
write<Sysctl::Ce>(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>(Hctl::Sdvs::VOLTAGE_3_0);
|
||||
break;
|
||||
|
||||
case VOLTAGE_1_8:
|
||||
write<Hctl::Sdvs>(Hctl::Sdvs::VOLTAGE_1_8);
|
||||
break;
|
||||
}
|
||||
|
||||
write<Capa::Vs18>(1);
|
||||
|
||||
if (voltage == VOLTAGE_3_0)
|
||||
write<Capa::Vs30>(1);
|
||||
}
|
||||
|
||||
bool init_stream(Delayer &delayer)
|
||||
{
|
||||
write<Ie>(0x307f0033);
|
||||
|
||||
/* start initialization sequence */
|
||||
write<Con::Init>(1);
|
||||
|
||||
write<Cmd>(0);
|
||||
|
||||
if (!wait_for<Stat::Cc>(1, delayer, 1000*1000, 0)) {
|
||||
PERR("init stream timed out");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* stop initialization sequence */
|
||||
write<Con::Init>(0);
|
||||
|
||||
write<Stat>(~0);
|
||||
read<Stat>();
|
||||
|
||||
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<Sysconfig>(0x2015);
|
||||
write<Hctl>(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<Blk>(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<Rsp10>() != 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<Rsp10>()))
|
||||
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<Sysctl::Clkd>(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<Pstate::Cmdi>(0, _delayer)) {
|
||||
PERR("wait for Pstate::Cmdi timed out");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* write command argument */
|
||||
write<Arg>(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>(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<Stat>();
|
||||
if (Stat::Erri::get(stat)) {
|
||||
PWRN("SD command error");
|
||||
if (Stat::Cto::get(stat))
|
||||
PWRN("timeout");
|
||||
|
||||
reset_cmd_line(_delayer);
|
||||
write<Stat::Cc>(~0);
|
||||
read<Stat>();
|
||||
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<Stat::Cc>(1);
|
||||
read<Stat>();
|
||||
|
||||
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<Rsp10>();
|
||||
cid.raw_1 = read<Rsp32>();
|
||||
cid.raw_2 = read<Rsp54>();
|
||||
cid.raw_3 = read<Rsp76>();
|
||||
return cid;
|
||||
}
|
||||
|
||||
Sd_card::Csd _read_csd()
|
||||
{
|
||||
Sd_card::Csd csd;
|
||||
csd.csd0 = read<Rsp10>();
|
||||
csd.csd1 = read<Rsp32>();
|
||||
csd.csd2 = read<Rsp54>();
|
||||
csd.csd3 = read<Rsp76>();
|
||||
return csd;
|
||||
}
|
||||
|
||||
unsigned _read_rca()
|
||||
{
|
||||
return Sd_card::Send_relative_addr::Response::Rca::get(read<Rsp10>());
|
||||
}
|
||||
|
||||
bool _wait_for_transfer_complete()
|
||||
{
|
||||
if (!wait_for<Stat::Tc>(1, _delayer, 1000*1000, 0)
|
||||
&& !wait_for<Stat::Tc>(1, _delayer)) {
|
||||
PERR("Stat::Tc timed out");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* clear transfer-completed bit */
|
||||
write<Stat::Tc>(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _wait_for_bre()
|
||||
{
|
||||
if (!wait_for<Pstate::Bre>(1, _delayer, 1000*1000, 0)
|
||||
&& !wait_for<Pstate::Bre>(1, _delayer)) {
|
||||
PERR("Pstate::Bre timed out");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _wait_for_bwe()
|
||||
{
|
||||
if (!wait_for<Pstate::Bwe>(1, _delayer, 1000*1000, 0)
|
||||
&& !wait_for<Pstate::Bwe>(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<Blk::Blen>(0x200);
|
||||
write<Blk::Nblk>(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<Data>();
|
||||
}
|
||||
|
||||
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<Blk::Blen>(0x200);
|
||||
write<Blk::Nblk>(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<Data>(*src++);
|
||||
}
|
||||
|
||||
return _wait_for_transfer_complete();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _MMCHS_H_ */
|
336
os/src/drivers/sd_card/omap4/sd_card.h
Normal file
336
os/src/drivers/sd_card/omap4/sd_card.h
Normal file
@ -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 <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 };
|
||||
|
||||
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 <unsigned _INDEX, Response RSP_TYPE>
|
||||
struct Command : Command_base
|
||||
{
|
||||
enum { INDEX = _INDEX };
|
||||
Command() : Command_base(_INDEX, RSP_TYPE) { }
|
||||
};
|
||||
|
||||
template <unsigned INDEX, Response RSP_TYPE>
|
||||
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 <unsigned INDEX, Response RSP_TYPE>
|
||||
bool issue_command(Prefixed_command<INDEX, RSP_TYPE> 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_ */
|
5
os/src/drivers/sd_card/omap4/target.mk
Normal file
5
os/src/drivers/sd_card/omap4/target.mk
Normal file
@ -0,0 +1,5 @@
|
||||
TARGET = sd_card_drv
|
||||
REQUIRES = omap4
|
||||
SRC_CC = main.cc
|
||||
LIBS = cxx env server signal
|
||||
INC_DIR += $(PRG_DIR)
|
Loading…
Reference in New Issue
Block a user