mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-21 10:01:57 +00:00
SD-card driver for the Raspberry Pi
The driver operates in PIO mode only. Depending on the block size (512 bytes versus 128 KiB), it has a troughput of 2 MiB/sec - 10 MiB/sec for reading and 173 KiB/sec - 8 MiB/sec for writing. Fixes #1475
This commit is contained in:
parent
87f13d77c0
commit
91e0a5d5dd
143
repos/os/src/drivers/sd_card/rpi/driver.h
Normal file
143
repos/os/src/drivers/sd_card/rpi/driver.h
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* \brief Raspberry Pi implementation of the Block::Driver interface
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2014-09-21
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DRIVER_H_
|
||||||
|
#define _DRIVER_H_
|
||||||
|
|
||||||
|
#include <util/mmio.h>
|
||||||
|
#include <os/attached_io_mem_dataspace.h>
|
||||||
|
#include <base/printf.h>
|
||||||
|
#include <timer_session/connection.h>
|
||||||
|
#include <block/component.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <sdhci.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;
|
||||||
|
|
||||||
|
/* memory map */
|
||||||
|
enum {
|
||||||
|
SDHCI_BASE = 0x20300000,
|
||||||
|
SDHCI_SIZE = 0x100,
|
||||||
|
SDHCI_IRQ = 62,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* display sub system registers */
|
||||||
|
Attached_io_mem_dataspace _sdhci_mmio { SDHCI_BASE, SDHCI_SIZE };
|
||||||
|
|
||||||
|
/* host-controller instance */
|
||||||
|
Sdhci_controller _controller;
|
||||||
|
|
||||||
|
bool const _use_dma;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Sdhci_driver(bool use_dma)
|
||||||
|
:
|
||||||
|
_controller((addr_t)_sdhci_mmio.local_addr<void>(),
|
||||||
|
_delayer, SDHCI_IRQ, use_dma),
|
||||||
|
_use_dma(use_dma)
|
||||||
|
{
|
||||||
|
Sd_card::Card_info const card_info = _controller.card_info();
|
||||||
|
|
||||||
|
PLOG("SD card detected");
|
||||||
|
PLOG("capacity: %zd MiB", card_info.capacity_mb());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************
|
||||||
|
** 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_ */
|
63
repos/os/src/drivers/sd_card/rpi/main.cc
Normal file
63
repos/os/src/drivers/sd_card/rpi/main.cc
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* \brief SD-card driver for Raspberry Pi
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2014-09-21
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <base/printf.h>
|
||||||
|
#include <os/server.h>
|
||||||
|
#include <platform_session/connection.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <driver.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct Main
|
||||||
|
{
|
||||||
|
Server::Entrypoint &ep;
|
||||||
|
|
||||||
|
Platform::Connection platform;
|
||||||
|
|
||||||
|
struct Factory : Block::Driver_factory
|
||||||
|
{
|
||||||
|
Block::Driver *create() {
|
||||||
|
return new (Genode::env()->heap()) Block::Sdhci_driver(false); }
|
||||||
|
|
||||||
|
void destroy(Block::Driver *driver) {
|
||||||
|
Genode::destroy(Genode::env()->heap(),
|
||||||
|
static_cast<Block::Sdhci_driver *>(driver)); }
|
||||||
|
} factory;
|
||||||
|
|
||||||
|
Block::Root root;
|
||||||
|
|
||||||
|
Main(Server::Entrypoint &ep)
|
||||||
|
: ep(ep), root(ep, Genode::env()->heap(), factory)
|
||||||
|
{
|
||||||
|
Genode::printf("--- SD card driver ---\n");
|
||||||
|
|
||||||
|
while (platform.power_state(Platform::Session::POWER_SDHCI) == 0)
|
||||||
|
platform.power_state(Platform::Session::POWER_SDHCI, true);
|
||||||
|
|
||||||
|
Genode::env()->parent()->announce(ep.manage(root));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/************
|
||||||
|
** Server **
|
||||||
|
************/
|
||||||
|
|
||||||
|
namespace Server {
|
||||||
|
char const *name() { return "sd_card_ep"; }
|
||||||
|
size_t stack_size() { return 2*1024*sizeof(long); }
|
||||||
|
void construct(Entrypoint &ep) { static Main server(ep); }
|
||||||
|
}
|
||||||
|
|
491
repos/os/src/drivers/sd_card/rpi/sdhci.h
Normal file
491
repos/os/src/drivers/sd_card/rpi/sdhci.h
Normal file
@ -0,0 +1,491 @@
|
|||||||
|
/*
|
||||||
|
* \brief SDHCI controller driver
|
||||||
|
* \author Norman Feske
|
||||||
|
* \author Christian Helmuth
|
||||||
|
* \date 2014-09-21
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SDHCI_H_
|
||||||
|
#define _SDHCI_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/mmio.h>
|
||||||
|
#include <os/attached_ram_dataspace.h>
|
||||||
|
#include <irq_session/connection.h>
|
||||||
|
#include <drivers/board_base.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <sd_card.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct Sdhci : Genode::Mmio
|
||||||
|
{
|
||||||
|
enum { verbose = false };
|
||||||
|
|
||||||
|
typedef Genode::size_t size_t;
|
||||||
|
|
||||||
|
struct Blksizecnt : Register<0x4, 32>
|
||||||
|
{
|
||||||
|
struct Blkcnt : Bitfield<16, 16> { };
|
||||||
|
struct Blksize : Bitfield<0, 10> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Resp0 : Register<0x10, 32> { };
|
||||||
|
struct Resp1 : Register<0x14, 32> { };
|
||||||
|
struct Resp2 : Register<0x18, 32> { };
|
||||||
|
struct Resp3 : Register<0x1c, 32> { };
|
||||||
|
|
||||||
|
struct Data : Register<0x20, 32> { };
|
||||||
|
|
||||||
|
struct Control0 : Register<0x28, 32>
|
||||||
|
{
|
||||||
|
struct Hctl_dwidth : Bitfield<1, 1> { };
|
||||||
|
struct Hctl_hs_en : Bitfield<2, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Control1 : Register<0x2c, 32>
|
||||||
|
{
|
||||||
|
struct Clk_internal_en : Bitfield<0, 1> { };
|
||||||
|
struct Clk_internal_stable : Bitfield<1, 1> { };
|
||||||
|
struct Clk_en : Bitfield<2, 1> { };
|
||||||
|
|
||||||
|
struct Clk_freq8 : Bitfield<8, 8> { };
|
||||||
|
struct Clk_freq_ms2 : Bitfield<6, 2> { };
|
||||||
|
struct Data_tounit : Bitfield<16, 4> { };
|
||||||
|
struct Srst_hc : Bitfield<24, 1> { };
|
||||||
|
struct Srst_cmd : Bitfield<25, 1> { };
|
||||||
|
struct Srst_data : Bitfield<26, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Status : Register<0x24, 32>
|
||||||
|
{
|
||||||
|
struct Inhibit : Bitfield<0, 2> { };
|
||||||
|
struct Bwe : Bitfield<10, 1> { };
|
||||||
|
struct Bre : Bitfield<11, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Arg1 : Register<0x8, 32> { };
|
||||||
|
|
||||||
|
struct Cmdtm : Register<0xc, 32>
|
||||||
|
{
|
||||||
|
struct Index : Bitfield<24, 6> { };
|
||||||
|
struct Isdata : Bitfield<21, 1> { };
|
||||||
|
struct Tm_blkcnt_en : Bitfield<1, 1> { };
|
||||||
|
struct Tm_multi_block : Bitfield<5, 1> { };
|
||||||
|
struct Tm_auto_cmd_en : Bitfield<2, 2>
|
||||||
|
{
|
||||||
|
enum { CMD12 = 1 };
|
||||||
|
};
|
||||||
|
struct Tm_dat_dir : Bitfield<4, 1>
|
||||||
|
{
|
||||||
|
enum { WRITE = 0, READ = 1 };
|
||||||
|
};
|
||||||
|
struct Rsp_type : Bitfield<16, 2>
|
||||||
|
{
|
||||||
|
enum Response { RESPONSE_NONE = 0,
|
||||||
|
RESPONSE_136_BIT = 1,
|
||||||
|
RESPONSE_48_BIT = 2,
|
||||||
|
RESPONSE_48_BIT_WITH_BUSY = 3 };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Interrupt : Register<0x30, 32>
|
||||||
|
{
|
||||||
|
struct Cmd_done : Bitfield<0, 1> { };
|
||||||
|
struct Data_done : Bitfield<1, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Irpt_mask : Register<0x34, 32> { };
|
||||||
|
struct Irpt_en : Register<0x38, 32> { };
|
||||||
|
|
||||||
|
Sdhci(Genode::addr_t const mmio_base) : Genode::Mmio(mmio_base) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Sdhci_controller : private Sdhci, public Sd_card::Host_controller
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Delayer &_delayer;
|
||||||
|
Sd_card::Card_info _card_info;
|
||||||
|
|
||||||
|
Genode::Irq_connection _irq;
|
||||||
|
|
||||||
|
void _set_and_enable_clock(unsigned divider)
|
||||||
|
{
|
||||||
|
Control1::access_t v = read<Control1>();
|
||||||
|
Control1::Clk_freq8::set(v, divider);
|
||||||
|
Control1::Clk_freq_ms2::set(v, 0);
|
||||||
|
Control1::Clk_internal_en::set(v, 1);
|
||||||
|
|
||||||
|
write<Control1>(v);
|
||||||
|
|
||||||
|
if (!wait_for<Control1::Clk_internal_stable>(1, _delayer)) {
|
||||||
|
PERR("could not set internal clock");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
write<Control1::Clk_en>(1);
|
||||||
|
|
||||||
|
_delayer.usleep(10*1000);
|
||||||
|
|
||||||
|
/* data timeout unit exponent */
|
||||||
|
write<Control1::Data_tounit>(0xe);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sd_card::Card_info _init()
|
||||||
|
{
|
||||||
|
using namespace Sd_card;
|
||||||
|
|
||||||
|
/* reset host controller */
|
||||||
|
{
|
||||||
|
Control1::access_t v = read<Control1>();
|
||||||
|
Control1::Srst_hc::set(v);
|
||||||
|
Control1::Srst_data::set(v);
|
||||||
|
write<Control1>(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wait_for<Control1::Srst_hc>(0, _delayer)) {
|
||||||
|
PERR("host-controller soft reset timed out");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable interrupt status reporting */
|
||||||
|
write<Irpt_mask>(~0UL);
|
||||||
|
write<Irpt_en>(~0UL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't read the capability register as the BCM2835 always
|
||||||
|
* returns all bits set to zero.
|
||||||
|
*/
|
||||||
|
|
||||||
|
_set_and_enable_clock(240);
|
||||||
|
|
||||||
|
if (!issue_command(Go_idle_state())) {
|
||||||
|
PWRN("Go_idle_state command failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
_delayer.usleep(2000);
|
||||||
|
|
||||||
|
if (!issue_command(Send_if_cond())) {
|
||||||
|
PWRN("Send_if_cond command failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read<Resp0>() != 0x1aa) {
|
||||||
|
PERR("unexpected response of Send_if_cond command");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to issue the same Sd_send_op_cond command multiple
|
||||||
|
* times. The first time, we receive the status information. On
|
||||||
|
* subsequent attempts, the response tells us that the card is
|
||||||
|
* busy. Usually, the command is issued twice. We give up if the
|
||||||
|
* card is not reaching busy state after one second.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int i = 1000;
|
||||||
|
for (; i > 0; --i) {
|
||||||
|
if (!issue_command(Sd_send_op_cond(0x18000, true))) {
|
||||||
|
PWRN("Sd_send_op_cond command failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Sd_card::Ocr::Busy::get(read<Resp0>()))
|
||||||
|
break;
|
||||||
|
|
||||||
|
_delayer.usleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
PERR("Sd_send_op_cond timed out, could no power-on SD card");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
Card_info card_info = _detect();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Switch card to use 4 data signals
|
||||||
|
*/
|
||||||
|
if (!issue_command(Set_bus_width(Set_bus_width::Arg::Bus_width::FOUR_BITS),
|
||||||
|
card_info.rca())) {
|
||||||
|
PWRN("Set_bus_width(FOUR_BITS) command failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* switch host controller to use 4 data signals */
|
||||||
|
{
|
||||||
|
Control0::access_t v = read<Control0>();
|
||||||
|
Control0::Hctl_dwidth::set(v);
|
||||||
|
Control0::Hctl_hs_en::set(v);
|
||||||
|
write<Control0>(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
_delayer.usleep(10*1000);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Accelerate clock, the divider is hard-coded for now.
|
||||||
|
*
|
||||||
|
* The Raspberry Pi report as clock of 250 MHz. According to the
|
||||||
|
* SDHCI specification, it is possible to driver SD cards with
|
||||||
|
* 50 MHz in high-speed mode (Hctl_hs_en).
|
||||||
|
*/
|
||||||
|
_set_and_enable_clock(5);
|
||||||
|
|
||||||
|
return card_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the block count for the next data transfer
|
||||||
|
*/
|
||||||
|
void _set_block_count(size_t block_count)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The 'Blksizecnt' register must be written in one step. If we
|
||||||
|
* used subsequent writes for the 'Blkcnt' and 'Blksize' bitfields,
|
||||||
|
* the host controller of the BCM2835 would fail to recognize any
|
||||||
|
* but the first write operation.
|
||||||
|
*/
|
||||||
|
Blksizecnt::access_t v = read<Blksizecnt>();
|
||||||
|
Blksizecnt::Blkcnt::set(v, block_count);
|
||||||
|
Blksizecnt::Blksize::set(v, 0x200);
|
||||||
|
write<Blksizecnt>(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename REG>
|
||||||
|
bool _poll_and_wait_for(unsigned value)
|
||||||
|
{
|
||||||
|
/* poll for a while */
|
||||||
|
if (wait_for<REG>(value, _delayer, 5000, 0))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* if the value were not reached while polling, start sleeping */
|
||||||
|
return wait_for<REG>(value, _delayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* \param mmio_base local base address of MMIO registers
|
||||||
|
*/
|
||||||
|
Sdhci_controller(Genode::addr_t const mmio_base, Delayer &delayer,
|
||||||
|
unsigned irq, bool use_dma)
|
||||||
|
:
|
||||||
|
Sdhci(mmio_base), _delayer(delayer), _card_info(_init()), _irq(irq)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************
|
||||||
|
** Sd_card::Host_controller interface **
|
||||||
|
****************************************/
|
||||||
|
|
||||||
|
bool _issue_command(Sd_card::Command_base const &command)
|
||||||
|
{
|
||||||
|
if (verbose)
|
||||||
|
PLOG("-> index=0x%08x, arg=0x%08x, rsp_type=%d",
|
||||||
|
command.index, command.arg, command.rsp_type);
|
||||||
|
|
||||||
|
if (!_poll_and_wait_for<Status::Inhibit>(0)) {
|
||||||
|
PERR("controller inhibits issueing commands");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write command argument */
|
||||||
|
write<Arg1>(command.arg);
|
||||||
|
|
||||||
|
/* assemble command register */
|
||||||
|
Cmdtm::access_t cmd = 0;
|
||||||
|
Cmdtm::Index::set(cmd, command.index);
|
||||||
|
if (command.transfer != Sd_card::TRANSFER_NONE) {
|
||||||
|
|
||||||
|
Cmdtm::Isdata::set(cmd);
|
||||||
|
Cmdtm::Tm_blkcnt_en::set(cmd);
|
||||||
|
Cmdtm::Tm_multi_block::set(cmd);
|
||||||
|
|
||||||
|
if (command.index == Sd_card::Read_multiple_block::INDEX
|
||||||
|
|| command.index == Sd_card::Write_multiple_block::INDEX) {
|
||||||
|
Cmdtm::Tm_auto_cmd_en::set(cmd, Cmdtm::Tm_auto_cmd_en::CMD12);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set data-direction bit depending on the command */
|
||||||
|
bool const read = command.transfer == Sd_card::TRANSFER_READ;
|
||||||
|
Cmdtm::Tm_dat_dir::set(cmd, read ? Cmdtm::Tm_dat_dir::READ
|
||||||
|
: Cmdtm::Tm_dat_dir::WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cmdtm::access_t rsp_type = 0;
|
||||||
|
switch (command.rsp_type) {
|
||||||
|
case Sd_card::RESPONSE_NONE: rsp_type = Cmdtm::Rsp_type::RESPONSE_NONE; break;
|
||||||
|
case Sd_card::RESPONSE_136_BIT: rsp_type = Cmdtm::Rsp_type::RESPONSE_136_BIT; break;
|
||||||
|
case Sd_card::RESPONSE_48_BIT: rsp_type = Cmdtm::Rsp_type::RESPONSE_48_BIT; break;
|
||||||
|
case Sd_card::RESPONSE_48_BIT_WITH_BUSY: rsp_type = Cmdtm::Rsp_type::RESPONSE_48_BIT_WITH_BUSY; break;
|
||||||
|
}
|
||||||
|
Cmdtm::Rsp_type::set(cmd, rsp_type);
|
||||||
|
|
||||||
|
/* write command */
|
||||||
|
write<Cmdtm>(cmd);
|
||||||
|
|
||||||
|
if (!_poll_and_wait_for<Interrupt::Cmd_done>(1)) {
|
||||||
|
PERR("command timed out");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clear interrupt state */
|
||||||
|
write<Interrupt::Cmd_done>(1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sd_card::Card_info card_info() const
|
||||||
|
{
|
||||||
|
return _card_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sd_card::Cid _read_cid()
|
||||||
|
{
|
||||||
|
Sd_card::Cid cid;
|
||||||
|
cid.raw_0 = read<Resp0>();
|
||||||
|
cid.raw_1 = read<Resp1>();
|
||||||
|
cid.raw_2 = read<Resp2>();
|
||||||
|
cid.raw_3 = read<Resp3>();
|
||||||
|
return cid;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sd_card::Csd _read_csd()
|
||||||
|
{
|
||||||
|
Sd_card::Csd csd;
|
||||||
|
csd.csd0 = read<Resp0>();
|
||||||
|
csd.csd1 = read<Resp1>();
|
||||||
|
csd.csd2 = read<Resp2>();
|
||||||
|
csd.csd3 = read<Resp3>();
|
||||||
|
return csd;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned _read_rca()
|
||||||
|
{
|
||||||
|
return Sd_card::Send_relative_addr::Response::Rca::get(read<Resp0>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data blocks from SD card
|
||||||
|
*
|
||||||
|
* \return true on success
|
||||||
|
*/
|
||||||
|
bool read_blocks(size_t block_number, size_t block_count, char *out_buffer)
|
||||||
|
{
|
||||||
|
using namespace Sd_card;
|
||||||
|
|
||||||
|
_set_block_count(block_count);
|
||||||
|
|
||||||
|
if (!issue_command(Read_multiple_block(block_number))) {
|
||||||
|
PERR("Read_multiple_block failed, Status: 0x%08x", read<Status>());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::access_t *dst = (Data::access_t *)(out_buffer);
|
||||||
|
for (size_t i = 0; i < block_count; i++) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check for buffer-read enable bit for each block
|
||||||
|
*
|
||||||
|
* According to the BCM2835 documentation, this bit is
|
||||||
|
* reserved but it actually corresponds to the bre status
|
||||||
|
* bit as described in the SDHCI specification.
|
||||||
|
*/
|
||||||
|
if (!_poll_and_wait_for<Status::Bre>(1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* read data from sdhci buffer */
|
||||||
|
for (size_t j = 0; j < 512/sizeof(Data::access_t); j++)
|
||||||
|
*dst++ = read<Data>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_poll_and_wait_for<Interrupt::Data_done>(1)) {
|
||||||
|
PERR("completion of read request failed (interrupt status %08x)",
|
||||||
|
read<Interrupt>());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clear interrupt state */
|
||||||
|
write<Interrupt::Data_done>(1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data blocks to SD card
|
||||||
|
*
|
||||||
|
* \return true on success
|
||||||
|
*/
|
||||||
|
bool write_blocks(size_t block_number, size_t block_count, char const *buffer)
|
||||||
|
{
|
||||||
|
using namespace Sd_card;
|
||||||
|
|
||||||
|
_set_block_count(block_count);
|
||||||
|
|
||||||
|
if (!issue_command(Write_multiple_block(block_number))) {
|
||||||
|
PERR("Write_multiple_block failed, Status: 0x%08x", read<Status>());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::access_t const *src = (Data::access_t const *)(buffer);
|
||||||
|
for (size_t i = 0; i < block_count; i++) {
|
||||||
|
|
||||||
|
/* check for buffer-write enable bit for each block */
|
||||||
|
if (!_poll_and_wait_for<Status::Bwe>(1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* write data into sdhci buffer */
|
||||||
|
for (size_t j = 0; j < 512/sizeof(Data::access_t); j++)
|
||||||
|
write<Data>(*src++);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_poll_and_wait_for<Interrupt::Data_done>(1)) {
|
||||||
|
PERR("completion of write request failed (interrupt status %08x)",
|
||||||
|
read<Interrupt>());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clear interrupt state */
|
||||||
|
write<Interrupt::Data_done>(1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data blocks from SD card via master DMA
|
||||||
|
*
|
||||||
|
* \return true on success
|
||||||
|
*/
|
||||||
|
bool read_blocks_dma(size_t block_number, size_t block_count,
|
||||||
|
Genode::addr_t out_buffer_phys)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data blocks to SD card via master DMA
|
||||||
|
*
|
||||||
|
* \return true on success
|
||||||
|
*/
|
||||||
|
bool write_blocks_dma(size_t block_number, size_t block_count,
|
||||||
|
Genode::addr_t buffer_phys)
|
||||||
|
{
|
||||||
|
using namespace Sd_card;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _SDHCI_H_ */
|
5
repos/os/src/drivers/sd_card/rpi/target.mk
Normal file
5
repos/os/src/drivers/sd_card/rpi/target.mk
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
TARGET = sd_card_drv
|
||||||
|
REQUIRES = platform_rpi
|
||||||
|
SRC_CC = main.cc
|
||||||
|
LIBS = base server
|
||||||
|
INC_DIR += $(PRG_DIR) $(PRG_DIR)/..
|
Loading…
x
Reference in New Issue
Block a user