mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-19 08:36:49 +00:00
Remove Block::Driver interface
This commit removes the aged Block::Driver interface that has already been superseded by the Block::Request_stream API for some time now. It is solely used by components whose existence is made redundant, like 'lx_block', or old drivers, like the SD-Card driver for PL180 and old i.MX53/6 systems or the RPi 1, that have not seen active use other than in the nightly CI runs. Fixes #5522.
This commit is contained in:
parent
5fdfc4fcea
commit
76ad585e56
@ -1,336 +0,0 @@
|
||||
/*
|
||||
* \brief Block-session component
|
||||
* \author Christian Helmuth
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2011-05-20
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__BLOCK__COMPONENT_H_
|
||||
#define _INCLUDE__BLOCK__COMPONENT_H_
|
||||
|
||||
#include <base/log.h>
|
||||
#include <base/component.h>
|
||||
#include <base/allocator_avl.h>
|
||||
#include <base/heap.h>
|
||||
#include <root/component.h>
|
||||
#include <block/driver.h>
|
||||
|
||||
namespace Block {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
class Session_component_base;
|
||||
class Session_component;
|
||||
class Root;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* We have a hen and egg situation that makes this base class necessary.
|
||||
* The Block::Session_rpc_object construction depends on a dataspace for
|
||||
* the packet stream. The dataspace on the other hand is constructed by
|
||||
* the driver, which is created on demand when creating a session.
|
||||
* When creating the driver, and dataspace outside the Session_component
|
||||
* constructor within _create_session of the root component, we would have
|
||||
* to destroy the driver and dataspace within the destructor body of
|
||||
* Session_component, which will lead to problems, because the packet stream
|
||||
* destructors will be called after the shared memory already vanished.
|
||||
*/
|
||||
class Block::Session_component_base
|
||||
{
|
||||
protected:
|
||||
|
||||
Driver_factory &_driver_factory;
|
||||
Driver &_driver;
|
||||
Driver::Dma_buffer _rq;
|
||||
|
||||
Session_component_base(Driver_factory &factory, size_t tx_buf_size)
|
||||
:
|
||||
_driver_factory(factory),
|
||||
_driver(*factory.create()),
|
||||
_rq(_driver.alloc_dma_buffer(tx_buf_size, UNCACHED))
|
||||
{ }
|
||||
|
||||
~Session_component_base()
|
||||
{
|
||||
_driver.free_dma_buffer(_rq.ds);
|
||||
_driver_factory.destroy(&_driver);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Block::Session_component : public Block::Session_component_base,
|
||||
public Block::Driver_session
|
||||
{
|
||||
private:
|
||||
|
||||
addr_t _rq_phys;
|
||||
Signal_handler<Session_component> _sink_ack;
|
||||
Signal_handler<Session_component> _sink_submit;
|
||||
bool _req_queue_full = false;
|
||||
bool _ack_queue_full = false;
|
||||
Packet_descriptor _p_to_handle { };
|
||||
unsigned _p_in_fly;
|
||||
Info const _info { _driver.info() };
|
||||
bool const _writeable;
|
||||
|
||||
/**
|
||||
* Acknowledge a packet already handled
|
||||
*/
|
||||
inline void _ack_packet(Packet_descriptor &packet)
|
||||
{
|
||||
if (!tx_sink()->ready_to_ack())
|
||||
error("not ready to ack!");
|
||||
|
||||
tx_sink()->acknowledge_packet(packet);
|
||||
_p_in_fly--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Range check packet request
|
||||
*/
|
||||
inline bool _range_check(Packet_descriptor &p) {
|
||||
return p.block_number() + p.block_count() - 1
|
||||
< _info.block_count; }
|
||||
|
||||
/**
|
||||
* Handle a single request
|
||||
*/
|
||||
void _handle_packet(Packet_descriptor packet)
|
||||
{
|
||||
_p_to_handle = packet;
|
||||
_p_to_handle.succeeded(false);
|
||||
|
||||
/* ignore invalid packets */
|
||||
bool const valid = _range_check(_p_to_handle)
|
||||
&& tx_sink()->packet_valid(packet)
|
||||
&& aligned(packet.offset(), (unsigned)_info.align_log2);
|
||||
if (!valid) {
|
||||
_ack_packet(_p_to_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
switch (_p_to_handle.operation()) {
|
||||
|
||||
case Block::Packet_descriptor::READ:
|
||||
if (_driver.dma_enabled())
|
||||
_driver.read_dma(packet.block_number(),
|
||||
packet.block_count(),
|
||||
_rq_phys + packet.offset(),
|
||||
_p_to_handle);
|
||||
else
|
||||
_driver.read(packet.block_number(),
|
||||
packet.block_count(),
|
||||
tx_sink()->packet_content(packet),
|
||||
_p_to_handle);
|
||||
break;
|
||||
|
||||
case Block::Packet_descriptor::WRITE:
|
||||
if (!_writeable) {
|
||||
_ack_packet(_p_to_handle);
|
||||
break;
|
||||
}
|
||||
if (_driver.dma_enabled())
|
||||
_driver.write_dma(packet.block_number(),
|
||||
packet.block_count(),
|
||||
_rq_phys + packet.offset(),
|
||||
_p_to_handle);
|
||||
else
|
||||
_driver.write(packet.block_number(),
|
||||
packet.block_count(),
|
||||
tx_sink()->packet_content(packet),
|
||||
_p_to_handle);
|
||||
break;
|
||||
|
||||
case Block::Packet_descriptor::SYNC:
|
||||
|
||||
/* perform (blocking) sync */
|
||||
_driver.sync();
|
||||
|
||||
_p_to_handle.succeeded(true);
|
||||
_ack_packet(_p_to_handle);
|
||||
_p_to_handle = Packet_descriptor();
|
||||
break;
|
||||
|
||||
case Block::Packet_descriptor::TRIM:
|
||||
|
||||
/* trim is a nop */
|
||||
_p_to_handle.succeeded(true);
|
||||
_ack_packet(_p_to_handle);
|
||||
_p_to_handle = Packet_descriptor();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Driver::Io_error();
|
||||
}
|
||||
} catch (Driver::Request_congestion) {
|
||||
_req_queue_full = true;
|
||||
} catch (Driver::Io_error) {
|
||||
_ack_packet(_p_to_handle);
|
||||
} catch (Genode::Packet_descriptor::Invalid_packet) {
|
||||
_p_to_handle = Packet_descriptor();
|
||||
Genode::error("dropping invalid Block packet");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever a signal from the packet-stream interface triggered
|
||||
*/
|
||||
void _signal()
|
||||
{
|
||||
/*
|
||||
* as long as more packets are available, and we're able to ack
|
||||
* them, and the driver's request queue isn't full,
|
||||
* direct the packet request to the driver backend
|
||||
*/
|
||||
for (_ack_queue_full = (_p_in_fly >= tx_sink()->ack_slots_free());
|
||||
!_req_queue_full && !_ack_queue_full
|
||||
&& tx_sink()->packet_avail();
|
||||
_ack_queue_full = (++_p_in_fly >= tx_sink()->ack_slots_free()))
|
||||
_handle_packet(tx_sink()->get_packet());
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param driver_factory factory to create and destroy driver objects
|
||||
* \param ep entrypoint handling this session component
|
||||
* \param buf_size size of packet-stream payload buffer
|
||||
*/
|
||||
Session_component(Driver_factory &driver_factory,
|
||||
Genode::Entrypoint &ep,
|
||||
Genode::Env::Local_rm &rm,
|
||||
size_t buf_size,
|
||||
bool writeable)
|
||||
: Session_component_base(driver_factory, buf_size),
|
||||
Driver_session(rm, _rq.ds, ep.rpc_ep()),
|
||||
_rq_phys(_rq.dma_addr),
|
||||
_sink_ack(ep, *this, &Session_component::_signal),
|
||||
_sink_submit(ep, *this, &Session_component::_signal),
|
||||
_req_queue_full(false),
|
||||
_p_in_fly(0),
|
||||
_writeable(writeable && _info.writeable)
|
||||
{
|
||||
_tx.sigh_ready_to_ack(_sink_ack);
|
||||
_tx.sigh_packet_avail(_sink_submit);
|
||||
|
||||
_driver.session(this);
|
||||
}
|
||||
|
||||
~Session_component() { _driver.session(nullptr); }
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
void ack_packet(Packet_descriptor &packet, bool success) override
|
||||
{
|
||||
packet.succeeded(success);
|
||||
_ack_packet(packet);
|
||||
|
||||
if (!_req_queue_full && !_ack_queue_full)
|
||||
return;
|
||||
|
||||
/*
|
||||
* when the driver's request queue was full,
|
||||
* handle last unprocessed packet taken out of submit queue
|
||||
*/
|
||||
if (_req_queue_full) {
|
||||
_req_queue_full = false;
|
||||
_handle_packet(_p_to_handle);
|
||||
}
|
||||
|
||||
/* resume packet processing */
|
||||
_signal();
|
||||
}
|
||||
|
||||
|
||||
/*******************************
|
||||
** Block session interface **
|
||||
*******************************/
|
||||
|
||||
Info info() const override { return _driver.info(); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Root component, handling new session requests
|
||||
*/
|
||||
class Block::Root : public Genode::Root_component<Block::Session_component,
|
||||
Single_client>
|
||||
{
|
||||
private:
|
||||
|
||||
Driver_factory &_driver_factory;
|
||||
Genode::Entrypoint &_ep;
|
||||
Genode::Env::Local_rm &_rm;
|
||||
bool const _writeable;
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Always returns the singleton block-session component
|
||||
*/
|
||||
Session_component *_create_session(const char *args) override
|
||||
{
|
||||
size_t ram_quota =
|
||||
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
|
||||
size_t tx_buf_size =
|
||||
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
|
||||
|
||||
/*
|
||||
* Check if donated ram quota suffices for both
|
||||
* communication buffers. Also check both sizes separately
|
||||
* to handle a possible overflow of the sum of both sizes.
|
||||
*/
|
||||
if (tx_buf_size > ram_quota) {
|
||||
error("insufficient 'ram_quota', got ", ram_quota, ", need ",
|
||||
tx_buf_size);
|
||||
throw Insufficient_ram_quota();
|
||||
}
|
||||
|
||||
bool writeable = _writeable
|
||||
? Arg_string::find_arg(args, "writeable").bool_value(true)
|
||||
: false;
|
||||
|
||||
return new (md_alloc()) Session_component(_driver_factory,
|
||||
_ep, _rm, tx_buf_size,
|
||||
writeable);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param ep entrypoint handling this root component
|
||||
* \param md_alloc allocator to allocate session components
|
||||
* \param rm region map
|
||||
* \param driver_factory factory to create and destroy driver backend
|
||||
*/
|
||||
Root(Genode::Entrypoint &ep,
|
||||
Allocator &md_alloc,
|
||||
Genode::Env::Local_rm &rm,
|
||||
Driver_factory &driver_factory,
|
||||
bool writeable)
|
||||
:
|
||||
Root_component(ep, md_alloc),
|
||||
_driver_factory(driver_factory), _ep(ep), _rm(rm), _writeable(writeable)
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__BLOCK__COMPONENT_H_ */
|
@ -1,261 +0,0 @@
|
||||
/*
|
||||
* \brief Block-driver interface
|
||||
* \author Christian Helmuth
|
||||
* \author Sebastian Sumpf
|
||||
* \author Stefan kalkowski
|
||||
* \date 2011-05-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__BLOCK__DRIVER_H_
|
||||
#define _INCLUDE__BLOCK__DRIVER_H_
|
||||
|
||||
#include <base/exception.h>
|
||||
#include <base/stdint.h>
|
||||
#include <base/signal.h>
|
||||
#include <base/ram_allocator.h>
|
||||
#include <block_session/rpc_object.h>
|
||||
|
||||
namespace Block {
|
||||
class Driver_session_base;
|
||||
class Driver_session;
|
||||
class Driver;
|
||||
struct Driver_factory;
|
||||
};
|
||||
|
||||
|
||||
struct Block::Driver_session_base : Genode::Interface
|
||||
{
|
||||
/**
|
||||
* 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:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param rm region map of local address space, used to attach
|
||||
* the packet-stream buffer to the local address space
|
||||
* \param tx_ds dataspace used as communication buffer
|
||||
* for the tx packet stream
|
||||
* \param ep entry point used for packet-stream channel
|
||||
*/
|
||||
Driver_session(Genode::Env::Local_rm &rm,
|
||||
Genode::Dataspace_capability tx_ds,
|
||||
Genode::Rpc_entrypoint &ep)
|
||||
: Session_rpc_object(rm, tx_ds, ep) { }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Interface to be implemented by the device-specific driver code
|
||||
*/
|
||||
class Block::Driver : Genode::Interface
|
||||
{
|
||||
private:
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Driver(Driver const &);
|
||||
Driver &operator = (Driver const &);
|
||||
|
||||
Genode::Ram_allocator &_ram;
|
||||
|
||||
Driver_session_base *_session = nullptr;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Exceptions
|
||||
*/
|
||||
class Io_error : public ::Genode::Exception { };
|
||||
class Request_congestion : public ::Genode::Exception { };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Driver(Genode::Ram_allocator &ram) : _ram(ram) { }
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~Driver() { }
|
||||
|
||||
/**
|
||||
* Request block-device information
|
||||
*/
|
||||
virtual Session::Info info() const = 0;
|
||||
|
||||
/**
|
||||
* Read from medium
|
||||
*
|
||||
* \param block_number number of first block to read
|
||||
* \param block_count number of blocks to read
|
||||
* \param buffer output buffer for read request
|
||||
* \param packet packet descriptor from the client
|
||||
*
|
||||
* \throw Request_congestion
|
||||
*
|
||||
* Note: should be overridden by DMA non-capable devices
|
||||
*/
|
||||
virtual void read(sector_t /* block_number */,
|
||||
Genode::size_t /* block_count */,
|
||||
char * /* buffer */,
|
||||
Packet_descriptor & /* packet */) {
|
||||
throw Io_error(); }
|
||||
|
||||
/**
|
||||
* Write to medium
|
||||
*
|
||||
* \param block_number number of first block to write
|
||||
* \param block_count number of blocks to write
|
||||
* \param buffer buffer for write request
|
||||
* \param packet packet descriptor from the client
|
||||
*
|
||||
* \throw Request_congestion
|
||||
*
|
||||
* Note: should be overridden by DMA non-capable, non-ROM devices
|
||||
*/
|
||||
virtual void write(sector_t /* block_number */,
|
||||
Genode::size_t /* block_count */,
|
||||
const char * /* buffer */,
|
||||
Packet_descriptor & /* packet */) {
|
||||
throw Io_error(); }
|
||||
|
||||
/**
|
||||
* Read from medium using DMA
|
||||
*
|
||||
* \param block_number number of first block to read
|
||||
* \param block_count number of blocks to read
|
||||
* \param phys phyiscal address of read buffer
|
||||
* \param packet packet descriptor from the client
|
||||
*
|
||||
* \throw Request_congestion
|
||||
*
|
||||
* Note: should be overridden by DMA capable devices
|
||||
*/
|
||||
virtual void read_dma(sector_t /* block_number */,
|
||||
Genode::size_t /* block_count */,
|
||||
Genode::addr_t /* phys */,
|
||||
Packet_descriptor & /* packet */) {
|
||||
throw Io_error(); }
|
||||
|
||||
/**
|
||||
* Write to medium using DMA
|
||||
*
|
||||
* \param block_number number of first block to write
|
||||
* \param block_count number of blocks to write
|
||||
* \param phys physical address of write buffer
|
||||
* \param packet packet descriptor from the client
|
||||
*
|
||||
* \throw Request_congestion
|
||||
*
|
||||
* Note: should be overridden by DMA capable, non-ROM devices
|
||||
*/
|
||||
virtual void write_dma(sector_t /* block_number */,
|
||||
Genode::size_t /* block_count */,
|
||||
Genode::addr_t /* phys */,
|
||||
Packet_descriptor & /* packet */) {
|
||||
throw Io_error(); }
|
||||
|
||||
/**
|
||||
* Check if DMA is enabled for driver
|
||||
*
|
||||
* \return true if DMA is enabled, false otherwise
|
||||
*
|
||||
* Note: has to be overriden by DMA-capable devices
|
||||
*/
|
||||
virtual bool dma_enabled() { return false; }
|
||||
|
||||
struct Dma_buffer
|
||||
{
|
||||
Genode::Ram_dataspace_capability ds;
|
||||
Genode::addr_t dma_addr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocate buffer which is suitable for DMA.
|
||||
*
|
||||
* Note: has to be overriden by DMA-capable devices
|
||||
*/
|
||||
virtual Dma_buffer alloc_dma_buffer(Genode::size_t, Genode::Cache) = 0;
|
||||
|
||||
/**
|
||||
* Free buffer which is suitable for DMA.
|
||||
*
|
||||
* Note: has to be overriden by DMA-capable devices
|
||||
*/
|
||||
virtual void free_dma_buffer(Genode::Ram_dataspace_capability c) {
|
||||
return _ram.free(c); }
|
||||
|
||||
/**
|
||||
* Synchronize with device.
|
||||
*
|
||||
* Note: should be overriden by (e.g. intermediate) components,
|
||||
* which cache data
|
||||
*/
|
||||
virtual void sync() {}
|
||||
|
||||
/**
|
||||
* Informs the driver that the client session was closed
|
||||
*
|
||||
* Note: drivers with state (e.g. asynchronously working)
|
||||
* should override this method, and reset their internal state
|
||||
*/
|
||||
virtual void session_invalidated() { }
|
||||
|
||||
/**
|
||||
* Set single session component of the driver
|
||||
*
|
||||
* Session might get used to acknowledge requests.
|
||||
*/
|
||||
void session(Driver_session_base *session) {
|
||||
if (!(_session = session)) session_invalidated(); }
|
||||
|
||||
/**
|
||||
* Acknowledge a packet after processing finished to the client
|
||||
*
|
||||
* \param p packet to acknowledge
|
||||
*/
|
||||
void ack_packet(Packet_descriptor &p, bool success = true) {
|
||||
if (_session) _session->ack_packet(p, success); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Interface for constructing the driver object
|
||||
*/
|
||||
struct Block::Driver_factory : Genode::Interface
|
||||
{
|
||||
/**
|
||||
* Construct new driver
|
||||
*/
|
||||
virtual Driver *create() = 0;
|
||||
|
||||
/**
|
||||
* Destroy driver
|
||||
*/
|
||||
virtual void destroy(Driver *driver) = 0;
|
||||
};
|
||||
|
||||
#endif /* _BLOCK__DRIVER_H_ */
|
@ -1,76 +0,0 @@
|
||||
assert {[have_board pbxa9]}
|
||||
|
||||
create_boot_directory
|
||||
import_from_depot [depot_user]/src/[base_src] \
|
||||
[depot_user]/src/init \
|
||||
[depot_user]/src/platform \
|
||||
[depot_user]/raw/pbxa9-devices
|
||||
|
||||
build { driver/sd_card app/block_tester }
|
||||
|
||||
install_config {
|
||||
<config>
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="PD"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="LOG"/>
|
||||
</parent-provides>
|
||||
<default caps="100" ram="1M"/>
|
||||
|
||||
<start name="platform" managing_system="yes">
|
||||
<provides><service name="Platform"/></provides>
|
||||
<config>
|
||||
<policy label="pl180_sd_card -> ">
|
||||
<device name="mmc0"/>
|
||||
</policy>
|
||||
</config>
|
||||
<route>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="timer">
|
||||
<provides><service name="Timer"/></provides>
|
||||
<route> <any-service> <parent/> </any-service> </route>
|
||||
</start>
|
||||
|
||||
<start name="pl180_sd_card">
|
||||
<provides><service name="Block"/></provides>
|
||||
<route>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="RM"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<any-service> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="block_tester" ram="64M">
|
||||
<config verbose="no" report="no" log="yes" stop_on_error="no">
|
||||
<tests>
|
||||
<sequential length="16M" size="1M" io_buffer="8M" batch="4"/>
|
||||
<sequential start="4000" write="yes" length="16M" size="1M" io_buffer="8M" batch="4"/>
|
||||
</tests>
|
||||
</config>
|
||||
<route> <any-service> <any-child/> <parent/> </any-service> </route>
|
||||
</start>
|
||||
</config> }
|
||||
|
||||
build_boot_image [build_artifacts]
|
||||
|
||||
set disk_image "bin/sd_card.img"
|
||||
if { [file exists $disk_image] == 0 } then {
|
||||
# create empty block device file
|
||||
catch { exec dd if=/dev/zero of=$disk_image bs=1M count=512 }
|
||||
}
|
||||
append qemu_args "-drive file=$disk_image,format=raw,if=sd,cache=writeback -nographic "
|
||||
run_genode_until {.*child "block_tester" exited with exit value 0.*\n} 30
|
||||
|
||||
exec rm $disk_image
|
||||
|
||||
# vi: set ft=tcl :
|
@ -1,103 +0,0 @@
|
||||
#
|
||||
# Check for platform support
|
||||
#
|
||||
if {[have_cmd_switch --autopilot]} {
|
||||
assert {![have_board virt_qemu_riscv] && ![have_board rpi]} \
|
||||
"Autopilot mode is not supported on this platform."
|
||||
}
|
||||
|
||||
proc buffer_size_kib {} {
|
||||
if {[have_board pbxa9]} { return [expr 12 * 1024] }
|
||||
if {[have_board imx6q_sabrelite]} { return [expr 1024] }
|
||||
if {[have_board imx53_qsb]} { return [expr 1024] }
|
||||
if {[have_board imx53_qsb_tz]} { return [expr 1024] }
|
||||
if {[have_board rpi]} { return [expr 4 * 1024] }
|
||||
assert {false} "Run script is not supported on this platform."
|
||||
}
|
||||
|
||||
proc sd_card {} {
|
||||
if {[have_board pbxa9]} { return pl180_sd_card }
|
||||
if {[have_board imx6q_sabrelite]} { return imx6_sd_card }
|
||||
if {[have_board imx53_qsb]} { return imx53_sd_card }
|
||||
if {[have_board imx53_qsb_tz]} { return imx53_sd_card }
|
||||
if {[have_board rpi]} { return rpi_sd_card }
|
||||
assert {false} "Run script is not supported on this platform."
|
||||
}
|
||||
|
||||
create_boot_directory
|
||||
import_from_depot [depot_user]/src/[base_src] \
|
||||
[depot_user]/src/init \
|
||||
[depot_user]/src/platform
|
||||
|
||||
install_config {
|
||||
<config verbose="yes">
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="PD"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="LOG"/>
|
||||
</parent-provides>
|
||||
<default caps="100" ram="1M"/>
|
||||
|
||||
<start name="platform" managing_system="yes">
|
||||
<provides><service name="Platform"/></provides>
|
||||
<config>
|
||||
<policy label="sd_card -> "> <device name="mmc0"/> </policy>
|
||||
</config>
|
||||
<route> <any-service> <parent/> </any-service> </route>
|
||||
</start>
|
||||
|
||||
<start name="timer">
|
||||
<provides><service name="Timer"/></provides>
|
||||
<route> <any-service> <parent/> </any-service> </route>
|
||||
</start>
|
||||
|
||||
<start name="sd_card" ram="16M">
|
||||
<binary name="} [sd_card] {"/>
|
||||
<provides><service name="Block"/></provides>
|
||||
<config benchmark="yes" buffer_size_kib="} [buffer_size_kib] {"/>
|
||||
<route>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="RM"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<any-service> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
</config> }
|
||||
|
||||
file copy [select_from_repositories board/[board]/devices] [run_dir]/genode/devices
|
||||
|
||||
build { driver/sd_card }
|
||||
|
||||
build_boot_image [build_artifacts]
|
||||
|
||||
|
||||
#
|
||||
# Run and evaluate the test
|
||||
#
|
||||
# If we run on qemu, provide a virtual disk
|
||||
#
|
||||
|
||||
set disk_image "bin/sd_card.img"
|
||||
if {[have_include power_on/qemu]} {
|
||||
|
||||
if { [file exists $disk_image] == 0 } then {
|
||||
catch { exec dd if=/dev/zero of=$disk_image bs=1M count=512 } }
|
||||
|
||||
append qemu_args "-drive file=$disk_image,format=raw,if=sd,cache=writeback "
|
||||
append qemu_args "-nographic "
|
||||
}
|
||||
|
||||
run_genode_until "--- SD card benchmark finished ---" 120
|
||||
|
||||
if {[have_include power_on/qemu]} {
|
||||
if { [file exists $disk_image] != 0 } then {
|
||||
exec rm -f $disk_image
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* \brief Advanced DMA 2
|
||||
* \author Martin Stein
|
||||
* \date 2015-02-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <cpu/memory_barrier.h>
|
||||
#include <dataspace/client.h>
|
||||
|
||||
/* local includes */
|
||||
#include <adma2.h>
|
||||
|
||||
using namespace Adma2;
|
||||
|
||||
|
||||
Table::Table(Platform::Connection &platform)
|
||||
:
|
||||
_ds(platform, _ds_size, UNCACHED),
|
||||
_base_virt(_ds.local_addr<Desc::access_t>())
|
||||
{ }
|
||||
|
||||
|
||||
int Table::setup_request(size_t const size, addr_t const buffer_phys)
|
||||
{
|
||||
/* sanity check */
|
||||
static size_t constexpr max_size = _max_desc * Desc::Length::max;
|
||||
if (size > max_size) {
|
||||
Genode::error("block request too large");
|
||||
return -1;
|
||||
}
|
||||
/* install new descriptors till they cover all requested bytes */
|
||||
addr_t consumed = 0;
|
||||
for (int index = 0; consumed < size; index++) {
|
||||
|
||||
/* clamp current request to maximum request size */
|
||||
size_t const remaining = size - consumed;
|
||||
size_t const curr = min(Desc::Length::max, remaining);
|
||||
|
||||
/* assemble new descriptor */
|
||||
Desc::access_t desc = 0;
|
||||
Desc::Address::set(desc, buffer_phys + consumed);
|
||||
Desc::Length::set(desc, curr);
|
||||
Desc::Act1::set(desc, 0);
|
||||
Desc::Act2::set(desc, 1);
|
||||
Desc::Valid::set(desc, 1);
|
||||
|
||||
/* let last descriptor generate transfer-complete signal */
|
||||
if (consumed + curr == size) { Desc::End::set(desc, 1); }
|
||||
|
||||
/* install and account descriptor */
|
||||
_base_virt[index] = desc;
|
||||
consumed += curr;
|
||||
}
|
||||
/* ensure that all descriptor writes were actually executed */
|
||||
Genode::memory_barrier();
|
||||
return 0;
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
/*
|
||||
* \brief Advanced DMA 2
|
||||
* \author Martin Stein
|
||||
* \date 2015-02-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _ADMA2_H_
|
||||
#define _ADMA2_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/register.h>
|
||||
#include <platform_session/dma_buffer.h>
|
||||
|
||||
namespace Adma2
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
class Desc;
|
||||
class Table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Descriptor layout
|
||||
*/
|
||||
struct Adma2::Desc : Register<64>
|
||||
{
|
||||
struct Valid : Bitfield<0, 1> { };
|
||||
struct End : Bitfield<1, 1> { };
|
||||
struct Int : Bitfield<2, 1> { };
|
||||
struct Act1 : Bitfield<4, 1> { };
|
||||
struct Act2 : Bitfield<5, 1> { };
|
||||
struct Length : Bitfield<16, 16>
|
||||
{
|
||||
/*
|
||||
* According to the 'SD Specifications, Part A2, SD Host
|
||||
* Controller, Simplified Specification, Version 2.00, February 8,
|
||||
* 2007, Table 1-10', a maximum length of 65536 bytes is achieved
|
||||
* by value 0. However, if we do so, the completion host-signal
|
||||
* times out now and then. Thus, we use the next lower possible
|
||||
* value.
|
||||
*/
|
||||
static constexpr addr_t align_log2 = 2;
|
||||
static constexpr size_t max = (1 << WIDTH) - (1 << align_log2);
|
||||
};
|
||||
struct Address : Bitfield<32, 32> { };
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Descriptor table
|
||||
*/
|
||||
class Adma2::Table
|
||||
{
|
||||
private:
|
||||
|
||||
static size_t constexpr _max_desc = 1024;
|
||||
static size_t constexpr _ds_size = _max_desc * sizeof(Desc::access_t);
|
||||
|
||||
Platform::Dma_buffer _ds;
|
||||
Desc::access_t * const _base_virt;
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Table(Table const &);
|
||||
Table &operator = (Table const &);
|
||||
|
||||
public:
|
||||
|
||||
Table(Platform::Connection &platform);
|
||||
|
||||
/**
|
||||
* Marshal descriptors according to block request
|
||||
*
|
||||
* \param size request size in bytes
|
||||
* \param buffer_phys physical base of transfer buffer
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 error
|
||||
*/
|
||||
int setup_request(size_t const size, addr_t const buffer_phys);
|
||||
|
||||
/*
|
||||
* Accessors
|
||||
*/
|
||||
|
||||
addr_t base_dma() const { return _ds.dma_addr(); }
|
||||
};
|
||||
|
||||
#endif /* _ADMA2_H_ */
|
@ -1,166 +0,0 @@
|
||||
/*
|
||||
* \brief SD-card benchmark
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \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 Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <timer_session/connection.h>
|
||||
#include <base/attached_ram_dataspace.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <platform_session/dma_buffer.h>
|
||||
|
||||
/* local includes */
|
||||
#include <driver.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
struct Benchmark
|
||||
{
|
||||
using Packet_descriptor = Block::Packet_descriptor;
|
||||
|
||||
struct Block_operation_failed : Exception { };
|
||||
|
||||
enum Operation { READ, WRITE };
|
||||
|
||||
struct Driver_session : Block::Driver_session_base
|
||||
{
|
||||
Signal_transmitter sig;
|
||||
unsigned long nr_of_acks { 0 };
|
||||
|
||||
Driver_session(Signal_context_capability sig) : sig(sig) { }
|
||||
|
||||
void ack_packet(Packet_descriptor &, bool success) override
|
||||
{
|
||||
if (!success) {
|
||||
throw Block_operation_failed(); }
|
||||
nr_of_acks++;
|
||||
sig.submit();
|
||||
}
|
||||
};
|
||||
|
||||
Env &env;
|
||||
Platform::Connection platform { env };
|
||||
Attached_rom_dataspace config { env, "config" };
|
||||
Packet_descriptor pkt { };
|
||||
uint64_t time_before_ms { };
|
||||
Timer::Connection timer { env };
|
||||
Operation operation { READ };
|
||||
Signal_handler<Benchmark> ack_handler { env.ep(), *this, &Benchmark::update_state };
|
||||
Driver_session drv_session { ack_handler };
|
||||
Sd_card::Driver drv { env, platform };
|
||||
size_t const buf_size_kib { config.xml().attribute_value("buffer_size_kib",
|
||||
(size_t)0) };
|
||||
size_t const buf_size { buf_size_kib * 1024 };
|
||||
Platform::Dma_buffer buf { platform, buf_size, UNCACHED };
|
||||
char *buf_virt { buf.local_addr<char>() };
|
||||
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 };
|
||||
|
||||
size_t req_size() const { return req_sizes[req_size_id]; }
|
||||
|
||||
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 */
|
||||
uint64_t const time_after_ms = timer.elapsed_ms();
|
||||
uint64_t const duration_ms = time_after_ms - time_before_ms;
|
||||
size_t const kib_per_sec = (size_t)((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;
|
||||
}
|
||||
}
|
||||
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 block_size = drv.info().block_size;
|
||||
size_t const cnt = req_size() / block_size;
|
||||
for (; buf_off_pend < buf_size; buf_off_pend += req_size()) {
|
||||
|
||||
/* calculate block offset */
|
||||
addr_t const nr = buf_off_pend / block_size;
|
||||
|
||||
if (drv.dma_enabled()) {
|
||||
|
||||
/* request with DMA */
|
||||
addr_t const phys = buf.dma_addr() + 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) { }
|
||||
}
|
||||
|
||||
Benchmark(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();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Benchmark(Benchmark const &);
|
||||
Benchmark &operator = (Benchmark const &);
|
||||
};
|
@ -1,56 +0,0 @@
|
||||
/*
|
||||
* \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 Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#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
|
||||
{
|
||||
protected:
|
||||
|
||||
Genode::size_t _block_size() const { return 512; }
|
||||
|
||||
Block::sector_t _block_count() const {
|
||||
return card_info().capacity_mb() * 1024 * 2; }
|
||||
|
||||
public:
|
||||
|
||||
Driver_base(Genode::Ram_allocator &ram)
|
||||
: Block::Driver(ram) { }
|
||||
|
||||
/*******************
|
||||
** Block::Driver **
|
||||
*******************/
|
||||
|
||||
Block::Session::Info info() const override
|
||||
{
|
||||
return { .block_size = _block_size(),
|
||||
.block_count = _block_count(),
|
||||
.align_log2 = Genode::log2(_block_size()),
|
||||
.writeable = true };
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _DRIVER_BASE_H_ */
|
@ -1,71 +0,0 @@
|
||||
/*
|
||||
* \brief SD-card driver
|
||||
* \author Martin Stein
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2013-03-06
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/component.h>
|
||||
#include <base/log.h>
|
||||
#include <base/heap.h>
|
||||
#include <block/component.h>
|
||||
#include <platform_session/connection.h>
|
||||
|
||||
/* local includes */
|
||||
#include <benchmark.h>
|
||||
#include <driver.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
struct Main
|
||||
{
|
||||
Env & env;
|
||||
Heap heap { env.ram(), env.rm() };
|
||||
Platform::Connection platform { env };
|
||||
|
||||
struct Factory : Block::Driver_factory
|
||||
{
|
||||
Env & env;
|
||||
Heap & heap;
|
||||
Platform::Connection & platform;
|
||||
|
||||
Factory(Env &env, Heap &heap, Platform::Connection &platform)
|
||||
: env(env), heap(heap), platform(platform) { }
|
||||
|
||||
Block::Driver *create() override {
|
||||
return new (&heap) Sd_card::Driver(env, platform); }
|
||||
|
||||
void destroy(Block::Driver *driver) override {
|
||||
Genode::destroy(&heap, static_cast<Sd_card::Driver*>(driver)); }
|
||||
|
||||
} factory { env, heap, platform };
|
||||
|
||||
Block::Root root { env.ep(), heap, env.rm(), factory, true };
|
||||
|
||||
Main(Genode::Env &env) : env(env)
|
||||
{
|
||||
Genode::log("--- SD card driver ---");
|
||||
env.parent().announce(env.ep().manage(root));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env)
|
||||
{
|
||||
bool benchmark = false;
|
||||
try {
|
||||
Attached_rom_dataspace config { env, "config" };
|
||||
benchmark = config.xml().attribute_value("benchmark", false);
|
||||
} catch(...) {}
|
||||
|
||||
if (benchmark) static Benchmark bench(env);
|
||||
else static Main main(env);
|
||||
}
|
@ -1,240 +0,0 @@
|
||||
/*
|
||||
* \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 Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* 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, Platform::Connection & platform)
|
||||
:
|
||||
Block::Driver(env.ram()),
|
||||
Platform::Device(platform),
|
||||
Platform::Device::Mmio<SIZE>(*this, { 0 }),
|
||||
_platform(platform),
|
||||
_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 = (short)(resp >> 16);
|
||||
|
||||
/*
|
||||
* Now, the card is in transfer mode...
|
||||
*/
|
||||
|
||||
/* CMD7: select card */
|
||||
_request(7, rca << 16, &resp);
|
||||
}
|
||||
|
||||
|
||||
Driver::~Driver() { }
|
||||
|
||||
|
||||
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, (uint32_t)((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, (uint32_t)((block_number + i) * _block_size),
|
||||
length, &resp);
|
||||
_write_data(length, buffer + (i * _block_size));
|
||||
}
|
||||
ack_packet(packet);
|
||||
}
|
||||
|
@ -1,161 +0,0 @@
|
||||
/*
|
||||
* \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 Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _DRIVER_H_
|
||||
#define _DRIVER_H_
|
||||
|
||||
/* local includes */
|
||||
#include <block/driver.h>
|
||||
#include <platform_session/device.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
namespace Sd_card {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
class Driver;
|
||||
}
|
||||
|
||||
|
||||
class Sd_card::Driver : public Block::Driver,
|
||||
private Platform::Device,
|
||||
private Platform::Device::Mmio<0>
|
||||
{
|
||||
private:
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Driver(Driver const &);
|
||||
Driver &operator = (Driver const &);
|
||||
|
||||
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 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,
|
||||
};
|
||||
|
||||
Platform::Connection & _platform;
|
||||
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);
|
||||
|
||||
/*
|
||||
* TODO report (and support) real capacity not just 512M
|
||||
*/
|
||||
size_t const _block_size = 512;
|
||||
Block::sector_t const _block_count = 0x20000000 / _block_size;
|
||||
|
||||
public:
|
||||
|
||||
Driver(Env &env, Platform::Connection & platform);
|
||||
~Driver();
|
||||
|
||||
|
||||
/******************
|
||||
** Block-driver **
|
||||
******************/
|
||||
|
||||
Dma_buffer alloc_dma_buffer(size_t size, Cache cache) override
|
||||
{
|
||||
Ram_dataspace_capability ds =
|
||||
_platform.retry_with_upgrade(Ram_quota{4096}, Cap_quota{2},
|
||||
[&] () { return _platform.alloc_dma_buffer(size, cache); });
|
||||
|
||||
return { .ds = ds,
|
||||
.dma_addr = _platform.dma_addr(ds) };
|
||||
}
|
||||
|
||||
Block::Session::Info info() const override
|
||||
{
|
||||
return { .block_size = _block_size,
|
||||
.block_count = _block_count,
|
||||
.align_log2 = log2(_block_size),
|
||||
.writeable = true };
|
||||
}
|
||||
|
||||
void read(Block::sector_t block_number,
|
||||
size_t block_count,
|
||||
char *buffer,
|
||||
Block::Packet_descriptor &packet) override;
|
||||
|
||||
void write(Block::sector_t block_number,
|
||||
size_t block_count,
|
||||
char const *buffer,
|
||||
Block::Packet_descriptor &packet) override;
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DRIVER_H_ */
|
@ -1,4 +0,0 @@
|
||||
TARGET = pl180_sd_card
|
||||
REQUIRES = arm_v7
|
||||
|
||||
include $(REP_DIR)/src/driver/sd_card/target.inc
|
@ -1,593 +0,0 @@
|
||||
/*
|
||||
* \brief SD card protocol definitions
|
||||
* \author Norman Feske
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2012-07-06
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SD_CARD_H_
|
||||
#define _SD_CARD_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/mmio.h>
|
||||
|
||||
namespace Sd_card {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
/**
|
||||
* Structure of the first word of a native mode R1 response
|
||||
*/
|
||||
struct R1_response_0 : Register<32>
|
||||
{
|
||||
struct Ready_for_data : Bitfield<8, 1> { };
|
||||
struct State : Bitfield<9, 4>
|
||||
{
|
||||
enum { PROGRAM = 7 };
|
||||
};
|
||||
struct Error : Bitfield<19, 1> { };
|
||||
|
||||
/**
|
||||
* Return wether the card is ready for data
|
||||
*
|
||||
* \param resp R1 response, word 0
|
||||
*/
|
||||
static bool card_ready(access_t const resp)
|
||||
{
|
||||
/*
|
||||
* Check both ready bit and state because not all cards handle
|
||||
* the status bits correctly.
|
||||
*/
|
||||
return Ready_for_data::get(resp) &&
|
||||
State::get(resp) != State::PROGRAM;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returned by 'Sd_send_op_cond'
|
||||
*/
|
||||
struct Ocr : Register<32>
|
||||
{
|
||||
struct Busy : Bitfield<31, 1> { };
|
||||
};
|
||||
|
||||
struct Cid
|
||||
{
|
||||
uint32_t raw_0, raw_1, raw_2, raw_3;
|
||||
|
||||
void print(Output &out) const
|
||||
{
|
||||
Genode::print(out, Hex(raw_3), " ", Hex(raw_2), " ",
|
||||
Hex(raw_1), " ", Hex(raw_0));
|
||||
}
|
||||
};
|
||||
|
||||
struct Csd0 : Register<32>
|
||||
{
|
||||
};
|
||||
|
||||
struct Csd1 : Register<32>
|
||||
{
|
||||
enum { BIT_BASE = 1*sizeof(access_t)*8 };
|
||||
|
||||
struct V2_device_size_lo : Bitfield<48 - BIT_BASE, 16> { };
|
||||
|
||||
struct V1_c_size_lo : Bitfield<62 - BIT_BASE, 2> { };
|
||||
struct V1_c_size_mult : Bitfield<47 - BIT_BASE, 3> { };
|
||||
};
|
||||
|
||||
struct Csd2 : Register<32>
|
||||
{
|
||||
enum { BIT_BASE = 2*sizeof(access_t)*8 };
|
||||
|
||||
struct V2_device_size_hi : Bitfield<64 - BIT_BASE, 6> { };
|
||||
|
||||
struct V1_read_bl_len : Bitfield<80 - BIT_BASE, 4> { };
|
||||
struct V1_c_size_hi : Bitfield<64 - BIT_BASE, 10> { };
|
||||
};
|
||||
|
||||
struct Csd3 : Register<32>
|
||||
{
|
||||
enum { BIT_BASE = 3*sizeof(access_t)*8 };
|
||||
|
||||
struct Version : Bitfield<126 - BIT_BASE, 2>
|
||||
{
|
||||
enum Type { STANDARD_CAPACITY = 0, HIGH_CAPACITY = 1, EXT_CSD = 3 };
|
||||
};
|
||||
|
||||
struct Mmc_spec_vers : Bitfield<122 - BIT_BASE, 4> { };
|
||||
};
|
||||
|
||||
struct Csd
|
||||
{
|
||||
Csd0::access_t csd0;
|
||||
Csd1::access_t csd1;
|
||||
Csd2::access_t csd2;
|
||||
Csd3::access_t csd3;
|
||||
};
|
||||
|
||||
|
||||
struct Ext_csd : Mmio<0xd8>
|
||||
{
|
||||
using Mmio::Mmio;
|
||||
|
||||
struct Revision : Register<0xc0, 8> { };
|
||||
struct Sector_count : Register<0xd4, 32> { };
|
||||
};
|
||||
|
||||
struct Arg : Register<32> { };
|
||||
|
||||
enum Response { RESPONSE_NONE,
|
||||
RESPONSE_136_BIT,
|
||||
RESPONSE_48_BIT,
|
||||
RESPONSE_48_BIT_WITH_BUSY };
|
||||
|
||||
enum Transfer { TRANSFER_NONE, TRANSFER_READ, TRANSFER_WRITE };
|
||||
|
||||
struct Command_base
|
||||
{
|
||||
unsigned index; /* command opcode */
|
||||
Arg::access_t arg; /* argument */
|
||||
Response rsp_type; /* response type */
|
||||
Transfer transfer; /* data transfer type */
|
||||
|
||||
Command_base(unsigned op, Response rsp_type, Transfer transfer)
|
||||
:
|
||||
index(op), arg(0), rsp_type(rsp_type), transfer(transfer)
|
||||
{ }
|
||||
|
||||
void print(Output &out) const
|
||||
{
|
||||
using Genode::print;
|
||||
print(out, "index=", index, ", arg=", arg, ", rsp_type=");
|
||||
switch (rsp_type) {
|
||||
case RESPONSE_NONE: print(out, "NONE"); break;
|
||||
case RESPONSE_136_BIT: print(out, "136_BIT"); break;
|
||||
case RESPONSE_48_BIT: print(out, "48_BIT"); break;
|
||||
case RESPONSE_48_BIT_WITH_BUSY: print(out, "48_BIT_WITH_BUSY"); break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <unsigned _INDEX, Response RSP_TYPE, Transfer TRANSFER = TRANSFER_NONE>
|
||||
struct Command : Command_base
|
||||
{
|
||||
enum { INDEX = _INDEX };
|
||||
Command() : Command_base(_INDEX, RSP_TYPE, TRANSFER) { }
|
||||
};
|
||||
|
||||
template <unsigned INDEX, Response RSP_TYPE, Transfer TRANSFER = TRANSFER_NONE>
|
||||
struct Prefixed_command : private Command_base
|
||||
{
|
||||
Prefixed_command() : Command_base(INDEX, RSP_TYPE, TRANSFER) { }
|
||||
|
||||
using Command_base::arg;
|
||||
|
||||
/**
|
||||
* Used by ACMD overload of 'issue_command()'
|
||||
*/
|
||||
Command_base const &base() const { return *this; }
|
||||
};
|
||||
|
||||
struct Go_idle_state : Command<0, RESPONSE_NONE> { };
|
||||
|
||||
struct All_send_cid : Command<2, RESPONSE_136_BIT> { };
|
||||
|
||||
struct Send_relative_addr : Command<3, RESPONSE_48_BIT>
|
||||
{
|
||||
struct Response : Sd_card::Arg
|
||||
{
|
||||
struct Rca : Bitfield<16, 16> { };
|
||||
};
|
||||
|
||||
Send_relative_addr(unsigned rca = 0)
|
||||
{
|
||||
Response::Rca::set(arg, rca);
|
||||
}
|
||||
};
|
||||
|
||||
struct Send_status : Command<13, RESPONSE_48_BIT>
|
||||
{
|
||||
struct Arg : Sd_card::Arg
|
||||
{
|
||||
struct Rca : Bitfield<16, 16> { };
|
||||
};
|
||||
};
|
||||
|
||||
struct Select_card : Command<7, RESPONSE_48_BIT>
|
||||
{
|
||||
struct Arg : Sd_card::Arg
|
||||
{
|
||||
struct Rca : Bitfield<16, 16> { };
|
||||
};
|
||||
|
||||
Select_card(unsigned rca)
|
||||
{
|
||||
Arg::Rca::set(arg, rca);
|
||||
}
|
||||
};
|
||||
|
||||
struct Send_if_cond : Command<8, RESPONSE_48_BIT>
|
||||
{
|
||||
struct Arg : Sd_card::Arg
|
||||
{
|
||||
struct Check_pattern : Bitfield<0, 8> { };
|
||||
struct Supply_voltage : Bitfield<8, 4> { };
|
||||
};
|
||||
|
||||
Send_if_cond()
|
||||
{
|
||||
Arg::Check_pattern::set(arg, 0xaa);
|
||||
Arg::Supply_voltage::set(arg, 1);
|
||||
}
|
||||
};
|
||||
|
||||
struct Send_csd : Command<9, RESPONSE_136_BIT>
|
||||
{
|
||||
struct Arg : Sd_card::Arg
|
||||
{
|
||||
struct Rca : Bitfield<16, 16> { };
|
||||
};
|
||||
|
||||
Send_csd(unsigned rca)
|
||||
{
|
||||
Arg::Rca::set(arg, rca);
|
||||
}
|
||||
};
|
||||
|
||||
struct Mmc_send_ext_csd : Command<8, RESPONSE_48_BIT_WITH_BUSY, TRANSFER_READ>
|
||||
{ };
|
||||
|
||||
struct Set_block_count : Command<23, RESPONSE_48_BIT>
|
||||
{
|
||||
Set_block_count(size_t count)
|
||||
{
|
||||
arg = count;
|
||||
};
|
||||
};
|
||||
|
||||
struct Set_blocklen : Command<16, RESPONSE_48_BIT>
|
||||
{
|
||||
Set_blocklen(size_t blocklen)
|
||||
{
|
||||
arg = blocklen;
|
||||
};
|
||||
};
|
||||
|
||||
struct Read_multiple_block : Command<18, RESPONSE_48_BIT, TRANSFER_READ>
|
||||
{
|
||||
Read_multiple_block(unsigned long addr)
|
||||
{
|
||||
arg = addr;
|
||||
}
|
||||
};
|
||||
|
||||
struct Write_multiple_block : Command<25, RESPONSE_48_BIT, TRANSFER_WRITE>
|
||||
{
|
||||
Write_multiple_block(unsigned long addr)
|
||||
{
|
||||
arg = addr;
|
||||
}
|
||||
};
|
||||
|
||||
struct Set_bus_width : Prefixed_command<6, RESPONSE_48_BIT>
|
||||
{
|
||||
struct Arg : Sd_card::Arg
|
||||
{
|
||||
struct Bus_width : Bitfield<0, 2>
|
||||
{
|
||||
enum Width { ONE_BIT = 0, FOUR_BITS = 2 };
|
||||
};
|
||||
};
|
||||
|
||||
Set_bus_width(Arg::Bus_width::Width width)
|
||||
{
|
||||
Arg::Bus_width::set(arg, width);
|
||||
}
|
||||
};
|
||||
|
||||
struct Mmc_switch : Command<6, RESPONSE_48_BIT>
|
||||
{
|
||||
enum { SWITCH_MODE_WRITE_BYTE = 0x3 };
|
||||
|
||||
struct Arg : Sd_card::Arg
|
||||
{
|
||||
struct Value : Bitfield<8, 8> { };
|
||||
struct Index : Bitfield<16, 8> { };
|
||||
struct Mode : Bitfield<24, 8> { };
|
||||
};
|
||||
|
||||
Mmc_switch(unsigned index, unsigned val)
|
||||
{
|
||||
Arg::Mode::set(arg, SWITCH_MODE_WRITE_BYTE);
|
||||
Arg::Index::set(arg, index);
|
||||
Arg::Value::set(arg, val);
|
||||
}
|
||||
};
|
||||
|
||||
struct Sd_send_op_cond : Prefixed_command<41, RESPONSE_48_BIT>
|
||||
{
|
||||
struct Arg : Sd_card::Arg
|
||||
{
|
||||
/**
|
||||
* Operating condition register
|
||||
*/
|
||||
struct Ocr : Bitfield<0, 24> { };
|
||||
|
||||
/**
|
||||
* Host capacity support
|
||||
*/
|
||||
struct Hcs : Bitfield<30, 1> { };
|
||||
};
|
||||
|
||||
Sd_send_op_cond(unsigned ocr, bool hcs)
|
||||
{
|
||||
Arg::Ocr::set(arg, ocr);
|
||||
Arg::Hcs::set(arg, hcs);
|
||||
}
|
||||
};
|
||||
|
||||
struct Mmc_send_op_cond : Command<1, RESPONSE_48_BIT>
|
||||
{
|
||||
struct Arg : Sd_card::Arg
|
||||
{
|
||||
/**
|
||||
* Operating condition register
|
||||
*/
|
||||
struct Ocr : Bitfield<0, 24> { };
|
||||
|
||||
/**
|
||||
* Host capacity support
|
||||
*/
|
||||
struct Hcs : Bitfield<30, 1> { };
|
||||
};
|
||||
|
||||
Mmc_send_op_cond(unsigned ocr, bool hcs)
|
||||
{
|
||||
Arg::Ocr::set(arg, ocr);
|
||||
Arg::Hcs::set(arg, hcs);
|
||||
}
|
||||
};
|
||||
|
||||
struct Stop_transmission : Command<12, RESPONSE_48_BIT> { };
|
||||
|
||||
struct Acmd_prefix : Command<55, RESPONSE_48_BIT>
|
||||
{
|
||||
struct Arg : Sd_card::Arg
|
||||
{
|
||||
struct Rca : Bitfield<16, 16> { };
|
||||
};
|
||||
|
||||
Acmd_prefix(unsigned rca)
|
||||
{
|
||||
Arg::Rca::set(arg, rca);
|
||||
}
|
||||
};
|
||||
|
||||
class Card_info
|
||||
{
|
||||
private:
|
||||
|
||||
unsigned _rca;
|
||||
size_t _capacity_mb;
|
||||
const Csd3::Version::Type _version;
|
||||
|
||||
public:
|
||||
|
||||
Card_info(unsigned rca, size_t capacity_mb, const Csd3::Version::Type version)
|
||||
: _rca(rca), _capacity_mb(capacity_mb), _version(version)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Return capacity in megabytes
|
||||
*/
|
||||
size_t capacity_mb() const { return _capacity_mb; }
|
||||
|
||||
/**
|
||||
* Return relative card address
|
||||
*/
|
||||
unsigned rca() const { return _rca; }
|
||||
|
||||
/**
|
||||
* Returns the version of the card
|
||||
*/
|
||||
Csd3::Version::Type version() const { return _version; }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* SD card host controller
|
||||
*/
|
||||
class Host_controller : Genode::Interface
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Exception type
|
||||
*/
|
||||
struct Detection_failed { };
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool _issue_command(Command_base const &command) = 0;
|
||||
|
||||
virtual Cid _read_cid() = 0;
|
||||
|
||||
virtual Csd _read_csd() = 0;
|
||||
|
||||
virtual unsigned _read_rca() = 0;
|
||||
|
||||
virtual size_t _read_ext_csd() { return 0; }
|
||||
|
||||
public:
|
||||
|
||||
virtual Card_info card_info() const = 0;
|
||||
|
||||
bool issue_command(Command_base const &command)
|
||||
{
|
||||
return _issue_command(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue application-specific command
|
||||
*
|
||||
* This overload is selected if the supplied command type has
|
||||
* 'Prefixed_command' as its base class. In this case, we need to
|
||||
* issue a CMD55 as command prefix followed by the actual command.
|
||||
*
|
||||
* \param prefix_rca argument to CMD55 prefix command
|
||||
*/
|
||||
template <unsigned INDEX, Response RSP_TYPE, Transfer TRANSFER>
|
||||
bool issue_command(Prefixed_command<INDEX, RSP_TYPE, TRANSFER> const &command,
|
||||
unsigned prefix_rca = 0)
|
||||
{
|
||||
/* send CMD55 prefix */
|
||||
if (!_issue_command(Acmd_prefix(prefix_rca))) {
|
||||
error("prefix command timed out");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* send actual command */
|
||||
return _issue_command(command.base());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Extract capacity information from CSD register
|
||||
*
|
||||
* \throw Detection_failed
|
||||
* \return capacity in 512-kByte blocks
|
||||
*/
|
||||
size_t _sd_card_device_size(Csd const csd)
|
||||
{
|
||||
/*
|
||||
* The capacity is reported via the CSD register. There are
|
||||
* two versions of this register. Standard-capacity cards
|
||||
* use version 1 whereas high-capacity cards use version 2.
|
||||
*/
|
||||
|
||||
if (Csd3::Version::get(csd.csd3) == Csd3::Version::STANDARD_CAPACITY) {
|
||||
/*
|
||||
* Calculation of the capacity according to the
|
||||
* "Physical Layer Simplified Specification Version 4.10",
|
||||
* Section 5.3.2.
|
||||
*/
|
||||
size_t const read_bl_len = Csd2::V1_read_bl_len::get(csd.csd2);
|
||||
size_t const c_size = (Csd2::V1_c_size_hi::get(csd.csd2) << 2)
|
||||
| Csd1::V1_c_size_lo::get(csd.csd1);
|
||||
|
||||
size_t const c_size_mult = Csd1::V1_c_size_mult::get(csd.csd1);
|
||||
size_t const mult = 1 << (c_size_mult + 2);
|
||||
size_t const block_len = 1 << read_bl_len;
|
||||
size_t const capacity = (c_size + 1)*mult*block_len;
|
||||
size_t const capacity512KiB = capacity / (512 * 1024);
|
||||
|
||||
return capacity512KiB;
|
||||
}
|
||||
|
||||
if (Csd3::Version::get(csd.csd3) == Csd3::Version::HIGH_CAPACITY)
|
||||
return ((Csd2::V2_device_size_hi::get(csd.csd2) << 16)
|
||||
| Csd1::V2_device_size_lo::get(csd.csd1)) + 1;
|
||||
|
||||
error("Could not detect SD-card capacity");
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Perform SD card detection sequence
|
||||
*
|
||||
* \throw Detection_failed
|
||||
*/
|
||||
Card_info _detect()
|
||||
{
|
||||
if (!issue_command(All_send_cid())) {
|
||||
warning("All_send_cid command failed");
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
Cid const cid = _read_cid();
|
||||
log("CID: ", cid);
|
||||
|
||||
if (!issue_command(Send_relative_addr())) {
|
||||
error("Send_relative_addr timed out");
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
unsigned const rca = _read_rca();
|
||||
log("RCA: ", Hex(rca));
|
||||
|
||||
if (!issue_command(Send_csd(rca))) {
|
||||
error("Send_csd failed");
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
Csd const csd = _read_csd();
|
||||
|
||||
if (!issue_command(Select_card(rca))) {
|
||||
error("Select_card failed");
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
return Card_info(rca, _sd_card_device_size(csd) / 2,
|
||||
static_cast<Csd3::Version::Type>(Csd3::Version::get(csd.csd3)));
|
||||
}
|
||||
|
||||
Card_info _detect_mmc()
|
||||
{
|
||||
if (!issue_command(All_send_cid())) {
|
||||
warning("All_send_cid command failed");
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
unsigned const rca = 1;
|
||||
|
||||
if (!issue_command(Send_relative_addr(rca))) {
|
||||
error("Send_relative_addr timed out");
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
if (!issue_command(Send_csd(rca))) {
|
||||
error("Send_csd failed");
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
Csd const csd = _read_csd();
|
||||
|
||||
if (Csd3::Version::get(csd.csd3) != Csd3::Version::EXT_CSD) {
|
||||
error("Csd version is not extented CSD");
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
if (Csd3::Mmc_spec_vers::get(csd.csd3) < 4) {
|
||||
error("Csd specific version is less than 4");
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
if (!issue_command(Select_card(rca))) {
|
||||
error("Select_card failed");
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
size_t device_size;
|
||||
if(!(device_size = _read_ext_csd())) {
|
||||
error("Could not read extented CSD");
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
return Card_info(rca, device_size,
|
||||
static_cast<Csd3::Version::Type>(Csd3::Version::get(csd.csd3)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _SD_CARD_H_ */
|
@ -1,6 +0,0 @@
|
||||
SRC_CC += main.cc driver.cc
|
||||
LIBS += base
|
||||
INC_DIR += $(PRG_DIR) $(REP_DIR)/src/driver/sd_card
|
||||
|
||||
vpath %.cc $(REP_DIR)/src/driver/sd_card
|
||||
vpath %.cc $(PRG_DIR)
|
@ -64,7 +64,6 @@ nova
|
||||
platform
|
||||
rtc
|
||||
rump_ext2
|
||||
sd_card_bench
|
||||
sd_card_zynq
|
||||
seoul-auto
|
||||
smartcard
|
||||
|
Loading…
x
Reference in New Issue
Block a user