sd_card: re-factorizea and clean up

Merge the platform-specific files and classes (they merely aggregated
themselves) so that each platform provides merely one class
Sd_card::Driver. Also, the Sd_card::Driver_base class is introduced for
the generic parts of Sd_card::Driver.

Ref #2206
This commit is contained in:
Martin Stein 2017-01-03 16:42:00 +01:00 committed by Norman Feske
parent b33c35a003
commit d07cd0e88e
36 changed files with 2501 additions and 3415 deletions

View File

@ -1,4 +1,5 @@
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/exynos5
SRC_CC += spec/exynos5/driver.cc
vpath main.cc $(REP_DIR)/src/test/sd_card_bench

View File

@ -1,3 +1,4 @@
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/exynos5
SRC_CC += spec/exynos5/driver.cc
include $(REP_DIR)/lib/mk/sd_card.inc

View File

@ -1,7 +1,7 @@
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
SRC_CC += spec/imx/adma2.cc
SRC_CC += spec/imx/sdhc.cc
SRC_CC += spec/imx53/sdhc.cc
SRC_CC += adma2.cc
SRC_CC += spec/imx/driver.cc
SRC_CC += spec/imx53/driver.cc
vpath main.cc $(REP_DIR)/src/test/sd_card_bench

View File

@ -1,6 +1,6 @@
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
SRC_CC += spec/imx/adma2.cc
SRC_CC += spec/imx/sdhc.cc
SRC_CC += spec/imx53/sdhc.cc
SRC_CC += adma2.cc
SRC_CC += spec/imx/driver.cc
SRC_CC += spec/imx53/driver.cc
include $(REP_DIR)/lib/mk/sd_card.inc

View File

@ -1,7 +1,7 @@
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
SRC_CC += spec/imx/adma2.cc
SRC_CC += spec/imx/sdhc.cc
SRC_CC += spec/imx6/sdhc.cc
SRC_CC += adma2.cc
SRC_CC += spec/imx/driver.cc
SRC_CC += spec/imx6/driver.cc
vpath main.cc $(REP_DIR)/src/test/sd_card_bench

View File

@ -1,6 +1,6 @@
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
SRC_CC += spec/imx/adma2.cc
SRC_CC += spec/imx/sdhc.cc
SRC_CC += spec/imx6/sdhc.cc
SRC_CC += adma2.cc
SRC_CC += spec/imx/driver.cc
SRC_CC += spec/imx6/driver.cc
include $(REP_DIR)/lib/mk/sd_card.inc

View File

@ -1,4 +1,5 @@
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/omap4
SRC_CC += spec/omap4/driver.cc
vpath main.cc $(REP_DIR)/src/test/sd_card_bench

View File

@ -1,3 +1,4 @@
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/omap4
SRC_CC += spec/omap4/driver.cc
include $(REP_DIR)/lib/mk/sd_card.inc

View File

@ -1,4 +1,5 @@
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/pl180
SRC_CC += spec/pl180/driver.cc
vpath main.cc $(REP_DIR)/src/test/sd_card_bench

View File

@ -1,3 +1,4 @@
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/pl180
SRC_CC += spec/pl180/driver.cc
include $(REP_DIR)/lib/mk/sd_card.inc

View File

@ -1,4 +1,5 @@
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/rpi
SRC_CC += spec/rpi/driver.cc
vpath main.cc $(REP_DIR)/src/test/sd_card_bench

View File

@ -1,3 +1,4 @@
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/rpi
SRC_CC += spec/rpi/driver.cc
include $(REP_DIR)/lib/mk/sd_card.inc

View File

@ -20,9 +20,9 @@
using namespace Adma2;
Table::Table()
Table::Table(Ram_session &ram)
:
_ds(env()->ram_session(), _ds_size, UNCACHED),
_ds(&ram, _ds_size, UNCACHED),
_base_virt(_ds.local_addr<Desc::access_t>()),
_base_phys(Dataspace_client(_ds.cap()).phys_addr())
{ }

View File

@ -66,7 +66,7 @@ class Adma2::Table
public:
Table();
Table(Ram_session &ram);
/**
* Marshal descriptors according to block request

View File

@ -0,0 +1,51 @@
/*
* \brief Generic parts of an SD card block-driver
* \author Norman Feske
* \author Timo Wischer
* \author Martin Stein
* \date 2017-01-03
*/
/*
* Copyright (C) 2017 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 _DRIVER_BASE_H_
#define _DRIVER_BASE_H_
/* Genode includes */
#include <block/component.h>
/* local includes */
#include <sd_card.h>
namespace Sd_card { class Driver_base; }
class Sd_card::Driver_base : public Block::Driver,
protected Host_controller
{
public:
/*******************
** Block::Driver **
*******************/
Genode::size_t block_size() override { return 512; }
Block::sector_t block_count() override {
return card_info().capacity_mb() * 1024 * 2; }
Block::Session::Operations ops() override
{
Block::Session::Operations ops;
ops.set_operation(Block::Packet_descriptor::READ);
ops.set_operation(Block::Packet_descriptor::WRITE);
return ops;
}
};
#endif /* _DRIVER_BASE_H_ */

View File

@ -36,10 +36,10 @@ struct Main
Factory(Env &env, Heap &heap) : env(env), heap(heap) { }
Block::Driver *create() {
return new (&heap) Block::Sdhci_driver(env); }
return new (&heap) Sd_card::Driver(env); }
void destroy(Block::Driver *driver) {
Genode::destroy(&heap, static_cast<Block::Sdhci_driver*>(driver)); }
Genode::destroy(&heap, static_cast<Sd_card::Driver*>(driver)); }
} factory { env, heap };

View File

@ -16,7 +16,7 @@
#define _SD_CARD_H_
/* Genode includes */
#include <util/register.h>
#include <util/mmio.h>
namespace Sd_card {

View File

@ -0,0 +1,432 @@
/*
* \brief Exynos5-specific implementation of the Block::Driver interface
* \author Sebastian Sumpf
* \author Martin Stein
* \date 2013-03-22
*/
/*
* Copyright (C) 2015-2017 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.
*/
/* local includes */
#include <driver.h>
using namespace Genode;
using namespace Sd_card;
Driver::Driver(Env &env) : Attached_mmio(MSH_BASE, MSH_SIZE), _env(env)
{
_irq.sigh(_irq_handler);
_irq.ack_irq();
log("SD/MMC card detected");
log("capacity: ", _card_info.capacity_mb(), " MiB");
}
void Driver::read_dma(Block::sector_t block_number,
size_t block_count,
addr_t buf_phys,
Block::Packet_descriptor &pkt)
{
if (_block_transfer.pending) {
throw Request_congestion(); }
if (!_setup_idmac_descriptor_table(block_count, buf_phys))
throw Io_error();
_block_transfer.packet = pkt;
_block_transfer.pending = true;
if (!_issue_command(Read_multiple_block(block_number))) {
error("Read_multiple_block failed");
throw Io_error();
}
}
void Driver::write_dma(Block::sector_t block_number,
size_t block_count,
addr_t buf_phys,
Block::Packet_descriptor &pkt)
{
if (_block_transfer.pending) {
throw Request_congestion(); }
if (!_setup_idmac_descriptor_table(block_count, buf_phys))
throw Io_error();
_block_transfer.packet = pkt;
_block_transfer.pending = true;
if (!_issue_command(Write_multiple_block(block_number))) {
error("Read_multiple_block failed");
throw Io_error();
}
}
bool Driver::_reset()
{
Mmio::write<Ctrl::Reset>(0x7);
if (!wait_for<Ctrl::Reset>(0, _delayer, 100, 1000)) {
error("Could not reset host contoller");
return false;
}
return true;
}
void Driver::_reset_fifo()
{
Mmio::write<Ctrl::Reset>(0x2);
if (!wait_for<Ctrl::Reset>(0, _delayer, 100, 1000)) {
error("Could not reset fifo"); }
}
void Driver::_disable_irq()
{
Mmio::write<Rintsts>(~0U);
Mmio::write<Intmask>(0);
}
bool Driver::_update_clock_registers()
{
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);
Mmio::write<Cmd>(cmd);
if (!wait_for<Cmd::Start_cmd>(0, _delayer)) {
error("Update clock registers failed");
return false;
}
return true;
}
bool Driver::_setup_bus(unsigned clock_div)
{
/* set host clock divider */
Mmio::write<Clkdiv>(clock_div);
if (!_update_clock_registers())
return false;
/* enable clock for card 1 */
Mmio::write<Clkena>(0x1);
if (!_update_clock_registers())
return false;
_delayer.usleep(10 * 1000);
return true;
}
Card_info Driver::_init()
{
Mmio::write<Pwren>(1);
if (!_reset())
throw Detection_failed();
Mmio::write<Emmc_ddr_req>(0x1);
_disable_irq();
Mmio::write<Tmout>(~0U);
Mmio::write<Idinten>(0);
Mmio::write<Bmod>(1);
Mmio::write<Bytcnt>(0);
Mmio::write<Fifoth>(0x203f0040);
/* set to one bit transfer Bit */
if (!_setup_bus(CLK_DIV_400Khz))
throw Detection_failed();
Mmio::write<Ctype>(BUS_WIDTH_1);
if (!issue_command(Go_idle_state())) {
warning("Go_idle_state command failed");
throw Detection_failed();
}
_delayer.usleep(2000);
if (!issue_command(Send_if_cond())) {
warning("Send_if_cond command failed");
throw Detection_failed();
}
/* if this succeeds it is an SD card */
if ((Mmio::read<Rsp0>() & 0xff) == 0xaa)
log("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))) {
warning("Sd_send_op_cond command failed");
throw Detection_failed();
}
arg = Mmio::read<Rsp0>();
arg = (voltages & (arg & 0x007FFF80)) | (arg & 0x60000000);
_delayer.usleep(1000);
if (Ocr::Busy::get(Mmio::read<Rsp0>()))
break;
}
if (i == 0) {
error("Send_op_cond timed out, could no power-on SD/MMC card");
throw Detection_failed();
}
Card_info card_info = _detect_mmc();
/* switch frequency to high speed */
enum { EXT_CSD_HS_TIMING = 185 };
if (!issue_command(Mmc_switch(EXT_CSD_HS_TIMING, 1))) {
error("Error setting high speed frequency");
throw Detection_failed();
}
enum { EXT_CSD_BUS_WIDTH = 183 };
/* set card to 8 bit */
if (!issue_command(Mmc_switch(EXT_CSD_BUS_WIDTH, 2))) {
error("Error setting card bus width");
throw Detection_failed();
}
Mmio::write<Ctype>(BUS_WIDTH_8);
/* set to eight bit transfer Bit */
if (!_setup_bus(CLK_DIV_52Mhz)) {
error("Error setting bus to high speed");
throw Detection_failed();
}
/* Enable IRQs data read timeout, data transfer done, resp error */
Mmio::write<Intmask>(0x28a);
Mmio::write<Ctrl::Global_interrupt>(1);
return card_info;
}
bool Driver::_setup_idmac_descriptor_table(size_t block_count,
addr_t phys_addr)
{
size_t const max_idmac_block_count = IDMAC_DESC_MAX_ENTRIES * 8;
if (block_count > max_idmac_block_count) {
error("Block request too large");
return false;
}
_reset_fifo();
Idmac_desc::Flags flags = Idmac_desc::FS;
size_t b = block_count;
int index = 0;
for (index = 0; b; index++, phys_addr += 0x1000, flags = Idmac_desc::NONE) {
b = _idmac_desc[index].set(b, block_size(), phys_addr, flags);
_idmac_desc[index].next =
_idmac_desc_phys + ((index + 1) * sizeof(Idmac_desc));
}
_idmac_desc[index].next = (unsigned)_idmac_desc;
_idmac_desc[index].flags |= Idmac_desc::ER;
Mmio::write<Dbaddr>(_idmac_desc_phys);
Mmio::write<Ctrl::Dma_enable>(1);
Mmio::write<Ctrl::Use_internal_dmac>(1);
Mmio::write<Bmod::Fixed_burst>(1);
Mmio::write<Bmod::Idmac_enable>(1);
Mmio::write<Blksize>(block_size());
Mmio::write<Bytcnt>(block_size() * block_count);
Mmio::write<Pldmnd>(1);
return true;
}
void Driver::_handle_irq()
{
_irq.ack_irq();
if (!_block_transfer.pending) {
return; }
bool success = false;
if (Mmio::read<Rintsts::Response_error>()) {
error("Response error");
}
if (Mmio::read<Rintsts::Data_read_timeout>()) {
error("Data read timeout");
}
if (Mmio::read<Rintsts::Data_crc_error>()) {
error("CRC error");
}
if (Mmio::read<Rintsts::Data_transfer_over>()) {
Mmio::write<Rintsts>(~0U);
if (!_issue_command(Stop_transmission())) {
error("unable to stop transmission");
} else {
success = true;
}
}
_block_transfer.pending = false;
ack_packet(_block_transfer.packet, success);
}
bool Driver::_issue_command(Command_base const &command)
{
if (!wait_for<Status::Data_busy>(0, _delayer, 10000, 100)) {
error("wait for State::Data_busy timed out ",
Hex(Mmio::read<Status>()));
return false;
}
Mmio::write<Rintsts>(~0UL);
/* write command argument */
Mmio::write<Cmdarg>(command.arg);
Cmd::access_t cmd = 0;
Cmd::Index::set(cmd, command.index);
if (command.transfer != TRANSFER_NONE) {
/* set data-direction bit depending on the command */
bool const write = command.transfer == 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 RESPONSE_NONE: rsp_type = Cmd::Rsp_type::RESPONSE_NONE; break;
case RESPONSE_136_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_136_BIT; break;
case RESPONSE_48_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_48_BIT; break;
case 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 */
Mmio::write<Cmd>(cmd);
if (!wait_for<Rintsts::Command_done>(1, _delayer, 10000, 100)) {
error("command failed "
"Rintst: ", Mmio::read<Rintsts>(), " "
"Mintst: ", Mmio::read<Mintsts>(), " "
"Status: ", Mmio::read<Status>());
if (Mmio::read<Rintsts::Response_timeout>())
warning("timeout");
if (Mmio::read<Rintsts::Response_error>())
warning("repsonse error");
return false;
}
/* acknowledge interrupt */
Mmio::write<Rintsts::Command_done>(1);
_delayer.usleep(100);
return true;
}
Cid Driver::_read_cid()
{
Cid cid;
cid.raw_0 = Mmio::read<Rsp0>();
cid.raw_1 = Mmio::read<Rsp1>();
cid.raw_2 = Mmio::read<Rsp2>();
cid.raw_3 = Mmio::read<Rsp3>();
return cid;
}
Csd Driver::_read_csd()
{
Csd csd;
csd.csd0 = Mmio::read<Rsp0>();
csd.csd1 = Mmio::read<Rsp1>();
csd.csd2 = Mmio::read<Rsp2>();
csd.csd3 = Mmio::read<Rsp3>();
return csd;
}
size_t Driver::_read_ext_csd()
{
Attached_ram_dataspace ds(&_env.ram(), 0x1000, UNCACHED);
addr_t phys = Dataspace_client(ds.cap()).phys_addr();
_setup_idmac_descriptor_table(1, phys);
if (!issue_command(Mmc_send_ext_csd()))
throw Detection_failed();
if (!wait_for<Rintsts::Data_transfer_over>(1, _delayer, 10000, 100)) {
error("cannot retrieve extented CSD");
throw Detection_failed();
}
/* clear IRQ */
Mmio::write<Rintsts::Data_transfer_over>(1);
/* contruct extented CSD */
Ext_csd csd((addr_t)ds.local_addr<addr_t>());
/* read revision */
if (csd.Mmio::read<Ext_csd::Revision>() < 2) {
error("extented CSD revision is < 2");
throw Detection_failed();
}
/* return sector count */
uint64_t capacity = csd.Mmio::read<Ext_csd::Sector_count>() * block_size();
/* to MB */
return capacity / (1024 * 1024);
}
size_t Driver::Idmac_desc::set(size_t block_count,
size_t block_size,
addr_t phys_addr,
Flags flag)
{
enum { MAX_BLOCKS = 8 };
flags = OWN | flag |
(block_count <= MAX_BLOCKS ? LD : (CH | DIC));
bytes = ((block_count < MAX_BLOCKS) ? block_count : MAX_BLOCKS) *
block_size;
addr = phys_addr;
return block_count < MAX_BLOCKS ?
0 : block_count - MAX_BLOCKS;
}

View File

@ -1,59 +1,153 @@
/*
* \brief Exynos5-specific implementation of the Block::Driver interface
* \author Sebastian Sumpf
* \author Martin Stein
* \date 2013-03-22
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
* Copyright (C) 2015-2017 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 _DRIVERS__SD_CARD__SPEC__EXYNOS5__DRIVER_H_
#define _DRIVERS__SD_CARD__SPEC__EXYNOS5__DRIVER_H_
#ifndef _DRIVER_H_
#define _DRIVER_H_
/* Genode includes */
#include <util/mmio.h>
#include <os/attached_io_mem_dataspace.h>
#include <base/log.h>
#include <os/attached_mmio.h>
#include <timer_session/connection.h>
#include <os/server.h>
#include <drivers/board_base.h>
#include <regulator_session/connection.h>
#include <irq_session/connection.h>
#include <os/attached_ram_dataspace.h>
/* local includes */
#include <dwmmc.h>
#include <driver_base.h>
namespace Block {
using namespace Genode;
class Sdhci_driver;
}
namespace Sd_card { class Driver; }
class Block::Sdhci_driver : public Block::Driver,
public Sd_ack_handler
class Sd_card::Driver : public Driver_base,
private Attached_mmio
{
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,
HOST_FREQ = 52000000,
CLK_FREQ = 400000000,
CLK_DIV_52Mhz = 4,
CLK_DIV_400Khz = 0xff,
MSH_BASE = 0x12200000,
MSH_SIZE = 0x10000,
IDMAC_DESC_MAX_ENTRIES = 1024
};
enum Bus_width {
BUS_WIDTH_1 = 0,
BUS_WIDTH_4 = 1,
BUS_WIDTH_8 = 1 << 16,
};
template <off_t OFFSET, bool STRICT_WRITE = false>
struct Register : Mmio::Register<OFFSET, 32, STRICT_WRITE> { };
struct Ctrl : Register<0x0>
{
struct Reset : Bitfield<0, 3> { };
struct Global_interrupt : Bitfield<4, 1> { };
struct Dma_enable : Bitfield<5, 1> { };
struct Use_internal_dmac : Bitfield<25, 1> { };
};
struct Pwren : Register<0x4> { };
struct Clkdiv : Register<0x8> { };
struct Clkena : Register<0x10> { };
struct Tmout : Register<0x14> { };
struct Ctype : Register<0x18, true> { };
struct Blksize : Register<0x1c> { };
struct Bytcnt : Register<0x20> { };
struct Intmask : Register<0x24> { };
struct Cmdarg : Register<0x28> { };
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> { };
};
struct Rsp0 : Register<0x30> { };
struct Rsp1 : Register<0x34> { };
struct Rsp2 : Register<0x38> { };
struct Rsp3 : Register<0x3c> { };
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> { };
};
struct Status : Register<0x48>
{
struct Data_busy : Bitfield<9, 1> { };
};
struct Fifoth : Register<0x4c> { };
struct Bmod : Register<0x80, true>
{
struct Fixed_burst : Bitfield<1, 1> { };
struct Idmac_enable : Bitfield<7, 1> { };
};
struct Pldmnd : Register<0x84> { };
struct Idsts : Register<0x8c> { };
struct Idinten : Register<0x90, true> { };
struct Dbaddr : Register<0x88> { };
struct Clksel : Register<0x9c> { };
struct Emmc_ddr_req : Register<0x10c, true> { };
struct Idmac_desc
{
enum Flags {
NONE = 0,
DIC = 1 << 1,
LD = 1 << 2,
FS = 1 << 3,
CH = 1 << 4,
ER = 1 << 5,
OWN = 1 << 31,
};
unsigned flags;
unsigned bytes;
unsigned addr;
unsigned next;
size_t set(size_t block_count,
size_t block_size,
addr_t phys_addr,
Flags flag);
};
struct Clock_regulator
@ -62,74 +156,84 @@ class Block::Sdhci_driver : public Block::Driver,
Clock_regulator(Env &env) : regulator(env, Regulator::CLK_MMC0) {
regulator.state(true); }
};
} _clock_regulator;
struct Timer_delayer : Timer::Connection, Mmio::Delayer
{
void usleep(unsigned us) {
Timer::Connection::usleep(us); }
};
struct Block_transfer
{
Block::Packet_descriptor packet;
bool pending = false;
};
Env &_env;
Timer_delayer _delayer;
Block_transfer _block_transfer;
Clock_regulator _clock_regulator { _env };
Signal_handler<Driver> _irq_handler { _env.ep(), *this, &Driver::_handle_irq };
Irq_connection _irq { Board_base::SDMMC0_IRQ };
Attached_ram_dataspace _idmac_desc_ds { &_env.ram(),
IDMAC_DESC_MAX_ENTRIES * sizeof(Idmac_desc),
UNCACHED };
Idmac_desc *const _idmac_desc { _idmac_desc_ds.local_addr<Idmac_desc>() };
addr_t const _idmac_desc_phys { Dataspace_client(_idmac_desc_ds.cap())
.phys_addr() };
Card_info _card_info { _init() };
bool _reset();
void _reset_fifo();
void _disable_irq();
bool _update_clock_registers();
bool _setup_bus(unsigned clock_div);
void _handle_irq();
Card_info _init();
bool _setup_idmac_descriptor_table(size_t block_count,
addr_t phys_addr);
/* display sub system registers */
Attached_io_mem_dataspace _mmio;
/*********************
** Host_controller **
*********************/
/* mobile storage host controller instance */
Exynos5_msh_controller _controller;
bool _issue_command(Command_base const &command) override;
Cid _read_cid() override ;
Csd _read_csd() override ;
size_t _read_ext_csd() override;
unsigned _read_rca() override { return 0; }
bool const _use_dma;
Card_info card_info() const override { return _card_info; }
public:
Sdhci_driver(Env &env)
:
_clock_regulator(env),
_mmio(MSH_BASE, MSH_SIZE),
_controller(env.ep(), (addr_t)_mmio.local_addr<void>(),
_delayer, *this, true),
_use_dma(true)
{
Sd_card::Card_info const card_info = _controller.card_info();
using Block::Driver::read;
using Block::Driver::write;
Genode::log("SD/MMC card detected");
Genode::log("capacity: ", card_info.capacity_mb(), " MiB");
}
void handle_ack(Block::Packet_descriptor pkt, bool success) {
ack_packet(pkt, success); }
Driver(Env &env);
/*****************************
** Block::Driver interface **
*****************************/
/*******************
** Block::Driver **
*******************/
Genode::size_t block_size() { return 512; }
void read_dma(Block::sector_t block_number,
size_t block_count,
addr_t buf_phys,
Block::Packet_descriptor &pkt) override;
virtual Block::sector_t block_count()
{
return _controller.card_info().capacity_mb() * 1024 * 2;
}
void write_dma(Block::sector_t block_number,
size_t block_count,
addr_t buf_phys,
Block::Packet_descriptor &pkt) override;
Block::Session::Operations ops()
{
Block::Session::Operations o;
o.set_operation(Block::Packet_descriptor::READ);
o.set_operation(Block::Packet_descriptor::WRITE);
return o;
}
bool dma_enabled() override { return true; }
void read_dma(Block::sector_t block_number,
Genode::size_t block_count,
Genode::addr_t phys,
Packet_descriptor &packet)
{
_controller.read_blocks_dma(block_number, block_count, phys, packet);
}
void write_dma(Block::sector_t block_number,
Genode::size_t block_count,
Genode::addr_t phys,
Packet_descriptor &packet)
{
_controller.write_blocks_dma(block_number, block_count, phys, packet);
}
bool dma_enabled() { return _use_dma; }
Ram_dataspace_capability alloc_dma_buffer(size_t size) override {
return _env.ram().alloc(size, UNCACHED); }
};
#endif /* _DRIVERS__SD_CARD__SPEC__EXYNOS5__DRIVER_H_ */
#endif /* _DRIVER_H_ */

View File

@ -1,710 +0,0 @@
/*
* \brief DesignWare Multimedia Card interface
* \author Sebastian Sumpf
* \date 2013-03-06
*/
/*
* Copyright (C) 2015 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 _DRIVERS__SD_CARD__SPEC__EXYNOS5__DWMMC_H_
#define _DRIVERS__SD_CARD__SPEC__EXYNOS5__DWMMC_H_
#include <drivers/board_base.h>
#include <irq_session/connection.h>
#include <os/attached_ram_dataspace.h>
#include <util/mmio.h>
#include <block/component.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> { };
};
/**
* Poll demant register
*/
struct Pldmnd : Register<0x84> { };
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)) {
Genode::error("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))
Genode::error("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)) {
Genode::error("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 Sd_ack_handler {
virtual void handle_ack(Block::Packet_descriptor pkt, bool success) = 0;
};
struct Exynos5_msh_controller : private Dwmmc, Sd_card::Host_controller
{
private:
enum { BLOCK_SIZE = 512 };
/**
* DMA descriptpor
*/
struct Idmac_desc
{
enum Flags {
NONE = 0,
DIC = 1 << 1,
LD = 1 << 2,
FS = 1 << 3,
CH = 1 << 4,
ER = 1 << 5,
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)
{
enum { MAX_BLOCKS = 8 };
flags = OWN | flag | (block_count <= MAX_BLOCKS ? LD : (CH | DIC));
bytes = ((block_count < MAX_BLOCKS) ? block_count : MAX_BLOCKS) * BLOCK_SIZE;
addr = phys_addr;
return block_count < MAX_BLOCKS ? 0 : block_count - MAX_BLOCKS;
}
void dump()
{
Genode::log("this: ", this, " "
"flags: ", Genode::Hex(flags), " "
"bytes: ", bytes, " "
"addr: ", Genode::Hex(addr), " "
"next:", Genode::Hex(next));
}
};
/*
* 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;
struct Block_transfer
{
Block::Packet_descriptor packet;
bool pending = false;
} _block_transfer;
Sd_ack_handler &_ack_handler;
Genode::Signal_handler<Exynos5_msh_controller> _irq_handler;
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())) {
Genode::warning("Go_idle_state command failed");
throw Detection_failed();
}
_delayer.usleep(2000);
if (!issue_command(Send_if_cond())) {
Genode::warning("Send_if_cond command failed");
throw Detection_failed();
}
/* if this succeeds it is an SD card */
if ((read<Rsp0>() & 0xff) == 0xaa)
Genode::log("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))) {
Genode::warning("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) {
Genode::error("Send_op_cond timed out, could no power-on SD/MMC card");
throw Detection_failed();
}
Card_info card_info = _detect_mmc();
/* switch frequency to high speed */
enum { EXT_CSD_HS_TIMING = 185 };
if (!issue_command(Mmc_switch(EXT_CSD_HS_TIMING, 1))) {
Genode::error("Error setting high speed frequency");
throw Detection_failed();
}
enum { EXT_CSD_BUS_WIDTH = 183 };
/* set card to 8 bit */
if (!issue_command(Mmc_switch(EXT_CSD_BUS_WIDTH, 2))) {
Genode::error("Error setting card bus width");
throw Detection_failed();
}
bus_width(BUS_WIDTH_8);
/* set to eight bit transfer Bit */
if (!setup_bus(CLK_DIV_52Mhz, _delayer)) {
Genode::error("Error setting bus to high speed");
throw Detection_failed();
}
/*
* 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) {
Genode::error("Block request too large");
return false;
}
reset_fifo(_delayer);
Idmac_desc::Flags flags = Idmac_desc::FS;
size_t b = block_count;
int index = 0;
for (index = 0; b;
index++, phys_addr += 0x1000, flags = Idmac_desc::NONE) {
b = _idmac_desc[index].set(b, phys_addr, flags);
_idmac_desc[index].next = _idmac_desc_phys + ((index + 1) * sizeof(Idmac_desc));
}
_idmac_desc[index].next = (unsigned)_idmac_desc;
_idmac_desc[index].flags |= Idmac_desc::ER;
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);
write<Pldmnd>(1);
return true;
}
void _handle_irq()
{
_irq.ack_irq();
if (!_block_transfer.pending) {
return; }
bool success = false;
if (read<Rintsts::Response_error>()) {
Genode::error("Response error");
}
if (read<Rintsts::Data_read_timeout>()) {
Genode::error("Data read timeout");
}
if (read<Rintsts::Data_crc_error>()) {
Genode::error("CRC error");
}
if (read<Rintsts::Data_transfer_over>()) {
write<Rintsts>(~0U);
if (!_issue_command(Sd_card::Stop_transmission())) {
Genode::error("unable to stop transmission");
} else {
success = true;
}
}
_block_transfer.pending = false;
_ack_handler.handle_ack(_block_transfer.packet, success);
}
public:
enum { IRQ_NUMBER = Genode::Board_base::SDMMC0_IRQ };
Exynos5_msh_controller(Server::Entrypoint &ep,
Genode::addr_t const mmio_base, Delayer &delayer,
Sd_ack_handler &ack_handler,
bool use_dma)
: Dwmmc(mmio_base),
_idmac_desc_ds(Genode::env()->ram_session(),
IDMAC_DESC_MAX_ENTRIES*sizeof(Idmac_desc),
Genode::UNCACHED),
_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()),
_ack_handler(ack_handler),
_irq_handler(ep, *this, &Exynos5_msh_controller::_handle_irq),
_irq(IRQ_NUMBER)
{
_irq.sigh(_irq_handler);
_irq.ack_irq();
}
bool _issue_command(Sd_card::Command_base const &command)
{
if (verbose)
Genode::log("-> ", command);
if (!wait_for<Status::Data_busy>(0, _delayer, 10000, 100)) {
Genode::error("wait for State::Data_busy timed out ",
Genode::Hex(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)) {
Genode::error("command failed "
"Rintst: ", read<Rintsts>(), " "
"Mintst: ", read<Mintsts>(), " "
"Status: ", read<Status>());
if (read<Rintsts::Response_timeout>())
Genode::warning("timeout");
if (read<Rintsts::Response_error>())
Genode::warning("repsonse error");
return false;
}
/* 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, UNCACHED);
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)) {
Genode::error("cannot retrieve 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) {
Genode::error("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;
}
void read_blocks_dma(Block::sector_t block_number, size_t block_count,
Genode::addr_t buffer_phys, Block::Packet_descriptor pkt)
{
if (_block_transfer.pending) {
throw Block::Driver::Request_congestion(); }
if (!_setup_idmac_descriptor_table(block_count, buffer_phys))
throw Block::Driver::Io_error();
_block_transfer.packet = pkt;
_block_transfer.pending = true;
if (!_issue_command(Sd_card::Read_multiple_block(block_number))) {
Genode::error("Read_multiple_block failed, Status: ",
Genode::Hex(read<Status>()));
throw Block::Driver::Io_error();
}
}
void write_blocks_dma(Block::sector_t block_number, size_t block_count,
Genode::addr_t buffer_phys, Block::Packet_descriptor pkt)
{
if (_block_transfer.pending) {
throw Block::Driver::Request_congestion(); }
if (!_setup_idmac_descriptor_table(block_count, buffer_phys))
throw Block::Driver::Io_error();
_block_transfer.packet = pkt;
_block_transfer.pending = true;
if (!_issue_command(Sd_card::Write_multiple_block(block_number))) {
Genode::error("Read_multiple_block failed, Status: ",
Genode::Hex(read<Status>()));
throw Block::Driver::Io_error();
}
}
};
#endif /* _DRIVERS__SD_CARD__SPEC__EXYNOS5__DWMMC_H_ */

View File

@ -12,13 +12,13 @@
*/
/* local includes */
#include <sdhc.h>
#include <driver.h>
using namespace Sd_card;
using namespace Genode;
int Sdhc::_wait_for_card_ready_mbw()
int Driver::_wait_for_card_ready_mbw()
{
/*
* Poll card status
@ -69,7 +69,7 @@ int Sdhc::_wait_for_card_ready_mbw()
}
int Sdhc::_stop_transmission()
int Driver::_stop_transmission()
{
/* write argument register */
Mmio::write<Cmdarg>(0);
@ -90,7 +90,7 @@ int Sdhc::_stop_transmission()
}
void Sdhc::_handle_irq()
void Driver::_handle_irq()
{
_irq.ack_irq();
@ -125,7 +125,7 @@ void Sdhc::_handle_irq()
}
int Sdhc::_wait_for_cmd_complete()
int Driver::_wait_for_cmd_complete()
{
if (!wait_for<Irqstat::Cc>(1, _delayer, 200, 5000)) {
error("command timed out");
@ -136,7 +136,7 @@ int Sdhc::_wait_for_cmd_complete()
}
bool Sdhc::_issue_command(Command_base const & command)
bool Driver::_issue_command(Command_base const & command)
{
/* get command characteristics */
bool const transfer = command.transfer != TRANSFER_NONE;
@ -178,7 +178,7 @@ bool Sdhc::_issue_command(Command_base const & command)
}
Cid Sdhc::_read_cid()
Cid Driver::_read_cid()
{
Cid cid;
cid.raw_0 = Mmio::read<Rsp136_0>();
@ -189,7 +189,7 @@ Cid Sdhc::_read_cid()
}
Csd Sdhc::_read_csd()
Csd Driver::_read_csd()
{
Csd csd;
csd.csd0 = Mmio::read<Rsp136_0>();
@ -200,76 +200,65 @@ Csd Sdhc::_read_csd()
}
unsigned Sdhc::_read_rca()
unsigned Driver::_read_rca()
{
Cmdrsp0::access_t const rsp0 = Mmio::read<Cmdrsp0>();
return Send_relative_addr::Response::Rca::get(rsp0);
}
bool Sdhc::read_blocks(size_t, size_t, char *)
void Driver::read_dma(Block::sector_t blk_nr,
size_t blk_cnt,
addr_t buf_phys,
Block::Packet_descriptor &packet)
{
error("block transfer without DMA not supported");
return false;
if (_prepare_dma_mb(packet, true, blk_cnt, buf_phys)) {
throw Io_error(); }
if (!issue_command(Read_multiple_block(blk_nr))) {
throw Io_error(); }
}
bool Sdhc::write_blocks(size_t, size_t, char const *)
void Driver::write_dma(Block::sector_t blk_nr,
size_t blk_cnt,
addr_t buf_phys,
Block::Packet_descriptor &packet)
{
error("block transfer without DMA not supported");
return false;
if (_prepare_dma_mb(packet, false, blk_cnt, buf_phys)) {
throw Io_error(); }
if (!issue_command(Write_multiple_block(blk_nr))) {
throw Io_error(); }
}
bool Sdhc::read_blocks_dma(Block::Packet_descriptor packet,
size_t blk_nr,
size_t blk_cnt,
addr_t buf_phys)
{
if (_prepare_dma_mb(packet, true, blk_cnt, buf_phys)) { return false; }
return issue_command(Read_multiple_block(blk_nr));
}
bool Sdhc::write_blocks_dma(Block::Packet_descriptor packet,
size_t blk_nr,
size_t blk_cnt,
addr_t buf_phys)
{
if (_prepare_dma_mb(packet, false, blk_cnt, buf_phys)) { return false; }
return issue_command(Write_multiple_block(blk_nr));
}
Sdhc::Sdhc(Env &env)
Driver::Driver(Env &env)
:
Attached_mmio(Board_base::SDHC_MMIO_BASE, Board_base::SDHC_MMIO_SIZE),
_irq_handler(env.ep(), *this, &Sdhc::_handle_irq), _irq(Board_base::SDHC_IRQ),
_card_info(_init()), _use_dma(true)
_env(env)
{
log("SD card detected");
log("capacity: ", card_info().capacity_mb(), " MiB");
}
int Sdhc::_prepare_dma_mb(Block::Packet_descriptor packet,
int Driver::_prepare_dma_mb(Block::Packet_descriptor packet,
bool reading,
size_t blk_cnt,
addr_t buf_phys)
{
if (_block_transfer.pending) {
throw Block::Driver::Request_congestion(); }
throw Request_congestion(); }
/* write ADMA2 table to DMA */
size_t const req_size = blk_cnt * BLOCK_SIZE;
size_t const req_size = blk_cnt * block_size();
if (_adma2_table.setup_request(req_size, buf_phys)) { return -1; }
/* configure DMA at host */
Mmio::write<Adsaddr>(_adma2_table.base_phys());
Mmio::write<Blkattr::Blksize>(BLOCK_SIZE);
Mmio::write<Blkattr::Blksize>(block_size());
Mmio::write<Blkattr::Blkcnt>(blk_cnt);
_block_transfer.read = reading;
@ -279,7 +268,7 @@ int Sdhc::_prepare_dma_mb(Block::Packet_descriptor packet,
}
int Sdhc::_wait_for_cmd_allowed()
int Driver::_wait_for_cmd_allowed()
{
/*
* At least after multi-block writes on i.MX53 with the fix for the broken
@ -298,7 +287,7 @@ int Sdhc::_wait_for_cmd_allowed()
}
Card_info Sdhc::_init()
Card_info Driver::_init()
{
/* install IRQ signal */
_irq.sigh(_irq_handler);
@ -413,7 +402,7 @@ Card_info Sdhc::_init()
_delayer.usleep(10000);
/* configure card to use given block size */
if (!issue_command(Set_blocklen(BLOCK_SIZE))) {
if (!issue_command(Set_blocklen(block_size()))) {
_detect_err("Set_blocklen command failed"); }
/* configure host buffer */
@ -432,14 +421,14 @@ Card_info Sdhc::_init()
}
void Sdhc::_detect_err(char const * const err)
void Driver::_detect_err(char const * const err)
{
error(err);
throw Detection_failed();
}
int Sdhc::_reset(Delayer &delayer)
int Driver::_reset(Delayer &delayer)
{
/* start reset */
Mmio::write<Sysctl::Rsta>(1);
@ -454,14 +443,14 @@ int Sdhc::_reset(Delayer &delayer)
}
void Sdhc::_disable_irqs()
void Driver::_disable_irqs()
{
Mmio::write<Irqstaten>(0);
Mmio::write<Irqsigen>(0);
}
void Sdhc::_enable_irqs()
void Driver::_enable_irqs()
{
Irq::access_t irq = 0;
Irq::Cc::set(irq, 1);
@ -481,7 +470,7 @@ void Sdhc::_enable_irqs()
}
void Sdhc::_bus_width(Bus_width bus_width)
void Driver::_bus_width(Bus_width bus_width)
{
switch (bus_width) {
case BUS_WIDTH_1: Mmio::write<Proctl::Dtw>(Proctl::Dtw::_1BIT); break;
@ -490,7 +479,7 @@ void Sdhc::_bus_width(Bus_width bus_width)
}
void Sdhc::_disable_clock()
void Driver::_disable_clock()
{
_disable_clock_preparation();
Sysctl::access_t sysctl = Mmio::read<Sysctl>();
@ -503,7 +492,7 @@ void Sdhc::_disable_clock()
}
void Sdhc::_enable_clock(Clock_divider divider)
void Driver::_enable_clock(Clock_divider divider)
{
Sysctl::access_t sysctl = Mmio::read<Sysctl>();
Sysctl::Ipgen::set(sysctl, 1);
@ -529,7 +518,7 @@ void Sdhc::_enable_clock(Clock_divider divider)
}
void Sdhc::_clock(Clock clock)
void Driver::_clock(Clock clock)
{
wait_for<Prsstat::Sdstb>(1, _delayer);
_disable_clock();

View File

@ -1,11 +1,11 @@
/*
* \brief Implementation of the Block::Driver interface
* \brief Secured Digital Host Controller
* \author Martin Stein
* \date 2015-02-04
* \date 2015-02-05
*/
/*
* Copyright (C) 2012-2016 Genode Labs GmbH
* Copyright (C) 2015-2016 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.
@ -14,7 +14,285 @@
#ifndef _DRIVER_H_
#define _DRIVER_H_
/* Genode includes */
#include <drivers/board_base.h>
#include <timer_session/connection.h>
#include <irq_session/connection.h>
#include <os/attached_mmio.h>
/* local includes */
#include <sdhc.h>
#include <driver_base.h>
#include <adma2.h>
namespace Sd_card { class Driver; }
class Sd_card::Driver : public Driver_base,
private Attached_mmio
{
private:
enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 };
enum Clock { CLOCK_INITIAL, CLOCK_OPERATIONAL };
enum Clock_divider { CLOCK_DIV_4, CLOCK_DIV_8, CLOCK_DIV_512 };
/********************
** MMIO structure **
********************/
struct Blkattr : Register<0x4, 32>
{
struct Blksize : Bitfield<0, 13> { };
struct Blkcnt : Bitfield<16, 16> { };
};
template <off_t OFFSET>
struct Cmdrsp_tpl : Register<OFFSET, 32>
{
struct Rsp136_8_24 : Register<OFFSET, 32>::template Bitfield<0, 24> { };
struct Rsp136_0_8 : Register<OFFSET, 32>::template Bitfield<24, 8> { };
};
struct Cmdarg : Register<0x8, 32> { };
struct Cmdrsp0 : Cmdrsp_tpl<0x10> { };
struct Cmdrsp1 : Cmdrsp_tpl<0x14> { };
struct Cmdrsp2 : Cmdrsp_tpl<0x18> { };
struct Cmdrsp3 : Cmdrsp_tpl<0x1c> { };
struct Rsp136_0 : Bitset_2<Cmdrsp3::Rsp136_0_8, Cmdrsp0::Rsp136_8_24> { };
struct Rsp136_1 : Bitset_2<Cmdrsp0::Rsp136_0_8, Cmdrsp1::Rsp136_8_24> { };
struct Rsp136_2 : Bitset_2<Cmdrsp1::Rsp136_0_8, Cmdrsp2::Rsp136_8_24> { };
struct Rsp136_3 : Bitset_2<Cmdrsp2::Rsp136_0_8, Cmdrsp3::Rsp136_8_24> { };
template <off_t OFFSET>
struct Xfertyp_base : Register<OFFSET, 32>
{
struct Dmaen : Register<OFFSET, 32>::template Bitfield<0, 1> { };
struct Bcen : Register<OFFSET, 32>::template Bitfield<1, 1> { };
struct Ac12en : Register<OFFSET, 32>::template Bitfield<2, 1> { };
struct Dtdsel : Register<OFFSET, 32>::template Bitfield<4, 1>
{
enum { WRITE = 0, READ = 1, };
};
struct Msbsel : Register<OFFSET, 32>::template Bitfield<5, 1> { };
};
struct Mixctrl : Xfertyp_base<0x48>
{
struct Ddren : Bitfield<3, 1> { };
struct Nibblepos : Bitfield<6, 1> { };
struct Ac23en : Bitfield<7, 1> { };
struct Always_ones : Bitfield<31, 1> { };
};
struct Xfertyp : Xfertyp_base<0xc>
{
struct Rsptyp : Bitfield<16, 2>
{
enum {
_0BIT = 0,
_136BIT = 1,
_48BIT = 2,
_48BIT_BUSY = 3,
};
};
struct Cccen : Bitfield<19, 1> { };
struct Cicen : Bitfield<20, 1> { };
struct Dpsel : Bitfield<21, 1> { };
struct Cmdtyp : Bitfield<22, 2>
{
enum { ABORT_CMD12 = 3 };
};
struct Cmdinx : Bitfield<24, 6> { };
};
struct Prsstat : Register<0x24, 32>
{
struct Cihb : Bitfield<0, 1> { };
struct Cdihb : Bitfield<1, 1> { };
struct Dla : Bitfield<2, 1> { };
struct Sdstb : Bitfield<3, 1> { };
};
struct Proctl : Register<0x28, 32>
{
struct Dtw : Bitfield<1, 2>
{
enum { _1BIT = 0, _4BIT = 1 };
};
struct Dmas : Bitfield<8, 2> { enum { ADMA2 = 2 }; };
};
struct Sysctl : Register<0x2c, 32>
{
struct Ipgen : Bitfield<0, 1> { };
struct Hcken : Bitfield<1, 1> { };
struct Peren : Bitfield<2, 1> { };
struct Dvs : Bitfield<4, 4>
{
enum { DIV1 = 0x0, DIV4 = 0x3, DIV16 = 0xf, };
};
struct Sdclkfs : Bitfield<8, 8>
{
enum { DIV1 = 0x00, DIV2 = 0x01, DIV32 = 0x10, };
};
struct Dtocv : Bitfield<16, 4>
{
enum {
SDCLK_TIMES_2_POW_28 = 0xf,
SDCLK_TIMES_2_POW_27 = 0xe,
SDCLK_TIMES_2_POW_13 = 0x0,
};
};
struct Ipp_rst_n : Bitfield<23, 1> { };
struct Rsta : Bitfield<24, 1> { };
struct Rstc : Bitfield<25, 1> { };
struct Rstd : Bitfield<26, 1> { };
};
template <off_t OFFSET>
struct Irq_tpl : Register<OFFSET, 32>
{
struct Cc : Register<OFFSET, 32>::template Bitfield<0, 1> { };
struct Tc : Register<OFFSET, 32>::template Bitfield<1, 1> { };
struct Dint : Register<OFFSET, 32>::template Bitfield<3, 1> { };
struct Ctoe : Register<OFFSET, 32>::template Bitfield<16, 1> { };
struct Cce : Register<OFFSET, 32>::template Bitfield<17, 1> { };
struct Cebe : Register<OFFSET, 32>::template Bitfield<18, 1> { };
struct Cie : Register<OFFSET, 32>::template Bitfield<19, 1> { };
struct Dtoe : Register<OFFSET, 32>::template Bitfield<20, 1> { };
struct Dce : Register<OFFSET, 32>::template Bitfield<21, 1> { };
struct Debe : Register<OFFSET, 32>::template Bitfield<22, 1> { };
struct Ac12e : Register<OFFSET, 32>::template Bitfield<24, 1> { };
struct Dmae : Register<OFFSET, 32>::template Bitfield<28, 1> { };
};
struct Irq : Irq_tpl<0> { };
struct Irqstat : Irq_tpl<0x30> { };
struct Irqstaten : Irq_tpl<0x34> { };
struct Irqsigen : Irq_tpl<0x38> { };
struct Maxcurrent : Register<0x48, 32> { };
struct Adsaddr : Register<0x58, 32> { };
struct Hostver : Register<0xfc, 32>
{
struct Svn : Bitfield<0, 8> { };
struct Vvn : Bitfield<8, 8> { };
};
struct Wml : Register<0x44, 32>
{
struct Rd_wml : Bitfield<0, 8> { };
struct Rd_brst_len : Bitfield<8, 5> { };
struct Wr_wml : Bitfield<16, 8> { };
struct Wr_brst_len : Bitfield<24, 5> { };
};
struct Vendspec : Register<0xc0, 32>
{
struct Frc_sdclk_on : Bitfield<8, 1> { };
};
/************************
** Utility structures **
************************/
struct Timer_delayer : Timer::Connection, Mmio::Delayer
{
void usleep(unsigned us) { Timer::Connection::usleep(us); }
};
struct Block_transfer
{
Block::Packet_descriptor packet;
bool pending = false;
bool read;
};
Env &_env;
Block_transfer _block_transfer;
Timer_delayer _delayer;
Signal_handler<Driver> _irq_handler { _env.ep(), *this,
&Driver::_handle_irq };
Irq_connection _irq { Board_base::SDHC_IRQ };
Card_info _card_info { _init() };
Adma2::Table _adma2_table { _env.ram() };
static bool _supported_host_version(Hostver::access_t hostver);
static void _watermark_level(Wml::access_t &wml);
void _handle_irq();
void _detect_err(char const * const err);
void _disable_irqs();
void _enable_irqs();
void _bus_width(Bus_width bus_width);
void _disable_clock();
void _disable_clock_preparation();
void _enable_clock(Clock_divider divider);
void _enable_clock_finish();
void _clock(Clock clock);
void _clock_finish(Clock clock);
int _reset(Delayer & delayer);
void _reset_amendments();
int _wait_for_cmd_allowed();
int _wait_for_cmd_complete();
int _wait_for_card_ready_mbw();
int _stop_transmission();
void _stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp);
int _wait_for_cmd_complete_mb_finish(bool const reading);
int _prepare_dma_mb(Block::Packet_descriptor packet,
bool reading,
size_t blk_cnt,
addr_t buf_phys);
bool _issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
bool const transfer,
bool const multiblock,
bool const reading);
Card_info _init();
/*********************
** Host_controller **
*********************/
Cid _read_cid() override;
Csd _read_csd() override;
unsigned _read_rca() override;
bool _issue_command(Command_base const & cmd) override;
Card_info card_info() const override { return _card_info; }
public:
using Block::Driver::read;
using Block::Driver::write;
Driver(Env &env);
/*******************
** Block::Driver **
*******************/
void read_dma(Block::sector_t block_number,
size_t block_count,
addr_t phys,
Block::Packet_descriptor &packet) override;
void write_dma(Block::sector_t block_number,
size_t block_count,
addr_t phys,
Block::Packet_descriptor &packet) override;
bool dma_enabled() override { return true; }
Ram_dataspace_capability alloc_dma_buffer(size_t size) override {
return _env.ram().alloc(size, UNCACHED); }
};
#endif /* _DRIVER_H_ */

View File

@ -1,338 +0,0 @@
/*
* \brief Secured Digital Host Controller
* \author Martin Stein
* \date 2015-02-05
*/
/*
* Copyright (C) 2015 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 _SDHC_H_
#define _SDHC_H_
/* Genode includes */
#include <base/log.h>
#include <os/attached_io_mem_dataspace.h>
#include <timer_session/connection.h>
#include <util/mmio.h>
#include <irq_session/connection.h>
#include <base/sleep.h>
#include <block/driver.h>
#include <block/component.h>
#include <drivers/board_base.h>
#include <os/attached_mmio.h>
/* local includes */
#include <sd_card.h>
#include <adma2.h>
namespace Genode { class Sdhc; }
class Genode::Sdhc : public Attached_mmio,
public Block::Driver,
public Sd_card::Host_controller
{
private:
struct Blkattr : Register<0x4, 32>
{
struct Blksize : Bitfield<0, 13> { };
struct Blkcnt : Bitfield<16, 16> { };
};
template <off_t OFFSET>
struct Cmdrsp_tpl : Register<OFFSET, 32>
{
struct Rsp136_8_24 : Register<OFFSET, 32>::template Bitfield<0, 24> { };
struct Rsp136_0_8 : Register<OFFSET, 32>::template Bitfield<24, 8> { };
};
struct Cmdarg : Register<0x8, 32> { };
struct Cmdrsp0 : Cmdrsp_tpl<0x10> { };
struct Cmdrsp1 : Cmdrsp_tpl<0x14> { };
struct Cmdrsp2 : Cmdrsp_tpl<0x18> { };
struct Cmdrsp3 : Cmdrsp_tpl<0x1c> { };
struct Rsp136_0 : Bitset_2<Cmdrsp3::Rsp136_0_8, Cmdrsp0::Rsp136_8_24> { };
struct Rsp136_1 : Bitset_2<Cmdrsp0::Rsp136_0_8, Cmdrsp1::Rsp136_8_24> { };
struct Rsp136_2 : Bitset_2<Cmdrsp1::Rsp136_0_8, Cmdrsp2::Rsp136_8_24> { };
struct Rsp136_3 : Bitset_2<Cmdrsp2::Rsp136_0_8, Cmdrsp3::Rsp136_8_24> { };
template <off_t OFFSET>
struct Xfertyp_base : Register<OFFSET, 32>
{
struct Dmaen : Register<OFFSET, 32>::template Bitfield<0, 1> { };
struct Bcen : Register<OFFSET, 32>::template Bitfield<1, 1> { };
struct Ac12en : Register<OFFSET, 32>::template Bitfield<2, 1> { };
struct Dtdsel : Register<OFFSET, 32>::template Bitfield<4, 1>
{
enum { WRITE = 0, READ = 1, };
};
struct Msbsel : Register<OFFSET, 32>::template Bitfield<5, 1> { };
};
struct Mixctrl : Xfertyp_base<0x48>
{
struct Ddren : Bitfield<3, 1> { };
struct Nibblepos : Bitfield<6, 1> { };
struct Ac23en : Bitfield<7, 1> { };
struct Always_ones : Bitfield<31, 1> { };
};
struct Xfertyp : Xfertyp_base<0xc>
{
struct Rsptyp : Bitfield<16, 2>
{
enum {
_0BIT = 0,
_136BIT = 1,
_48BIT = 2,
_48BIT_BUSY = 3,
};
};
struct Cccen : Bitfield<19, 1> { };
struct Cicen : Bitfield<20, 1> { };
struct Dpsel : Bitfield<21, 1> { };
struct Cmdtyp : Bitfield<22, 2>
{
enum { ABORT_CMD12 = 3 };
};
struct Cmdinx : Bitfield<24, 6> { };
};
struct Prsstat : Register<0x24, 32>
{
struct Cihb : Bitfield<0, 1> { };
struct Cdihb : Bitfield<1, 1> { };
struct Dla : Bitfield<2, 1> { };
struct Sdstb : Bitfield<3, 1> { };
};
struct Proctl : Register<0x28, 32>
{
struct Dtw : Bitfield<1, 2>
{
enum { _1BIT = 0, _4BIT = 1 };
};
struct Dmas : Bitfield<8, 2> { enum { ADMA2 = 2 }; };
};
struct Sysctl : Register<0x2c, 32>
{
struct Ipgen : Bitfield<0, 1> { };
struct Hcken : Bitfield<1, 1> { };
struct Peren : Bitfield<2, 1> { };
struct Dvs : Bitfield<4, 4>
{
enum { DIV1 = 0x0, DIV4 = 0x3, DIV16 = 0xf, };
};
struct Sdclkfs : Bitfield<8, 8>
{
enum { DIV1 = 0x00, DIV2 = 0x01, DIV32 = 0x10, };
};
struct Dtocv : Bitfield<16, 4>
{
enum {
SDCLK_TIMES_2_POW_28 = 0xf,
SDCLK_TIMES_2_POW_27 = 0xe,
SDCLK_TIMES_2_POW_13 = 0x0,
};
};
struct Ipp_rst_n : Bitfield<23, 1> { };
struct Rsta : Bitfield<24, 1> { };
struct Rstc : Bitfield<25, 1> { };
struct Rstd : Bitfield<26, 1> { };
};
template <off_t OFFSET>
struct Irq_tpl : Register<OFFSET, 32>
{
struct Cc : Register<OFFSET, 32>::template Bitfield<0, 1> { };
struct Tc : Register<OFFSET, 32>::template Bitfield<1, 1> { };
struct Dint : Register<OFFSET, 32>::template Bitfield<3, 1> { };
struct Ctoe : Register<OFFSET, 32>::template Bitfield<16, 1> { };
struct Cce : Register<OFFSET, 32>::template Bitfield<17, 1> { };
struct Cebe : Register<OFFSET, 32>::template Bitfield<18, 1> { };
struct Cie : Register<OFFSET, 32>::template Bitfield<19, 1> { };
struct Dtoe : Register<OFFSET, 32>::template Bitfield<20, 1> { };
struct Dce : Register<OFFSET, 32>::template Bitfield<21, 1> { };
struct Debe : Register<OFFSET, 32>::template Bitfield<22, 1> { };
struct Ac12e : Register<OFFSET, 32>::template Bitfield<24, 1> { };
struct Dmae : Register<OFFSET, 32>::template Bitfield<28, 1> { };
};
struct Irq : Irq_tpl<0> { };
struct Irqstat : Irq_tpl<0x30> { };
struct Irqstaten : Irq_tpl<0x34> { };
struct Irqsigen : Irq_tpl<0x38> { };
struct Maxcurrent : Register<0x48, 32> { };
struct Adsaddr : Register<0x58, 32> { };
struct Hostver : Register<0xfc, 32>
{
struct Svn : Bitfield<0, 8> { };
struct Vvn : Bitfield<8, 8> { };
};
struct Wml : Register<0x44, 32>
{
struct Rd_wml : Bitfield<0, 8> { };
struct Rd_brst_len : Bitfield<8, 5> { };
struct Wr_wml : Bitfield<16, 8> { };
struct Wr_brst_len : Bitfield<24, 5> { };
};
struct Vendspec : Register<0xc0, 32>
{
struct Frc_sdclk_on : Bitfield<8, 1> { };
};
enum { BLOCK_SIZE = 512 };
enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 };
enum Clock { CLOCK_INITIAL, CLOCK_OPERATIONAL };
enum Clock_divider { CLOCK_DIV_4, CLOCK_DIV_8, CLOCK_DIV_512 };
struct Timer_delayer : Timer::Connection, Mmio::Delayer
{
void usleep(unsigned us) { Timer::Connection::usleep(us); }
} _delayer;
struct Block_transfer
{
Block::Packet_descriptor packet;
bool pending = false;
bool read;
} _block_transfer;
Signal_handler<Sdhc> _irq_handler;
Irq_connection _irq;
Sd_card::Card_info _card_info;
bool const _use_dma;
Adma2::Table _adma2_table;
static bool _supported_host_version(Hostver::access_t hostver);
static void _watermark_level(Wml::access_t &wml);
void _handle_irq();
void _detect_err(char const * const err);
void _disable_irqs();
void _enable_irqs();
void _bus_width(Bus_width bus_width);
void _disable_clock();
void _disable_clock_preparation();
void _enable_clock(Clock_divider divider);
void _enable_clock_finish();
void _clock(Clock clock);
void _clock_finish(Clock clock);
int _reset(Delayer & delayer);
void _reset_amendments();
int _wait_for_cmd_allowed();
int _wait_for_cmd_complete();
int _wait_for_card_ready_mbw();
int _stop_transmission();
void _stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp);
int _wait_for_cmd_complete_mb_finish(bool const reading);
int _prepare_dma_mb(Block::Packet_descriptor packet, bool reading, size_t blk_cnt, addr_t buf_phys);
bool _issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
bool const transfer,
bool const multiblock,
bool const reading);
Sd_card::Card_info _init();
/****************************************
** Sd_card::Host_controller interface **
****************************************/
Sd_card::Cid _read_cid();
Sd_card::Csd _read_csd();
unsigned _read_rca();
bool _issue_command(Sd_card::Command_base const & command);
public:
/**
* Constructor
*/
Sdhc(Env &env);
/****************************************
** Sd_card::Host_controller interface **
****************************************/
bool read_blocks(size_t, size_t, char *);
bool write_blocks(size_t, size_t, char const *);
bool read_blocks_dma(Block::Packet_descriptor packet, size_t blk_nr, size_t blk_cnt, addr_t buf_phys);
bool write_blocks_dma(Block::Packet_descriptor packet, size_t blk_nr, size_t blk_cnt, addr_t buf_phys);
Sd_card::Card_info card_info() const { return _card_info; }
/*****************************
** Block::Driver interface **
*****************************/
Genode::size_t block_size() { return 512; }
virtual Block::sector_t block_count()
{
return card_info().capacity_mb() * 1024 * 2;
}
Block::Session::Operations ops()
{
Block::Session::Operations o;
o.set_operation(Block::Packet_descriptor::READ);
o.set_operation(Block::Packet_descriptor::WRITE);
return o;
}
void read(Block::sector_t block_number,
Genode::size_t block_count,
char *out_buffer,
Block::Packet_descriptor &packet)
{
// if (!read_blocks(block_number, block_count, out_buffer))
throw Io_error();
}
void write(Block::sector_t block_number,
Genode::size_t block_count,
char const *buffer,
Block::Packet_descriptor &packet)
{
// if (!write_blocks(block_number, block_count, buffer))
throw Io_error();
}
void read_dma(Block::sector_t block_number,
Genode::size_t block_count,
Genode::addr_t phys,
Block::Packet_descriptor &packet)
{
if (!read_blocks_dma(packet, block_number, block_count, phys))
throw Io_error();
}
void write_dma(Block::sector_t block_number,
Genode::size_t block_count,
Genode::addr_t phys,
Block::Packet_descriptor &packet)
{
if (!write_blocks_dma(packet, block_number, block_count, phys))
throw Io_error();
}
bool dma_enabled() { return _use_dma; }
Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size) {
return Genode::env()->ram_session()->alloc(size, UNCACHED); }
void free_dma_buffer(Genode::Ram_dataspace_capability c) {
return Genode::env()->ram_session()->free(c); }
};
namespace Block { using Sdhci_driver = Genode::Sdhc; }
#endif /* _SDHC_H_ */

View File

@ -12,13 +12,13 @@
*/
/* local includes */
#include <sdhc.h>
#include <driver.h>
using namespace Sd_card;
using namespace Genode;
void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
void Driver::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
{
Xfertyp::Msbsel::set(xfertyp, 1);
Xfertyp::Bcen::set(xfertyp, 1);
@ -26,7 +26,7 @@ void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
}
int Sdhc::_wait_for_cmd_complete_mb_finish(bool const reading)
int Driver::_wait_for_cmd_complete_mb_finish(bool const reading)
{
if (reading) { return 0; }
@ -48,7 +48,7 @@ int Sdhc::_wait_for_cmd_complete_mb_finish(bool const reading)
}
bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
bool Driver::_issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
bool const transfer,
bool const multiblock,
bool const reading)
@ -65,8 +65,7 @@ bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
if (reading) {
Xfertyp::Ac12en::set(xfertyp, 1); }
if (_use_dma) {
Xfertyp::Dmaen::set(xfertyp, 1); }
Xfertyp::Dmaen::set(xfertyp, 1);
}
Xfertyp::Dtdsel::set(xfertyp,
reading ? Xfertyp::Dtdsel::READ : Xfertyp::Dtdsel::WRITE);
@ -75,21 +74,21 @@ bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
}
bool Sdhc::_supported_host_version(Hostver::access_t hostver)
bool Driver::_supported_host_version(Hostver::access_t hostver)
{
return Hostver::Vvn::get(hostver) == 18 &&
Hostver::Svn::get(hostver) == 1;
}
void Sdhc::_watermark_level(Wml::access_t &wml)
void Driver::_watermark_level(Wml::access_t &wml)
{
Wml::Wr_wml::set(wml, 16);
Wml::Wr_brst_len::set(wml, 8);
}
void Sdhc::_reset_amendments()
void Driver::_reset_amendments()
{
/*
* The SDHC specification says that a software reset shouldn't
@ -106,7 +105,7 @@ void Sdhc::_reset_amendments()
}
void Sdhc::_clock_finish(Clock clock)
void Driver::_clock_finish(Clock clock)
{
Mmio::write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
switch (clock) {
@ -116,5 +115,5 @@ void Sdhc::_clock_finish(Clock clock)
}
void Sdhc::_disable_clock_preparation() { }
void Sdhc::_enable_clock_finish() { }
void Driver::_disable_clock_preparation() { }
void Driver::_enable_clock_finish() { }

View File

@ -12,13 +12,13 @@
*/
/* local includes */
#include <sdhc.h>
#include <driver.h>
using namespace Sd_card;
using namespace Genode;
void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
void Driver::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
{
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
Mixctrl::Dmaen::set(mixctrl, 1);
@ -33,20 +33,20 @@ void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
}
int Sdhc::_wait_for_cmd_complete_mb_finish(bool const reading)
int Driver::_wait_for_cmd_complete_mb_finish(bool const reading)
{
/* we can't use the "Auto Command 12" feature as it does not work */
return _stop_transmission() ? -1 : 0;
}
bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &,
bool Driver::_issue_cmd_finish_xfertyp(Xfertyp::access_t &,
bool const transfer,
bool const multiblock,
bool const reading)
{
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
Mixctrl::Dmaen ::set(mixctrl, transfer && multiblock && _use_dma);
Mixctrl::Dmaen ::set(mixctrl, transfer && multiblock);
Mixctrl::Bcen ::set(mixctrl, transfer);
Mixctrl::Ac12en ::set(mixctrl, 0);
Mixctrl::Msbsel ::set(mixctrl, transfer);
@ -64,21 +64,21 @@ bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &,
}
bool Sdhc::_supported_host_version(Hostver::access_t hostver)
bool Driver::_supported_host_version(Hostver::access_t hostver)
{
return Hostver::Vvn::get(hostver) == 0 &&
Hostver::Svn::get(hostver) == 3;
}
void Sdhc::_watermark_level(Wml::access_t &wml)
void Driver::_watermark_level(Wml::access_t &wml)
{
Wml::Wr_wml::set(wml, 64);
Wml::Wr_brst_len::set(wml, 16);
}
void Sdhc::_reset_amendments()
void Driver::_reset_amendments()
{
/* the USDHC doesn't reset the Mixer Control register automatically */
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
@ -95,7 +95,7 @@ void Sdhc::_reset_amendments()
}
void Sdhc::_clock_finish(Clock clock)
void Driver::_clock_finish(Clock clock)
{
switch (clock) {
case CLOCK_INITIAL:
@ -111,6 +111,7 @@ void Sdhc::_clock_finish(Clock clock)
}
void Sdhc::_disable_clock_preparation() { Mmio::write<Vendspec::Frc_sdclk_on>(0); }
void Driver::_disable_clock_preparation() {
Mmio::write<Vendspec::Frc_sdclk_on>(0); }
void Sdhc::_enable_clock_finish() { Mmio::write<Vendspec::Frc_sdclk_on>(0); }
void Driver::_enable_clock_finish() { Mmio::write<Vendspec::Frc_sdclk_on>(0); }

View File

@ -0,0 +1,443 @@
/*
* \brief OMAP4-specific implementation of the Block::Driver interface
* \author Norman Feske
* \date 2012-07-19
*/
/*
* Copyright (C) 2012-2017 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.
*/
/* local includes */
#include <driver.h>
using namespace Genode;
using namespace Sd_card;
Card_info Driver::_init()
{
Mmio::write<Sysconfig>(0x2015);
Mmio::write<Hctl>(0x0);
_set_bus_power(VOLTAGE_3_0);
if (!_sd_bus_power_on()) {
error("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)) {
error("set_clock failed");
throw Detection_failed();
}
if (!_init_stream()) {
error("sending the initialization stream failed");
throw Detection_failed();
}
Mmio::write<Blk>(0);
_delayer.usleep(1000);
if (!issue_command(Go_idle_state())) {
error("Go_idle_state command failed");
throw Detection_failed();
}
_delayer.usleep(2000);
if (!issue_command(Send_if_cond())) {
error("Send_if_cond command failed");
throw Detection_failed();
}
if (Mmio::read<Rsp10>() != 0x1aa) {
error("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))) {
warning("Sd_send_op_cond command failed");
throw Detection_failed();
}
if (Ocr::Busy::get(Mmio::read<Rsp10>()))
break;
_delayer.usleep(1000);
}
if (i == 0) {
error("Sd_send_op_cond timed out, could no power-on SD card");
throw Detection_failed();
}
Card_info card_info = _detect();
/*
* Switch card to use 4 data signals
*/
if (!issue_command(Set_bus_width(Set_bus_width::Arg::Bus_width::FOUR_BITS),
card_info.rca())) {
warning("Set_bus_width(FOUR_BITS) command failed");
throw Detection_failed();
}
_bus_width(BUS_WIDTH_4);
_delayer.usleep(10*1000);
_stop_clock();
if (!_set_and_enable_clock(CLOCK_DIV_0)) {
error("set_clock failed");
throw Detection_failed();
}
/* enable IRQs */
Mmio::write<Ie::Tc_enable>(1);
Mmio::write<Ie::Cto_enable>(1);
Mmio::write<Ise::Tc_sigen>(1);
Mmio::write<Ise::Cto_sigen>(1);
return card_info;
}
bool Driver::_wait_for_bre()
{
if (!wait_for<Pstate::Bre>(1, _delayer, 1000*1000, 0)) {
if (!wait_for<Pstate::Bre>(1, _delayer)) {
error("Pstate::Bre timed out");
return false;
}
}
return true;
}
bool Driver::_wait_for_bwe()
{
if (!wait_for<Pstate::Bwe>(1, _delayer, 1000*1000, 0)) {
if (!wait_for<Pstate::Bwe>(1, _delayer)) {
error("Pstate::Bwe timed out");
return false;
}
}
return true;
}
void Driver::_handle_irq()
{
_irq.ack_irq();
if (!_block_transfer.pending) {
return; }
if (Mmio::read<Stat::Tc>() != 1) {
warning("unexpected interrupt, Stat: ", Hex(Mmio::read<Stat>()));
return;
}
Mmio::write<Stat::Tc>(1);
if (Mmio::read<Stat>() != 0) {
warning("unexpected state ("
"Stat: ", Hex(Mmio::read<Stat>()), " "
"Blen: ", Hex(Mmio::read<Blk::Blen>()), " "
"Nblk: ", Mmio::read<Blk::Nblk>());
return;
}
_block_transfer.pending = false;
ack_packet(_block_transfer.packet, true);
}
bool Driver::_reset_cmd_line()
{
Mmio::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)) {
error("reset of cmd line timed out (src != 1)");
return false;
}
if (!wait_for<Sysctl::Src>(0, _delayer, 1000, 0)) {
error("reset of cmd line timed out (src != 0)");
return false;
}
return true;
}
void Driver::_disable_irq()
{
Mmio::write<Ise>(0);
Mmio::write<Ie>(0);
Mmio::write<Stat>(~0);
}
void Driver::_bus_width(Bus_width bus_width)
{
switch (bus_width) {
case BUS_WIDTH_1:
Mmio::write<Con::Dw8>(0);
Mmio::write<Hctl::Dtw>(Hctl::Dtw::ONE_BIT);
break;
case BUS_WIDTH_4:
Mmio::write<Con::Dw8>(0);
Mmio::write<Hctl::Dtw>(Hctl::Dtw::FOUR_BITS);
break;
}
}
bool Driver::_sd_bus_power_on()
{
Mmio::write<Hctl::Sdbp>(Hctl::Sdbp::POWER_ON);
if (!wait_for<Hctl::Sdbp>(1, _delayer)) {
error("setting Hctl::Sdbp timed out");
return false;
}
return true;
}
bool Driver::_set_and_enable_clock(enum Clock_divider divider)
{
Mmio::write<Sysctl::Dto>(Sysctl::Dto::TCF_2_POW_27);
switch (divider) {
case CLOCK_DIV_0: Mmio::write<Sysctl::Clkd>(0); break;
case CLOCK_DIV_240: Mmio::write<Sysctl::Clkd>(240); break;
}
Mmio::write<Sysctl::Ice>(1);
/* wait for clock to become stable */
if (!wait_for<Sysctl::Ics>(1, _delayer)) {
error("clock enable timed out");
return false;
}
/* enable clock */
Mmio::write<Sysctl::Ce>(1);
return true;
}
void Driver::_set_bus_power(Voltage voltage)
{
switch (voltage) {
case VOLTAGE_3_0:
Mmio::write<Hctl::Sdvs>(Hctl::Sdvs::VOLTAGE_3_0);
break;
case VOLTAGE_1_8:
Mmio::write<Hctl::Sdvs>(Hctl::Sdvs::VOLTAGE_1_8);
break;
}
Mmio::write<Capa::Vs18>(1);
if (voltage == VOLTAGE_3_0)
Mmio::write<Capa::Vs30>(1);
}
bool Driver::_init_stream()
{
Mmio::write<Ie>(0x307f0033);
/* start initialization sequence */
Mmio::write<Con::Init>(1);
Mmio::write<Cmd>(0);
if (!wait_for<Stat::Cc>(1, _delayer, 1000*1000, 0)) {
error("init stream timed out");
return false;
}
/* stop initialization sequence */
Mmio::write<Con::Init>(0);
Mmio::write<Stat>(~0);
Mmio::read<Stat>();
return true;
}
bool Driver::_issue_command(Command_base const &command)
{
if (!wait_for<Pstate::Cmdi>(0, _delayer)) {
error("wait for Pstate::Cmdi timed out");
return false;
}
/* write command argument */
Mmio::write<Arg>(command.arg);
/* assemble command register */
Cmd::access_t cmd = 0;
Cmd::Index::set(cmd, command.index);
if (command.transfer != TRANSFER_NONE) {
Cmd::Dp::set(cmd);
Cmd::Bce::set(cmd);
Cmd::Msbs::set(cmd);
if (command.index == Read_multiple_block::INDEX ||
command.index == Write_multiple_block::INDEX)
{
Cmd::Acen::set(cmd);
}
/* set data-direction bit depending on the command */
bool const read = command.transfer == TRANSFER_READ;
Cmd::Ddir::set(cmd, read ? Cmd::Ddir::READ : Cmd::Ddir::WRITE);
}
Cmd::access_t rsp_type = 0;
switch (command.rsp_type) {
case RESPONSE_NONE: rsp_type = Cmd::Rsp_type::RESPONSE_NONE; break;
case RESPONSE_136_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_136_BIT; break;
case RESPONSE_48_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_48_BIT; break;
case 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 */
Mmio::write<Cmd>(cmd);
bool result = false;
/* wait until command is completed, return false on timeout */
for (unsigned long i = 0; i < 1000*1000; i++) {
Stat::access_t const stat = Mmio::read<Stat>();
if (Stat::Erri::get(stat)) {
warning("SD command error");
if (Stat::Cto::get(stat))
warning("timeout");
_reset_cmd_line();
Mmio::write<Stat::Cc>(~0);
Mmio::read<Stat>();
result = false;
break;
}
if (Stat::Cc::get(stat) == 1) {
result = true;
break;
}
}
/* clear status of command-completed bit */
Mmio::write<Stat::Cc>(1);
Mmio::read<Stat>();
return result;
}
Cid Driver::_read_cid()
{
Cid cid;
cid.raw_0 = Mmio::read<Rsp10>();
cid.raw_1 = Mmio::read<Rsp32>();
cid.raw_2 = Mmio::read<Rsp54>();
cid.raw_3 = Mmio::read<Rsp76>();
return cid;
}
Csd Driver::_read_csd()
{
Csd csd;
csd.csd0 = Mmio::read<Rsp10>();
csd.csd1 = Mmio::read<Rsp32>();
csd.csd2 = Mmio::read<Rsp54>();
csd.csd3 = Mmio::read<Rsp76>();
return csd;
}
Driver::Driver(Env &env)
:
Attached_mmio(MMCHS1_MMIO_BASE, MMCHS1_MMIO_SIZE), _ep(env.ep())
{
_irq.sigh(_irq_handler);
_irq.ack_irq();
log("SD card detected");
log("capacity: ", _card_info.capacity_mb(), " MiB");
}
void Driver::read(Block::sector_t block_number,
size_t block_count,
char *buffer,
Block::Packet_descriptor &pkt)
{
if (_block_transfer.pending) {
throw Request_congestion(); }
Mmio::write<Blk::Blen>(block_size());
Mmio::write<Blk::Nblk>(block_count);
_block_transfer.packet = pkt;
_block_transfer.pending = true;
if (!issue_command(Read_multiple_block(block_number))) {
error("Read_multiple_block failed");
throw Io_error();
}
size_t const num_accesses = block_count * block_size() /
sizeof(Data::access_t);
Data::access_t *dst = (Data::access_t *)(buffer);
for (size_t i = 0; i < num_accesses; i++) {
if (!_wait_for_bre())
throw Io_error();
*dst++ = Mmio::read<Data>();
}
}
void Driver::write(Block::sector_t block_number,
size_t block_count,
char const *buffer,
Block::Packet_descriptor &pkt)
{
if (_block_transfer.pending) {
throw Request_congestion(); }
Mmio::write<Blk::Blen>(block_size());
Mmio::write<Blk::Nblk>(block_count);
_block_transfer.packet = pkt;
_block_transfer.pending = true;
if (!issue_command(Write_multiple_block(block_number))) {
error("Write_multiple_block failed");
throw Io_error();
}
size_t const num_accesses = block_count * block_size() /
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()) {
throw Io_error(); }
Mmio::write<Data>(*src++);
}
}

View File

@ -5,137 +5,217 @@
*/
/*
* Copyright (C) 2012-2015 Genode Labs GmbH
* Copyright (C) 2012-2017 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 _DRIVERS__SD_CARD__SPEC__OMAP4__DRIVER_H_
#define _DRIVERS__SD_CARD__SPEC__OMAP4__DRIVER_H_
#ifndef _DRIVER_H_
#define _DRIVER_H_
#include <util/mmio.h>
#include <os/attached_io_mem_dataspace.h>
#include <base/log.h>
/* Genode includes */
#include <os/attached_mmio.h>
#include <drivers/board_base.h>
#include <timer_session/connection.h>
#include <os/server.h>
#include <irq_session/connection.h>
/* local includes */
#include <mmchs.h>
#include <driver_base.h>
namespace Block {
using namespace Genode;
class Sdhci_driver;
}
namespace Sd_card { class Driver; }
class Block::Sdhci_driver : public Block::Driver,
public Sd_ack_handler
class Sd_card::Driver : public Driver_base,
private Attached_mmio
{
private:
struct Timer_delayer : Timer::Connection, Mmio::Delayer
{
/**
* Implementation of 'Delayer' interface
*/
void usleep(unsigned us) { Timer::Connection::usleep(us); }
} _delayer;
/* memory map */
enum {
MMCHS1_MMIO_BASE = 0x4809c000,
MMCHS1_MMIO_SIZE = 0x00001000,
};
/* display sub system registers */
Attached_io_mem_dataspace _mmchs1_mmio;
enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 };
/* hsmmc controller instance */
Omap4_hsmmc_controller _controller;
enum Clock_divider { CLOCK_DIV_0, CLOCK_DIV_240 };
bool const _use_dma;
enum Voltage { VOLTAGE_3_0, VOLTAGE_1_8 };
struct Sysconfig : Register<0x110, 32> { };
struct Con : Register<0x12c, 32>
{
struct Init : Bitfield<1, 1> { };
struct Dw8 : Bitfield<5, 1> { };
};
struct Cmd : Register<0x20c, 32>
{
struct Bce : Bitfield<1, 1> { };
struct Acen : Bitfield<2, 1> { };
struct Msbs : Bitfield<5, 1> { };
struct Index : Bitfield<24, 6> { };
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 };
};
struct Ddir : Bitfield<4, 1>
{
enum { WRITE = 0, READ = 1 };
};
};
struct Blk : Register<0x204, 32>
{
struct Blen : Bitfield<0, 12> { };
struct Nblk : Bitfield<16, 16> { };
};
struct Arg : Register<0x208, 32> { };
struct Rsp10 : Register<0x210, 32> { };
struct Rsp32 : Register<0x214, 32> { };
struct Rsp54 : Register<0x218, 32> { };
struct Rsp76 : Register<0x21c, 32> { };
struct Data : Register<0x220, 32> { };
struct Pstate : Register<0x224, 32>
{
struct Cmdi : Bitfield<0, 1> { };
struct Bwe : Bitfield<10, 1> { };
struct Bre : Bitfield<11, 1> { };
};
struct Hctl : Register<0x228, 32>
{
struct Sdbp : Bitfield<8, 1>
{
enum { POWER_OFF = 0, POWER_ON = 1 };
};
struct Sdvs : Bitfield<9, 3>
{
enum Voltage { VOLTAGE_1_8 = 5,
VOLTAGE_3_0 = 6,
VOLTAGE_3_3 = 7 };
};
struct Dtw : Bitfield<1, 1>
{
enum { ONE_BIT = 0, FOUR_BITS = 1 };
};
};
struct Sysctl : Register<0x22c, 32>
{
struct Ice : Bitfield<0, 1> { };
struct Ics : Bitfield<1, 1> { };
struct Ce : Bitfield<2, 1> { };
struct Clkd : Bitfield<6, 10> { };
struct Src : Bitfield<25, 1> { };
struct Dto : Bitfield<16, 4>
{
enum { TCF_2_POW_27 = 0xe };
};
};
struct Stat : Register<0x230, 32>
{
struct Tc : Bitfield<1, 1> { };
struct Cc : Bitfield<0, 1> { };
struct Erri : Bitfield<15, 1> { };
struct Cto : Bitfield<16, 1> { };
};
struct Ie : Register<0x234, 32>
{
struct Tc_enable : Bitfield<1, 1> { };
struct Cto_enable : Bitfield<16, 1> { };
};
struct Ise : Register<0x238, 32>
{
struct Tc_sigen : Bitfield<1, 1> { };
struct Cto_sigen : Bitfield<16, 1> { };
};
struct Capa : Register<0x240, 32>
{
struct Vs30 : Bitfield<25, 1> { };
struct Vs18 : Bitfield<26, 1> { };
};
struct Block_transfer
{
Block::Packet_descriptor packet;
bool pending = false;
};
struct Timer_delayer : Timer::Connection, Mmio::Delayer
{
void usleep(unsigned us) { Timer::Connection::usleep(us); }
};
Entrypoint &_ep;
Block_transfer _block_transfer;
Timer_delayer _delayer;
Signal_handler<Driver> _irq_handler { _ep, *this, &Driver::_handle_irq };
Irq_connection _irq { Board_base::HSMMC_IRQ };
Card_info _card_info { _init() };
Card_info _init();
bool _wait_for_bre();
bool _wait_for_bwe();
void _handle_irq();
bool _reset_cmd_line();
void _disable_irq();
void _bus_width(Bus_width bus_width);
bool _sd_bus_power_on();
bool _set_and_enable_clock(enum Clock_divider divider);
void _set_bus_power(Voltage voltage);
bool _init_stream();
void _stop_clock() { Mmio::write<Sysctl::Ce>(0); }
/*********************
** Host_controller **
*********************/
bool _issue_command(Command_base const &command) override;
Cid _read_cid() override;
Csd _read_csd() override;
Card_info card_info() const override { return _card_info; }
unsigned _read_rca() override {
return Send_relative_addr::Response::Rca::get(Mmio::read<Rsp10>()); }
public:
struct Dma_not_supported : Exception { };
Sdhci_driver(Env &env)
:
_mmchs1_mmio(MMCHS1_MMIO_BASE, MMCHS1_MMIO_SIZE),
_controller(env.ep(), (addr_t)_mmchs1_mmio.local_addr<void>(),
_delayer, *this, false),
_use_dma(false)
{
if (_use_dma) { throw Dma_not_supported(); }
Sd_card::Card_info const card_info = _controller.card_info();
Genode::log("SD card detected");
Genode::log("capacity: ", card_info.capacity_mb(), " MiB");
}
void handle_ack(Block::Packet_descriptor pkt, bool success) {
ack_packet(pkt, success); }
Driver(Env &env);
/*****************************
** Block::Driver interface **
*****************************/
/*******************
** Block::Driver **
*******************/
Genode::size_t block_size() { return 512; }
void read(Block::sector_t block_number,
size_t block_count,
char *buffer,
Block::Packet_descriptor &pkt) override;
virtual Block::sector_t block_count()
{
return _controller.card_info().capacity_mb() * 1024 * 2;
}
Block::Session::Operations ops()
{
Block::Session::Operations o;
o.set_operation(Block::Packet_descriptor::READ);
o.set_operation(Block::Packet_descriptor::WRITE);
return o;
}
void read(Block::sector_t block_number,
Genode::size_t block_count,
char *out_buffer,
Packet_descriptor &packet)
{
_controller.read_blocks(block_number, block_count, out_buffer, packet);
}
void write(Block::sector_t block_number,
Genode::size_t block_count,
char const *buffer,
Packet_descriptor &packet)
{
_controller.write_blocks(block_number, block_count, buffer, packet);
}
void read_dma(Block::sector_t block_number,
Genode::size_t block_count,
Genode::addr_t phys,
Packet_descriptor &packet)
{
_controller.read_blocks_dma(block_number, block_count, phys, packet);
}
void write_dma(Block::sector_t block_number,
Genode::size_t block_count,
Genode::addr_t phys,
Packet_descriptor &packet)
{
_controller.write_blocks_dma(block_number, block_count, phys, packet);
}
bool dma_enabled() { return _use_dma; }
Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size) {
return Genode::env()->ram_session()->alloc(size, UNCACHED); }
void free_dma_buffer(Genode::Ram_dataspace_capability c) {
return Genode::env()->ram_session()->free(c); }
void write(Block::sector_t block_number,
size_t block_count,
char const *buffer,
Block::Packet_descriptor &pkt) override;
};
#endif /* _DRIVERS__SD_CARD__SPEC__OMAP4__DRIVER_H_ */
#endif /* _DRIVER_H_ */

View File

@ -1,999 +0,0 @@
/*
* \brief OMAP4 MMCHS controller registers
* \author Norman Feske
* \date 2012-07-03
*/
/*
* Copyright (C) 2012-2015 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 _DRIVERS__SD_CARD__SPEC__OMAP4__MMCHS_H_
#define _DRIVERS__SD_CARD__SPEC__OMAP4__MMCHS_H_
/* Genode includes */
#include <util/mmio.h>
#include <os/attached_ram_dataspace.h>
#include <irq_session/connection.h>
#include <drivers/board_base.h>
#include <block/component.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> { };
/**
* Master master slave selection (set if master DMA)
*/
struct Dma_mns : Bitfield<20, 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> { };
/**
* DMA enable
*/
struct De : Bitfield<0, 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> { };
/**
* Transfer completed
*/
struct Tc_enable : Bitfield<1, 1> { };
/**
* Card interrupt
*/
struct Cirq_enable : Bitfield<8, 1> { };
/**
* Command timeout error
*/
struct Cto_enable : Bitfield<16, 1> { };
};
struct Ise : Register<0x238, 32>
{
/*
* The naming follows the lines of the 'Ie' register
*/
struct Tc_sigen : Bitfield<1, 1> { };
struct Cto_sigen : Bitfield<16, 1> { };
};
/**
* Capabilities
*/
struct Capa : Register<0x240, 32>
{
struct Vs30 : Bitfield<25, 1> { };
struct Vs18 : Bitfield<26, 1> { };
};
/**
* ADMA system address
*
* Base address of the ADMA descriptor table
*/
struct Admasal : Register<0x258, 32> { };
/**
* ADMA descriptor layout
*/
struct Adma_desc : Genode::Register<64>
{
struct Valid : Bitfield<0, 1> { };
struct Ent : Bitfield<1, 1> { };
struct Int : Bitfield<2, 1> { };
struct Act1 : Bitfield<4, 1> { };
struct Act2 : Bitfield<5, 1> { };
struct Length : Bitfield<16, 16> { };
struct Address : Bitfield<32, 32> { };
};
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)) {
Genode::error("reset of cmd line timed out (src != 1)");
return false;
}
if (!wait_for<Sysctl::Src>(0, delayer, 1000, 0)) {
Genode::error("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)) {
Genode::error("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_4 };
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_4:
write<Con::Dw8>(0);
write<Hctl::Dtw>(Hctl::Dtw::FOUR_BITS);
break;
}
}
bool sd_bus_power_on(Delayer &delayer)
{
write<Hctl::Sdbp>(Hctl::Sdbp::POWER_ON);
if (!wait_for<Hctl::Sdbp>(1, delayer)) {
Genode::error("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)) {
Genode::error("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)) {
Genode::error("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 Sd_ack_handler {
virtual void handle_ack(Block::Packet_descriptor pkt, bool success) = 0;
};
struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
{
private:
struct Block_transfer
{
Block::Packet_descriptor packet;
bool pending = false;
} _block_transfer;
Delayer &_delayer;
Sd_card::Card_info _card_info;
bool const _use_dma;
/*
* DMA memory for holding the ADMA descriptor table
*/
enum { ADMA_DESC_MAX_ENTRIES = 1024 };
Genode::Attached_ram_dataspace _adma_desc_ds;
Adma_desc::access_t * const _adma_desc;
Genode::addr_t const _adma_desc_phys;
Sd_ack_handler &_ack_handler;
Genode::Signal_handler<Omap4_hsmmc_controller> _irq_handler;
Genode::Irq_connection _irq;
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)) {
Genode::error("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)) {
Genode::error("set_clock failed");
throw Detection_failed();
}
if (!init_stream(_delayer)) {
Genode::error("sending the initialization stream failed");
throw Detection_failed();
}
write<Blk>(0);
_delayer.usleep(1000);
if (!issue_command(Go_idle_state())) {
Genode::error("Go_idle_state command failed");
throw Detection_failed();
}
_delayer.usleep(2000);
if (!issue_command(Send_if_cond())) {
Genode::error("Send_if_cond command failed");
throw Detection_failed();
}
if (read<Rsp10>() != 0x1aa) {
Genode::error("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))) {
Genode::warning("Sd_send_op_cond command failed");
throw Detection_failed();
}
if (Ocr::Busy::get(read<Rsp10>()))
break;
_delayer.usleep(1000);
}
if (i == 0) {
Genode::error("Sd_send_op_cond timed out, could no power-on SD card");
throw Detection_failed();
}
Card_info card_info = _detect();
/*
* Switch card to use 4 data signals
*/
if (!issue_command(Set_bus_width(Set_bus_width::Arg::Bus_width::FOUR_BITS),
card_info.rca())) {
Genode::warning("Set_bus_width(FOUR_BITS) command failed");
throw Detection_failed();
}
bus_width(BUS_WIDTH_4);
_delayer.usleep(10*1000);
stop_clock();
if (!set_and_enable_clock(CLOCK_DIV_0, _delayer)) {
Genode::error("set_clock failed");
throw Detection_failed();
}
/* enable master DMA */
write<Con::Dma_mns>(1);
/* enable IRQs */
write<Ie::Tc_enable>(1);
write<Ie::Cto_enable>(1);
write<Ise::Tc_sigen>(1);
write<Ise::Cto_sigen>(1);
return card_info;
}
/**
* Marshal ADMA descriptors according to block request
*
* \return false if block request is too large
*/
bool _setup_adma_descriptor_table(size_t block_count,
Genode::addr_t out_buffer_phys)
{
using namespace Sd_card;
/* reset ADMA offset to first descriptor */
write<Admasal>(_adma_desc_phys);
enum { BLOCK_SIZE = 512 /* bytes */ };
size_t const max_adma_request_size = 64*1024 - 4; /* bytes */
/*
* sanity check
*
* XXX An alternative to this sanity check would be to expose
* the maximum DMA transfer size to the driver and let the
* driver partition large requests into ones that are
* supported by the controller.
*/
if (block_count*BLOCK_SIZE > max_adma_request_size*ADMA_DESC_MAX_ENTRIES) {
Genode::error("Block request too large");
return false;
}
/*
* Each ADMA descriptor can transfer up to MAX_ADMA_REQUEST_SIZE
* bytes. If the request is larger, we generate a list of ADMA
* descriptors.
*/
size_t const total_bytes = block_count*BLOCK_SIZE;
/* number of bytes for which descriptors have been created */
addr_t consumed_bytes = 0;
for (int index = 0; consumed_bytes < total_bytes; index++) {
size_t const remaining_bytes = total_bytes - consumed_bytes;
/* clamp current request to maximum ADMA request size */
size_t const curr_bytes = Genode::min(max_adma_request_size,
remaining_bytes);
/*
* Assemble new ADMA descriptor
*/
Adma_desc::access_t desc = 0;
Adma_desc::Address::set(desc, out_buffer_phys + consumed_bytes);
Adma_desc::Length::set(desc, curr_bytes);
/* set action to transfer */
Adma_desc::Act1::set(desc, 0);
Adma_desc::Act2::set(desc, 1);
Adma_desc::Valid::set(desc, 1);
/*
* Let the last descriptor generate transfer-complete interrupt
*/
if (consumed_bytes + curr_bytes == total_bytes)
Adma_desc::Ent::set(desc, 1);
/* install descriptor into ADMA descriptor table */
_adma_desc[index] = desc;
consumed_bytes += curr_bytes;
}
return true;
}
bool _wait_for_bre()
{
if (!wait_for<Pstate::Bre>(1, _delayer, 1000*1000, 0)
&& !wait_for<Pstate::Bre>(1, _delayer)) {
Genode::error("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)) {
Genode::error("Pstate::Bwe timed out");
return false;
}
return true;
}
void _handle_irq()
{
_irq.ack_irq();
if (!_block_transfer.pending) {
return; }
if (read<Stat::Tc>() != 1) {
Genode::warning("unexpected interrupt, Stat: ", Genode::Hex(read<Stat>()));
return;
}
write<Stat::Tc>(1);
if (read<Stat>() != 0) {
Genode::warning("unexpected state ("
"Stat: ", Genode::Hex(read<Stat>()), " "
"Blen: ", Genode::Hex(read<Blk::Blen>()), " "
"Nblk: ", read<Blk::Nblk>());
return;
}
_block_transfer.pending = false;
_ack_handler.handle_ack(_block_transfer.packet, true);
}
public:
enum { IRQ_NUMBER = Genode::Board_base::HSMMC_IRQ };
/**
* Constructor
*
* \param mmio_base local base address of MMIO registers
*/
Omap4_hsmmc_controller(Genode::Entrypoint &ep,
Genode::addr_t const mmio_base, Delayer &delayer,
Sd_ack_handler &ack_handler, bool use_dma)
:
Mmchs(mmio_base), _delayer(delayer), _card_info(_init()),
_use_dma(use_dma),
_adma_desc_ds(Genode::env()->ram_session(),
ADMA_DESC_MAX_ENTRIES*sizeof(Adma_desc::access_t),
Genode::UNCACHED),
_adma_desc(_adma_desc_ds.local_addr<Adma_desc::access_t>()),
_adma_desc_phys(Genode::Dataspace_client(_adma_desc_ds.cap()).phys_addr()),
_ack_handler(ack_handler),
_irq_handler(ep, *this, &Omap4_hsmmc_controller::_handle_irq),
_irq(IRQ_NUMBER)
{
_irq.sigh(_irq_handler);
_irq.ack_irq();
}
/****************************************
** Sd_card::Host_controller interface **
****************************************/
bool _issue_command(Sd_card::Command_base const &command)
{
if (verbose)
Genode::log("-> ", command);
if (!wait_for<Pstate::Cmdi>(0, _delayer)) {
Genode::error("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.transfer != Sd_card::TRANSFER_NONE) {
Cmd::Dp::set(cmd);
Cmd::Bce::set(cmd);
Cmd::Msbs::set(cmd);
if (command.index == Sd_card::Read_multiple_block::INDEX
|| command.index == Sd_card::Write_multiple_block::INDEX) {
Cmd::Acen::set(cmd);
if (_use_dma)
Cmd::De::set(cmd);
}
/* set data-direction bit depending on the command */
bool const read = command.transfer == Sd_card::TRANSFER_READ;
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 = 0; i < 1000*1000; i++) {
Stat::access_t const stat = read<Stat>();
if (Stat::Erri::get(stat)) {
Genode::warning("SD command error");
if (Stat::Cto::get(stat))
Genode::warning("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)
Genode::log("<- ", 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>());
}
void read_blocks(size_t block_number, size_t block_count, char *out_buffer,
Block::Packet_descriptor pkt)
{
using namespace Sd_card;
if (_block_transfer.pending) {
throw Block::Driver::Request_congestion(); }
write<Blk::Blen>(0x200);
write<Blk::Nblk>(block_count);
_block_transfer.packet = pkt;
_block_transfer.pending = true;
if (!issue_command(Read_multiple_block(block_number))) {
Genode::error("Read_multiple_block failed");
throw Block::Driver::Io_error();
}
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())
throw Block::Driver::Io_error();
*dst++ = read<Data>();
}
}
void write_blocks(size_t block_number, size_t block_count, char const *buffer,
Block::Packet_descriptor pkt)
{
using namespace Sd_card;
if (_block_transfer.pending) {
throw Block::Driver::Request_congestion(); }
write<Blk::Blen>(0x200);
write<Blk::Nblk>(block_count);
_block_transfer.packet = pkt;
_block_transfer.pending = true;
if (!issue_command(Write_multiple_block(block_number))) {
Genode::error("Write_multiple_block failed");
throw Block::Driver::Io_error();
}
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())
throw Block::Driver::Io_error();
write<Data>(*src++);
}
}
void read_blocks_dma(size_t block_number, size_t block_count,
Genode::addr_t out_buffer_phys,
Block::Packet_descriptor pkt)
{
using namespace Sd_card;
if (_block_transfer.pending) {
throw Block::Driver::Request_congestion(); }
write<Blk::Blen>(0x200);
write<Blk::Nblk>(block_count);
_setup_adma_descriptor_table(block_count, out_buffer_phys);
_block_transfer.packet = pkt;
_block_transfer.pending = true;
if (!issue_command(Read_multiple_block(block_number))) {
Genode::error("Read_multiple_block failed");
throw Block::Driver::Io_error();
}
}
void write_blocks_dma(size_t block_number, size_t block_count,
Genode::addr_t buffer_phys,
Block::Packet_descriptor pkt)
{
using namespace Sd_card;
if (_block_transfer.pending) {
throw Block::Driver::Request_congestion(); }
write<Blk::Blen>(0x200);
write<Blk::Nblk>(block_count);
_setup_adma_descriptor_table(block_count, buffer_phys);
_block_transfer.packet = pkt;
_block_transfer.pending = true;
if (!issue_command(Write_multiple_block(block_number))) {
Genode::error("Write_multiple_block failed");
throw Block::Driver::Io_error();
}
}
};
#endif /* _DRIVERS__SD_CARD__SPEC__OMAP4__MMCHS_H_ */

View File

@ -0,0 +1,239 @@
/*
* \brief PL180-specific implementation of the Block::Driver interface
* \author Christian Helmuth
* \author Martin Stein
* \date 2011-05-19
*/
/*
* Copyright (C) 2011-2017 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.
*/
/* local includes */
#include <driver.h>
using namespace Genode;
using namespace Sd_card;
void Driver::_write_command(unsigned cmd_index, bool resp)
{
enum Bits {
CmdIndex = 0x3f,
Response = 1 << 6,
Enable = 1 << 10
};
cmd_index = (cmd_index & CmdIndex) | Enable;
cmd_index |= (resp ? Response : 0);
_write_reg(Command, cmd_index);
while (!_read_reg(Status) & (CmdRespEnd | CmdSent)) ;
}
void Driver::_request(unsigned char cmd,
unsigned *out_resp)
{
_write_reg(Argument, 0);
_write_command(cmd, (out_resp != 0));
if (out_resp)
*out_resp = _read_reg(Response0);
_clear_status();
}
void Driver::_request(unsigned char cmd,
unsigned arg,
unsigned *out_resp)
{
_write_reg(Argument, arg);
_write_command(cmd, (out_resp != 0));
if (out_resp)
*out_resp = _read_reg(Response0);
_clear_status();
}
void Driver::_read_request(unsigned char cmd,
unsigned arg,
unsigned length,
unsigned *out_resp)
{
/*
* FIXME on real hardware the blocksize must be written into
* DataCtrl:BlockSize.
*/
enum { CTRL_ENABLE = 0x01, CTRL_READ = 0x02 };
_write_reg(DataLength, length);
_write_reg(DataCtrl, CTRL_ENABLE | CTRL_READ);
_write_reg(Argument, arg);
_write_command(cmd, (out_resp != 0));
if (out_resp)
*out_resp = _read_reg(Response0);
_clear_status();
}
void Driver::_write_request(unsigned char cmd,
unsigned arg,
unsigned length,
unsigned *out_resp)
{
/*
* FIXME on real hardware the blocksize must be written into
* DataCtrl:BlockSize.
*/
enum { CTRL_ENABLE = 0x01 };
_write_reg(DataLength, length);
_write_reg(DataCtrl, CTRL_ENABLE);
_write_reg(Argument, arg);
_write_command(cmd, (out_resp != 0));
if (out_resp)
*out_resp = _read_reg(Response0);
_clear_status();
}
void Driver::_read_data(unsigned length,
char *out_buffer)
{
unsigned *buf = reinterpret_cast<unsigned *>(out_buffer);
for (unsigned count = 0; count < length / 4; ) {
/*
* The FIFO contains at least 'remainder - FifoCnt' words.
*/
int chunk = length / 4 - count - _read_reg(FifoCnt);
count += chunk;
for ( ; chunk > 0; --chunk)
buf[count - chunk] = _read_reg(FIFO);
}
_clear_status();
}
void Driver::_write_data(unsigned length,
char const *buffer)
{
enum { FIFO_SIZE = 16 };
unsigned const *buf = reinterpret_cast<unsigned const *>(buffer);
for (unsigned count = 0; count < length / 4; ) {
uint32_t status;
while (!((status = _read_reg(Status)) & TxFifoHalfEmpty)) ;
int chunk = (status & TxFifoEmpty) ? FIFO_SIZE : FIFO_SIZE / 2;
count += chunk;
for ( ; chunk > 0; --chunk)
_write_reg(FIFO, buf[count - chunk]);
}
_clear_status();
}
Driver::Driver(Env &env) : Attached_mmio(PL180_PHYS, PL180_SIZE), _timer(env)
{
enum { POWER_UP = 2, POWER_ON = 3 };
_write_reg(Power, POWER_UP);
_timer.msleep(10);
_write_reg(Power, POWER_ON);
_timer.msleep(10);
_clear_status();
/* CMD0: go idle state */
_request(0, 0);
/*
* CMD8: send interface condition
*
* XXX only one hard-coded value currently.
*/
unsigned resp;
_request(8, 0x1aa, &resp);
/*
* ACMD41: card send operating condition
*
* This is an application-specific command and, therefore, consists
* of prefix command CMD55 + CMD41.
*/
_request(55, 0, &resp);
_request(41, 0x4000, &resp);
/* CMD2: all send card identification (CID) */
_request(2, &resp);
/* CMD3: send relative card address (RCA) */
_request(3, &resp);
unsigned short rca = resp >> 16;
/*
* Now, the card is in transfer mode...
*/
/* CMD7: select card */
_request(7, rca << 16, &resp);
}
void Driver::read(Block::sector_t block_number,
size_t block_count,
char *buffer,
Block::Packet_descriptor &packet)
{
unsigned resp;
unsigned length = block_size();
for (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.
*/
_read_request(17, (block_number + i) * block_size(),
length, &resp);
_read_data(length, buffer + (i * block_size()));
}
ack_packet(packet);
}
void Driver::write(Block::sector_t block_number,
size_t block_count,
char const *buffer,
Block::Packet_descriptor &packet)
{
unsigned resp;
unsigned length = block_size();
for (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.
*/
_write_request(24, (block_number + i) * block_size(),
length, &resp);
_write_data(length, buffer + (i * block_size()));
}
ack_packet(packet);
}
Block::Session::Operations Driver::ops()
{
Block::Session::Operations ops;
ops.set_operation(Block::Packet_descriptor::READ);
ops.set_operation(Block::Packet_descriptor::WRITE);
return ops;
}

View File

@ -1,11 +1,12 @@
/*
* \brief SD-card protocol
* \brief PL180-specific implementation of the Block::Driver interface
* \author Christian Helmuth
* \author Martin Stein
* \date 2011-05-19
*/
/*
* Copyright (C) 2011-2013 Genode Labs GmbH
* Copyright (C) 2011-2017 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.
@ -14,131 +15,122 @@
#ifndef _DRIVER_H_
#define _DRIVER_H_
/* Genode includes */
#include <block/driver.h>
/* local includes */
#include <pl180.h>
#include <os/attached_mmio.h>
#include <block/driver.h>
#include <pl180_defs.h>
#include <timer_session/connection.h>
namespace Block {
namespace Sd_card {
using namespace Genode;
class Sdhci_driver;
class Driver;
}
class Block::Sdhci_driver : public Block::Driver
class Sd_card::Driver : public Block::Driver, private Attached_mmio
{
private:
Pl180 _hd;
enum Register {
Power = 0x000, /* power control */
Argument = 0x008, /* argument for command */
Command = 0x00c, /* command index and type */
Response0 = 0x014, /* command response (card status, read only) */
DataLength = 0x028, /* number of bytes in data transfer (block size) */
DataCtrl = 0x02c, /* data transfer control */
Status = 0x034, /* controller status flags (read only) */
Clear = 0x038, /* status clear (write only) */
Mask0 = 0x03c, /* interrupt 0 mask */
Mask1 = 0x040, /* interrupt 1 mask */
FifoCnt = 0x048, /* data FIFO counter (in words, read only) */
FIFO = 0x080, /* data FIFO */
};
enum { BLOCK_SIZE = 512 };
enum Flag {
CmdCrcFail = 0x000001, /* command response received (CRC failed) */
DataCrcFail = 0x000002, /* data block sent/received (CRC failed) */
CmdTimeOut = 0x000004, /* command response timeout */
DataTimeOut = 0x000008, /* data timeout */
TxUnderrun = 0x000010, /* tx fifo underrun */
RxUnderrun = 0x000020, /* rx fifo underrun */
CmdRespEnd = 0x000040, /* command response received (CRC ok) */
CmdSent = 0x000080, /* command sent (no response required) */
DataEnd = 0x000100, /* data counter zero */
StartBitErr = 0x000200, /* start bit not detected */
DataBlockEnd = 0x000400, /* data block sent/received (CRC ok) */
CmdActive = 0x000800, /* command transfer in progress */
TxActive = 0x001000, /* data tx in progress */
RxActive = 0x002000, /* data rx in progress */
TxFifoHalfEmpty = 0x004000,
RxFifoHalfFull = 0x008000,
TxFifoFull = 0x010000,
RxFifoFull = 0x020000,
TxFifoEmpty = 0x040000,
RxFifoEmpty = 0x080000,
TxDataAvlbl = 0x100000,
RxDataAvlbl = 0x200000,
};
Timer::Connection _timer;
uint32_t volatile *_base { local_addr<unsigned volatile>() };
uint32_t _read_reg(Register reg) const { return _base[reg >> 2]; }
void _write_reg(Register reg, uint32_t v) { _base[reg >> 2] = v; }
void _clear_status() { _write_reg(Clear, ~0); }
void _request(unsigned char cmd,
unsigned *out_resp);
void _request(unsigned char cmd,
unsigned arg,
unsigned *out_resp);
void _read_request(unsigned char cmd,
unsigned arg,
unsigned length,
unsigned *out_resp);
void _write_request(unsigned char cmd,
unsigned arg,
unsigned length,
unsigned *out_resp);
void _read_data(unsigned length, char *out_buffer);
void _write_data(unsigned length, char const *buffer);
void _write_command(unsigned cmd_index, bool resp);
public:
Sdhci_driver(Env &) : _hd(PL180_PHYS, PL180_SIZE)
{
unsigned resp;
/* CMD0: go idle state */
_hd.request(0, 0);
Driver(Env &env);
/*
* 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);
/******************
** Block-driver **
******************/
/* CMD2: all send card identification (CID) */
_hd.request(2, &resp);
Genode::size_t block_size() override { return 512; }
Block::Session::Operations ops() override;
/* CMD3: send relative card address (RCA) */
_hd.request(3, &resp);
unsigned short rca = resp >> 16;
void read(Block::sector_t block_number,
size_t block_count,
char *buffer,
Block::Packet_descriptor &packet);
/*
* Now, the card is in transfer mode...
*/
void write(Block::sector_t block_number,
size_t block_count,
char const *buffer,
Block::Packet_descriptor &packet);
/* 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
*/
Block::sector_t block_count() { return 0x20000000 / BLOCK_SIZE; }
Block::Session::Operations ops()
{
Block::Session::Operations o;
o.set_operation(Block::Packet_descriptor::READ);
o.set_operation(Block::Packet_descriptor::WRITE);
return o;
}
void read(Block::sector_t block_number,
Genode::size_t block_count,
char *out_buffer,
Block::Packet_descriptor &packet)
{
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));
}
ack_packet(packet);
}
void write(Block::sector_t block_number,
Genode::size_t block_count,
char const *buffer,
Block::Packet_descriptor &packet)
{
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));
}
ack_packet(packet);
}
Block::sector_t block_count() override {
return 0x20000000 / block_size(); }
};
#endif /* _DRIVER_H_ */

View File

@ -1,86 +0,0 @@
/*
* \brief SD-card host-interface driver
* \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 _HOST_DRIVER_H_
#define _HOST_DRIVER_H_
struct Host_driver
{
/**
* Send request
*
* \param cmd command index
* \param out_resp response for command
* (set to 0 if not expected)
*/
virtual void request(unsigned char cmd,
unsigned *out_resp) = 0;
/**
* Send request
*
* \param cmd command index
* \param arg command argument
* \param out_resp response for command
* (set to 0 if not expected)
*/
virtual void request(unsigned char cmd,
unsigned arg,
unsigned *out_resp) = 0;
/**
* Send data-read request
*
* \param cmd command index
* \param arg command argument
* \param length number of bytes in data transfer
* \param out_resp response for command
* (set to 0 if not expected)
*/
virtual void read_request(unsigned char cmd,
unsigned arg,
unsigned length,
unsigned *out_resp) = 0;
/**
* Send data-write request
*
* \param cmd command index
* \param arg command argument
* \param length number of bytes in data transfer
* \param out_resp response for command
* (set to 0 if not expected)
*/
virtual void write_request(unsigned char cmd,
unsigned arg,
unsigned length,
unsigned *out_resp) = 0;
/**
* Read data
*
* Used to read the data after a successful data-read request.
*/
virtual void read_data(unsigned length,
char *out_buffer) = 0;
/**
* Write data
*
* Used to write the data after a successful data-write request.
*/
virtual void write_data(unsigned length,
char const *buffer) = 0;
};
#endif /* _HOST_DRIVER_H_ */

View File

@ -1,232 +0,0 @@
/*
* \brief PL180 MMCI driver
* \author Christian Helmuth
* \date 2011-05-19
*/
/*
* Copyright (C) 2011-2016 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 _DRIVERS__SD_CARD__SPEC__PL180__PL180_H_
#define _DRIVERS__SD_CARD__SPEC__PL180__PL180_H_
#include <base/log.h>
#include <os/attached_io_mem_dataspace.h>
#include <timer_session/connection.h>
#include "host_driver.h"
class Pl180 : public Host_driver
{
private:
/*
* Constants for flags, registers, etc. only visible in this file are
* exceptionally named in mixed case to relieve correlation between
* implementation and specification.
*/
/**
* Register offsets
*
* Registers are read/writable unless explicitly stated.
*/
enum Register {
Power = 0x000, /* power control */
Argument = 0x008, /* argument for command */
Command = 0x00c, /* command index and type */
Response0 = 0x014, /* command response (card status, read only) */
DataLength = 0x028, /* number of bytes in data transfer (block size) */
DataCtrl = 0x02c, /* data transfer control */
Status = 0x034, /* controller status flags (read only) */
Clear = 0x038, /* status clear (write only) */
Mask0 = 0x03c, /* interrupt 0 mask */
Mask1 = 0x040, /* interrupt 1 mask */
FifoCnt = 0x048, /* data FIFO counter (in words, read only) */
FIFO = 0x080, /* data FIFO */
};
/**
* Flags
*/
enum Flag {
CmdCrcFail = 0x000001, /* command response received (CRC failed) */
DataCrcFail = 0x000002, /* data block sent/received (CRC failed) */
CmdTimeOut = 0x000004, /* command response timeout */
DataTimeOut = 0x000008, /* data timeout */
TxUnderrun = 0x000010, /* tx fifo underrun */
RxUnderrun = 0x000020, /* rx fifo underrun */
CmdRespEnd = 0x000040, /* command response received (CRC ok) */
CmdSent = 0x000080, /* command sent (no response required) */
DataEnd = 0x000100, /* data counter zero */
StartBitErr = 0x000200, /* start bit not detected */
DataBlockEnd = 0x000400, /* data block sent/received (CRC ok) */
CmdActive = 0x000800, /* command transfer in progress */
TxActive = 0x001000, /* data tx in progress */
RxActive = 0x002000, /* data rx in progress */
TxFifoHalfEmpty = 0x004000,
RxFifoHalfFull = 0x008000,
TxFifoFull = 0x010000,
RxFifoFull = 0x020000,
TxFifoEmpty = 0x040000,
RxFifoEmpty = 0x080000,
TxDataAvlbl = 0x100000,
RxDataAvlbl = 0x200000,
};
Timer::Connection _timer;
Genode::Attached_io_mem_dataspace _io_mem;
Genode::uint32_t volatile *_base;
Genode::uint32_t _read_reg(Register reg) const
{
return _base[reg >> 2];
}
void _write_reg(Register reg, Genode::uint32_t v)
{
_base[reg >> 2] = v;
}
void _write_command(unsigned cmd_index, bool resp)
{
enum Bits {
CmdIndex = 0x3f,
Response = 1 << 6,
Enable = 1 << 10
};
cmd_index = (cmd_index & CmdIndex) | Enable;
cmd_index |= (resp ? Response : 0);
_write_reg(Command, cmd_index);
while (!_read_reg(Status) & (CmdRespEnd | CmdSent)) ;
}
void _clear_status() { _write_reg(Clear, ~0); }
public:
Pl180(Genode::addr_t mmio_base, Genode::size_t mmio_size)
:
_io_mem(mmio_base, mmio_size),
_base(_io_mem.local_addr<unsigned volatile>())
{
enum { POWER_UP = 2, POWER_ON = 3 };
_write_reg(Power, POWER_UP);
_timer.msleep(10);
_write_reg(Power, POWER_ON);
_timer.msleep(10);
_clear_status();
}
/***************************
** Host driver interface **
***************************/
void request(unsigned char cmd,
unsigned *out_resp)
{
_write_reg(Argument, 0);
_write_command(cmd, (out_resp != 0));
if (out_resp)
*out_resp = _read_reg(Response0);
_clear_status();
}
void request(unsigned char cmd,
unsigned arg,
unsigned *out_resp)
{
_write_reg(Argument, arg);
_write_command(cmd, (out_resp != 0));
if (out_resp)
*out_resp = _read_reg(Response0);
_clear_status();
}
void read_request(unsigned char cmd,
unsigned arg,
unsigned length,
unsigned *out_resp)
{
/*
* FIXME on real hardware the blocksize must be written into
* DataCtrl:BlockSize.
*/
enum { CTRL_ENABLE = 0x01, CTRL_READ = 0x02 };
_write_reg(DataLength, length);
_write_reg(DataCtrl, CTRL_ENABLE | CTRL_READ);
_write_reg(Argument, arg);
_write_command(cmd, (out_resp != 0));
if (out_resp)
*out_resp = _read_reg(Response0);
_clear_status();
}
void write_request(unsigned char cmd,
unsigned arg,
unsigned length,
unsigned *out_resp)
{
/*
* FIXME on real hardware the blocksize must be written into
* DataCtrl:BlockSize.
*/
enum { CTRL_ENABLE = 0x01 };
_write_reg(DataLength, length);
_write_reg(DataCtrl, CTRL_ENABLE);
_write_reg(Argument, arg);
_write_command(cmd, (out_resp != 0));
if (out_resp)
*out_resp = _read_reg(Response0);
_clear_status();
}
void read_data(unsigned length,
char *out_buffer)
{
unsigned *buf = reinterpret_cast<unsigned *>(out_buffer);
for (unsigned count = 0; count < length / 4; ) {
/*
* The FIFO contains at least 'remainder - FifoCnt' words.
*/
int chunk = length / 4 - count - _read_reg(FifoCnt);
count += chunk;
for ( ; chunk > 0; --chunk)
buf[count - chunk] = _read_reg(FIFO);
}
_clear_status();
}
void write_data(unsigned length,
char const *buffer)
{
enum { FIFO_SIZE = 16 };
unsigned const *buf = reinterpret_cast<unsigned const *>(buffer);
for (unsigned count = 0; count < length / 4; ) {
Genode::uint32_t status;
while (!((status = _read_reg(Status)) & TxFifoHalfEmpty)) ;
int chunk = (status & TxFifoEmpty) ? FIFO_SIZE : FIFO_SIZE / 2;
count += chunk;
for ( ; chunk > 0; --chunk)
_write_reg(FIFO, buf[count - chunk]);
}
_clear_status();
}
};
#endif /* _DRIVERS__SD_CARD__SPEC__PL180__PL180_H_ */

View File

@ -0,0 +1,318 @@
/*
* \brief Raspberry Pi SDHCI driver
* \author Norman Feske
* \author Christian Helmuth
* \author Timo Wischer
* \author Martin Stein
* \date 2014-09-21
*/
/*
* Copyright (C) 2014-2017 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.
*/
/* local includes */
#include <driver.h>
using namespace Genode;
using namespace Sd_card;
Driver::Driver(Env &env)
:
Attached_mmio(Board_base::SDHCI_BASE, Board_base::SDHCI_SIZE),
_ram(env.ram())
{
log("SD card detected");
log("capacity: ", _card_info.capacity_mb(), " MiB");
}
void Driver::_set_and_enable_clock(unsigned divider)
{
Control1::access_t ctl1 = Mmio::Mmio::read<Control1>();
Control1::Clk_freq8::set(ctl1, divider);
Control1::Clk_freq_ms2::set(ctl1, 0);
Control1::Clk_internal_en::set(ctl1, 1);
Mmio::write<Control1>(ctl1);
if (!wait_for<Control1::Clk_internal_stable>(1, _delayer)) {
error("could not set internal clock");
throw Detection_failed();
}
Mmio::write<Control1::Clk_en>(1);
_delayer.usleep(10*1000);
/* data timeout unit exponent */
Mmio::write<Control1::Data_tounit>(0xe);
}
Card_info Driver::_init()
{
/* reset host controller */
Control1::access_t v = Mmio::read<Control1>();
Control1::Srst_hc::set(v);
Control1::Srst_data::set(v);
Mmio::write<Control1>(v);
if (!wait_for<Control1::Srst_hc>(0, _delayer)) {
error("host-controller soft reset timed out");
throw Detection_failed();
}
log("SDHCI version: ", Mmio::read<Host_version::Vendor>(), " "
"(specification ", Mmio::read<Host_version::Spec>() + 1, ".0)");
/* Enable sd card power */
Mmio::write<Host_ctrl>(Host_ctrl::Power::bits(1)
| Host_ctrl::Voltage::bits(Host_ctrl::Voltage::V33));
/* enable interrupt status reporting */
Mmio::write<Irpt_mask>(~0UL);
Mmio::write<Irpt_en>(~0UL);
/*
* We don't read the capability register as the BCM2835 always
* returns all bits set to zero.
*/
_set_and_enable_clock(240);
if (!issue_command(Go_idle_state())) {
warning("Go_idle_state command failed");
throw Detection_failed();
}
_delayer.usleep(2000);
if (!issue_command(Send_if_cond())) {
warning("Send_if_cond command failed");
throw Detection_failed();
}
if (Mmio::read<Resp0>() != 0x1aa) {
error("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))) {
warning("Sd_send_op_cond command failed");
throw Detection_failed();
}
if (Ocr::Busy::get(Mmio::read<Resp0>()))
break;
_delayer.usleep(1000);
}
if (i == 0) {
error("Sd_send_op_cond timed out, could no power-on SD card");
throw Detection_failed();
}
Card_info card_info = _detect();
/*
* Switch card to use 4 data signals
*/
if (!issue_command(Set_bus_width(Set_bus_width::Arg::Bus_width::FOUR_BITS),
card_info.rca())) {
warning("Set_bus_width(FOUR_BITS) command failed");
throw Detection_failed();
}
/* switch host controller to use 4 data signals */
Control0::access_t ctl0 = Mmio::read<Control0>();
Control0::Hctl_dwidth::set(ctl0);
Control0::Hctl_hs_en::set(ctl0);
Mmio::write<Control0>(ctl0);
_delayer.usleep(10*1000);
/*
* Accelerate clock, the divider is hard-coded for now.
*
* The Raspberry Pi report as clock of 250 MHz. According to the
* SDHCI specification, it is possible to driver SD cards with
* 50 MHz in high-speed mode (Hctl_hs_en).
*/
_set_and_enable_clock(5);
return card_info;
}
void Driver::_set_block_count(size_t block_count)
{
/*
* The 'Blksizecnt' register must be written in one step. If we
* used subsequent writes for the 'Blkcnt' and 'Blksize' bitfields,
* the host controller of the BCM2835 would fail to recognize any
* but the first write operation.
*/
Blksizecnt::access_t v = Mmio::read<Blksizecnt>();
Blksizecnt::Blkcnt::set(v, block_count);
Blksizecnt::Blksize::set(v, block_size());
Mmio::write<Blksizecnt>(v);
}
size_t Driver::_block_to_command_address(const size_t block_number)
{
/* use byte position for addressing with standard cards */
if (_card_info.version() == Csd3::Version::STANDARD_CAPACITY) {
return block_number * block_size();
}
return block_number;
}
bool Driver::_issue_command(Command_base const &command)
{
if (!_poll_and_wait_for<Status::Inhibit>(0)) {
error("controller inhibits issueing commands");
return false;
}
/* write command argument */
Mmio::write<Arg1>(command.arg);
/* assemble command register */
Cmdtm::access_t cmd = 0;
Cmdtm::Index::set(cmd, command.index);
if (command.transfer != TRANSFER_NONE) {
Cmdtm::Isdata::set(cmd);
Cmdtm::Tm_blkcnt_en::set(cmd);
Cmdtm::Tm_multi_block::set(cmd);
if (command.index == Read_multiple_block::INDEX ||
command.index == Write_multiple_block::INDEX)
{
Cmdtm::Tm_auto_cmd_en::set(cmd, Cmdtm::Tm_auto_cmd_en::CMD12);
}
/* set data-direction bit depending on the command */
bool const read = command.transfer == TRANSFER_READ;
Cmdtm::Tm_dat_dir::set(cmd, read ? Cmdtm::Tm_dat_dir::READ
: Cmdtm::Tm_dat_dir::WRITE);
}
Cmdtm::access_t rsp_type = 0;
switch (command.rsp_type) {
case RESPONSE_NONE: rsp_type = Cmdtm::Rsp_type::RESPONSE_NONE; break;
case RESPONSE_136_BIT: rsp_type = Cmdtm::Rsp_type::RESPONSE_136_BIT; break;
case RESPONSE_48_BIT: rsp_type = Cmdtm::Rsp_type::RESPONSE_48_BIT; break;
case RESPONSE_48_BIT_WITH_BUSY: rsp_type = Cmdtm::Rsp_type::RESPONSE_48_BIT_WITH_BUSY; break;
}
Cmdtm::Rsp_type::set(cmd, rsp_type);
/* write command */
Mmio::write<Cmdtm>(cmd);
if (!_poll_and_wait_for<Interrupt::Cmd_done>(1)) {
error("command timed out");
return false;
}
/* clear interrupt state */
Mmio::write<Interrupt::Cmd_done>(1);
return true;
}
Cid Driver::_read_cid()
{
Cid cid;
cid.raw_0 = Mmio::read<Resp0_136>();
cid.raw_1 = Mmio::read<Resp1_136>();
cid.raw_2 = Mmio::read<Resp2_136>();
cid.raw_3 = Mmio::read<Resp3_136>();
return cid;
}
Csd Driver::_read_csd()
{
Csd csd;
csd.csd0 = Mmio::read<Resp0_136>();
csd.csd1 = Mmio::read<Resp1_136>();
csd.csd2 = Mmio::read<Resp2_136>();
csd.csd3 = Mmio::read<Resp3_136>();
return csd;
}
void Driver::read(Block::sector_t block_number,
size_t block_count,
char *out_buffer,
Block::Packet_descriptor &packet)
{
_set_block_count(block_count);
if (!issue_command(Read_multiple_block(_block_to_command_address(block_number)))) {
error("Read_multiple_block failed");
throw Io_error();
}
Data::access_t *dst = (Data::access_t *)(out_buffer);
for (size_t i = 0; i < block_count; i++) {
/*
* Check for buffer-read enable bit for each block
*
* According to the BCM2835 documentation, this bit is
* reserved but it actually corresponds to the bre status
* bit as described in the SDHCI specification.
*/
if (!_poll_and_wait_for<Status::Bre>(1)) {
throw Io_error(); }
/* read data from sdhci buffer */
for (size_t j = 0; j < block_size() / sizeof(Data::access_t); j++)
*dst++ = Mmio::read<Data>();
}
if (!_poll_and_wait_for<Interrupt::Data_done>(1)) {
error("completion of read request failed");
throw Io_error();
}
/* clear interrupt state */
Mmio::write<Interrupt::Data_done>(1);
ack_packet(packet);
}
void Driver::write(Block::sector_t block_number,
size_t block_count,
char const *buffer,
Block::Packet_descriptor &packet)
{
_set_block_count(block_count);
if (!issue_command(Write_multiple_block(_block_to_command_address(block_number)))) {
error("Write_multiple_block failed");
throw Io_error();
}
Data::access_t const *src = (Data::access_t const *)(buffer);
for (size_t i = 0; i < block_count; i++) {
/* check for buffer-write enable bit for each block */
if (!_poll_and_wait_for<Status::Bwe>(1)) {
throw Io_error(); }
/* write data into sdhci buffer */
for (size_t j = 0; j < block_size() / sizeof(Data::access_t); j++)
Mmio::write<Data>(*src++);
}
if (!_poll_and_wait_for<Interrupt::Data_done>(1)) {
error("completion of write request failed");
throw Io_error();
}
/* clear interrupt state */
Mmio::write<Interrupt::Data_done>(1);
ack_packet(packet);
}

View File

@ -1,138 +1,216 @@
/*
* \brief Raspberry Pi implementation of the Block::Driver interface
* \brief Raspberry Pi SDHCI driver
* \author Norman Feske
* \author Timo Wischer
* \author Martin Stein
* \date 2014-09-21
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
* Copyright (C) 2014-2017 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 _DRIVERS__SD_CARD__SPEC__RPI__DRIVER_H_
#define _DRIVERS__SD_CARD__SPEC__RPI__DRIVER_H_
#ifndef _DRIVER_H_
#define _DRIVER_H_
#include <util/mmio.h>
#include <os/attached_io_mem_dataspace.h>
#include <base/log.h>
/* Genode includes */
#include <timer_session/connection.h>
#include <block/component.h>
#include <drivers/board_base.h>
#include <os/attached_mmio.h>
#include <irq_session/connection.h>
/* local includes */
#include <sdhci.h>
#include <driver_base.h>
namespace Block {
using namespace Genode;
class Sdhci_driver;
}
namespace Sd_card { class Driver; }
class Block::Sdhci_driver : public Block::Driver
class Sd_card::Driver : public Driver_base,
private Attached_mmio
{
private:
struct Blksizecnt : Register<0x4, 32>
{
struct Blkcnt : Bitfield<16, 16> { };
struct Blksize : Bitfield<0, 10> { };
};
struct Resp0 : Register<0x10, 32> { };
struct Resp1 : Register<0x14, 32> { };
struct Resp2 : Register<0x18, 32> { };
struct Resp3 : Register<0x1c, 32> { };
/*
* Handle the SDHCI quirk that responses of 136-bit requests are
* shifted by 8 bits.
*/
template <off_t OFFSET>
struct Cmdresp_tpl : Register<OFFSET, 32>
{
struct Resp_8_24 : Register<OFFSET, 32>::template Bitfield<0, 24> { };
struct Resp_0_8 : Register<OFFSET, 32>::template Bitfield<24, 8> { };
};
struct Cmdresp0 : Cmdresp_tpl<0x10> { };
struct Cmdresp1 : Cmdresp_tpl<0x14> { };
struct Cmdresp2 : Cmdresp_tpl<0x18> { };
struct Cmdresp3 : Cmdresp_tpl<0x1c> { };
struct Resp0_136 : Bitset_2<Cmdresp3::Resp_0_8, Cmdresp0::Resp_8_24> { };
struct Resp1_136 : Bitset_2<Cmdresp0::Resp_0_8, Cmdresp1::Resp_8_24> { };
struct Resp2_136 : Bitset_2<Cmdresp1::Resp_0_8, Cmdresp2::Resp_8_24> { };
struct Resp3_136 : Bitset_2<Cmdresp2::Resp_0_8, Cmdresp3::Resp_8_24> { };
struct Data : Register<0x20, 32> { };
struct Control0 : Register<0x28, 32>
{
struct Hctl_dwidth : Bitfield<1, 1> { };
struct Hctl_hs_en : Bitfield<2, 1> { };
};
struct Control1 : Register<0x2c, 32>
{
struct Clk_internal_en : Bitfield<0, 1> { };
struct Clk_internal_stable : Bitfield<1, 1> { };
struct Clk_en : Bitfield<2, 1> { };
struct Clk_freq8 : Bitfield<8, 8> { };
struct Clk_freq_ms2 : Bitfield<6, 2> { };
struct Data_tounit : Bitfield<16, 4> { };
struct Srst_hc : Bitfield<24, 1> { };
struct Srst_cmd : Bitfield<25, 1> { };
struct Srst_data : Bitfield<26, 1> { };
};
struct Status : Register<0x24, 32>
{
struct Inhibit : Bitfield<0, 2> { };
struct Bwe : Bitfield<10, 1> { };
struct Bre : Bitfield<11, 1> { };
};
struct Host_ctrl : Register<0x28, 32>
{
struct Voltage : Bitfield<9, 3> {
enum {
V18 = 0b101,
V30 = 0b110,
V33 = 0b111,
};
};
struct Power : Bitfield<8, 1> { };
};
struct Arg1 : Register<0x8, 32> { };
struct Cmdtm : Register<0xc, 32>
{
struct Index : Bitfield<24, 6> { };
struct Isdata : Bitfield<21, 1> { };
struct Tm_blkcnt_en : Bitfield<1, 1> { };
struct Tm_multi_block : Bitfield<5, 1> { };
struct Tm_auto_cmd_en : Bitfield<2, 2>
{
enum { CMD12 = 1 };
};
struct Tm_dat_dir : Bitfield<4, 1>
{
enum { WRITE = 0, READ = 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 };
};
};
struct Interrupt : Register<0x30, 32>
{
struct Cmd_done : Bitfield<0, 1> { };
struct Data_done : Bitfield<1, 1> { };
};
struct Irpt_mask : Register<0x34, 32> { };
struct Irpt_en : Register<0x38, 32> { };
struct Capabilities : Register<0x40, 32> { };
struct Host_version : Register<0xFE, 16>
{
struct Spec : Bitfield<0, 8> { };
struct Vendor : Bitfield<8, 8> { };
};
struct Timer_delayer : Timer::Connection, Mmio::Delayer
{
/**
* Implementation of 'Delayer' interface
*/
void usleep(unsigned us) { Timer::Connection::usleep(us); }
};
} _delayer;
Ram_session &_ram;
Timer_delayer _delayer;
Irq_connection _irq { Board_base::SDHCI_IRQ };
Card_info _card_info { _init() };
/* display sub system registers */
Attached_io_mem_dataspace _sdhci_mmio { Board_base::SDHCI_BASE, Board_base::SDHCI_SIZE };
template <typename REG>
bool _poll_and_wait_for(unsigned value)
{
/* poll for a while */
if (!wait_for<REG>(value, _delayer, 5000, 0)) {
/* host-controller instance */
Sdhci_controller _controller;
/* if the value was not reached while polling, start sleeping */
if (!wait_for<REG>(value, _delayer)) {
return false; }
}
return true;
}
bool const _use_dma;
Card_info _init();
void _set_and_enable_clock(unsigned divider);
void _set_block_count(size_t block_count);
size_t _block_to_command_address(size_t block_number);
/*********************
** Host_controller **
*********************/
bool _issue_command(Command_base const &command) override;
Cid _read_cid() override;
Csd _read_csd() override;
Card_info card_info() const override { return _card_info; }
unsigned _read_rca() override {
return Send_relative_addr::Response::Rca::get(Mmio::read<Resp0>()); }
public:
Sdhci_driver(Env &)
:
_controller((addr_t)_sdhci_mmio.local_addr<void>(),
_delayer, Board_base::SDHCI_IRQ, false, true),
_use_dma(false)
{
Sd_card::Card_info const card_info = _controller.card_info();
Genode::log("SD card detected");
Genode::log("capacity: ", card_info.capacity_mb(), " MiB");
}
Driver(Env &env);
/*****************************
** Block::Driver interface **
*****************************/
/*******************
** Block::Driver **
*******************/
Genode::size_t block_size() { return Sdhci_controller::Block_size; }
void read(Block::sector_t block_number,
size_t block_count,
char *buffer,
Block::Packet_descriptor &packet) override;
virtual Block::sector_t block_count()
{
return _controller.card_info().capacity_mb() * 1024 * 2;
}
void write(Block::sector_t block_number,
size_t block_count,
char const *buffer,
Block::Packet_descriptor &packet) override;
Block::Session::Operations ops()
{
Block::Session::Operations o;
o.set_operation(Block::Packet_descriptor::READ);
o.set_operation(Block::Packet_descriptor::WRITE);
return o;
}
void read(Block::sector_t block_number,
Genode::size_t block_count,
char *out_buffer,
Packet_descriptor &packet)
{
if (!_controller.read_blocks(block_number, block_count, out_buffer))
throw Io_error();
ack_packet(packet);
}
void write(Block::sector_t block_number,
Genode::size_t block_count,
char const *buffer,
Packet_descriptor &packet)
{
if (!_controller.write_blocks(block_number, block_count, buffer))
throw Io_error();
ack_packet(packet);
}
void read_dma(Block::sector_t block_number,
Genode::size_t block_count,
Genode::addr_t phys,
Packet_descriptor &packet)
{
if (!_controller.read_blocks_dma(block_number, block_count, phys))
throw Io_error();
ack_packet(packet);
}
void write_dma(Block::sector_t block_number,
Genode::size_t block_count,
Genode::addr_t phys,
Packet_descriptor &packet)
{
if (!_controller.write_blocks_dma(block_number, block_count, phys))
throw Io_error();
ack_packet(packet);
}
bool dma_enabled() { return _use_dma; }
Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size) {
return Genode::env()->ram_session()->alloc(size, UNCACHED); }
void free_dma_buffer(Genode::Ram_dataspace_capability c) {
return Genode::env()->ram_session()->free(c); }
Ram_dataspace_capability alloc_dma_buffer(size_t size) override {
return _ram.alloc(size, UNCACHED); }
};
#endif /* _DRIVERS__SD_CARD__SPEC__RPI__DRIVER_H_ */
#endif /* _DRIVER_H_ */

View File

@ -1,561 +0,0 @@
/*
* \brief SDHCI controller driver
* \author Norman Feske
* \author Christian Helmuth
* \author Timo Wischer
* \date 2014-09-21
*/
/*
* Copyright (C) 2014 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 _DRIVERS__SD_CARD__SPEC__RPI__SDHCI_H_
#define _DRIVERS__SD_CARD__SPEC__RPI__SDHCI_H_
/* Genode includes */
#include <util/mmio.h>
#include <os/attached_ram_dataspace.h>
#include <irq_session/connection.h>
#include <drivers/board_base.h>
/* local includes */
#include <sd_card.h>
struct Sdhci : Genode::Mmio
{
enum { verbose = false };
typedef Genode::size_t size_t;
struct Blksizecnt : Register<0x4, 32>
{
struct Blkcnt : Bitfield<16, 16> { };
struct Blksize : Bitfield<0, 10> { };
};
struct Resp0 : Register<0x10, 32> { };
struct Resp1 : Register<0x14, 32> { };
struct Resp2 : Register<0x18, 32> { };
struct Resp3 : Register<0x1c, 32> { };
/*
* Handle the SDHCI quirk that responses of 136-bit requests are shifted
* by 8 bits.
*/
template <Genode::off_t OFFSET>
struct Cmdresp_tpl : Register<OFFSET, 32>
{
struct Resp_8_24 : Register<OFFSET, 32>::template Bitfield<0, 24> { };
struct Resp_0_8 : Register<OFFSET, 32>::template Bitfield<24, 8> { };
};
struct Cmdresp0 : Cmdresp_tpl<0x10> { };
struct Cmdresp1 : Cmdresp_tpl<0x14> { };
struct Cmdresp2 : Cmdresp_tpl<0x18> { };
struct Cmdresp3 : Cmdresp_tpl<0x1c> { };
struct Resp0_136 : Genode::Bitset_2<Cmdresp3::Resp_0_8, Cmdresp0::Resp_8_24> { };
struct Resp1_136 : Genode::Bitset_2<Cmdresp0::Resp_0_8, Cmdresp1::Resp_8_24> { };
struct Resp2_136 : Genode::Bitset_2<Cmdresp1::Resp_0_8, Cmdresp2::Resp_8_24> { };
struct Resp3_136 : Genode::Bitset_2<Cmdresp2::Resp_0_8, Cmdresp3::Resp_8_24> { };
struct Data : Register<0x20, 32> { };
struct Control0 : Register<0x28, 32>
{
struct Hctl_dwidth : Bitfield<1, 1> { };
struct Hctl_hs_en : Bitfield<2, 1> { };
};
struct Control1 : Register<0x2c, 32>
{
struct Clk_internal_en : Bitfield<0, 1> { };
struct Clk_internal_stable : Bitfield<1, 1> { };
struct Clk_en : Bitfield<2, 1> { };
struct Clk_freq8 : Bitfield<8, 8> { };
struct Clk_freq_ms2 : Bitfield<6, 2> { };
struct Data_tounit : Bitfield<16, 4> { };
struct Srst_hc : Bitfield<24, 1> { };
struct Srst_cmd : Bitfield<25, 1> { };
struct Srst_data : Bitfield<26, 1> { };
};
struct Status : Register<0x24, 32>
{
struct Inhibit : Bitfield<0, 2> { };
struct Bwe : Bitfield<10, 1> { };
struct Bre : Bitfield<11, 1> { };
};
struct Host_ctrl : Register<0x28, 32>
{
struct Voltage : Bitfield<9, 3> {
enum {
V18 = 0b101,
V30 = 0b110,
V33 = 0b111,
};
};
struct Power : Bitfield<8, 1> { };
};
struct Arg1 : Register<0x8, 32> { };
struct Cmdtm : Register<0xc, 32>
{
struct Index : Bitfield<24, 6> { };
struct Isdata : Bitfield<21, 1> { };
struct Tm_blkcnt_en : Bitfield<1, 1> { };
struct Tm_multi_block : Bitfield<5, 1> { };
struct Tm_auto_cmd_en : Bitfield<2, 2>
{
enum { CMD12 = 1 };
};
struct Tm_dat_dir : Bitfield<4, 1>
{
enum { WRITE = 0, READ = 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 };
};
};
struct Interrupt : Register<0x30, 32>
{
struct Cmd_done : Bitfield<0, 1> { };
struct Data_done : Bitfield<1, 1> { };
};
struct Irpt_mask : Register<0x34, 32> { };
struct Irpt_en : Register<0x38, 32> { };
struct Capabilities : Register<0x40, 32> { };
struct Host_version : Register<0xFE, 16>
{
struct Spec : Bitfield<0, 8> { };
struct Vendor : Bitfield<8, 8> { };
};
Sdhci(Genode::addr_t const mmio_base) : Genode::Mmio(mmio_base) { }
};
struct Sdhci_controller : private Sdhci, public Sd_card::Host_controller
{
enum { Block_size = 0x200 };
private:
Delayer &_delayer;
Sd_card::Card_info _card_info;
Genode::Irq_connection _irq;
void _set_and_enable_clock(unsigned divider)
{
Control1::access_t v = read<Control1>();
Control1::Clk_freq8::set(v, divider);
Control1::Clk_freq_ms2::set(v, 0);
Control1::Clk_internal_en::set(v, 1);
write<Control1>(v);
if (!wait_for<Control1::Clk_internal_stable>(1, _delayer)) {
Genode::error("could not set internal clock");
throw Detection_failed();
}
write<Control1::Clk_en>(1);
_delayer.usleep(10*1000);
/* data timeout unit exponent */
write<Control1::Data_tounit>(0xe);
}
Sd_card::Card_info _init(const bool set_voltage)
{
using namespace Sd_card;
/* reset host controller */
{
Control1::access_t v = read<Control1>();
Control1::Srst_hc::set(v);
Control1::Srst_data::set(v);
write<Control1>(v);
}
if (!wait_for<Control1::Srst_hc>(0, _delayer)) {
Genode::error("host-controller soft reset timed out");
throw Detection_failed();
}
Genode::log("SDHCI version: ", read<Host_version::Vendor>(), " "
"(specification ", read<Host_version::Spec>() + 1, ".0)");
/*
* Raspberry Pi (BCM2835) does not need to
* set the sd card voltage and
* power up the host controller.
* This registers are reserved and
* always have to be written to 0.
*/
if (set_voltage) {
/* Enable sd card power */
write<Host_ctrl>(Host_ctrl::Power::bits(1)
| Host_ctrl::Voltage::bits(Host_ctrl::Voltage::V33));
}
/* enable interrupt status reporting */
write<Irpt_mask>(~0UL);
write<Irpt_en>(~0UL);
/*
* We don't read the capability register as the BCM2835 always
* returns all bits set to zero.
*/
_set_and_enable_clock(240);
if (!issue_command(Go_idle_state())) {
Genode::warning("Go_idle_state command failed");
throw Detection_failed();
}
_delayer.usleep(2000);
if (!issue_command(Send_if_cond())) {
Genode::warning("Send_if_cond command failed");
throw Detection_failed();
}
if (read<Resp0>() != 0x1aa) {
Genode::error("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))) {
Genode::warning("Sd_send_op_cond command failed");
throw Detection_failed();
}
if (Sd_card::Ocr::Busy::get(read<Resp0>()))
break;
_delayer.usleep(1000);
}
if (i == 0) {
Genode::error("Sd_send_op_cond timed out, could no power-on SD card");
throw Detection_failed();
}
Card_info card_info = _detect();
/*
* Switch card to use 4 data signals
*/
if (!issue_command(Set_bus_width(Set_bus_width::Arg::Bus_width::FOUR_BITS),
card_info.rca())) {
Genode::warning("Set_bus_width(FOUR_BITS) command failed");
throw Detection_failed();
}
/* switch host controller to use 4 data signals */
{
Control0::access_t v = read<Control0>();
Control0::Hctl_dwidth::set(v);
Control0::Hctl_hs_en::set(v);
write<Control0>(v);
}
_delayer.usleep(10*1000);
/*
* Accelerate clock, the divider is hard-coded for now.
*
* The Raspberry Pi report as clock of 250 MHz. According to the
* SDHCI specification, it is possible to driver SD cards with
* 50 MHz in high-speed mode (Hctl_hs_en).
*/
_set_and_enable_clock(5);
return card_info;
}
/**
* Define the block count for the next data transfer
*/
void _set_block_count(size_t block_count)
{
/*
* The 'Blksizecnt' register must be written in one step. If we
* used subsequent writes for the 'Blkcnt' and 'Blksize' bitfields,
* the host controller of the BCM2835 would fail to recognize any
* but the first write operation.
*/
Blksizecnt::access_t v = read<Blksizecnt>();
Blksizecnt::Blkcnt::set(v, block_count);
Blksizecnt::Blksize::set(v, Block_size);
write<Blksizecnt>(v);
}
template <typename REG>
bool _poll_and_wait_for(unsigned value)
{
/* poll for a while */
if (wait_for<REG>(value, _delayer, 5000, 0))
return true;
/* if the value were not reached while polling, start sleeping */
return wait_for<REG>(value, _delayer);
}
public:
/**
* Constructor
*
* \param mmio_base local base address of MMIO registers
*/
Sdhci_controller(Genode::addr_t const mmio_base, Delayer &delayer,
unsigned irq, bool use_dma, const bool set_voltage = false)
:
Sdhci(mmio_base), _delayer(delayer), _card_info(_init(set_voltage)), _irq(irq)
{ }
/****************************************
** Sd_card::Host_controller interface **
****************************************/
bool _issue_command(Sd_card::Command_base const &command)
{
if (verbose)
Genode::log("-> ", command);
if (!_poll_and_wait_for<Status::Inhibit>(0)) {
Genode::error("controller inhibits issueing commands");
return false;
}
/* write command argument */
write<Arg1>(command.arg);
/* assemble command register */
Cmdtm::access_t cmd = 0;
Cmdtm::Index::set(cmd, command.index);
if (command.transfer != Sd_card::TRANSFER_NONE) {
Cmdtm::Isdata::set(cmd);
Cmdtm::Tm_blkcnt_en::set(cmd);
Cmdtm::Tm_multi_block::set(cmd);
if (command.index == Sd_card::Read_multiple_block::INDEX
|| command.index == Sd_card::Write_multiple_block::INDEX) {
Cmdtm::Tm_auto_cmd_en::set(cmd, Cmdtm::Tm_auto_cmd_en::CMD12);
}
/* set data-direction bit depending on the command */
bool const read = command.transfer == Sd_card::TRANSFER_READ;
Cmdtm::Tm_dat_dir::set(cmd, read ? Cmdtm::Tm_dat_dir::READ
: Cmdtm::Tm_dat_dir::WRITE);
}
Cmdtm::access_t rsp_type = 0;
switch (command.rsp_type) {
case Sd_card::RESPONSE_NONE: rsp_type = Cmdtm::Rsp_type::RESPONSE_NONE; break;
case Sd_card::RESPONSE_136_BIT: rsp_type = Cmdtm::Rsp_type::RESPONSE_136_BIT; break;
case Sd_card::RESPONSE_48_BIT: rsp_type = Cmdtm::Rsp_type::RESPONSE_48_BIT; break;
case Sd_card::RESPONSE_48_BIT_WITH_BUSY: rsp_type = Cmdtm::Rsp_type::RESPONSE_48_BIT_WITH_BUSY; break;
}
Cmdtm::Rsp_type::set(cmd, rsp_type);
/* write command */
write<Cmdtm>(cmd);
if (!_poll_and_wait_for<Interrupt::Cmd_done>(1)) {
Genode::error("command timed out");
return false;
}
/* clear interrupt state */
write<Interrupt::Cmd_done>(1);
return true;
}
Sd_card::Card_info card_info() const
{
return _card_info;
}
Sd_card::Cid _read_cid()
{
Sd_card::Cid cid;
cid.raw_0 = read<Resp0_136>();
cid.raw_1 = read<Resp1_136>();
cid.raw_2 = read<Resp2_136>();
cid.raw_3 = read<Resp3_136>();
return cid;
}
Sd_card::Csd _read_csd()
{
Sd_card::Csd csd;
csd.csd0 = read<Resp0_136>();
csd.csd1 = read<Resp1_136>();
csd.csd2 = read<Resp2_136>();
csd.csd3 = read<Resp3_136>();
return csd;
}
unsigned _read_rca()
{
return Sd_card::Send_relative_addr::Response::Rca::get(read<Resp0>());
}
size_t _block_to_command_address(const size_t block_number)
{
/* use byte position for addressing with standard cards */
if (_card_info.version() == Sd_card::Csd3::Version::STANDARD_CAPACITY) {
return block_number * Block_size;
}
return block_number;
}
/**
* 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;
_set_block_count(block_count);
if (!issue_command(Read_multiple_block(_block_to_command_address(block_number)))) {
Genode::error("Read_multiple_block failed, Status: ",
Genode::Hex(read<Status>()));
return false;
}
Data::access_t *dst = (Data::access_t *)(out_buffer);
for (size_t i = 0; i < block_count; i++) {
/*
* Check for buffer-read enable bit for each block
*
* According to the BCM2835 documentation, this bit is
* reserved but it actually corresponds to the bre status
* bit as described in the SDHCI specification.
*/
if (!_poll_and_wait_for<Status::Bre>(1))
return false;
/* read data from sdhci buffer */
for (size_t j = 0; j < 512/sizeof(Data::access_t); j++)
*dst++ = read<Data>();
}
if (!_poll_and_wait_for<Interrupt::Data_done>(1)) {
Genode::error("completion of read request failed (interrupt "
"status ", Genode::Hex(read<Interrupt>()), ")");
return false;
}
/* clear interrupt state */
write<Interrupt::Data_done>(1);
return true;
}
/**
* 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;
_set_block_count(block_count);
if (!issue_command(Write_multiple_block(_block_to_command_address(block_number)))) {
Genode::error("Write_multiple_block failed, Status: ",
Genode::Hex(read<Status>()));
return false;
}
Data::access_t const *src = (Data::access_t const *)(buffer);
for (size_t i = 0; i < block_count; i++) {
/* check for buffer-write enable bit for each block */
if (!_poll_and_wait_for<Status::Bwe>(1))
return false;
/* write data into sdhci buffer */
for (size_t j = 0; j < 512/sizeof(Data::access_t); j++)
write<Data>(*src++);
}
if (!_poll_and_wait_for<Interrupt::Data_done>(1)) {
Genode::error("completion of write request failed (interrupt "
"status ", read<Interrupt>(), ")");
return false;
}
/* clear interrupt state */
write<Interrupt::Data_done>(1);
return true;
}
/**
* Read data blocks from SD card via master DMA
*
* \return true on success
*/
bool read_blocks_dma(size_t block_number, size_t block_count,
Genode::addr_t out_buffer_phys)
{
return false;
}
/**
* Write data blocks to SD card via master DMA
*
* \return true on success
*/
bool write_blocks_dma(size_t block_number, size_t block_count,
Genode::addr_t buffer_phys)
{
using namespace Sd_card;
return false;
}
};
#endif /* _DRIVERS__SD_CARD__SPEC__RPI__SDHCI_H_ */

View File

@ -55,7 +55,7 @@ struct Main
Operation operation { READ };
Signal_handler<Main> ack_handler { env.ep(), *this, &Main::update_state };
Driver_session drv_session { ack_handler };
Block::Sdhci_driver drv { env };
Sd_card::Driver drv { env };
size_t const buf_size_kib { config()->xml_node()
.attribute_value("buffer_size_kib",
(size_t)0) };