sd_card: get rid of wait_for_irq

Most implementations use a Signal_handler now to acknowledge the packet
instead of waiting for the transfer completion. The exceptions to that are
the non-DMA implementations for RPI and PL180

Ref #2206
This commit is contained in:
Martin Stein 2016-12-23 14:53:30 +01:00 committed by Norman Feske
parent 9e1f3259c5
commit b33c35a003
13 changed files with 543 additions and 582 deletions

View File

@ -24,13 +24,30 @@
#include <block_session/rpc_object.h>
namespace Block {
class Driver_session_base;
class Driver_session;
class Driver;
struct Driver_factory;
};
class Block::Driver_session : public Block::Session_rpc_object
struct Block::Driver_session_base
{
/**
* Acknowledges a packet processed by the driver to the client
*
* \param packet the packet to acknowledge
* \param success indicated whether the processing was successful
*
* \throw Ack_congestion
*/
virtual void ack_packet(Packet_descriptor &packet,
bool success) = 0;
};
class Block::Driver_session : public Driver_session_base,
public Block::Session_rpc_object
{
public:
@ -44,17 +61,6 @@ class Block::Driver_session : public Block::Session_rpc_object
Driver_session(Genode::Dataspace_capability tx_ds,
Genode::Rpc_entrypoint &ep)
: Session_rpc_object(tx_ds, ep) { }
/**
* Acknowledges a packet processed by the driver to the client
*
* \param packet the packet to acknowledge
* \param success indicated whether the processing was successful
*
* \throw Ack_congestion
*/
virtual void ack_packet(Packet_descriptor &packet,
bool success) = 0;
};
@ -65,7 +71,7 @@ class Block::Driver
{
private:
Driver_session *_session = nullptr;
Driver_session_base *_session = nullptr;
public:
@ -214,7 +220,7 @@ class Block::Driver
*
* Session might get used to acknowledge requests.
*/
void session(Driver_session *session) {
void session(Driver_session_base *session) {
if (!(_session = session)) session_invalidated(); }
/**

View File

@ -2,13 +2,13 @@
# Check for platform support
#
if {[expr ![have_spec exynos5] && \
![have_spec omap4] && \
![have_spec imx6] && \
![have_spec imx53] && \
![have_spec pl180] && \
![have_spec rpi]]} \
{
if {[have_spec pl180]} { set buffer_size_kib [expr 12 * 1024]
} elseif {[have_spec imx6]} { set buffer_size_kib [expr 1024]
} elseif {[have_spec imx53]} { set buffer_size_kib [expr 1024]
} elseif {[have_spec rpi]} { set buffer_size_kib [expr 4 * 1024]
} elseif {[have_spec omap4]} { set buffer_size_kib [expr 4 * 1024]
} elseif {[have_spec exynos5]} { set buffer_size_kib [expr 1024]
} else {
puts "\n Run script is not supported on this platform. \n";
exit 0;
}
@ -29,6 +29,7 @@ append_platform_drv_build_components
build $build_components
create_boot_directory
#
# Install the config
#
@ -51,6 +52,7 @@ append config {
append_platform_drv_config
append config {
<start name="timer">
<resource name="RAM" quantum="1M"/>
@ -58,7 +60,11 @@ append config {
</start>
<start name="sd_card_bench">
<resource name="RAM" quantum="16M"/>
<provides><service name="Block"/></provides>
<provides><service name="Block"/></provides>}
append config "<config buffer_size_kib=\"$buffer_size_kib\"/>"
append config {
</start>
</config>}

View File

@ -19,7 +19,6 @@
#include <os/attached_io_mem_dataspace.h>
#include <base/log.h>
#include <timer_session/connection.h>
#include <block/component.h>
#include <os/server.h>
#include <regulator_session/connection.h>
@ -32,7 +31,8 @@ namespace Block {
}
class Block::Sdhci_driver : public Block::Driver
class Block::Sdhci_driver : public Block::Driver,
public Sd_ack_handler
{
private:
@ -81,7 +81,7 @@ class Block::Sdhci_driver : public Block::Driver
_clock_regulator(env),
_mmio(MSH_BASE, MSH_SIZE),
_controller(env.ep(), (addr_t)_mmio.local_addr<void>(),
_delayer, true),
_delayer, *this, true),
_use_dma(true)
{
Sd_card::Card_info const card_info = _controller.card_info();
@ -90,6 +90,9 @@ class Block::Sdhci_driver : public Block::Driver
Genode::log("capacity: ", card_info.capacity_mb(), " MiB");
}
void handle_ack(Block::Packet_descriptor pkt, bool success) {
ack_packet(pkt, success); }
/*****************************
** Block::Driver interface **
@ -110,14 +113,12 @@ class Block::Sdhci_driver : public Block::Driver
return o;
}
void read_dma(Block::sector_t block_number,
Genode::size_t block_count,
Genode::addr_t phys,
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);
_controller.read_blocks_dma(block_number, block_count, phys, packet);
}
void write_dma(Block::sector_t block_number,
@ -125,9 +126,7 @@ class Block::Sdhci_driver : public Block::Driver
Genode::addr_t phys,
Packet_descriptor &packet)
{
if (!_controller.write_blocks_dma(block_number, block_count, phys))
throw Io_error();
ack_packet(packet);
_controller.write_blocks_dma(block_number, block_count, phys, packet);
}
bool dma_enabled() { return _use_dma; }

View File

@ -18,6 +18,7 @@
#include <irq_session/connection.h>
#include <os/attached_ram_dataspace.h>
#include <util/mmio.h>
#include <block/component.h>
#include <sd_card.h>
@ -268,6 +269,11 @@ struct Dwmmc : Genode::Mmio
};
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:
@ -325,9 +331,16 @@ struct Exynos5_msh_controller : private Dwmmc, Sd_card::Host_controller
Delayer &_delayer;
Sd_card::Card_info _card_info;
Genode::Irq_connection _irq;
Genode::Signal_receiver _irq_rec;
Genode::Signal_context _irq_ctx;
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()
{
@ -473,51 +486,42 @@ struct Exynos5_msh_controller : private Dwmmc, Sd_card::Host_controller
return true;
}
void _wait_for_irq()
void _handle_irq()
{
/*
* Acknowledge the IRQ first to implicitly activate
* receiving of further IRQ signals on the first usage
* of this method.
*/
_irq.ack_irq();
_irq_rec.wait_for_signal();
}
bool _wait_for_transfer_complete()
{
while (1) {
_wait_for_irq();
if (!_block_transfer.pending) {
return; }
if (read<Rintsts::Data_transfer_over>()) {
write<Rintsts>(~0U);
return true;
}
if (read<Rintsts::Response_error>()) {
Genode::error("Response error");
return false;
}
if (read<Rintsts::Data_read_timeout>()) {
Genode::error("Data read timeout");
return false;
}
if (read<Rintsts::Data_crc_error>()) {
Genode::error("CRC error");
return false;
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(),
@ -525,13 +529,15 @@ struct Exynos5_msh_controller : private Dwmmc, Sd_card::Host_controller
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()), _irq(IRQ_NUMBER)
_delayer(delayer), _card_info(_init()),
_ack_handler(ack_handler),
_irq_handler(ep, *this, &Exynos5_msh_controller::_handle_irq),
_irq(IRQ_NUMBER)
{
_irq.sigh(_irq_rec.manage(&_irq_ctx));
_irq.sigh(_irq_handler);
_irq.ack_irq();
}
~Exynos5_msh_controller() { _irq_rec.dissolve(&_irq_ctx); }
bool _issue_command(Sd_card::Command_base const &command)
{
if (verbose)
@ -661,49 +667,44 @@ struct Exynos5_msh_controller : private Dwmmc, Sd_card::Host_controller
return _card_info;
}
bool read_blocks_dma(Block::sector_t block_number, size_t block_count,
Genode::addr_t buffer_phys)
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))
return false;
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>()));
return false;
throw Block::Driver::Io_error();
}
bool complete = _wait_for_transfer_complete();
if (!_issue_command(Sd_card::Stop_transmission())) {
Genode::error("unable to stop transmission");
return false;
}
return complete;
}
bool write_blocks_dma(Block::sector_t block_number, size_t block_count,
Genode::addr_t buffer_phys)
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))
return false;
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>()));
return false;
throw Block::Driver::Io_error();
}
bool complete = _wait_for_transfer_complete();
if (!_issue_command(Sd_card::Stop_transmission())) {
Genode::error("unable to stop transmission");
return false;
}
return complete;
}
};
#endif /* _DRIVERS__SD_CARD__SPEC__EXYNOS5__DWMMC_H_ */

View File

@ -14,123 +14,7 @@
#ifndef _DRIVER_H_
#define _DRIVER_H_
/* Genode includes */
#include <util/mmio.h>
#include <os/attached_io_mem_dataspace.h>
#include <base/log.h>
#include <timer_session/connection.h>
#include <block/component.h>
#include <drivers/board_base.h>
#include <os/server.h>
/* local includes */
#include <sdhc.h>
namespace Block {
using namespace Genode;
class Sdhci_driver;
}
class Block::Sdhci_driver : public Block::Driver
{
private:
struct Timer_delayer : Timer::Connection, Mmio::Delayer
{
/**
* Implementation of 'Delayer' interface
*/
void usleep(unsigned us) { Timer::Connection::usleep(us); }
} _delayer;
Attached_io_mem_dataspace _sdhc_mmio;
Sdhc _controller;
bool const _use_dma;
public:
Sdhci_driver(Env &)
:
_sdhc_mmio(Board_base::SDHC_MMIO_BASE, Board_base::SDHC_MMIO_SIZE),
_controller((addr_t)_sdhc_mmio.local_addr<void>(),
Board_base::SDHC_IRQ, _delayer, true),
_use_dma(true)
{
Sd_card::Card_info const card_info = _controller.card_info();
log("SD card detected");
log("capacity: ", card_info.capacity_mb(), " MiB");
}
/*****************************
** Block::Driver interface **
*****************************/
Genode::size_t block_size() { return 512; }
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)
{
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); }
};
#endif /* _DRIVER_H_ */

View File

@ -49,14 +49,14 @@ int Sdhc::_wait_for_card_ready_mbw()
/* send command as soon as the host allows it */
if (_wait_for_cmd_allowed()) { return -1; }
write<Cmdarg>(cmdarg);
write<Xfertyp>(xfertyp);
Mmio::write<Cmdarg>(cmdarg);
Mmio::write<Xfertyp>(xfertyp);
/* wait for command completion */
if (_wait_for_cmd_complete()) { return -1; }
/* check for errors */
R1_response_0::access_t const resp = read<Cmdrsp0>();
R1_response_0::access_t const resp = Mmio::read<Cmdrsp0>();
if (R1_response_0::Error::get(resp)) {
error("Reading card status after multiblock write failed");
return -1;
@ -72,7 +72,7 @@ int Sdhc::_wait_for_card_ready_mbw()
int Sdhc::_stop_transmission()
{
/* write argument register */
write<Cmdarg>(0);
Mmio::write<Cmdarg>(0);
/* write command register */
Xfertyp::access_t xfertyp = 0;
@ -82,7 +82,7 @@ int Sdhc::_stop_transmission()
Xfertyp::Cicen::set(xfertyp, 1);
Xfertyp::Rsptyp::set(xfertyp, Xfertyp::Rsptyp::_48BIT_BUSY);
_stop_transmission_finish_xfertyp(xfertyp);
write<Xfertyp>(xfertyp);
Mmio::write<Xfertyp>(xfertyp);
/* wait for command completion */
if (_wait_for_cmd_complete()) { return -1; }
@ -90,8 +90,14 @@ int Sdhc::_stop_transmission()
}
int Sdhc::_wait_for_cmd_complete_mb(bool const r)
void Sdhc::_handle_irq()
{
_irq.ack_irq();
/* the handler is only for block transfers, on other commands we poll */
if (!_block_transfer.pending) {
return; }
/*
* The host signals on multi-block transfers seem to be broken.
* Synchronizing to "Transfer Complete" before returning from transfers
@ -99,38 +105,33 @@ int Sdhc::_wait_for_cmd_complete_mb(bool const r)
* done with other controllers - isn't sufficient. Instead, both "Transfer
* Complete" and "Command Complete" must be gathered.
*/
Irqstat::access_t constexpr irq_goal =
Irq::Cc::reg_mask() | Irq::Tc::reg_mask();
/* wait for a first signal */
_wait_for_irq();
Irqstat::access_t const irq = read<Irqstat>();
/*
* Poll for missing signal because interrupts are edge-triggered
* and could thus got lost in the meantime.
*/
if (irq != irq_goal) {
if (!wait_for<Irqstat>(irq_goal, _delayer)) {
error("Completion host signal timed out");
return -1;
}
if (!wait_for<Irqstat::Cc>(1, _delayer) ||
!wait_for<Irqstat::Tc>(1, _delayer))
{
error("Completion host signal timed out");
throw -1;
}
/* acknowledge completion signals */
write<Irqstat>(irq_goal);
return _wait_for_cmd_complete_mb_finish(r);
Irqstat::access_t irqstat = 0;
Irqstat::Cc::set(irqstat, 1);
Irqstat::Tc::set(irqstat, 1);
Mmio::write<Irqstat>(irqstat);
if (_wait_for_cmd_complete_mb_finish(_block_transfer.read)) {
throw -1; }
_block_transfer.pending = false;
ack_packet(_block_transfer.packet, true);
}
int Sdhc::_wait_for_cmd_complete()
{
/* wait for "Command Completion" signal and acknowledge it */
_wait_for_irq();
if (read<Irqstat>() != Irqstat::Cc::reg_mask()) {
error("received unexpected host signal");
if (!wait_for<Irqstat::Cc>(1, _delayer, 200, 5000)) {
error("command timed out");
return -1;
}
write<Irqstat>(Irqstat::Cc::reg_mask());
Mmio::write<Irqstat>(Irqstat::Cc::reg_mask());
return 0;
}
@ -169,22 +170,21 @@ bool Sdhc::_issue_command(Command_base const & command)
}
/* version-dependent transfer settings and issue command */
_issue_cmd_finish_xfertyp(xfertyp, transfer, multiblock, reading);
write<Cmdarg>(command.arg);
write<Xfertyp>(xfertyp);
Mmio::write<Cmdarg>(command.arg);
Mmio::write<Xfertyp>(xfertyp);
/* wait for completion */
return multiblock ? !_wait_for_cmd_complete_mb(reading) :
!_wait_for_cmd_complete();
/* for block transfers there's a signal handler, on other commands poll */
return transfer ? true : !_wait_for_cmd_complete();
}
Cid Sdhc::_read_cid()
{
Cid cid;
cid.raw_0 = read<Rsp136_0>();
cid.raw_1 = read<Rsp136_1>();
cid.raw_2 = read<Rsp136_2>();
cid.raw_3 = read<Rsp136_3>();
cid.raw_0 = Mmio::read<Rsp136_0>();
cid.raw_1 = Mmio::read<Rsp136_1>();
cid.raw_2 = Mmio::read<Rsp136_2>();
cid.raw_3 = Mmio::read<Rsp136_3>();
return cid;
}
@ -192,70 +192,89 @@ Cid Sdhc::_read_cid()
Csd Sdhc::_read_csd()
{
Csd csd;
csd.csd0 = read<Rsp136_0>();
csd.csd1 = read<Rsp136_1>();
csd.csd2 = read<Rsp136_2>();
csd.csd3 = read<Rsp136_3>();
csd.csd0 = Mmio::read<Rsp136_0>();
csd.csd1 = Mmio::read<Rsp136_1>();
csd.csd2 = Mmio::read<Rsp136_2>();
csd.csd3 = Mmio::read<Rsp136_3>();
return csd;
}
unsigned Sdhc::_read_rca()
{
Cmdrsp0::access_t const rsp0 = read<Cmdrsp0>();
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 *)
{
error("block transfer without DMA not supported by now");
error("block transfer without DMA not supported");
return false;
}
bool Sdhc::write_blocks(size_t, size_t, char const *)
{
error("block transfer without DMA not supported by now");
error("block transfer without DMA not supported");
return false;
}
bool Sdhc::read_blocks_dma(size_t blk_nr, size_t blk_cnt, addr_t buf_phys)
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(blk_cnt, buf_phys)) { return false; }
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(size_t blk_nr, size_t blk_cnt,
addr_t buf_phys)
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(blk_cnt, buf_phys)) { return false; }
if (_prepare_dma_mb(packet, false, blk_cnt, buf_phys)) { return false; }
return issue_command(Write_multiple_block(blk_nr));
}
Sdhc::Sdhc(addr_t const base,
unsigned const irq,
Delayer &delayer,
bool const use_dma)
Sdhc::Sdhc(Env &env)
:
Mmio(base), _irq(irq), _delayer(delayer), _card_info(_init()),
_use_dma(use_dma)
{ }
int Sdhc::_prepare_dma_mb(size_t blk_cnt, addr_t buf_phys)
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)
{
log("SD card detected");
log("capacity: ", card_info().capacity_mb(), " MiB");
}
int Sdhc::_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(); }
/* write ADMA2 table to DMA */
size_t const req_size = blk_cnt * BLOCK_SIZE;
if (_adma2_table.setup_request(req_size, buf_phys)) { return -1; }
/* configure DMA at host */
write<Adsaddr>(_adma2_table.base_phys());
write<Blkattr::Blksize>(BLOCK_SIZE);
write<Blkattr::Blkcnt>(blk_cnt);
Mmio::write<Adsaddr>(_adma2_table.base_phys());
Mmio::write<Blkattr::Blksize>(BLOCK_SIZE);
Mmio::write<Blkattr::Blkcnt>(blk_cnt);
_block_transfer.read = reading;
_block_transfer.packet = packet;
_block_transfer.pending = true;
return 0;
}
@ -279,24 +298,17 @@ int Sdhc::_wait_for_cmd_allowed()
}
void Sdhc::_wait_for_irq()
{
/* acknowledge IRQ first, to activate IRQ propagation initially */
_irq.ack_irq();
_irq_rec.wait_for_signal();
}
Card_info Sdhc::_init()
{
/* install IRQ signal */
_irq.sigh(_irq_rec.manage(&_irq_ctx));
_irq.sigh(_irq_handler);
_irq.ack_irq();
/* configure host for initialization stage */
if (_reset(_delayer)) { _detect_err("Host reset failed"); }
_disable_irqs();
if (!_supported_host_version(read<Hostver>())) {
if (!_supported_host_version(Mmio::read<Hostver>())) {
error("host version not supported");
throw Detection_failed();
}
@ -332,7 +344,7 @@ Card_info Sdhc::_init()
if (!issue_command(Send_if_cond())) {
_detect_err("Send_if_cond command failed"); }
if (read<Cmdrsp0>() != 0x1aa) {
if (Mmio::read<Cmdrsp0>() != 0x1aa) {
_detect_err("Unexpected response of Send_if_cond command"); }
/*
@ -352,7 +364,7 @@ Card_info Sdhc::_init()
if (!issue_command(Send_if_cond())) {
_detect_err("Send_if_cond failed"); }
if (read<Cmdrsp0>() != 0x1aa) {
if (Mmio::read<Cmdrsp0>() != 0x1aa) {
_detect_err("Unexpected response of Send_if_cond command"); }
/*
@ -369,7 +381,7 @@ Card_info Sdhc::_init()
if (!issue_command(Sd_send_op_cond(0x200000, true))) {
_detect_err("Sd_send_op_cond command failed"); }
if (Ocr::Busy::get(read<Cmdrsp0>())) { break; }
if (Ocr::Busy::get(Mmio::read<Cmdrsp0>())) { break; }
_delayer.usleep(1000);
}
if (!i) { _detect_err("Could not power-on SD card"); }
@ -405,16 +417,16 @@ Card_info Sdhc::_init()
_detect_err("Set_blocklen command failed"); }
/* configure host buffer */
Wml::access_t wml = read<Wml>();
Wml::access_t wml = Mmio::read<Wml>();
_watermark_level(wml);
write<Wml>(wml);
Mmio::write<Wml>(wml);
/* configure ADMA */
write<Proctl::Dmas>(Proctl::Dmas::ADMA2);
Mmio::write<Proctl::Dmas>(Proctl::Dmas::ADMA2);
/* configure interrupts for operational mode */
_disable_irqs();
write<Irqstat>(~0);
Mmio::write<Irqstat>(~0);
_enable_irqs();
return card_info;
}
@ -430,7 +442,7 @@ void Sdhc::_detect_err(char const * const err)
int Sdhc::_reset(Delayer &delayer)
{
/* start reset */
write<Sysctl::Rsta>(1);
Mmio::write<Sysctl::Rsta>(1);
_reset_amendments();
/* wait for reset completion */
@ -444,8 +456,8 @@ int Sdhc::_reset(Delayer &delayer)
void Sdhc::_disable_irqs()
{
write<Irqstaten>(0);
write<Irqsigen>(0);
Mmio::write<Irqstaten>(0);
Mmio::write<Irqsigen>(0);
}
@ -464,16 +476,16 @@ void Sdhc::_enable_irqs()
Irq::Debe::set(irq, 1);
Irq::Ac12e::set(irq, 1);
Irq::Dmae::set(irq, 1);
write<Irqstaten>(irq);
write<Irqsigen>(irq);
Mmio::write<Irqstaten>(irq);
Mmio::write<Irqsigen>(irq);
}
void Sdhc::_bus_width(Bus_width bus_width)
{
switch (bus_width) {
case BUS_WIDTH_1: write<Proctl::Dtw>(Proctl::Dtw::_1BIT); break;
case BUS_WIDTH_4: write<Proctl::Dtw>(Proctl::Dtw::_4BIT); break;
case BUS_WIDTH_1: Mmio::write<Proctl::Dtw>(Proctl::Dtw::_1BIT); break;
case BUS_WIDTH_4: Mmio::write<Proctl::Dtw>(Proctl::Dtw::_4BIT); break;
}
}
@ -481,19 +493,19 @@ void Sdhc::_bus_width(Bus_width bus_width)
void Sdhc::_disable_clock()
{
_disable_clock_preparation();
Sysctl::access_t sysctl = read<Sysctl>();
Sysctl::access_t sysctl = Mmio::read<Sysctl>();
Sysctl::Ipgen::set(sysctl, 0);
Sysctl::Hcken::set(sysctl, 0);
Sysctl::Peren::set(sysctl, 0);
Sysctl::Dvs::set(sysctl, Sysctl::Dvs::DIV1);
Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV1);
write<Sysctl>(sysctl);
Mmio::write<Sysctl>(sysctl);
}
void Sdhc::_enable_clock(Clock_divider divider)
{
Sysctl::access_t sysctl = read<Sysctl>();
Sysctl::access_t sysctl = Mmio::read<Sysctl>();
Sysctl::Ipgen::set(sysctl, 1);
Sysctl::Hcken::set(sysctl, 1);
Sysctl::Peren::set(sysctl, 1);
@ -511,7 +523,7 @@ void Sdhc::_enable_clock(Clock_divider divider)
Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV32);
break;
}
write<Sysctl>(sysctl);
Mmio::write<Sysctl>(sysctl);
_enable_clock_finish();
_delayer.usleep(1000);
}

View File

@ -15,9 +15,16 @@
#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>
@ -26,7 +33,9 @@
namespace Genode { class Sdhc; }
class Genode::Sdhc : public Sd_card::Host_controller, public Mmio
class Genode::Sdhc : public Attached_mmio,
public Block::Driver,
public Sd_card::Host_controller
{
private:
@ -179,17 +188,30 @@ class Genode::Sdhc : public Sd_card::Host_controller, public Mmio
enum Clock_divider { CLOCK_DIV_4, CLOCK_DIV_8, CLOCK_DIV_512 };
Irq_connection _irq;
Signal_receiver _irq_rec;
Signal_context _irq_ctx;
Delayer & _delayer;
Sd_card::Card_info _card_info;
bool const _use_dma;
Adma2::Table _adma2_table;
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();
@ -200,7 +222,6 @@ class Genode::Sdhc : public Sd_card::Host_controller, public Mmio
void _enable_clock_finish();
void _clock(Clock clock);
void _clock_finish(Clock clock);
void _wait_for_irq();
int _reset(Delayer & delayer);
void _reset_amendments();
int _wait_for_cmd_allowed();
@ -208,9 +229,8 @@ class Genode::Sdhc : public Sd_card::Host_controller, public Mmio
int _wait_for_card_ready_mbw();
int _stop_transmission();
void _stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp);
int _wait_for_cmd_complete_mb(bool const r);
int _wait_for_cmd_complete_mb_finish(bool const reading);
int _prepare_dma_mb(size_t blk_cnt, addr_t buf_phys);
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,
@ -232,18 +252,8 @@ class Genode::Sdhc : public Sd_card::Host_controller, public Mmio
/**
* Constructor
*
* \param base local base address of MMIO registers
* \param irq host-interrupt ID
* \param delayer delayer timing of MMIO accesses
* \param use_dma wether to use DMA or direct IO for transfers
*/
Sdhc(addr_t const base,
unsigned const irq,
Delayer &delayer,
bool const use_dma);
~Sdhc() { _irq_rec.dissolve(&_irq_ctx); }
Sdhc(Env &env);
/****************************************
@ -252,10 +262,77 @@ class Genode::Sdhc : public Sd_card::Host_controller, public Mmio
bool read_blocks(size_t, size_t, char *);
bool write_blocks(size_t, size_t, char const *);
bool read_blocks_dma(size_t blk_nr, size_t blk_cnt, addr_t buf_phys);
bool write_blocks_dma(size_t blk_nr, size_t blk_cnt, addr_t buf_phys);
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

@ -98,21 +98,21 @@ void Sdhc::_reset_amendments()
* nonetheless which disables clocks that card detection relies
* on.
*/
Sysctl::access_t sysctl = read<Sysctl>();
Sysctl::access_t sysctl = Mmio::read<Sysctl>();
Sysctl::Ipgen::set(sysctl, 1);
Sysctl::Hcken::set(sysctl, 1);
Sysctl::Peren::set(sysctl, 1);
write<Sysctl>(sysctl);
Mmio::write<Sysctl>(sysctl);
}
void Sdhc::_clock_finish(Clock clock)
{
write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
Mmio::write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
switch (clock) {
case CLOCK_INITIAL: _enable_clock(CLOCK_DIV_512); break;
case CLOCK_OPERATIONAL: _enable_clock(CLOCK_DIV_8); break; }
write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
Mmio::write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
}

View File

@ -20,7 +20,7 @@ using namespace Genode;
void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
{
Mixctrl::access_t mixctrl = read<Mixctrl>();
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
Mixctrl::Dmaen::set(mixctrl, 1);
Mixctrl::Bcen::set(mixctrl, 1);
Mixctrl::Ac12en::set(mixctrl, 0);
@ -29,7 +29,7 @@ void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
Mixctrl::Msbsel::set(mixctrl, 1);
Mixctrl::Nibblepos::set(mixctrl, 0);
Mixctrl::Ac23en::set(mixctrl, 0);
write<Mixctrl>(mixctrl);
Mmio::write<Mixctrl>(mixctrl);
}
@ -45,7 +45,7 @@ bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &,
bool const multiblock,
bool const reading)
{
Mixctrl::access_t mixctrl = read<Mixctrl>();
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
Mixctrl::Dmaen ::set(mixctrl, transfer && multiblock && _use_dma);
Mixctrl::Bcen ::set(mixctrl, transfer);
Mixctrl::Ac12en ::set(mixctrl, 0);
@ -59,7 +59,7 @@ bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &,
if (_wait_for_cmd_allowed()) {
return false; }
write<Mixctrl>(mixctrl);
Mmio::write<Mixctrl>(mixctrl);
return true;
}
@ -81,7 +81,7 @@ void Sdhc::_watermark_level(Wml::access_t &wml)
void Sdhc::_reset_amendments()
{
/* the USDHC doesn't reset the Mixer Control register automatically */
Mixctrl::access_t mixctrl = read<Mixctrl>();
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
Mixctrl::Dmaen::set(mixctrl, 0);
Mixctrl::Bcen::set(mixctrl, 0);
Mixctrl::Ac12en::set(mixctrl, 0);
@ -91,7 +91,7 @@ void Sdhc::_reset_amendments()
Mixctrl::Nibblepos::set(mixctrl, 0);
Mixctrl::Ac23en::set(mixctrl, 0);
Mixctrl::Always_ones::set(mixctrl, 1);
write<Mixctrl>(mixctrl);
Mmio::write<Mixctrl>(mixctrl);
}
@ -99,18 +99,18 @@ void Sdhc::_clock_finish(Clock clock)
{
switch (clock) {
case CLOCK_INITIAL:
write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_13);
Mmio::write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_13);
_enable_clock(CLOCK_DIV_512);
break;
case CLOCK_OPERATIONAL:
write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_28);
write<Sysctl::Ipp_rst_n>(0);
Mmio::write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_28);
Mmio::write<Sysctl::Ipp_rst_n>(0);
_enable_clock(CLOCK_DIV_4);
break;
}
}
void Sdhc::_disable_clock_preparation() { write<Vendspec::Frc_sdclk_on>(0); }
void Sdhc::_disable_clock_preparation() { Mmio::write<Vendspec::Frc_sdclk_on>(0); }
void Sdhc::_enable_clock_finish() { write<Vendspec::Frc_sdclk_on>(0); }
void Sdhc::_enable_clock_finish() { Mmio::write<Vendspec::Frc_sdclk_on>(0); }

View File

@ -18,7 +18,6 @@
#include <os/attached_io_mem_dataspace.h>
#include <base/log.h>
#include <timer_session/connection.h>
#include <block/component.h>
#include <os/server.h>
/* local includes */
@ -30,7 +29,8 @@ namespace Block {
}
class Block::Sdhci_driver : public Block::Driver
class Block::Sdhci_driver : public Block::Driver,
public Sd_ack_handler
{
private:
@ -60,11 +60,11 @@ class Block::Sdhci_driver : public Block::Driver
struct Dma_not_supported : Exception { };
Sdhci_driver(Env &)
Sdhci_driver(Env &env)
:
_mmchs1_mmio(MMCHS1_MMIO_BASE, MMCHS1_MMIO_SIZE),
_controller((addr_t)_mmchs1_mmio.local_addr<void>(),
_delayer, false),
_controller(env.ep(), (addr_t)_mmchs1_mmio.local_addr<void>(),
_delayer, *this, false),
_use_dma(false)
{
if (_use_dma) { throw Dma_not_supported(); }
@ -74,6 +74,9 @@ class Block::Sdhci_driver : public Block::Driver
Genode::log("capacity: ", card_info.capacity_mb(), " MiB");
}
void handle_ack(Block::Packet_descriptor pkt, bool success) {
ack_packet(pkt, success); }
/*****************************
** Block::Driver interface **
@ -99,9 +102,7 @@ class Block::Sdhci_driver : public Block::Driver
char *out_buffer,
Packet_descriptor &packet)
{
if (!_controller.read_blocks(block_number, block_count, out_buffer))
throw Io_error();
ack_packet(packet);
_controller.read_blocks(block_number, block_count, out_buffer, packet);
}
void write(Block::sector_t block_number,
@ -109,9 +110,7 @@ class Block::Sdhci_driver : public Block::Driver
char const *buffer,
Packet_descriptor &packet)
{
if (!_controller.write_blocks(block_number, block_count, buffer))
throw Io_error();
ack_packet(packet);
_controller.write_blocks(block_number, block_count, buffer, packet);
}
void read_dma(Block::sector_t block_number,
@ -119,9 +118,7 @@ class Block::Sdhci_driver : public Block::Driver
Genode::addr_t phys,
Packet_descriptor &packet)
{
if (!_controller.read_blocks_dma(block_number, block_count, phys))
throw Io_error();
ack_packet(packet);
_controller.read_blocks_dma(block_number, block_count, phys, packet);
}
void write_dma(Block::sector_t block_number,
@ -129,9 +126,7 @@ class Block::Sdhci_driver : public Block::Driver
Genode::addr_t phys,
Packet_descriptor &packet)
{
if (!_controller.write_blocks_dma(block_number, block_count, phys))
throw Io_error();
ack_packet(packet);
_controller.write_blocks_dma(block_number, block_count, phys, packet);
}
bool dma_enabled() { return _use_dma; }

View File

@ -19,6 +19,7 @@
#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>
@ -355,7 +356,6 @@ struct Mmchs : Genode::Mmio
Genode::error("reset of cmd line timed out (src != 0)");
return false;
}
return true;
}
@ -484,10 +484,22 @@ struct Mmchs : Genode::Mmio
};
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;
@ -500,9 +512,9 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
Adma_desc::access_t * const _adma_desc;
Genode::addr_t const _adma_desc_phys;
Genode::Irq_connection _irq;
Genode::Signal_receiver _irq_rec;
Genode::Signal_context _irq_ctx;
Sd_ack_handler &_ack_handler;
Genode::Signal_handler<Omap4_hsmmc_controller> _irq_handler;
Genode::Irq_connection _irq;
Sd_card::Card_info _init()
{
@ -690,19 +702,6 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
return true;
}
bool _wait_for_transfer_complete()
{
if (!wait_for<Stat::Tc>(1, _delayer, 1000*1000, 0)
&& !wait_for<Stat::Tc>(1, _delayer)) {
Genode::error("Stat::Tc timed out");
return false;
}
/* clear transfer-completed bit */
write<Stat::Tc>(1);
return true;
}
bool _wait_for_bre()
{
if (!wait_for<Pstate::Bre>(1, _delayer, 1000*1000, 0)
@ -723,38 +722,29 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
return true;
}
bool _wait_for_transfer_complete_irq()
void _handle_irq()
{
/*
* XXX For now, the driver works fully synchronous. We merely use
* the interrupt mechanism to yield CPU time to concurrently
* running processes.
*/
for (;;) {
/*
* We ack the IRQ first to implicitly active receiving
* IRQ signals when entering this loop for the first time.
*/
_irq.ack_irq();
_irq_rec.wait_for_signal();
_irq.ack_irq();
/* check for transfer completion */
if (read<Stat::Tc>() == 1) {
/* clear transfer-completed bit */
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 true;
}
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:
@ -766,8 +756,9 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
*
* \param mmio_base local base address of MMIO registers
*/
Omap4_hsmmc_controller(Genode::addr_t const mmio_base, Delayer &delayer,
bool use_dma)
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),
@ -776,13 +767,14 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
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_rec.manage(&_irq_ctx));
_irq.sigh(_irq_handler);
_irq.ack_irq();
}
~Omap4_hsmmc_controller() { _irq_rec.dissolve(&_irq_ctx); }
/****************************************
** Sd_card::Host_controller interface **
@ -897,22 +889,23 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
return Sd_card::Send_relative_addr::Response::Rca::get(read<Rsp10>());
}
/**
* Read data blocks from SD card
*
* \return true on success
*/
bool read_blocks(size_t block_number, size_t block_count, char *out_buffer)
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, Stat: ",
Genode::Hex(read<Stat>()));
return false;
Genode::error("Read_multiple_block failed");
throw Block::Driver::Io_error();
}
size_t const num_accesses = block_count*512/sizeof(Data::access_t);
@ -920,29 +913,29 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
for (size_t i = 0; i < num_accesses; i++) {
if (!_wait_for_bre())
return false;
throw Block::Driver::Io_error();
*dst++ = read<Data>();
}
return _wait_for_transfer_complete();
}
/**
* Write data blocks to SD card
*
* \return true on success
*/
bool write_blocks(size_t block_number, size_t block_count, char const *buffer)
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");
return false;
throw Block::Driver::Io_error();
}
size_t const num_accesses = block_count*512/sizeof(Data::access_t);
@ -950,59 +943,56 @@ struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
for (size_t i = 0; i < num_accesses; i++) {
if (!_wait_for_bwe())
return false;
throw Block::Driver::Io_error();
write<Data>(*src++);
}
return _wait_for_transfer_complete();
}
/**
* 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)
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);
if (!issue_command(Read_multiple_block(block_number))) {
Genode::error("Read_multiple_block failed, Stat: ",
Genode::Hex(read<Stat>()));
return false;
}
_block_transfer.packet = pkt;
_block_transfer.pending = true;
return _wait_for_transfer_complete_irq();
if (!issue_command(Read_multiple_block(block_number))) {
Genode::error("Read_multiple_block failed");
throw Block::Driver::Io_error();
}
}
/**
* 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)
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");
return false;
throw Block::Driver::Io_error();
}
return _wait_for_transfer_complete_irq();
}
};

View File

@ -27,6 +27,7 @@ namespace Block {
class Sdhci_driver;
}
class Block::Sdhci_driver : public Block::Driver
{
private:

View File

@ -14,7 +14,6 @@
/* Genode includes */
#include <base/component.h>
#include <base/log.h>
#include <timer_session/connection.h>
#include <os/attached_ram_dataspace.h>
#include <os/config.h>
@ -24,146 +23,137 @@
using namespace Genode;
struct Operation
{
virtual void operator () (Block::Driver &driver,
Genode::addr_t block_number,
Genode::size_t block_count,
Genode::addr_t buffer_phys,
char *buffer_virt) = 0;
};
/*
* \param total_size total number of bytes to read
* \param request_size number of bytes per request
*/
static void run_benchmark(Block::Driver &driver,
Timer::Session &timer,
char *buffer_virt,
Genode::addr_t buffer_phys,
Genode::size_t buffer_size,
Genode::size_t request_size,
Operation &operation)
{
using namespace Genode;
log("request_size=", request_size, " bytes");
size_t const time_before_ms = timer.elapsed_ms();
size_t num_requests = buffer_size / request_size;
/*
* Trim number of requests if it would take to much time
*/
if (num_requests > 320) {
buffer_size = 320 * request_size;
num_requests = buffer_size / request_size;
}
for (size_t i = 0; i < num_requests; i++)
{
size_t const block_count = request_size / driver.block_size();
addr_t const block_number = i*block_count;
operation(driver, block_number, block_count,
buffer_phys + i*request_size,
buffer_virt + i*request_size);
}
size_t const time_after_ms = timer.elapsed_ms();
size_t const duration_ms = time_after_ms - time_before_ms;
/*
* Convert bytes per milliseconds to kilobytes per seconds
*
* (total_size / 1024) / (duration_ms / 1000)
*/
size_t const buffer_size_kb = buffer_size / 1024;
size_t const throughput_kb_per_sec = (1000*buffer_size_kb) / duration_ms;
log(" duration: ", duration_ms, " ms");
log(" amount: ", buffer_size_kb, " KiB");
log(" throughput: ", throughput_kb_per_sec, " KiB/sec");
}
struct Main
{
Main(Env &env)
using Packet_descriptor = Block::Packet_descriptor;
struct Block_operation_failed : Exception { };
enum Operation { READ, WRITE };
struct Driver_session : Block::Driver_session_base
{
log("--- SD card benchmark ---");
Signal_transmitter sig;
unsigned long nr_of_acks { 0 };
Driver_session(Signal_context_capability sig) : sig(sig) { }
static Block::Sdhci_driver driver(env);
bool const use_dma = driver.dma_enabled();
static Timer::Connection timer;
long const request_sizes[] = {
512, 1024, 2048, 4096, 8192, 16384, 32768, 64*1024, 128*1024, 0 };
/* total size of communication buffer */
size_t const buffer_size = 10 * 1024 * 1024;
/* allocate read/write buffer */
static Attached_ram_dataspace buffer(&env.ram(), buffer_size, Genode::UNCACHED);
char * const buffer_virt = buffer.local_addr<char>();
addr_t const buffer_phys = Dataspace_client(buffer.cap()).phys_addr();
/*
* Benchmark reading from SD card
*/
log("\n-- reading from SD card (", use_dma ? "" : "not ", "using DMA) --");
struct Read : Operation
void ack_packet(Packet_descriptor &, bool success)
{
void operator () (Block::Driver &driver,
addr_t number, size_t count, addr_t phys, char *virt)
{
Block::Packet_descriptor p;
if (driver.dma_enabled())
driver.read_dma(number, count, phys, p);
else
driver.read(number, count, virt, p);
}
} read_operation;
for (unsigned i = 0; request_sizes[i]; i++) {
run_benchmark(driver, timer, buffer_virt, buffer_phys, buffer_size,
request_sizes[i], read_operation);
if (!success) {
throw Block_operation_failed(); }
nr_of_acks++;
sig.submit();
}
};
/*
* Benchmark writing to SD card
*
* We write back the content of the buffer, which we just filled during the
* read benchmark. If both read and write succeed, the SD card will retain
* its original content.
*/
Env &env;
Packet_descriptor pkt;
unsigned long time_before_ms;
Timer::Connection timer;
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 };
size_t const buf_size_kib { config()->xml_node()
.attribute_value("buffer_size_kib",
(size_t)0) };
size_t const buf_size { buf_size_kib * 1024 };
Attached_ram_dataspace buf { &env.ram(), buf_size, UNCACHED };
char *buf_virt { buf.local_addr<char>() };
addr_t buf_phys { Dataspace_client(buf.cap())
.phys_addr() };
size_t buf_off_done { 0 };
size_t buf_off_pend { 0 };
unsigned req_size_id { 0 };
size_t req_sizes[9] { 512, 1024, 1024 * 2, 1024 * 4,
1024 * 8, 1024 * 16, 1024 * 32,
1024 * 64, 1024 * 128 };
log("\n-- writing to SD card (", use_dma ? "" : "not ", "using DMA) --");
size_t req_size() const { return req_sizes[req_size_id]; }
struct Write : Operation
{
void operator () (Block::Driver &driver,
addr_t number, size_t count, addr_t phys, char *virt)
{
Block::Packet_descriptor p;
if (driver.dma_enabled())
driver.write_dma(number, count, phys, p);
else
driver.write(number, count, virt, p);
void update_state()
{
/* raise done counter and check if the buffer is full */
buf_off_done += drv_session.nr_of_acks * req_size();
drv_session.nr_of_acks = 0;
if (buf_off_done == buf_size) {
/* print stats for the current request size */
unsigned long const time_after_ms = timer.elapsed_ms();
unsigned long const duration_ms = time_after_ms - time_before_ms;
size_t const kib_per_sec = (1000 * buf_size_kib) /
duration_ms;
log(" duration: ", duration_ms, " ms");
log(" amount: ", buf_size_kib, " KiB");
log(" throughput: ", kib_per_sec, " KiB/sec");
/* go to next request size */
buf_off_pend = 0;
buf_off_done = 0;
req_size_id++;
/* check if we have done all request sizes for an operation */
if (req_size_id == sizeof(req_sizes)/sizeof(req_sizes[0])) {
/* go to next operation or end the test */
log("");
req_size_id = 0;
switch (operation) {
case READ:
operation = WRITE;
log("-- writing to SD card --");
break;
case WRITE:
log("--- SD card benchmark finished ---");
return;
}
}
} write_operation;
log(" request size ", req_size(), " bytes");
time_before_ms = timer.elapsed_ms();
}
/* issue as many requests for the current request size as possible */
try {
size_t const cnt = req_size() / drv.block_size();
for (; buf_off_pend < buf_size; buf_off_pend += req_size()) {
for (unsigned i = 0; request_sizes[i]; i++)
run_benchmark(driver, timer, buffer_virt, buffer_phys, buffer_size,
request_sizes[i], write_operation);
/* calculate block offset */
addr_t const nr = buf_off_pend / drv.block_size();
log("\n--- SD card benchmark finished ---");
if (drv.dma_enabled()) {
/* request with DMA */
addr_t const phys = buf_phys + buf_off_pend;
switch (operation) {
case READ: drv.read_dma(nr, cnt, phys, pkt); break;
case WRITE: drv.write_dma(nr, cnt, phys, pkt); break; }
} else {
/* request without DMA */
char *const virt = buf_virt + buf_off_pend;
switch (operation) {
case READ: drv.read(nr, cnt, virt, pkt); break;
case WRITE: drv.write(nr, cnt, virt, pkt); break; }
}
}
} catch (Block::Driver::Request_congestion) { }
}
Main(Env &env) : env(env)
{
log("");
log("--- SD card benchmark (", drv.dma_enabled() ? "with" : "no", " DMA) ---");
drv.session(&drv_session);
/* start issuing requests */
log("");
log("-- reading from SD card --");
log(" request size ", req_size(), " bytes");
time_before_ms = timer.elapsed_ms();
update_state();
}
};