mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-19 00:43:19 +00:00
sd_card: re-factorizea and clean up
Merge the platform-specific files and classes (they merely aggregated themselves) so that each platform provides merely one class Sd_card::Driver. Also, the Sd_card::Driver_base class is introduced for the generic parts of Sd_card::Driver. Ref #2206
This commit is contained in:
parent
b33c35a003
commit
d07cd0e88e
@ -1,4 +1,5 @@
|
|||||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/exynos5
|
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/exynos5
|
||||||
|
SRC_CC += spec/exynos5/driver.cc
|
||||||
|
|
||||||
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/exynos5
|
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/exynos5
|
||||||
|
SRC_CC += spec/exynos5/driver.cc
|
||||||
|
|
||||||
include $(REP_DIR)/lib/mk/sd_card.inc
|
include $(REP_DIR)/lib/mk/sd_card.inc
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
|
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
|
||||||
SRC_CC += spec/imx/adma2.cc
|
SRC_CC += adma2.cc
|
||||||
SRC_CC += spec/imx/sdhc.cc
|
SRC_CC += spec/imx/driver.cc
|
||||||
SRC_CC += spec/imx53/sdhc.cc
|
SRC_CC += spec/imx53/driver.cc
|
||||||
|
|
||||||
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
|
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
|
||||||
SRC_CC += spec/imx/adma2.cc
|
SRC_CC += adma2.cc
|
||||||
SRC_CC += spec/imx/sdhc.cc
|
SRC_CC += spec/imx/driver.cc
|
||||||
SRC_CC += spec/imx53/sdhc.cc
|
SRC_CC += spec/imx53/driver.cc
|
||||||
|
|
||||||
include $(REP_DIR)/lib/mk/sd_card.inc
|
include $(REP_DIR)/lib/mk/sd_card.inc
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
|
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
|
||||||
SRC_CC += spec/imx/adma2.cc
|
SRC_CC += adma2.cc
|
||||||
SRC_CC += spec/imx/sdhc.cc
|
SRC_CC += spec/imx/driver.cc
|
||||||
SRC_CC += spec/imx6/sdhc.cc
|
SRC_CC += spec/imx6/driver.cc
|
||||||
|
|
||||||
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
|
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
|
||||||
SRC_CC += spec/imx/adma2.cc
|
SRC_CC += adma2.cc
|
||||||
SRC_CC += spec/imx/sdhc.cc
|
SRC_CC += spec/imx/driver.cc
|
||||||
SRC_CC += spec/imx6/sdhc.cc
|
SRC_CC += spec/imx6/driver.cc
|
||||||
|
|
||||||
include $(REP_DIR)/lib/mk/sd_card.inc
|
include $(REP_DIR)/lib/mk/sd_card.inc
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/omap4
|
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/omap4
|
||||||
|
SRC_CC += spec/omap4/driver.cc
|
||||||
|
|
||||||
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/omap4
|
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/omap4
|
||||||
|
SRC_CC += spec/omap4/driver.cc
|
||||||
|
|
||||||
include $(REP_DIR)/lib/mk/sd_card.inc
|
include $(REP_DIR)/lib/mk/sd_card.inc
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/pl180
|
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/pl180
|
||||||
|
SRC_CC += spec/pl180/driver.cc
|
||||||
|
|
||||||
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/pl180
|
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/pl180
|
||||||
|
SRC_CC += spec/pl180/driver.cc
|
||||||
|
|
||||||
include $(REP_DIR)/lib/mk/sd_card.inc
|
include $(REP_DIR)/lib/mk/sd_card.inc
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/rpi
|
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/rpi
|
||||||
|
SRC_CC += spec/rpi/driver.cc
|
||||||
|
|
||||||
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/rpi
|
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/rpi
|
||||||
|
SRC_CC += spec/rpi/driver.cc
|
||||||
|
|
||||||
include $(REP_DIR)/lib/mk/sd_card.inc
|
include $(REP_DIR)/lib/mk/sd_card.inc
|
||||||
|
@ -20,9 +20,9 @@
|
|||||||
using namespace Adma2;
|
using namespace Adma2;
|
||||||
|
|
||||||
|
|
||||||
Table::Table()
|
Table::Table(Ram_session &ram)
|
||||||
:
|
:
|
||||||
_ds(env()->ram_session(), _ds_size, UNCACHED),
|
_ds(&ram, _ds_size, UNCACHED),
|
||||||
_base_virt(_ds.local_addr<Desc::access_t>()),
|
_base_virt(_ds.local_addr<Desc::access_t>()),
|
||||||
_base_phys(Dataspace_client(_ds.cap()).phys_addr())
|
_base_phys(Dataspace_client(_ds.cap()).phys_addr())
|
||||||
{ }
|
{ }
|
@ -66,7 +66,7 @@ class Adma2::Table
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Table();
|
Table(Ram_session &ram);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marshal descriptors according to block request
|
* Marshal descriptors according to block request
|
51
repos/os/src/drivers/sd_card/driver_base.h
Normal file
51
repos/os/src/drivers/sd_card/driver_base.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* \brief Generic parts of an SD card block-driver
|
||||||
|
* \author Norman Feske
|
||||||
|
* \author Timo Wischer
|
||||||
|
* \author Martin Stein
|
||||||
|
* \date 2017-01-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DRIVER_BASE_H_
|
||||||
|
#define _DRIVER_BASE_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <block/component.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <sd_card.h>
|
||||||
|
|
||||||
|
namespace Sd_card { class Driver_base; }
|
||||||
|
|
||||||
|
|
||||||
|
class Sd_card::Driver_base : public Block::Driver,
|
||||||
|
protected Host_controller
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/*******************
|
||||||
|
** Block::Driver **
|
||||||
|
*******************/
|
||||||
|
|
||||||
|
Genode::size_t block_size() override { return 512; }
|
||||||
|
|
||||||
|
Block::sector_t block_count() override {
|
||||||
|
return card_info().capacity_mb() * 1024 * 2; }
|
||||||
|
|
||||||
|
Block::Session::Operations ops() override
|
||||||
|
{
|
||||||
|
Block::Session::Operations ops;
|
||||||
|
ops.set_operation(Block::Packet_descriptor::READ);
|
||||||
|
ops.set_operation(Block::Packet_descriptor::WRITE);
|
||||||
|
return ops;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _DRIVER_BASE_H_ */
|
@ -36,10 +36,10 @@ struct Main
|
|||||||
Factory(Env &env, Heap &heap) : env(env), heap(heap) { }
|
Factory(Env &env, Heap &heap) : env(env), heap(heap) { }
|
||||||
|
|
||||||
Block::Driver *create() {
|
Block::Driver *create() {
|
||||||
return new (&heap) Block::Sdhci_driver(env); }
|
return new (&heap) Sd_card::Driver(env); }
|
||||||
|
|
||||||
void destroy(Block::Driver *driver) {
|
void destroy(Block::Driver *driver) {
|
||||||
Genode::destroy(&heap, static_cast<Block::Sdhci_driver*>(driver)); }
|
Genode::destroy(&heap, static_cast<Sd_card::Driver*>(driver)); }
|
||||||
|
|
||||||
} factory { env, heap };
|
} factory { env, heap };
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
#define _SD_CARD_H_
|
#define _SD_CARD_H_
|
||||||
|
|
||||||
/* Genode includes */
|
/* Genode includes */
|
||||||
#include <util/register.h>
|
#include <util/mmio.h>
|
||||||
|
|
||||||
namespace Sd_card {
|
namespace Sd_card {
|
||||||
|
|
||||||
|
432
repos/os/src/drivers/sd_card/spec/exynos5/driver.cc
Normal file
432
repos/os/src/drivers/sd_card/spec/exynos5/driver.cc
Normal file
@ -0,0 +1,432 @@
|
|||||||
|
/*
|
||||||
|
* \brief Exynos5-specific implementation of the Block::Driver interface
|
||||||
|
* \author Sebastian Sumpf
|
||||||
|
* \author Martin Stein
|
||||||
|
* \date 2013-03-22
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <driver.h>
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
using namespace Sd_card;
|
||||||
|
|
||||||
|
|
||||||
|
Driver::Driver(Env &env) : Attached_mmio(MSH_BASE, MSH_SIZE), _env(env)
|
||||||
|
{
|
||||||
|
_irq.sigh(_irq_handler);
|
||||||
|
_irq.ack_irq();
|
||||||
|
|
||||||
|
log("SD/MMC card detected");
|
||||||
|
log("capacity: ", _card_info.capacity_mb(), " MiB");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::read_dma(Block::sector_t block_number,
|
||||||
|
size_t block_count,
|
||||||
|
addr_t buf_phys,
|
||||||
|
Block::Packet_descriptor &pkt)
|
||||||
|
{
|
||||||
|
if (_block_transfer.pending) {
|
||||||
|
throw Request_congestion(); }
|
||||||
|
|
||||||
|
if (!_setup_idmac_descriptor_table(block_count, buf_phys))
|
||||||
|
throw Io_error();
|
||||||
|
|
||||||
|
_block_transfer.packet = pkt;
|
||||||
|
_block_transfer.pending = true;
|
||||||
|
|
||||||
|
if (!_issue_command(Read_multiple_block(block_number))) {
|
||||||
|
error("Read_multiple_block failed");
|
||||||
|
throw Io_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::write_dma(Block::sector_t block_number,
|
||||||
|
size_t block_count,
|
||||||
|
addr_t buf_phys,
|
||||||
|
Block::Packet_descriptor &pkt)
|
||||||
|
{
|
||||||
|
if (_block_transfer.pending) {
|
||||||
|
throw Request_congestion(); }
|
||||||
|
|
||||||
|
if (!_setup_idmac_descriptor_table(block_count, buf_phys))
|
||||||
|
throw Io_error();
|
||||||
|
|
||||||
|
_block_transfer.packet = pkt;
|
||||||
|
_block_transfer.pending = true;
|
||||||
|
|
||||||
|
if (!_issue_command(Write_multiple_block(block_number))) {
|
||||||
|
error("Read_multiple_block failed");
|
||||||
|
throw Io_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Driver::_reset()
|
||||||
|
{
|
||||||
|
Mmio::write<Ctrl::Reset>(0x7);
|
||||||
|
|
||||||
|
if (!wait_for<Ctrl::Reset>(0, _delayer, 100, 1000)) {
|
||||||
|
error("Could not reset host contoller");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_reset_fifo()
|
||||||
|
{
|
||||||
|
Mmio::write<Ctrl::Reset>(0x2);
|
||||||
|
|
||||||
|
if (!wait_for<Ctrl::Reset>(0, _delayer, 100, 1000)) {
|
||||||
|
error("Could not reset fifo"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_disable_irq()
|
||||||
|
{
|
||||||
|
Mmio::write<Rintsts>(~0U);
|
||||||
|
Mmio::write<Intmask>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Driver::_update_clock_registers()
|
||||||
|
{
|
||||||
|
Cmd::access_t cmd = 0;
|
||||||
|
Cmd::Wait_prvdata_complete::set(cmd, 1);
|
||||||
|
Cmd::Update_clock_registers_only::set(cmd, 1);
|
||||||
|
Cmd::Start_cmd::set(cmd, 1);
|
||||||
|
Mmio::write<Cmd>(cmd);
|
||||||
|
|
||||||
|
if (!wait_for<Cmd::Start_cmd>(0, _delayer)) {
|
||||||
|
error("Update clock registers failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Driver::_setup_bus(unsigned clock_div)
|
||||||
|
{
|
||||||
|
/* set host clock divider */
|
||||||
|
Mmio::write<Clkdiv>(clock_div);
|
||||||
|
|
||||||
|
if (!_update_clock_registers())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* enable clock for card 1 */
|
||||||
|
Mmio::write<Clkena>(0x1);
|
||||||
|
|
||||||
|
if (!_update_clock_registers())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_delayer.usleep(10 * 1000);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Card_info Driver::_init()
|
||||||
|
{
|
||||||
|
Mmio::write<Pwren>(1);
|
||||||
|
if (!_reset())
|
||||||
|
throw Detection_failed();
|
||||||
|
|
||||||
|
Mmio::write<Emmc_ddr_req>(0x1);
|
||||||
|
|
||||||
|
_disable_irq();
|
||||||
|
|
||||||
|
Mmio::write<Tmout>(~0U);
|
||||||
|
Mmio::write<Idinten>(0);
|
||||||
|
Mmio::write<Bmod>(1);
|
||||||
|
Mmio::write<Bytcnt>(0);
|
||||||
|
Mmio::write<Fifoth>(0x203f0040);
|
||||||
|
|
||||||
|
/* set to one bit transfer Bit */
|
||||||
|
if (!_setup_bus(CLK_DIV_400Khz))
|
||||||
|
throw Detection_failed();
|
||||||
|
|
||||||
|
Mmio::write<Ctype>(BUS_WIDTH_1);
|
||||||
|
|
||||||
|
if (!issue_command(Go_idle_state())) {
|
||||||
|
warning("Go_idle_state command failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
_delayer.usleep(2000);
|
||||||
|
|
||||||
|
if (!issue_command(Send_if_cond())) {
|
||||||
|
warning("Send_if_cond command failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
/* if this succeeds it is an SD card */
|
||||||
|
if ((Mmio::read<Rsp0>() & 0xff) == 0xaa)
|
||||||
|
log("Found SD card");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to issue the same Mmc_send_op_cond command multiple
|
||||||
|
* times. The first time, we receive the status information. On
|
||||||
|
* subsequent attempts, the response tells us that the card is
|
||||||
|
* busy. Usually, the command is issued twice. We give up if the
|
||||||
|
* card is not reaching busy state after one second.
|
||||||
|
*/
|
||||||
|
unsigned i = 1000;
|
||||||
|
unsigned voltages = 0x300080;
|
||||||
|
unsigned arg = 0;
|
||||||
|
for (; i > 0; --i) {
|
||||||
|
if (!issue_command(Mmc_send_op_cond(arg, true))) {
|
||||||
|
warning("Sd_send_op_cond command failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
arg = Mmio::read<Rsp0>();
|
||||||
|
arg = (voltages & (arg & 0x007FFF80)) | (arg & 0x60000000);
|
||||||
|
|
||||||
|
_delayer.usleep(1000);
|
||||||
|
|
||||||
|
if (Ocr::Busy::get(Mmio::read<Rsp0>()))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i == 0) {
|
||||||
|
error("Send_op_cond timed out, could no power-on SD/MMC card");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
Card_info card_info = _detect_mmc();
|
||||||
|
|
||||||
|
/* switch frequency to high speed */
|
||||||
|
enum { EXT_CSD_HS_TIMING = 185 };
|
||||||
|
if (!issue_command(Mmc_switch(EXT_CSD_HS_TIMING, 1))) {
|
||||||
|
error("Error setting high speed frequency");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
enum { EXT_CSD_BUS_WIDTH = 183 };
|
||||||
|
/* set card to 8 bit */
|
||||||
|
if (!issue_command(Mmc_switch(EXT_CSD_BUS_WIDTH, 2))) {
|
||||||
|
error("Error setting card bus width");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
Mmio::write<Ctype>(BUS_WIDTH_8);
|
||||||
|
|
||||||
|
/* set to eight bit transfer Bit */
|
||||||
|
if (!_setup_bus(CLK_DIV_52Mhz)) {
|
||||||
|
error("Error setting bus to high speed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
/* Enable IRQs data read timeout, data transfer done, resp error */
|
||||||
|
Mmio::write<Intmask>(0x28a);
|
||||||
|
Mmio::write<Ctrl::Global_interrupt>(1);
|
||||||
|
return card_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Driver::_setup_idmac_descriptor_table(size_t block_count,
|
||||||
|
addr_t phys_addr)
|
||||||
|
{
|
||||||
|
size_t const max_idmac_block_count = IDMAC_DESC_MAX_ENTRIES * 8;
|
||||||
|
|
||||||
|
if (block_count > max_idmac_block_count) {
|
||||||
|
error("Block request too large");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_reset_fifo();
|
||||||
|
|
||||||
|
Idmac_desc::Flags flags = Idmac_desc::FS;
|
||||||
|
size_t b = block_count;
|
||||||
|
int index = 0;
|
||||||
|
for (index = 0; b; index++, phys_addr += 0x1000, flags = Idmac_desc::NONE) {
|
||||||
|
b = _idmac_desc[index].set(b, block_size(), phys_addr, flags);
|
||||||
|
_idmac_desc[index].next =
|
||||||
|
_idmac_desc_phys + ((index + 1) * sizeof(Idmac_desc));
|
||||||
|
}
|
||||||
|
_idmac_desc[index].next = (unsigned)_idmac_desc;
|
||||||
|
_idmac_desc[index].flags |= Idmac_desc::ER;
|
||||||
|
Mmio::write<Dbaddr>(_idmac_desc_phys);
|
||||||
|
|
||||||
|
Mmio::write<Ctrl::Dma_enable>(1);
|
||||||
|
Mmio::write<Ctrl::Use_internal_dmac>(1);
|
||||||
|
|
||||||
|
Mmio::write<Bmod::Fixed_burst>(1);
|
||||||
|
Mmio::write<Bmod::Idmac_enable>(1);
|
||||||
|
|
||||||
|
Mmio::write<Blksize>(block_size());
|
||||||
|
Mmio::write<Bytcnt>(block_size() * block_count);
|
||||||
|
|
||||||
|
Mmio::write<Pldmnd>(1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_handle_irq()
|
||||||
|
{
|
||||||
|
_irq.ack_irq();
|
||||||
|
|
||||||
|
if (!_block_transfer.pending) {
|
||||||
|
return; }
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
if (Mmio::read<Rintsts::Response_error>()) {
|
||||||
|
error("Response error");
|
||||||
|
}
|
||||||
|
if (Mmio::read<Rintsts::Data_read_timeout>()) {
|
||||||
|
error("Data read timeout");
|
||||||
|
}
|
||||||
|
if (Mmio::read<Rintsts::Data_crc_error>()) {
|
||||||
|
error("CRC error");
|
||||||
|
}
|
||||||
|
if (Mmio::read<Rintsts::Data_transfer_over>()) {
|
||||||
|
Mmio::write<Rintsts>(~0U);
|
||||||
|
if (!_issue_command(Stop_transmission())) {
|
||||||
|
error("unable to stop transmission");
|
||||||
|
} else {
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_block_transfer.pending = false;
|
||||||
|
ack_packet(_block_transfer.packet, success);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Driver::_issue_command(Command_base const &command)
|
||||||
|
{
|
||||||
|
if (!wait_for<Status::Data_busy>(0, _delayer, 10000, 100)) {
|
||||||
|
error("wait for State::Data_busy timed out ",
|
||||||
|
Hex(Mmio::read<Status>()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mmio::write<Rintsts>(~0UL);
|
||||||
|
|
||||||
|
/* write command argument */
|
||||||
|
Mmio::write<Cmdarg>(command.arg);
|
||||||
|
|
||||||
|
Cmd::access_t cmd = 0;
|
||||||
|
Cmd::Index::set(cmd, command.index);
|
||||||
|
|
||||||
|
if (command.transfer != TRANSFER_NONE) {
|
||||||
|
/* set data-direction bit depending on the command */
|
||||||
|
bool const write = command.transfer == TRANSFER_WRITE;
|
||||||
|
Cmd::Data_expected::set(cmd, 1);
|
||||||
|
Cmd::Write::set(cmd, write ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cmd::access_t rsp_type = 0;
|
||||||
|
switch (command.rsp_type) {
|
||||||
|
case RESPONSE_NONE: rsp_type = Cmd::Rsp_type::RESPONSE_NONE; break;
|
||||||
|
case RESPONSE_136_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_136_BIT; break;
|
||||||
|
case RESPONSE_48_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_48_BIT; break;
|
||||||
|
case RESPONSE_48_BIT_WITH_BUSY: rsp_type = Cmd::Rsp_type::RESPONSE_48_BIT_WITH_BUSY; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cmd::Rsp_type::set(cmd, rsp_type);
|
||||||
|
Cmd::Start_cmd::set(cmd, 1);
|
||||||
|
Cmd::Use_hold_reg::set(cmd ,1);
|
||||||
|
Cmd::Wait_prvdata_complete::set(cmd, 1);
|
||||||
|
|
||||||
|
if (command.index == 0)
|
||||||
|
Cmd::Init_sequence::set(cmd, 1);
|
||||||
|
|
||||||
|
/* issue command */
|
||||||
|
Mmio::write<Cmd>(cmd);
|
||||||
|
|
||||||
|
if (!wait_for<Rintsts::Command_done>(1, _delayer, 10000, 100)) {
|
||||||
|
error("command failed "
|
||||||
|
"Rintst: ", Mmio::read<Rintsts>(), " "
|
||||||
|
"Mintst: ", Mmio::read<Mintsts>(), " "
|
||||||
|
"Status: ", Mmio::read<Status>());
|
||||||
|
|
||||||
|
if (Mmio::read<Rintsts::Response_timeout>())
|
||||||
|
warning("timeout");
|
||||||
|
|
||||||
|
if (Mmio::read<Rintsts::Response_error>())
|
||||||
|
warning("repsonse error");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* acknowledge interrupt */
|
||||||
|
Mmio::write<Rintsts::Command_done>(1);
|
||||||
|
|
||||||
|
_delayer.usleep(100);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Cid Driver::_read_cid()
|
||||||
|
{
|
||||||
|
Cid cid;
|
||||||
|
cid.raw_0 = Mmio::read<Rsp0>();
|
||||||
|
cid.raw_1 = Mmio::read<Rsp1>();
|
||||||
|
cid.raw_2 = Mmio::read<Rsp2>();
|
||||||
|
cid.raw_3 = Mmio::read<Rsp3>();
|
||||||
|
return cid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Csd Driver::_read_csd()
|
||||||
|
{
|
||||||
|
Csd csd;
|
||||||
|
csd.csd0 = Mmio::read<Rsp0>();
|
||||||
|
csd.csd1 = Mmio::read<Rsp1>();
|
||||||
|
csd.csd2 = Mmio::read<Rsp2>();
|
||||||
|
csd.csd3 = Mmio::read<Rsp3>();
|
||||||
|
return csd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t Driver::_read_ext_csd()
|
||||||
|
{
|
||||||
|
Attached_ram_dataspace ds(&_env.ram(), 0x1000, UNCACHED);
|
||||||
|
|
||||||
|
addr_t phys = Dataspace_client(ds.cap()).phys_addr();
|
||||||
|
_setup_idmac_descriptor_table(1, phys);
|
||||||
|
|
||||||
|
if (!issue_command(Mmc_send_ext_csd()))
|
||||||
|
throw Detection_failed();
|
||||||
|
|
||||||
|
if (!wait_for<Rintsts::Data_transfer_over>(1, _delayer, 10000, 100)) {
|
||||||
|
error("cannot retrieve extented CSD");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
/* clear IRQ */
|
||||||
|
Mmio::write<Rintsts::Data_transfer_over>(1);
|
||||||
|
|
||||||
|
/* contruct extented CSD */
|
||||||
|
Ext_csd csd((addr_t)ds.local_addr<addr_t>());
|
||||||
|
|
||||||
|
/* read revision */
|
||||||
|
if (csd.Mmio::read<Ext_csd::Revision>() < 2) {
|
||||||
|
error("extented CSD revision is < 2");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return sector count */
|
||||||
|
uint64_t capacity = csd.Mmio::read<Ext_csd::Sector_count>() * block_size();
|
||||||
|
|
||||||
|
/* to MB */
|
||||||
|
return capacity / (1024 * 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t Driver::Idmac_desc::set(size_t block_count,
|
||||||
|
size_t block_size,
|
||||||
|
addr_t phys_addr,
|
||||||
|
Flags flag)
|
||||||
|
{
|
||||||
|
enum { MAX_BLOCKS = 8 };
|
||||||
|
flags = OWN | flag |
|
||||||
|
(block_count <= MAX_BLOCKS ? LD : (CH | DIC));
|
||||||
|
bytes = ((block_count < MAX_BLOCKS) ? block_count : MAX_BLOCKS) *
|
||||||
|
block_size;
|
||||||
|
addr = phys_addr;
|
||||||
|
|
||||||
|
return block_count < MAX_BLOCKS ?
|
||||||
|
0 : block_count - MAX_BLOCKS;
|
||||||
|
}
|
@ -1,59 +1,153 @@
|
|||||||
/*
|
/*
|
||||||
* \brief Exynos5-specific implementation of the Block::Driver interface
|
* \brief Exynos5-specific implementation of the Block::Driver interface
|
||||||
* \author Sebastian Sumpf
|
* \author Sebastian Sumpf
|
||||||
|
* \author Martin Stein
|
||||||
* \date 2013-03-22
|
* \date 2013-03-22
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 Genode Labs GmbH
|
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _DRIVERS__SD_CARD__SPEC__EXYNOS5__DRIVER_H_
|
#ifndef _DRIVER_H_
|
||||||
#define _DRIVERS__SD_CARD__SPEC__EXYNOS5__DRIVER_H_
|
#define _DRIVER_H_
|
||||||
|
|
||||||
/* Genode includes */
|
/* Genode includes */
|
||||||
#include <util/mmio.h>
|
#include <os/attached_mmio.h>
|
||||||
#include <os/attached_io_mem_dataspace.h>
|
|
||||||
#include <base/log.h>
|
|
||||||
#include <timer_session/connection.h>
|
#include <timer_session/connection.h>
|
||||||
#include <os/server.h>
|
#include <drivers/board_base.h>
|
||||||
#include <regulator_session/connection.h>
|
#include <regulator_session/connection.h>
|
||||||
|
#include <irq_session/connection.h>
|
||||||
|
#include <os/attached_ram_dataspace.h>
|
||||||
|
|
||||||
/* local includes */
|
/* local includes */
|
||||||
#include <dwmmc.h>
|
#include <driver_base.h>
|
||||||
|
|
||||||
namespace Block {
|
namespace Sd_card { class Driver; }
|
||||||
using namespace Genode;
|
|
||||||
class Sdhci_driver;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Block::Sdhci_driver : public Block::Driver,
|
class Sd_card::Driver : public Driver_base,
|
||||||
public Sd_ack_handler
|
private Attached_mmio
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
struct Timer_delayer : Timer::Connection, Mmio::Delayer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Implementation of 'Delayer' interface
|
|
||||||
*/
|
|
||||||
void usleep(unsigned us)
|
|
||||||
{
|
|
||||||
/* polling */
|
|
||||||
if (us == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Timer::Connection::usleep(us);
|
|
||||||
}
|
|
||||||
} _delayer;
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
MSH_BASE = 0x12200000, /* host controller base for eMMC */
|
HOST_FREQ = 52000000,
|
||||||
|
CLK_FREQ = 400000000,
|
||||||
|
CLK_DIV_52Mhz = 4,
|
||||||
|
CLK_DIV_400Khz = 0xff,
|
||||||
|
MSH_BASE = 0x12200000,
|
||||||
MSH_SIZE = 0x10000,
|
MSH_SIZE = 0x10000,
|
||||||
|
IDMAC_DESC_MAX_ENTRIES = 1024
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Bus_width {
|
||||||
|
BUS_WIDTH_1 = 0,
|
||||||
|
BUS_WIDTH_4 = 1,
|
||||||
|
BUS_WIDTH_8 = 1 << 16,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <off_t OFFSET, bool STRICT_WRITE = false>
|
||||||
|
struct Register : Mmio::Register<OFFSET, 32, STRICT_WRITE> { };
|
||||||
|
|
||||||
|
struct Ctrl : Register<0x0>
|
||||||
|
{
|
||||||
|
struct Reset : Bitfield<0, 3> { };
|
||||||
|
struct Global_interrupt : Bitfield<4, 1> { };
|
||||||
|
struct Dma_enable : Bitfield<5, 1> { };
|
||||||
|
struct Use_internal_dmac : Bitfield<25, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Pwren : Register<0x4> { };
|
||||||
|
struct Clkdiv : Register<0x8> { };
|
||||||
|
struct Clkena : Register<0x10> { };
|
||||||
|
struct Tmout : Register<0x14> { };
|
||||||
|
struct Ctype : Register<0x18, true> { };
|
||||||
|
struct Blksize : Register<0x1c> { };
|
||||||
|
struct Bytcnt : Register<0x20> { };
|
||||||
|
struct Intmask : Register<0x24> { };
|
||||||
|
struct Cmdarg : Register<0x28> { };
|
||||||
|
|
||||||
|
struct Cmd : Register<0x2c>
|
||||||
|
{
|
||||||
|
struct Index : Bitfield<0, 6> { };
|
||||||
|
struct Rsp_type : Bitfield<6, 3>
|
||||||
|
{
|
||||||
|
enum Response { RESPONSE_NONE = 0,
|
||||||
|
RESPONSE_48_BIT = 1,
|
||||||
|
RESPONSE_48_BIT_WITH_BUSY = 5,
|
||||||
|
RESPONSE_136_BIT = 7,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
struct Data_expected : Bitfield<9, 1> { };
|
||||||
|
struct Write : Bitfield<10, 1> { };
|
||||||
|
struct Wait_prvdata_complete : Bitfield<13, 1> { };
|
||||||
|
struct Init_sequence : Bitfield<15, 1> { };
|
||||||
|
struct Update_clock_registers_only : Bitfield<21, 1> { };
|
||||||
|
struct Use_hold_reg : Bitfield<29, 1> { };
|
||||||
|
struct Start_cmd : Bitfield<31, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Rsp0 : Register<0x30> { };
|
||||||
|
struct Rsp1 : Register<0x34> { };
|
||||||
|
struct Rsp2 : Register<0x38> { };
|
||||||
|
struct Rsp3 : Register<0x3c> { };
|
||||||
|
|
||||||
|
struct Mintsts : Register<0x40> { };
|
||||||
|
struct Rintsts : Register<0x44, true>
|
||||||
|
{
|
||||||
|
struct Response_error : Bitfield<1, 1> { };
|
||||||
|
struct Data_transfer_over : Bitfield<3, 1> { };
|
||||||
|
struct Command_done : Bitfield<2, 1> { };
|
||||||
|
struct Data_crc_error : Bitfield<7, 1> { };
|
||||||
|
struct Response_timeout : Bitfield<8, 1> { };
|
||||||
|
struct Data_read_timeout : Bitfield<9, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Status : Register<0x48>
|
||||||
|
{
|
||||||
|
struct Data_busy : Bitfield<9, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Fifoth : Register<0x4c> { };
|
||||||
|
|
||||||
|
struct Bmod : Register<0x80, true>
|
||||||
|
{
|
||||||
|
struct Fixed_burst : Bitfield<1, 1> { };
|
||||||
|
struct Idmac_enable : Bitfield<7, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Pldmnd : Register<0x84> { };
|
||||||
|
struct Idsts : Register<0x8c> { };
|
||||||
|
struct Idinten : Register<0x90, true> { };
|
||||||
|
struct Dbaddr : Register<0x88> { };
|
||||||
|
struct Clksel : Register<0x9c> { };
|
||||||
|
struct Emmc_ddr_req : Register<0x10c, true> { };
|
||||||
|
|
||||||
|
struct Idmac_desc
|
||||||
|
{
|
||||||
|
enum Flags {
|
||||||
|
NONE = 0,
|
||||||
|
DIC = 1 << 1,
|
||||||
|
LD = 1 << 2,
|
||||||
|
FS = 1 << 3,
|
||||||
|
CH = 1 << 4,
|
||||||
|
ER = 1 << 5,
|
||||||
|
OWN = 1 << 31,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned flags;
|
||||||
|
unsigned bytes;
|
||||||
|
unsigned addr;
|
||||||
|
unsigned next;
|
||||||
|
|
||||||
|
size_t set(size_t block_count,
|
||||||
|
size_t block_size,
|
||||||
|
addr_t phys_addr,
|
||||||
|
Flags flag);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Clock_regulator
|
struct Clock_regulator
|
||||||
@ -62,74 +156,84 @@ class Block::Sdhci_driver : public Block::Driver,
|
|||||||
|
|
||||||
Clock_regulator(Env &env) : regulator(env, Regulator::CLK_MMC0) {
|
Clock_regulator(Env &env) : regulator(env, Regulator::CLK_MMC0) {
|
||||||
regulator.state(true); }
|
regulator.state(true); }
|
||||||
|
};
|
||||||
|
|
||||||
} _clock_regulator;
|
struct Timer_delayer : Timer::Connection, Mmio::Delayer
|
||||||
|
{
|
||||||
|
void usleep(unsigned us) {
|
||||||
|
Timer::Connection::usleep(us); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Block_transfer
|
||||||
|
{
|
||||||
|
Block::Packet_descriptor packet;
|
||||||
|
bool pending = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
Env &_env;
|
||||||
|
Timer_delayer _delayer;
|
||||||
|
Block_transfer _block_transfer;
|
||||||
|
Clock_regulator _clock_regulator { _env };
|
||||||
|
Signal_handler<Driver> _irq_handler { _env.ep(), *this, &Driver::_handle_irq };
|
||||||
|
Irq_connection _irq { Board_base::SDMMC0_IRQ };
|
||||||
|
Attached_ram_dataspace _idmac_desc_ds { &_env.ram(),
|
||||||
|
IDMAC_DESC_MAX_ENTRIES * sizeof(Idmac_desc),
|
||||||
|
UNCACHED };
|
||||||
|
Idmac_desc *const _idmac_desc { _idmac_desc_ds.local_addr<Idmac_desc>() };
|
||||||
|
addr_t const _idmac_desc_phys { Dataspace_client(_idmac_desc_ds.cap())
|
||||||
|
.phys_addr() };
|
||||||
|
Card_info _card_info { _init() };
|
||||||
|
|
||||||
|
bool _reset();
|
||||||
|
void _reset_fifo();
|
||||||
|
void _disable_irq();
|
||||||
|
bool _update_clock_registers();
|
||||||
|
bool _setup_bus(unsigned clock_div);
|
||||||
|
void _handle_irq();
|
||||||
|
Card_info _init();
|
||||||
|
|
||||||
|
bool _setup_idmac_descriptor_table(size_t block_count,
|
||||||
|
addr_t phys_addr);
|
||||||
|
|
||||||
|
|
||||||
/* display sub system registers */
|
/*********************
|
||||||
Attached_io_mem_dataspace _mmio;
|
** Host_controller **
|
||||||
|
*********************/
|
||||||
|
|
||||||
/* mobile storage host controller instance */
|
bool _issue_command(Command_base const &command) override;
|
||||||
Exynos5_msh_controller _controller;
|
Cid _read_cid() override ;
|
||||||
|
Csd _read_csd() override ;
|
||||||
|
size_t _read_ext_csd() override;
|
||||||
|
unsigned _read_rca() override { return 0; }
|
||||||
|
|
||||||
bool const _use_dma;
|
Card_info card_info() const override { return _card_info; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Sdhci_driver(Env &env)
|
using Block::Driver::read;
|
||||||
:
|
using Block::Driver::write;
|
||||||
_clock_regulator(env),
|
|
||||||
_mmio(MSH_BASE, MSH_SIZE),
|
|
||||||
_controller(env.ep(), (addr_t)_mmio.local_addr<void>(),
|
|
||||||
_delayer, *this, true),
|
|
||||||
_use_dma(true)
|
|
||||||
{
|
|
||||||
Sd_card::Card_info const card_info = _controller.card_info();
|
|
||||||
|
|
||||||
Genode::log("SD/MMC card detected");
|
Driver(Env &env);
|
||||||
Genode::log("capacity: ", card_info.capacity_mb(), " MiB");
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_ack(Block::Packet_descriptor pkt, bool success) {
|
|
||||||
ack_packet(pkt, success); }
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************
|
/*******************
|
||||||
** Block::Driver interface **
|
** Block::Driver **
|
||||||
*****************************/
|
*******************/
|
||||||
|
|
||||||
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_dma(Block::sector_t block_number,
|
void read_dma(Block::sector_t block_number,
|
||||||
Genode::size_t block_count,
|
size_t block_count,
|
||||||
Genode::addr_t phys,
|
addr_t buf_phys,
|
||||||
Packet_descriptor &packet)
|
Block::Packet_descriptor &pkt) override;
|
||||||
{
|
|
||||||
_controller.read_blocks_dma(block_number, block_count, phys, packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_dma(Block::sector_t block_number,
|
void write_dma(Block::sector_t block_number,
|
||||||
Genode::size_t block_count,
|
size_t block_count,
|
||||||
Genode::addr_t phys,
|
addr_t buf_phys,
|
||||||
Packet_descriptor &packet)
|
Block::Packet_descriptor &pkt) override;
|
||||||
{
|
|
||||||
_controller.write_blocks_dma(block_number, block_count, phys, packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dma_enabled() { return _use_dma; }
|
bool dma_enabled() override { return true; }
|
||||||
|
|
||||||
|
Ram_dataspace_capability alloc_dma_buffer(size_t size) override {
|
||||||
|
return _env.ram().alloc(size, UNCACHED); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _DRIVERS__SD_CARD__SPEC__EXYNOS5__DRIVER_H_ */
|
#endif /* _DRIVER_H_ */
|
||||||
|
@ -1,710 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief DesignWare Multimedia Card interface
|
|
||||||
* \author Sebastian Sumpf
|
|
||||||
* \date 2013-03-06
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2015 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU General Public License version 2.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _DRIVERS__SD_CARD__SPEC__EXYNOS5__DWMMC_H_
|
|
||||||
#define _DRIVERS__SD_CARD__SPEC__EXYNOS5__DWMMC_H_
|
|
||||||
|
|
||||||
#include <drivers/board_base.h>
|
|
||||||
#include <irq_session/connection.h>
|
|
||||||
#include <os/attached_ram_dataspace.h>
|
|
||||||
#include <util/mmio.h>
|
|
||||||
#include <block/component.h>
|
|
||||||
|
|
||||||
#include <sd_card.h>
|
|
||||||
|
|
||||||
struct Dwmmc : Genode::Mmio
|
|
||||||
{
|
|
||||||
enum { verbose = false };
|
|
||||||
|
|
||||||
/*
|
|
||||||
* These apply to card controller 0 and 1 only
|
|
||||||
*/
|
|
||||||
enum {
|
|
||||||
HOST_FREQ = 52000000, /* Hz */
|
|
||||||
CLK_FREQ = 400000000, /* Hz */
|
|
||||||
|
|
||||||
/* CLK_FREQ / (2 * CLK_DIV <= HOST_FREQ) */
|
|
||||||
CLK_DIV_52Mhz = 4,
|
|
||||||
CLK_DIV_400Khz = 0xff,
|
|
||||||
};
|
|
||||||
|
|
||||||
Dwmmc(Genode::addr_t base) : Genode::Mmio(base) { }
|
|
||||||
|
|
||||||
template <Genode::off_t OFFSET, bool STRICT_WRITE = false>
|
|
||||||
struct Register : Genode::Mmio::Register<OFFSET, 32, STRICT_WRITE> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Control register
|
|
||||||
*/
|
|
||||||
struct Ctrl : Register<0x0>
|
|
||||||
{
|
|
||||||
/* Controller/FIFO/DMA reset */
|
|
||||||
struct Reset : Bitfield<0, 3> { };
|
|
||||||
struct Global_interrupt : Bitfield<4, 1> { };
|
|
||||||
struct Dma_enable : Bitfield<5, 1> { };
|
|
||||||
struct Use_internal_dmac : Bitfield<25, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Power-enable register
|
|
||||||
*/
|
|
||||||
struct Pwren : Register<0x4> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clock-devider register
|
|
||||||
*/
|
|
||||||
struct Clkdiv : Register<0x8> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clock-enable register
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct Clkena : Register<0x10> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timeout register
|
|
||||||
*/
|
|
||||||
struct Tmout : Register<0x14> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Card-type register
|
|
||||||
*/
|
|
||||||
struct Ctype : Register<0x18, true> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Block-size register
|
|
||||||
*/
|
|
||||||
struct Blksize : Register<0x1c> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Byte-count register
|
|
||||||
*/
|
|
||||||
struct Bytcnt : Register<0x20> { };
|
|
||||||
/**
|
|
||||||
* Interrupt-mask register
|
|
||||||
*/
|
|
||||||
struct Intmask : Register<0x24> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Command-argument register
|
|
||||||
*/
|
|
||||||
struct Cmdarg : Register<0x28> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Command register
|
|
||||||
*/
|
|
||||||
struct Cmd : Register<0x2c>
|
|
||||||
{
|
|
||||||
struct Index : Bitfield<0, 6> { };
|
|
||||||
struct Rsp_type : Bitfield<6, 3>
|
|
||||||
{
|
|
||||||
enum Response { RESPONSE_NONE = 0,
|
|
||||||
RESPONSE_48_BIT = 1,
|
|
||||||
RESPONSE_48_BIT_WITH_BUSY = 5,
|
|
||||||
RESPONSE_136_BIT = 7,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
struct Data_expected : Bitfield<9, 1> { };
|
|
||||||
struct Write : Bitfield<10, 1> { };
|
|
||||||
struct Wait_prvdata_complete : Bitfield<13, 1> { };
|
|
||||||
struct Init_sequence : Bitfield<15, 1> { };
|
|
||||||
struct Update_clock_registers_only : Bitfield<21, 1> { };
|
|
||||||
struct Use_hold_reg : Bitfield<29, 1> { };
|
|
||||||
struct Start_cmd : Bitfield<31, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response bits 0..127
|
|
||||||
*/
|
|
||||||
struct Rsp0 : Register<0x30> { };
|
|
||||||
struct Rsp1 : Register<0x34> { };
|
|
||||||
struct Rsp2 : Register<0x38> { };
|
|
||||||
struct Rsp3 : Register<0x3c> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interrupt-status register
|
|
||||||
*/
|
|
||||||
struct Mintsts : Register<0x40> { };
|
|
||||||
struct Rintsts : Register<0x44, true>
|
|
||||||
{
|
|
||||||
struct Response_error : Bitfield<1, 1> { };
|
|
||||||
struct Data_transfer_over : Bitfield<3, 1> { };
|
|
||||||
struct Command_done : Bitfield<2, 1> { };
|
|
||||||
struct Data_crc_error : Bitfield<7, 1> { };
|
|
||||||
struct Response_timeout : Bitfield<8, 1> { };
|
|
||||||
struct Data_read_timeout : Bitfield<9, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status register
|
|
||||||
*/
|
|
||||||
struct Status : Register<0x48>
|
|
||||||
{
|
|
||||||
struct Data_busy : Bitfield<9, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fifo-threshold register
|
|
||||||
*/
|
|
||||||
struct Fifoth : Register<0x4c> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bus-mode register
|
|
||||||
*/
|
|
||||||
struct Bmod : Register<0x80, true>
|
|
||||||
{
|
|
||||||
struct Fixed_burst : Bitfield<1, 1> { };
|
|
||||||
struct Idmac_enable : Bitfield<7, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Poll demant register
|
|
||||||
*/
|
|
||||||
struct Pldmnd : Register<0x84> { };
|
|
||||||
|
|
||||||
struct Idsts : Register<0x8c> { };
|
|
||||||
struct Idinten : Register<0x90, true> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Descriptor list base-address register
|
|
||||||
*/
|
|
||||||
struct Dbaddr : Register<0x88> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clock selector
|
|
||||||
*/
|
|
||||||
struct Clksel : Register<0x9c> { };
|
|
||||||
|
|
||||||
struct Emmc_ddr_req : Register<0x10c, true> { };
|
|
||||||
|
|
||||||
typedef Genode::size_t size_t;
|
|
||||||
|
|
||||||
void powerup()
|
|
||||||
{
|
|
||||||
write<Pwren>(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool reset(Delayer &delayer)
|
|
||||||
{
|
|
||||||
/* set all three bits */
|
|
||||||
write<Ctrl::Reset>(0x7);
|
|
||||||
|
|
||||||
if (!wait_for<Ctrl::Reset>(0, delayer, 100, 1000)) {
|
|
||||||
Genode::error("Could not reset host contoller");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset_fifo(Delayer &delayer)
|
|
||||||
{
|
|
||||||
write<Ctrl::Reset>(0x2);
|
|
||||||
|
|
||||||
if (!wait_for<Ctrl::Reset>(0, delayer, 100, 1000))
|
|
||||||
Genode::error("Could not reset fifo");
|
|
||||||
}
|
|
||||||
|
|
||||||
void disable_irq()
|
|
||||||
{
|
|
||||||
write<Rintsts>(~0U);
|
|
||||||
write<Intmask>(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Bus_width {
|
|
||||||
BUS_WIDTH_1 = 0,
|
|
||||||
BUS_WIDTH_4 = 1,
|
|
||||||
BUS_WIDTH_8 = 1 << 16,
|
|
||||||
};
|
|
||||||
|
|
||||||
void bus_width(Bus_width bus_width)
|
|
||||||
{
|
|
||||||
write<Ctype>(bus_width);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool update_clock_registers(Delayer &delayer)
|
|
||||||
{
|
|
||||||
Cmd::access_t cmd = 0;
|
|
||||||
Cmd::Wait_prvdata_complete::set(cmd, 1);
|
|
||||||
Cmd::Update_clock_registers_only::set(cmd, 1);
|
|
||||||
Cmd::Start_cmd::set(cmd, 1);
|
|
||||||
|
|
||||||
write<Cmd>(cmd);
|
|
||||||
|
|
||||||
if (!wait_for<Cmd::Start_cmd>(0, delayer)) {
|
|
||||||
Genode::error("Update clock registers failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setup_bus(unsigned clock_div, Delayer &delayer)
|
|
||||||
{
|
|
||||||
/* set host clock divider */
|
|
||||||
write<Clkdiv>(clock_div);
|
|
||||||
|
|
||||||
if (!update_clock_registers(delayer))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* enable clock for card 1 */
|
|
||||||
write<Clkena>(0x1);
|
|
||||||
|
|
||||||
if (!update_clock_registers(delayer))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
delayer.usleep(10 * 1000);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct Sd_ack_handler {
|
|
||||||
virtual void handle_ack(Block::Packet_descriptor pkt, bool success) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct Exynos5_msh_controller : private Dwmmc, Sd_card::Host_controller
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
enum { BLOCK_SIZE = 512 };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DMA descriptpor
|
|
||||||
*/
|
|
||||||
struct Idmac_desc
|
|
||||||
{
|
|
||||||
enum Flags {
|
|
||||||
NONE = 0,
|
|
||||||
DIC = 1 << 1,
|
|
||||||
LD = 1 << 2,
|
|
||||||
FS = 1 << 3,
|
|
||||||
CH = 1 << 4,
|
|
||||||
ER = 1 << 5,
|
|
||||||
OWN = 1 << 31,
|
|
||||||
};
|
|
||||||
|
|
||||||
unsigned flags;
|
|
||||||
unsigned bytes;
|
|
||||||
unsigned addr;
|
|
||||||
unsigned next;
|
|
||||||
|
|
||||||
size_t set(size_t block_count, Genode::addr_t phys_addr, Flags flag)
|
|
||||||
{
|
|
||||||
enum { MAX_BLOCKS = 8 };
|
|
||||||
flags = OWN | flag | (block_count <= MAX_BLOCKS ? LD : (CH | DIC));
|
|
||||||
bytes = ((block_count < MAX_BLOCKS) ? block_count : MAX_BLOCKS) * BLOCK_SIZE;
|
|
||||||
addr = phys_addr;
|
|
||||||
|
|
||||||
return block_count < MAX_BLOCKS ? 0 : block_count - MAX_BLOCKS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dump()
|
|
||||||
{
|
|
||||||
Genode::log("this: ", this, " "
|
|
||||||
"flags: ", Genode::Hex(flags), " "
|
|
||||||
"bytes: ", bytes, " "
|
|
||||||
"addr: ", Genode::Hex(addr), " "
|
|
||||||
"next:", Genode::Hex(next));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* DMA descriptors
|
|
||||||
*/
|
|
||||||
enum { IDMAC_DESC_MAX_ENTRIES = 1024 /* can be up to 65536 */};
|
|
||||||
Genode::Attached_ram_dataspace _idmac_desc_ds;
|
|
||||||
Idmac_desc * const _idmac_desc;
|
|
||||||
Genode::addr_t const _idmac_desc_phys;
|
|
||||||
|
|
||||||
Delayer &_delayer;
|
|
||||||
Sd_card::Card_info _card_info;
|
|
||||||
|
|
||||||
struct Block_transfer
|
|
||||||
{
|
|
||||||
Block::Packet_descriptor packet;
|
|
||||||
bool pending = false;
|
|
||||||
|
|
||||||
} _block_transfer;
|
|
||||||
|
|
||||||
Sd_ack_handler &_ack_handler;
|
|
||||||
Genode::Signal_handler<Exynos5_msh_controller> _irq_handler;
|
|
||||||
Genode::Irq_connection _irq;
|
|
||||||
|
|
||||||
Sd_card::Card_info _init()
|
|
||||||
{
|
|
||||||
using namespace Sd_card;
|
|
||||||
|
|
||||||
powerup();
|
|
||||||
|
|
||||||
if (!reset(_delayer))
|
|
||||||
throw Detection_failed();
|
|
||||||
|
|
||||||
write<Emmc_ddr_req>(0x1);
|
|
||||||
|
|
||||||
disable_irq();
|
|
||||||
|
|
||||||
write<Tmout>(~0U);
|
|
||||||
write<Idinten>(0);
|
|
||||||
write<Bmod>(1);
|
|
||||||
write<Bytcnt>(0);
|
|
||||||
|
|
||||||
write<Fifoth>(0x203f0040);
|
|
||||||
|
|
||||||
/* set to one bit transfer Bit */
|
|
||||||
if (!setup_bus(CLK_DIV_400Khz, _delayer))
|
|
||||||
throw Detection_failed();
|
|
||||||
|
|
||||||
bus_width(BUS_WIDTH_1);
|
|
||||||
|
|
||||||
if (!issue_command(Go_idle_state())) {
|
|
||||||
Genode::warning("Go_idle_state command failed");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
_delayer.usleep(2000);
|
|
||||||
|
|
||||||
if (!issue_command(Send_if_cond())) {
|
|
||||||
Genode::warning("Send_if_cond command failed");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if this succeeds it is an SD card */
|
|
||||||
if ((read<Rsp0>() & 0xff) == 0xaa)
|
|
||||||
Genode::log("Found SD card");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We need to issue the same Mmc_send_op_cond command multiple
|
|
||||||
* times. The first time, we receive the status information. On
|
|
||||||
* subsequent attempts, the response tells us that the card is
|
|
||||||
* busy. Usually, the command is issued twice. We give up if the
|
|
||||||
* card is not reaching busy state after one second.
|
|
||||||
*/
|
|
||||||
unsigned i = 1000;
|
|
||||||
unsigned voltages = 0x300080;
|
|
||||||
unsigned arg = 0;
|
|
||||||
for (; i > 0; --i) {
|
|
||||||
if (!issue_command(Mmc_send_op_cond(arg, true))) {
|
|
||||||
Genode::warning("Sd_send_op_cond command failed");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
arg = read<Rsp0>();
|
|
||||||
arg = (voltages & (arg & 0x007FFF80)) | (arg & 0x60000000);
|
|
||||||
|
|
||||||
_delayer.usleep(1000);
|
|
||||||
|
|
||||||
if (Ocr::Busy::get(read<Rsp0>()))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
Genode::error("Send_op_cond timed out, could no power-on SD/MMC card");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
Card_info card_info = _detect_mmc();
|
|
||||||
|
|
||||||
/* switch frequency to high speed */
|
|
||||||
enum { EXT_CSD_HS_TIMING = 185 };
|
|
||||||
if (!issue_command(Mmc_switch(EXT_CSD_HS_TIMING, 1))) {
|
|
||||||
Genode::error("Error setting high speed frequency");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
enum { EXT_CSD_BUS_WIDTH = 183 };
|
|
||||||
/* set card to 8 bit */
|
|
||||||
if (!issue_command(Mmc_switch(EXT_CSD_BUS_WIDTH, 2))) {
|
|
||||||
Genode::error("Error setting card bus width");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
bus_width(BUS_WIDTH_8);
|
|
||||||
|
|
||||||
/* set to eight bit transfer Bit */
|
|
||||||
if (!setup_bus(CLK_DIV_52Mhz, _delayer)) {
|
|
||||||
Genode::error("Error setting bus to high speed");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Enable Interrupts data read timeout | data transfer done | response
|
|
||||||
* error
|
|
||||||
*/
|
|
||||||
write<Intmask>(0x28a);
|
|
||||||
write<Ctrl::Global_interrupt>(1);
|
|
||||||
return card_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _setup_idmac_descriptor_table(size_t block_count,
|
|
||||||
Genode::addr_t phys_addr)
|
|
||||||
{
|
|
||||||
size_t const max_idmac_block_count = IDMAC_DESC_MAX_ENTRIES * 8;
|
|
||||||
|
|
||||||
if (block_count > max_idmac_block_count) {
|
|
||||||
Genode::error("Block request too large");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset_fifo(_delayer);
|
|
||||||
|
|
||||||
Idmac_desc::Flags flags = Idmac_desc::FS;
|
|
||||||
size_t b = block_count;
|
|
||||||
int index = 0;
|
|
||||||
for (index = 0; b;
|
|
||||||
index++, phys_addr += 0x1000, flags = Idmac_desc::NONE) {
|
|
||||||
b = _idmac_desc[index].set(b, phys_addr, flags);
|
|
||||||
_idmac_desc[index].next = _idmac_desc_phys + ((index + 1) * sizeof(Idmac_desc));
|
|
||||||
}
|
|
||||||
|
|
||||||
_idmac_desc[index].next = (unsigned)_idmac_desc;
|
|
||||||
_idmac_desc[index].flags |= Idmac_desc::ER;
|
|
||||||
write<Dbaddr>(_idmac_desc_phys);
|
|
||||||
|
|
||||||
write<Ctrl::Dma_enable>(1);
|
|
||||||
write<Ctrl::Use_internal_dmac>(1);
|
|
||||||
|
|
||||||
write<Bmod::Fixed_burst>(1);
|
|
||||||
write<Bmod::Idmac_enable>(1);
|
|
||||||
|
|
||||||
write<Blksize>(BLOCK_SIZE);
|
|
||||||
write<Bytcnt>(BLOCK_SIZE * block_count);
|
|
||||||
|
|
||||||
write<Pldmnd>(1);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handle_irq()
|
|
||||||
{
|
|
||||||
_irq.ack_irq();
|
|
||||||
|
|
||||||
if (!_block_transfer.pending) {
|
|
||||||
return; }
|
|
||||||
|
|
||||||
bool success = false;
|
|
||||||
if (read<Rintsts::Response_error>()) {
|
|
||||||
Genode::error("Response error");
|
|
||||||
}
|
|
||||||
if (read<Rintsts::Data_read_timeout>()) {
|
|
||||||
Genode::error("Data read timeout");
|
|
||||||
}
|
|
||||||
if (read<Rintsts::Data_crc_error>()) {
|
|
||||||
Genode::error("CRC error");
|
|
||||||
}
|
|
||||||
if (read<Rintsts::Data_transfer_over>()) {
|
|
||||||
write<Rintsts>(~0U);
|
|
||||||
if (!_issue_command(Sd_card::Stop_transmission())) {
|
|
||||||
Genode::error("unable to stop transmission");
|
|
||||||
} else {
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_block_transfer.pending = false;
|
|
||||||
_ack_handler.handle_ack(_block_transfer.packet, success);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
enum { IRQ_NUMBER = Genode::Board_base::SDMMC0_IRQ };
|
|
||||||
|
|
||||||
Exynos5_msh_controller(Server::Entrypoint &ep,
|
|
||||||
Genode::addr_t const mmio_base, Delayer &delayer,
|
|
||||||
Sd_ack_handler &ack_handler,
|
|
||||||
bool use_dma)
|
|
||||||
: Dwmmc(mmio_base),
|
|
||||||
_idmac_desc_ds(Genode::env()->ram_session(),
|
|
||||||
IDMAC_DESC_MAX_ENTRIES*sizeof(Idmac_desc),
|
|
||||||
Genode::UNCACHED),
|
|
||||||
_idmac_desc(_idmac_desc_ds.local_addr<Idmac_desc>()),
|
|
||||||
_idmac_desc_phys(Genode::Dataspace_client(_idmac_desc_ds.cap()).phys_addr()),
|
|
||||||
_delayer(delayer), _card_info(_init()),
|
|
||||||
_ack_handler(ack_handler),
|
|
||||||
_irq_handler(ep, *this, &Exynos5_msh_controller::_handle_irq),
|
|
||||||
_irq(IRQ_NUMBER)
|
|
||||||
{
|
|
||||||
_irq.sigh(_irq_handler);
|
|
||||||
_irq.ack_irq();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _issue_command(Sd_card::Command_base const &command)
|
|
||||||
{
|
|
||||||
if (verbose)
|
|
||||||
Genode::log("-> ", command);
|
|
||||||
|
|
||||||
if (!wait_for<Status::Data_busy>(0, _delayer, 10000, 100)) {
|
|
||||||
Genode::error("wait for State::Data_busy timed out ",
|
|
||||||
Genode::Hex(read<Status>()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
write<Rintsts>(~0UL);
|
|
||||||
|
|
||||||
/* write command argument */
|
|
||||||
write<Cmdarg>(command.arg);
|
|
||||||
|
|
||||||
Cmd::access_t cmd = 0;
|
|
||||||
Cmd::Index::set(cmd, command.index);
|
|
||||||
|
|
||||||
if (command.transfer != Sd_card::TRANSFER_NONE) {
|
|
||||||
/* set data-direction bit depending on the command */
|
|
||||||
bool const write = command.transfer == Sd_card::TRANSFER_WRITE;
|
|
||||||
Cmd::Data_expected::set(cmd, 1);
|
|
||||||
Cmd::Write::set(cmd, write ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Cmd::access_t rsp_type = 0;
|
|
||||||
switch (command.rsp_type) {
|
|
||||||
case Sd_card::RESPONSE_NONE: rsp_type = Cmd::Rsp_type::RESPONSE_NONE; break;
|
|
||||||
case Sd_card::RESPONSE_136_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_136_BIT; break;
|
|
||||||
case Sd_card::RESPONSE_48_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_48_BIT; break;
|
|
||||||
case Sd_card::RESPONSE_48_BIT_WITH_BUSY: rsp_type = Cmd::Rsp_type::RESPONSE_48_BIT_WITH_BUSY; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Cmd::Rsp_type::set(cmd, rsp_type);
|
|
||||||
Cmd::Start_cmd::set(cmd, 1);
|
|
||||||
Cmd::Use_hold_reg::set(cmd ,1);
|
|
||||||
Cmd::Wait_prvdata_complete::set(cmd, 1);
|
|
||||||
|
|
||||||
if (command.index == 0)
|
|
||||||
Cmd::Init_sequence::set(cmd, 1);
|
|
||||||
|
|
||||||
/* issue command */
|
|
||||||
write<Cmd>(cmd);
|
|
||||||
|
|
||||||
if (!wait_for<Rintsts::Command_done>(1, _delayer, 10000, 100)) {
|
|
||||||
Genode::error("command failed "
|
|
||||||
"Rintst: ", read<Rintsts>(), " "
|
|
||||||
"Mintst: ", read<Mintsts>(), " "
|
|
||||||
"Status: ", read<Status>());
|
|
||||||
|
|
||||||
if (read<Rintsts::Response_timeout>())
|
|
||||||
Genode::warning("timeout");
|
|
||||||
|
|
||||||
if (read<Rintsts::Response_error>())
|
|
||||||
Genode::warning("repsonse error");
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* acknowledge interrupt */
|
|
||||||
write<Rintsts::Command_done>(1);
|
|
||||||
|
|
||||||
_delayer.usleep(100);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sd_card::Cid _read_cid()
|
|
||||||
{
|
|
||||||
Sd_card::Cid cid;
|
|
||||||
cid.raw_0 = read<Rsp0>();
|
|
||||||
cid.raw_1 = read<Rsp1>();
|
|
||||||
cid.raw_2 = read<Rsp2>();
|
|
||||||
cid.raw_3 = read<Rsp3>();
|
|
||||||
return cid;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sd_card::Csd _read_csd()
|
|
||||||
{
|
|
||||||
Sd_card::Csd csd;
|
|
||||||
csd.csd0 = read<Rsp0>();
|
|
||||||
csd.csd1 = read<Rsp1>();
|
|
||||||
csd.csd2 = read<Rsp2>();
|
|
||||||
csd.csd3 = read<Rsp3>();
|
|
||||||
return csd;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned _read_rca() { return 0; }
|
|
||||||
|
|
||||||
size_t _read_ext_csd()
|
|
||||||
{
|
|
||||||
using namespace Genode;
|
|
||||||
Attached_ram_dataspace ds(env()->ram_session(), 0x1000, UNCACHED);
|
|
||||||
|
|
||||||
addr_t phys = Genode::Dataspace_client(ds.cap()).phys_addr();
|
|
||||||
_setup_idmac_descriptor_table(1, phys);
|
|
||||||
|
|
||||||
if (!issue_command(Sd_card::Mmc_send_ext_csd()))
|
|
||||||
throw Detection_failed();
|
|
||||||
|
|
||||||
if (!wait_for<Rintsts::Data_transfer_over>(1, _delayer)) {
|
|
||||||
Genode::error("cannot retrieve extented CSD");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
/* clear IRQ */
|
|
||||||
write<Rintsts::Data_transfer_over>(1);
|
|
||||||
|
|
||||||
/* contruct extented CSD */
|
|
||||||
Sd_card::Ext_csd csd((addr_t)ds.local_addr<addr_t>());
|
|
||||||
|
|
||||||
/* read revision */
|
|
||||||
if (csd.read<Sd_card::Ext_csd::Revision>() < 2) {
|
|
||||||
Genode::error("extented CSD revision is < 2");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* return sector count */
|
|
||||||
uint64_t capacity = csd.read<Sd_card::Ext_csd::Sector_count>() * BLOCK_SIZE;
|
|
||||||
|
|
||||||
/* to MB */
|
|
||||||
return capacity / (1024 * 1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
Sd_card::Card_info card_info() const
|
|
||||||
{
|
|
||||||
return _card_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_blocks_dma(Block::sector_t block_number, size_t block_count,
|
|
||||||
Genode::addr_t buffer_phys, Block::Packet_descriptor pkt)
|
|
||||||
{
|
|
||||||
if (_block_transfer.pending) {
|
|
||||||
throw Block::Driver::Request_congestion(); }
|
|
||||||
|
|
||||||
if (!_setup_idmac_descriptor_table(block_count, buffer_phys))
|
|
||||||
throw Block::Driver::Io_error();
|
|
||||||
|
|
||||||
_block_transfer.packet = pkt;
|
|
||||||
_block_transfer.pending = true;
|
|
||||||
|
|
||||||
if (!_issue_command(Sd_card::Read_multiple_block(block_number))) {
|
|
||||||
Genode::error("Read_multiple_block failed, Status: ",
|
|
||||||
Genode::Hex(read<Status>()));
|
|
||||||
throw Block::Driver::Io_error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_blocks_dma(Block::sector_t block_number, size_t block_count,
|
|
||||||
Genode::addr_t buffer_phys, Block::Packet_descriptor pkt)
|
|
||||||
{
|
|
||||||
if (_block_transfer.pending) {
|
|
||||||
throw Block::Driver::Request_congestion(); }
|
|
||||||
|
|
||||||
if (!_setup_idmac_descriptor_table(block_count, buffer_phys))
|
|
||||||
throw Block::Driver::Io_error();
|
|
||||||
|
|
||||||
_block_transfer.packet = pkt;
|
|
||||||
_block_transfer.pending = true;
|
|
||||||
|
|
||||||
if (!_issue_command(Sd_card::Write_multiple_block(block_number))) {
|
|
||||||
Genode::error("Read_multiple_block failed, Status: ",
|
|
||||||
Genode::Hex(read<Status>()));
|
|
||||||
throw Block::Driver::Io_error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* _DRIVERS__SD_CARD__SPEC__EXYNOS5__DWMMC_H_ */
|
|
@ -12,13 +12,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* local includes */
|
/* local includes */
|
||||||
#include <sdhc.h>
|
#include <driver.h>
|
||||||
|
|
||||||
using namespace Sd_card;
|
using namespace Sd_card;
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
|
|
||||||
|
|
||||||
int Sdhc::_wait_for_card_ready_mbw()
|
int Driver::_wait_for_card_ready_mbw()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Poll card status
|
* Poll card status
|
||||||
@ -69,7 +69,7 @@ int Sdhc::_wait_for_card_ready_mbw()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Sdhc::_stop_transmission()
|
int Driver::_stop_transmission()
|
||||||
{
|
{
|
||||||
/* write argument register */
|
/* write argument register */
|
||||||
Mmio::write<Cmdarg>(0);
|
Mmio::write<Cmdarg>(0);
|
||||||
@ -90,7 +90,7 @@ int Sdhc::_stop_transmission()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_handle_irq()
|
void Driver::_handle_irq()
|
||||||
{
|
{
|
||||||
_irq.ack_irq();
|
_irq.ack_irq();
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ void Sdhc::_handle_irq()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Sdhc::_wait_for_cmd_complete()
|
int Driver::_wait_for_cmd_complete()
|
||||||
{
|
{
|
||||||
if (!wait_for<Irqstat::Cc>(1, _delayer, 200, 5000)) {
|
if (!wait_for<Irqstat::Cc>(1, _delayer, 200, 5000)) {
|
||||||
error("command timed out");
|
error("command timed out");
|
||||||
@ -136,7 +136,7 @@ int Sdhc::_wait_for_cmd_complete()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Sdhc::_issue_command(Command_base const & command)
|
bool Driver::_issue_command(Command_base const & command)
|
||||||
{
|
{
|
||||||
/* get command characteristics */
|
/* get command characteristics */
|
||||||
bool const transfer = command.transfer != TRANSFER_NONE;
|
bool const transfer = command.transfer != TRANSFER_NONE;
|
||||||
@ -178,7 +178,7 @@ bool Sdhc::_issue_command(Command_base const & command)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Cid Sdhc::_read_cid()
|
Cid Driver::_read_cid()
|
||||||
{
|
{
|
||||||
Cid cid;
|
Cid cid;
|
||||||
cid.raw_0 = Mmio::read<Rsp136_0>();
|
cid.raw_0 = Mmio::read<Rsp136_0>();
|
||||||
@ -189,7 +189,7 @@ Cid Sdhc::_read_cid()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Csd Sdhc::_read_csd()
|
Csd Driver::_read_csd()
|
||||||
{
|
{
|
||||||
Csd csd;
|
Csd csd;
|
||||||
csd.csd0 = Mmio::read<Rsp136_0>();
|
csd.csd0 = Mmio::read<Rsp136_0>();
|
||||||
@ -200,76 +200,65 @@ Csd Sdhc::_read_csd()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned Sdhc::_read_rca()
|
unsigned Driver::_read_rca()
|
||||||
{
|
{
|
||||||
Cmdrsp0::access_t const rsp0 = Mmio::read<Cmdrsp0>();
|
Cmdrsp0::access_t const rsp0 = Mmio::read<Cmdrsp0>();
|
||||||
return Send_relative_addr::Response::Rca::get(rsp0);
|
return Send_relative_addr::Response::Rca::get(rsp0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Sdhc::read_blocks(size_t, size_t, char *)
|
void Driver::read_dma(Block::sector_t blk_nr,
|
||||||
{
|
|
||||||
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");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool Sdhc::read_blocks_dma(Block::Packet_descriptor packet,
|
|
||||||
size_t blk_nr,
|
|
||||||
size_t blk_cnt,
|
size_t blk_cnt,
|
||||||
addr_t buf_phys)
|
addr_t buf_phys,
|
||||||
|
Block::Packet_descriptor &packet)
|
||||||
{
|
{
|
||||||
if (_prepare_dma_mb(packet, true, blk_cnt, buf_phys)) { return false; }
|
if (_prepare_dma_mb(packet, true, blk_cnt, buf_phys)) {
|
||||||
|
throw Io_error(); }
|
||||||
|
|
||||||
return issue_command(Read_multiple_block(blk_nr));
|
if (!issue_command(Read_multiple_block(blk_nr))) {
|
||||||
|
throw Io_error(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Sdhc::write_blocks_dma(Block::Packet_descriptor packet,
|
void Driver::write_dma(Block::sector_t blk_nr,
|
||||||
size_t blk_nr,
|
|
||||||
size_t blk_cnt,
|
size_t blk_cnt,
|
||||||
addr_t buf_phys)
|
addr_t buf_phys,
|
||||||
|
Block::Packet_descriptor &packet)
|
||||||
{
|
{
|
||||||
if (_prepare_dma_mb(packet, false, blk_cnt, buf_phys)) { return false; }
|
if (_prepare_dma_mb(packet, false, blk_cnt, buf_phys)) {
|
||||||
|
throw Io_error(); }
|
||||||
|
|
||||||
return issue_command(Write_multiple_block(blk_nr));
|
if (!issue_command(Write_multiple_block(blk_nr))) {
|
||||||
|
throw Io_error(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Sdhc::Sdhc(Env &env)
|
Driver::Driver(Env &env)
|
||||||
:
|
:
|
||||||
Attached_mmio(Board_base::SDHC_MMIO_BASE, Board_base::SDHC_MMIO_SIZE),
|
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),
|
_env(env)
|
||||||
_card_info(_init()), _use_dma(true)
|
|
||||||
{
|
{
|
||||||
log("SD card detected");
|
log("SD card detected");
|
||||||
log("capacity: ", card_info().capacity_mb(), " MiB");
|
log("capacity: ", card_info().capacity_mb(), " MiB");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Sdhc::_prepare_dma_mb(Block::Packet_descriptor packet,
|
int Driver::_prepare_dma_mb(Block::Packet_descriptor packet,
|
||||||
bool reading,
|
bool reading,
|
||||||
size_t blk_cnt,
|
size_t blk_cnt,
|
||||||
addr_t buf_phys)
|
addr_t buf_phys)
|
||||||
{
|
{
|
||||||
if (_block_transfer.pending) {
|
if (_block_transfer.pending) {
|
||||||
throw Block::Driver::Request_congestion(); }
|
throw Request_congestion(); }
|
||||||
|
|
||||||
|
|
||||||
/* write ADMA2 table to DMA */
|
/* write ADMA2 table to DMA */
|
||||||
size_t const req_size = blk_cnt * BLOCK_SIZE;
|
size_t const req_size = blk_cnt * block_size();
|
||||||
if (_adma2_table.setup_request(req_size, buf_phys)) { return -1; }
|
if (_adma2_table.setup_request(req_size, buf_phys)) { return -1; }
|
||||||
|
|
||||||
/* configure DMA at host */
|
/* configure DMA at host */
|
||||||
Mmio::write<Adsaddr>(_adma2_table.base_phys());
|
Mmio::write<Adsaddr>(_adma2_table.base_phys());
|
||||||
Mmio::write<Blkattr::Blksize>(BLOCK_SIZE);
|
Mmio::write<Blkattr::Blksize>(block_size());
|
||||||
Mmio::write<Blkattr::Blkcnt>(blk_cnt);
|
Mmio::write<Blkattr::Blkcnt>(blk_cnt);
|
||||||
|
|
||||||
_block_transfer.read = reading;
|
_block_transfer.read = reading;
|
||||||
@ -279,7 +268,7 @@ int Sdhc::_prepare_dma_mb(Block::Packet_descriptor packet,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Sdhc::_wait_for_cmd_allowed()
|
int Driver::_wait_for_cmd_allowed()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* At least after multi-block writes on i.MX53 with the fix for the broken
|
* At least after multi-block writes on i.MX53 with the fix for the broken
|
||||||
@ -298,7 +287,7 @@ int Sdhc::_wait_for_cmd_allowed()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Card_info Sdhc::_init()
|
Card_info Driver::_init()
|
||||||
{
|
{
|
||||||
/* install IRQ signal */
|
/* install IRQ signal */
|
||||||
_irq.sigh(_irq_handler);
|
_irq.sigh(_irq_handler);
|
||||||
@ -413,7 +402,7 @@ Card_info Sdhc::_init()
|
|||||||
_delayer.usleep(10000);
|
_delayer.usleep(10000);
|
||||||
|
|
||||||
/* configure card to use given block size */
|
/* configure card to use given block size */
|
||||||
if (!issue_command(Set_blocklen(BLOCK_SIZE))) {
|
if (!issue_command(Set_blocklen(block_size()))) {
|
||||||
_detect_err("Set_blocklen command failed"); }
|
_detect_err("Set_blocklen command failed"); }
|
||||||
|
|
||||||
/* configure host buffer */
|
/* configure host buffer */
|
||||||
@ -432,14 +421,14 @@ Card_info Sdhc::_init()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_detect_err(char const * const err)
|
void Driver::_detect_err(char const * const err)
|
||||||
{
|
{
|
||||||
error(err);
|
error(err);
|
||||||
throw Detection_failed();
|
throw Detection_failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Sdhc::_reset(Delayer &delayer)
|
int Driver::_reset(Delayer &delayer)
|
||||||
{
|
{
|
||||||
/* start reset */
|
/* start reset */
|
||||||
Mmio::write<Sysctl::Rsta>(1);
|
Mmio::write<Sysctl::Rsta>(1);
|
||||||
@ -454,14 +443,14 @@ int Sdhc::_reset(Delayer &delayer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_disable_irqs()
|
void Driver::_disable_irqs()
|
||||||
{
|
{
|
||||||
Mmio::write<Irqstaten>(0);
|
Mmio::write<Irqstaten>(0);
|
||||||
Mmio::write<Irqsigen>(0);
|
Mmio::write<Irqsigen>(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_enable_irqs()
|
void Driver::_enable_irqs()
|
||||||
{
|
{
|
||||||
Irq::access_t irq = 0;
|
Irq::access_t irq = 0;
|
||||||
Irq::Cc::set(irq, 1);
|
Irq::Cc::set(irq, 1);
|
||||||
@ -481,7 +470,7 @@ void Sdhc::_enable_irqs()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_bus_width(Bus_width bus_width)
|
void Driver::_bus_width(Bus_width bus_width)
|
||||||
{
|
{
|
||||||
switch (bus_width) {
|
switch (bus_width) {
|
||||||
case BUS_WIDTH_1: Mmio::write<Proctl::Dtw>(Proctl::Dtw::_1BIT); break;
|
case BUS_WIDTH_1: Mmio::write<Proctl::Dtw>(Proctl::Dtw::_1BIT); break;
|
||||||
@ -490,7 +479,7 @@ void Sdhc::_bus_width(Bus_width bus_width)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_disable_clock()
|
void Driver::_disable_clock()
|
||||||
{
|
{
|
||||||
_disable_clock_preparation();
|
_disable_clock_preparation();
|
||||||
Sysctl::access_t sysctl = Mmio::read<Sysctl>();
|
Sysctl::access_t sysctl = Mmio::read<Sysctl>();
|
||||||
@ -503,7 +492,7 @@ void Sdhc::_disable_clock()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_enable_clock(Clock_divider divider)
|
void Driver::_enable_clock(Clock_divider divider)
|
||||||
{
|
{
|
||||||
Sysctl::access_t sysctl = Mmio::read<Sysctl>();
|
Sysctl::access_t sysctl = Mmio::read<Sysctl>();
|
||||||
Sysctl::Ipgen::set(sysctl, 1);
|
Sysctl::Ipgen::set(sysctl, 1);
|
||||||
@ -529,7 +518,7 @@ void Sdhc::_enable_clock(Clock_divider divider)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_clock(Clock clock)
|
void Driver::_clock(Clock clock)
|
||||||
{
|
{
|
||||||
wait_for<Prsstat::Sdstb>(1, _delayer);
|
wait_for<Prsstat::Sdstb>(1, _delayer);
|
||||||
_disable_clock();
|
_disable_clock();
|
@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* \brief Implementation of the Block::Driver interface
|
* \brief Secured Digital Host Controller
|
||||||
* \author Martin Stein
|
* \author Martin Stein
|
||||||
* \date 2015-02-04
|
* \date 2015-02-05
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2012-2016 Genode Labs GmbH
|
* Copyright (C) 2015-2016 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
@ -14,7 +14,285 @@
|
|||||||
#ifndef _DRIVER_H_
|
#ifndef _DRIVER_H_
|
||||||
#define _DRIVER_H_
|
#define _DRIVER_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <drivers/board_base.h>
|
||||||
|
#include <timer_session/connection.h>
|
||||||
|
#include <irq_session/connection.h>
|
||||||
|
#include <os/attached_mmio.h>
|
||||||
|
|
||||||
/* local includes */
|
/* local includes */
|
||||||
#include <sdhc.h>
|
#include <driver_base.h>
|
||||||
|
#include <adma2.h>
|
||||||
|
|
||||||
|
namespace Sd_card { class Driver; }
|
||||||
|
|
||||||
|
|
||||||
|
class Sd_card::Driver : public Driver_base,
|
||||||
|
private Attached_mmio
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 };
|
||||||
|
|
||||||
|
enum Clock { CLOCK_INITIAL, CLOCK_OPERATIONAL };
|
||||||
|
|
||||||
|
enum Clock_divider { CLOCK_DIV_4, CLOCK_DIV_8, CLOCK_DIV_512 };
|
||||||
|
|
||||||
|
|
||||||
|
/********************
|
||||||
|
** MMIO structure **
|
||||||
|
********************/
|
||||||
|
|
||||||
|
struct Blkattr : Register<0x4, 32>
|
||||||
|
{
|
||||||
|
struct Blksize : Bitfield<0, 13> { };
|
||||||
|
struct Blkcnt : Bitfield<16, 16> { };
|
||||||
|
};
|
||||||
|
template <off_t OFFSET>
|
||||||
|
struct Cmdrsp_tpl : Register<OFFSET, 32>
|
||||||
|
{
|
||||||
|
struct Rsp136_8_24 : Register<OFFSET, 32>::template Bitfield<0, 24> { };
|
||||||
|
struct Rsp136_0_8 : Register<OFFSET, 32>::template Bitfield<24, 8> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Cmdarg : Register<0x8, 32> { };
|
||||||
|
struct Cmdrsp0 : Cmdrsp_tpl<0x10> { };
|
||||||
|
struct Cmdrsp1 : Cmdrsp_tpl<0x14> { };
|
||||||
|
struct Cmdrsp2 : Cmdrsp_tpl<0x18> { };
|
||||||
|
struct Cmdrsp3 : Cmdrsp_tpl<0x1c> { };
|
||||||
|
struct Rsp136_0 : Bitset_2<Cmdrsp3::Rsp136_0_8, Cmdrsp0::Rsp136_8_24> { };
|
||||||
|
struct Rsp136_1 : Bitset_2<Cmdrsp0::Rsp136_0_8, Cmdrsp1::Rsp136_8_24> { };
|
||||||
|
struct Rsp136_2 : Bitset_2<Cmdrsp1::Rsp136_0_8, Cmdrsp2::Rsp136_8_24> { };
|
||||||
|
struct Rsp136_3 : Bitset_2<Cmdrsp2::Rsp136_0_8, Cmdrsp3::Rsp136_8_24> { };
|
||||||
|
|
||||||
|
template <off_t OFFSET>
|
||||||
|
struct Xfertyp_base : Register<OFFSET, 32>
|
||||||
|
{
|
||||||
|
struct Dmaen : Register<OFFSET, 32>::template Bitfield<0, 1> { };
|
||||||
|
struct Bcen : Register<OFFSET, 32>::template Bitfield<1, 1> { };
|
||||||
|
struct Ac12en : Register<OFFSET, 32>::template Bitfield<2, 1> { };
|
||||||
|
struct Dtdsel : Register<OFFSET, 32>::template Bitfield<4, 1>
|
||||||
|
{
|
||||||
|
enum { WRITE = 0, READ = 1, };
|
||||||
|
};
|
||||||
|
struct Msbsel : Register<OFFSET, 32>::template Bitfield<5, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Mixctrl : Xfertyp_base<0x48>
|
||||||
|
{
|
||||||
|
struct Ddren : Bitfield<3, 1> { };
|
||||||
|
struct Nibblepos : Bitfield<6, 1> { };
|
||||||
|
struct Ac23en : Bitfield<7, 1> { };
|
||||||
|
struct Always_ones : Bitfield<31, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Xfertyp : Xfertyp_base<0xc>
|
||||||
|
{
|
||||||
|
struct Rsptyp : Bitfield<16, 2>
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
_0BIT = 0,
|
||||||
|
_136BIT = 1,
|
||||||
|
_48BIT = 2,
|
||||||
|
_48BIT_BUSY = 3,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
struct Cccen : Bitfield<19, 1> { };
|
||||||
|
struct Cicen : Bitfield<20, 1> { };
|
||||||
|
struct Dpsel : Bitfield<21, 1> { };
|
||||||
|
struct Cmdtyp : Bitfield<22, 2>
|
||||||
|
{
|
||||||
|
enum { ABORT_CMD12 = 3 };
|
||||||
|
};
|
||||||
|
struct Cmdinx : Bitfield<24, 6> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Prsstat : Register<0x24, 32>
|
||||||
|
{
|
||||||
|
struct Cihb : Bitfield<0, 1> { };
|
||||||
|
struct Cdihb : Bitfield<1, 1> { };
|
||||||
|
struct Dla : Bitfield<2, 1> { };
|
||||||
|
struct Sdstb : Bitfield<3, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Proctl : Register<0x28, 32>
|
||||||
|
{
|
||||||
|
struct Dtw : Bitfield<1, 2>
|
||||||
|
{
|
||||||
|
enum { _1BIT = 0, _4BIT = 1 };
|
||||||
|
};
|
||||||
|
struct Dmas : Bitfield<8, 2> { enum { ADMA2 = 2 }; };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Sysctl : Register<0x2c, 32>
|
||||||
|
{
|
||||||
|
struct Ipgen : Bitfield<0, 1> { };
|
||||||
|
struct Hcken : Bitfield<1, 1> { };
|
||||||
|
struct Peren : Bitfield<2, 1> { };
|
||||||
|
struct Dvs : Bitfield<4, 4>
|
||||||
|
{
|
||||||
|
enum { DIV1 = 0x0, DIV4 = 0x3, DIV16 = 0xf, };
|
||||||
|
};
|
||||||
|
struct Sdclkfs : Bitfield<8, 8>
|
||||||
|
{
|
||||||
|
enum { DIV1 = 0x00, DIV2 = 0x01, DIV32 = 0x10, };
|
||||||
|
};
|
||||||
|
struct Dtocv : Bitfield<16, 4>
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
SDCLK_TIMES_2_POW_28 = 0xf,
|
||||||
|
SDCLK_TIMES_2_POW_27 = 0xe,
|
||||||
|
SDCLK_TIMES_2_POW_13 = 0x0,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
struct Ipp_rst_n : Bitfield<23, 1> { };
|
||||||
|
struct Rsta : Bitfield<24, 1> { };
|
||||||
|
struct Rstc : Bitfield<25, 1> { };
|
||||||
|
struct Rstd : Bitfield<26, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
template <off_t OFFSET>
|
||||||
|
struct Irq_tpl : Register<OFFSET, 32>
|
||||||
|
{
|
||||||
|
struct Cc : Register<OFFSET, 32>::template Bitfield<0, 1> { };
|
||||||
|
struct Tc : Register<OFFSET, 32>::template Bitfield<1, 1> { };
|
||||||
|
struct Dint : Register<OFFSET, 32>::template Bitfield<3, 1> { };
|
||||||
|
struct Ctoe : Register<OFFSET, 32>::template Bitfield<16, 1> { };
|
||||||
|
struct Cce : Register<OFFSET, 32>::template Bitfield<17, 1> { };
|
||||||
|
struct Cebe : Register<OFFSET, 32>::template Bitfield<18, 1> { };
|
||||||
|
struct Cie : Register<OFFSET, 32>::template Bitfield<19, 1> { };
|
||||||
|
struct Dtoe : Register<OFFSET, 32>::template Bitfield<20, 1> { };
|
||||||
|
struct Dce : Register<OFFSET, 32>::template Bitfield<21, 1> { };
|
||||||
|
struct Debe : Register<OFFSET, 32>::template Bitfield<22, 1> { };
|
||||||
|
struct Ac12e : Register<OFFSET, 32>::template Bitfield<24, 1> { };
|
||||||
|
struct Dmae : Register<OFFSET, 32>::template Bitfield<28, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Irq : Irq_tpl<0> { };
|
||||||
|
struct Irqstat : Irq_tpl<0x30> { };
|
||||||
|
struct Irqstaten : Irq_tpl<0x34> { };
|
||||||
|
struct Irqsigen : Irq_tpl<0x38> { };
|
||||||
|
struct Maxcurrent : Register<0x48, 32> { };
|
||||||
|
struct Adsaddr : Register<0x58, 32> { };
|
||||||
|
|
||||||
|
struct Hostver : Register<0xfc, 32>
|
||||||
|
{
|
||||||
|
struct Svn : Bitfield<0, 8> { };
|
||||||
|
struct Vvn : Bitfield<8, 8> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Wml : Register<0x44, 32>
|
||||||
|
{
|
||||||
|
struct Rd_wml : Bitfield<0, 8> { };
|
||||||
|
struct Rd_brst_len : Bitfield<8, 5> { };
|
||||||
|
struct Wr_wml : Bitfield<16, 8> { };
|
||||||
|
struct Wr_brst_len : Bitfield<24, 5> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Vendspec : Register<0xc0, 32>
|
||||||
|
{
|
||||||
|
struct Frc_sdclk_on : Bitfield<8, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/************************
|
||||||
|
** Utility structures **
|
||||||
|
************************/
|
||||||
|
|
||||||
|
struct Timer_delayer : Timer::Connection, Mmio::Delayer
|
||||||
|
{
|
||||||
|
void usleep(unsigned us) { Timer::Connection::usleep(us); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Block_transfer
|
||||||
|
{
|
||||||
|
Block::Packet_descriptor packet;
|
||||||
|
bool pending = false;
|
||||||
|
bool read;
|
||||||
|
};
|
||||||
|
|
||||||
|
Env &_env;
|
||||||
|
Block_transfer _block_transfer;
|
||||||
|
Timer_delayer _delayer;
|
||||||
|
Signal_handler<Driver> _irq_handler { _env.ep(), *this,
|
||||||
|
&Driver::_handle_irq };
|
||||||
|
Irq_connection _irq { Board_base::SDHC_IRQ };
|
||||||
|
Card_info _card_info { _init() };
|
||||||
|
Adma2::Table _adma2_table { _env.ram() };
|
||||||
|
|
||||||
|
static bool _supported_host_version(Hostver::access_t hostver);
|
||||||
|
static void _watermark_level(Wml::access_t &wml);
|
||||||
|
|
||||||
|
void _handle_irq();
|
||||||
|
void _detect_err(char const * const err);
|
||||||
|
void _disable_irqs();
|
||||||
|
void _enable_irqs();
|
||||||
|
void _bus_width(Bus_width bus_width);
|
||||||
|
void _disable_clock();
|
||||||
|
void _disable_clock_preparation();
|
||||||
|
void _enable_clock(Clock_divider divider);
|
||||||
|
void _enable_clock_finish();
|
||||||
|
void _clock(Clock clock);
|
||||||
|
void _clock_finish(Clock clock);
|
||||||
|
int _reset(Delayer & delayer);
|
||||||
|
void _reset_amendments();
|
||||||
|
int _wait_for_cmd_allowed();
|
||||||
|
int _wait_for_cmd_complete();
|
||||||
|
int _wait_for_card_ready_mbw();
|
||||||
|
int _stop_transmission();
|
||||||
|
void _stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp);
|
||||||
|
int _wait_for_cmd_complete_mb_finish(bool const reading);
|
||||||
|
|
||||||
|
int _prepare_dma_mb(Block::Packet_descriptor packet,
|
||||||
|
bool reading,
|
||||||
|
size_t blk_cnt,
|
||||||
|
addr_t buf_phys);
|
||||||
|
|
||||||
|
bool _issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
|
||||||
|
bool const transfer,
|
||||||
|
bool const multiblock,
|
||||||
|
bool const reading);
|
||||||
|
|
||||||
|
Card_info _init();
|
||||||
|
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
** Host_controller **
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
Cid _read_cid() override;
|
||||||
|
Csd _read_csd() override;
|
||||||
|
unsigned _read_rca() override;
|
||||||
|
bool _issue_command(Command_base const & cmd) override;
|
||||||
|
|
||||||
|
Card_info card_info() const override { return _card_info; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
using Block::Driver::read;
|
||||||
|
using Block::Driver::write;
|
||||||
|
|
||||||
|
Driver(Env &env);
|
||||||
|
|
||||||
|
|
||||||
|
/*******************
|
||||||
|
** Block::Driver **
|
||||||
|
*******************/
|
||||||
|
|
||||||
|
void read_dma(Block::sector_t block_number,
|
||||||
|
size_t block_count,
|
||||||
|
addr_t phys,
|
||||||
|
Block::Packet_descriptor &packet) override;
|
||||||
|
|
||||||
|
void write_dma(Block::sector_t block_number,
|
||||||
|
size_t block_count,
|
||||||
|
addr_t phys,
|
||||||
|
Block::Packet_descriptor &packet) override;
|
||||||
|
|
||||||
|
bool dma_enabled() override { return true; }
|
||||||
|
|
||||||
|
Ram_dataspace_capability alloc_dma_buffer(size_t size) override {
|
||||||
|
return _env.ram().alloc(size, UNCACHED); }
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _DRIVER_H_ */
|
#endif /* _DRIVER_H_ */
|
||||||
|
@ -1,338 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief Secured Digital Host Controller
|
|
||||||
* \author Martin Stein
|
|
||||||
* \date 2015-02-05
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2015 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU General Public License version 2.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SDHC_H_
|
|
||||||
#define _SDHC_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <base/log.h>
|
|
||||||
#include <os/attached_io_mem_dataspace.h>
|
|
||||||
#include <timer_session/connection.h>
|
|
||||||
#include <util/mmio.h>
|
|
||||||
#include <irq_session/connection.h>
|
|
||||||
#include <base/sleep.h>
|
|
||||||
#include <block/driver.h>
|
|
||||||
#include <block/component.h>
|
|
||||||
#include <drivers/board_base.h>
|
|
||||||
#include <os/attached_mmio.h>
|
|
||||||
|
|
||||||
/* local includes */
|
|
||||||
#include <sd_card.h>
|
|
||||||
#include <adma2.h>
|
|
||||||
|
|
||||||
namespace Genode { class Sdhc; }
|
|
||||||
|
|
||||||
|
|
||||||
class Genode::Sdhc : public Attached_mmio,
|
|
||||||
public Block::Driver,
|
|
||||||
public Sd_card::Host_controller
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
struct Blkattr : Register<0x4, 32>
|
|
||||||
{
|
|
||||||
struct Blksize : Bitfield<0, 13> { };
|
|
||||||
struct Blkcnt : Bitfield<16, 16> { };
|
|
||||||
};
|
|
||||||
template <off_t OFFSET>
|
|
||||||
struct Cmdrsp_tpl : Register<OFFSET, 32>
|
|
||||||
{
|
|
||||||
struct Rsp136_8_24 : Register<OFFSET, 32>::template Bitfield<0, 24> { };
|
|
||||||
struct Rsp136_0_8 : Register<OFFSET, 32>::template Bitfield<24, 8> { };
|
|
||||||
};
|
|
||||||
struct Cmdarg : Register<0x8, 32> { };
|
|
||||||
struct Cmdrsp0 : Cmdrsp_tpl<0x10> { };
|
|
||||||
struct Cmdrsp1 : Cmdrsp_tpl<0x14> { };
|
|
||||||
struct Cmdrsp2 : Cmdrsp_tpl<0x18> { };
|
|
||||||
struct Cmdrsp3 : Cmdrsp_tpl<0x1c> { };
|
|
||||||
struct Rsp136_0 : Bitset_2<Cmdrsp3::Rsp136_0_8, Cmdrsp0::Rsp136_8_24> { };
|
|
||||||
struct Rsp136_1 : Bitset_2<Cmdrsp0::Rsp136_0_8, Cmdrsp1::Rsp136_8_24> { };
|
|
||||||
struct Rsp136_2 : Bitset_2<Cmdrsp1::Rsp136_0_8, Cmdrsp2::Rsp136_8_24> { };
|
|
||||||
struct Rsp136_3 : Bitset_2<Cmdrsp2::Rsp136_0_8, Cmdrsp3::Rsp136_8_24> { };
|
|
||||||
|
|
||||||
template <off_t OFFSET>
|
|
||||||
struct Xfertyp_base : Register<OFFSET, 32>
|
|
||||||
{
|
|
||||||
struct Dmaen : Register<OFFSET, 32>::template Bitfield<0, 1> { };
|
|
||||||
struct Bcen : Register<OFFSET, 32>::template Bitfield<1, 1> { };
|
|
||||||
struct Ac12en : Register<OFFSET, 32>::template Bitfield<2, 1> { };
|
|
||||||
struct Dtdsel : Register<OFFSET, 32>::template Bitfield<4, 1>
|
|
||||||
{
|
|
||||||
enum { WRITE = 0, READ = 1, };
|
|
||||||
};
|
|
||||||
struct Msbsel : Register<OFFSET, 32>::template Bitfield<5, 1> { };
|
|
||||||
};
|
|
||||||
struct Mixctrl : Xfertyp_base<0x48>
|
|
||||||
{
|
|
||||||
struct Ddren : Bitfield<3, 1> { };
|
|
||||||
struct Nibblepos : Bitfield<6, 1> { };
|
|
||||||
struct Ac23en : Bitfield<7, 1> { };
|
|
||||||
struct Always_ones : Bitfield<31, 1> { };
|
|
||||||
};
|
|
||||||
struct Xfertyp : Xfertyp_base<0xc>
|
|
||||||
{
|
|
||||||
struct Rsptyp : Bitfield<16, 2>
|
|
||||||
{
|
|
||||||
enum {
|
|
||||||
_0BIT = 0,
|
|
||||||
_136BIT = 1,
|
|
||||||
_48BIT = 2,
|
|
||||||
_48BIT_BUSY = 3,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
struct Cccen : Bitfield<19, 1> { };
|
|
||||||
struct Cicen : Bitfield<20, 1> { };
|
|
||||||
struct Dpsel : Bitfield<21, 1> { };
|
|
||||||
struct Cmdtyp : Bitfield<22, 2>
|
|
||||||
{
|
|
||||||
enum { ABORT_CMD12 = 3 };
|
|
||||||
};
|
|
||||||
struct Cmdinx : Bitfield<24, 6> { };
|
|
||||||
};
|
|
||||||
struct Prsstat : Register<0x24, 32>
|
|
||||||
{
|
|
||||||
struct Cihb : Bitfield<0, 1> { };
|
|
||||||
struct Cdihb : Bitfield<1, 1> { };
|
|
||||||
struct Dla : Bitfield<2, 1> { };
|
|
||||||
struct Sdstb : Bitfield<3, 1> { };
|
|
||||||
};
|
|
||||||
struct Proctl : Register<0x28, 32>
|
|
||||||
{
|
|
||||||
struct Dtw : Bitfield<1, 2>
|
|
||||||
{
|
|
||||||
enum { _1BIT = 0, _4BIT = 1 };
|
|
||||||
};
|
|
||||||
struct Dmas : Bitfield<8, 2> { enum { ADMA2 = 2 }; };
|
|
||||||
};
|
|
||||||
struct Sysctl : Register<0x2c, 32>
|
|
||||||
{
|
|
||||||
struct Ipgen : Bitfield<0, 1> { };
|
|
||||||
struct Hcken : Bitfield<1, 1> { };
|
|
||||||
struct Peren : Bitfield<2, 1> { };
|
|
||||||
struct Dvs : Bitfield<4, 4>
|
|
||||||
{
|
|
||||||
enum { DIV1 = 0x0, DIV4 = 0x3, DIV16 = 0xf, };
|
|
||||||
};
|
|
||||||
struct Sdclkfs : Bitfield<8, 8>
|
|
||||||
{
|
|
||||||
enum { DIV1 = 0x00, DIV2 = 0x01, DIV32 = 0x10, };
|
|
||||||
};
|
|
||||||
struct Dtocv : Bitfield<16, 4>
|
|
||||||
{
|
|
||||||
enum {
|
|
||||||
SDCLK_TIMES_2_POW_28 = 0xf,
|
|
||||||
SDCLK_TIMES_2_POW_27 = 0xe,
|
|
||||||
SDCLK_TIMES_2_POW_13 = 0x0,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
struct Ipp_rst_n : Bitfield<23, 1> { };
|
|
||||||
struct Rsta : Bitfield<24, 1> { };
|
|
||||||
struct Rstc : Bitfield<25, 1> { };
|
|
||||||
struct Rstd : Bitfield<26, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
template <off_t OFFSET>
|
|
||||||
struct Irq_tpl : Register<OFFSET, 32>
|
|
||||||
{
|
|
||||||
struct Cc : Register<OFFSET, 32>::template Bitfield<0, 1> { };
|
|
||||||
struct Tc : Register<OFFSET, 32>::template Bitfield<1, 1> { };
|
|
||||||
struct Dint : Register<OFFSET, 32>::template Bitfield<3, 1> { };
|
|
||||||
struct Ctoe : Register<OFFSET, 32>::template Bitfield<16, 1> { };
|
|
||||||
struct Cce : Register<OFFSET, 32>::template Bitfield<17, 1> { };
|
|
||||||
struct Cebe : Register<OFFSET, 32>::template Bitfield<18, 1> { };
|
|
||||||
struct Cie : Register<OFFSET, 32>::template Bitfield<19, 1> { };
|
|
||||||
struct Dtoe : Register<OFFSET, 32>::template Bitfield<20, 1> { };
|
|
||||||
struct Dce : Register<OFFSET, 32>::template Bitfield<21, 1> { };
|
|
||||||
struct Debe : Register<OFFSET, 32>::template Bitfield<22, 1> { };
|
|
||||||
struct Ac12e : Register<OFFSET, 32>::template Bitfield<24, 1> { };
|
|
||||||
struct Dmae : Register<OFFSET, 32>::template Bitfield<28, 1> { };
|
|
||||||
};
|
|
||||||
struct Irq : Irq_tpl<0> { };
|
|
||||||
struct Irqstat : Irq_tpl<0x30> { };
|
|
||||||
struct Irqstaten : Irq_tpl<0x34> { };
|
|
||||||
struct Irqsigen : Irq_tpl<0x38> { };
|
|
||||||
struct Maxcurrent : Register<0x48, 32> { };
|
|
||||||
struct Adsaddr : Register<0x58, 32> { };
|
|
||||||
struct Hostver : Register<0xfc, 32>
|
|
||||||
{
|
|
||||||
struct Svn : Bitfield<0, 8> { };
|
|
||||||
struct Vvn : Bitfield<8, 8> { };
|
|
||||||
};
|
|
||||||
struct Wml : Register<0x44, 32>
|
|
||||||
{
|
|
||||||
struct Rd_wml : Bitfield<0, 8> { };
|
|
||||||
struct Rd_brst_len : Bitfield<8, 5> { };
|
|
||||||
struct Wr_wml : Bitfield<16, 8> { };
|
|
||||||
struct Wr_brst_len : Bitfield<24, 5> { };
|
|
||||||
};
|
|
||||||
struct Vendspec : Register<0xc0, 32>
|
|
||||||
{
|
|
||||||
struct Frc_sdclk_on : Bitfield<8, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
enum { BLOCK_SIZE = 512 };
|
|
||||||
|
|
||||||
enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 };
|
|
||||||
|
|
||||||
enum Clock { CLOCK_INITIAL, CLOCK_OPERATIONAL };
|
|
||||||
|
|
||||||
enum Clock_divider { CLOCK_DIV_4, CLOCK_DIV_8, CLOCK_DIV_512 };
|
|
||||||
|
|
||||||
struct Timer_delayer : Timer::Connection, Mmio::Delayer
|
|
||||||
{
|
|
||||||
void usleep(unsigned us) { Timer::Connection::usleep(us); }
|
|
||||||
|
|
||||||
} _delayer;
|
|
||||||
|
|
||||||
struct Block_transfer
|
|
||||||
{
|
|
||||||
Block::Packet_descriptor packet;
|
|
||||||
bool pending = false;
|
|
||||||
bool read;
|
|
||||||
|
|
||||||
} _block_transfer;
|
|
||||||
|
|
||||||
Signal_handler<Sdhc> _irq_handler;
|
|
||||||
Irq_connection _irq;
|
|
||||||
Sd_card::Card_info _card_info;
|
|
||||||
bool const _use_dma;
|
|
||||||
Adma2::Table _adma2_table;
|
|
||||||
|
|
||||||
static bool _supported_host_version(Hostver::access_t hostver);
|
|
||||||
static void _watermark_level(Wml::access_t &wml);
|
|
||||||
|
|
||||||
void _handle_irq();
|
|
||||||
void _detect_err(char const * const err);
|
|
||||||
void _disable_irqs();
|
|
||||||
void _enable_irqs();
|
|
||||||
void _bus_width(Bus_width bus_width);
|
|
||||||
void _disable_clock();
|
|
||||||
void _disable_clock_preparation();
|
|
||||||
void _enable_clock(Clock_divider divider);
|
|
||||||
void _enable_clock_finish();
|
|
||||||
void _clock(Clock clock);
|
|
||||||
void _clock_finish(Clock clock);
|
|
||||||
int _reset(Delayer & delayer);
|
|
||||||
void _reset_amendments();
|
|
||||||
int _wait_for_cmd_allowed();
|
|
||||||
int _wait_for_cmd_complete();
|
|
||||||
int _wait_for_card_ready_mbw();
|
|
||||||
int _stop_transmission();
|
|
||||||
void _stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp);
|
|
||||||
int _wait_for_cmd_complete_mb_finish(bool const reading);
|
|
||||||
int _prepare_dma_mb(Block::Packet_descriptor packet, bool reading, size_t blk_cnt, addr_t buf_phys);
|
|
||||||
bool _issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
|
|
||||||
bool const transfer,
|
|
||||||
bool const multiblock,
|
|
||||||
bool const reading);
|
|
||||||
|
|
||||||
Sd_card::Card_info _init();
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************
|
|
||||||
** Sd_card::Host_controller interface **
|
|
||||||
****************************************/
|
|
||||||
|
|
||||||
Sd_card::Cid _read_cid();
|
|
||||||
Sd_card::Csd _read_csd();
|
|
||||||
unsigned _read_rca();
|
|
||||||
bool _issue_command(Sd_card::Command_base const & command);
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
Sdhc(Env &env);
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************
|
|
||||||
** Sd_card::Host_controller interface **
|
|
||||||
****************************************/
|
|
||||||
|
|
||||||
bool read_blocks(size_t, size_t, char *);
|
|
||||||
bool write_blocks(size_t, size_t, char const *);
|
|
||||||
bool read_blocks_dma(Block::Packet_descriptor packet, size_t blk_nr, size_t blk_cnt, addr_t buf_phys);
|
|
||||||
bool write_blocks_dma(Block::Packet_descriptor packet, size_t blk_nr, size_t blk_cnt, addr_t buf_phys);
|
|
||||||
|
|
||||||
Sd_card::Card_info card_info() const { return _card_info; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************
|
|
||||||
** Block::Driver interface **
|
|
||||||
*****************************/
|
|
||||||
|
|
||||||
Genode::size_t block_size() { return 512; }
|
|
||||||
|
|
||||||
virtual Block::sector_t block_count()
|
|
||||||
{
|
|
||||||
return card_info().capacity_mb() * 1024 * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
Block::Session::Operations ops()
|
|
||||||
{
|
|
||||||
Block::Session::Operations o;
|
|
||||||
o.set_operation(Block::Packet_descriptor::READ);
|
|
||||||
o.set_operation(Block::Packet_descriptor::WRITE);
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
void read(Block::sector_t block_number,
|
|
||||||
Genode::size_t block_count,
|
|
||||||
char *out_buffer,
|
|
||||||
Block::Packet_descriptor &packet)
|
|
||||||
{
|
|
||||||
// if (!read_blocks(block_number, block_count, out_buffer))
|
|
||||||
throw Io_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(Block::sector_t block_number,
|
|
||||||
Genode::size_t block_count,
|
|
||||||
char const *buffer,
|
|
||||||
Block::Packet_descriptor &packet)
|
|
||||||
{
|
|
||||||
// if (!write_blocks(block_number, block_count, buffer))
|
|
||||||
throw Io_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_dma(Block::sector_t block_number,
|
|
||||||
Genode::size_t block_count,
|
|
||||||
Genode::addr_t phys,
|
|
||||||
Block::Packet_descriptor &packet)
|
|
||||||
{
|
|
||||||
if (!read_blocks_dma(packet, block_number, block_count, phys))
|
|
||||||
throw Io_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_dma(Block::sector_t block_number,
|
|
||||||
Genode::size_t block_count,
|
|
||||||
Genode::addr_t phys,
|
|
||||||
Block::Packet_descriptor &packet)
|
|
||||||
{
|
|
||||||
if (!write_blocks_dma(packet, block_number, block_count, phys))
|
|
||||||
throw Io_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dma_enabled() { return _use_dma; }
|
|
||||||
|
|
||||||
Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size) {
|
|
||||||
return Genode::env()->ram_session()->alloc(size, UNCACHED); }
|
|
||||||
|
|
||||||
void free_dma_buffer(Genode::Ram_dataspace_capability c) {
|
|
||||||
return Genode::env()->ram_session()->free(c); }
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Block { using Sdhci_driver = Genode::Sdhc; }
|
|
||||||
|
|
||||||
#endif /* _SDHC_H_ */
|
|
@ -12,13 +12,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* local includes */
|
/* local includes */
|
||||||
#include <sdhc.h>
|
#include <driver.h>
|
||||||
|
|
||||||
using namespace Sd_card;
|
using namespace Sd_card;
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
|
void Driver::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
|
||||||
{
|
{
|
||||||
Xfertyp::Msbsel::set(xfertyp, 1);
|
Xfertyp::Msbsel::set(xfertyp, 1);
|
||||||
Xfertyp::Bcen::set(xfertyp, 1);
|
Xfertyp::Bcen::set(xfertyp, 1);
|
||||||
@ -26,7 +26,7 @@ void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Sdhc::_wait_for_cmd_complete_mb_finish(bool const reading)
|
int Driver::_wait_for_cmd_complete_mb_finish(bool const reading)
|
||||||
{
|
{
|
||||||
if (reading) { return 0; }
|
if (reading) { return 0; }
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ int Sdhc::_wait_for_cmd_complete_mb_finish(bool const reading)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
|
bool Driver::_issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
|
||||||
bool const transfer,
|
bool const transfer,
|
||||||
bool const multiblock,
|
bool const multiblock,
|
||||||
bool const reading)
|
bool const reading)
|
||||||
@ -65,8 +65,7 @@ bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
|
|||||||
if (reading) {
|
if (reading) {
|
||||||
Xfertyp::Ac12en::set(xfertyp, 1); }
|
Xfertyp::Ac12en::set(xfertyp, 1); }
|
||||||
|
|
||||||
if (_use_dma) {
|
Xfertyp::Dmaen::set(xfertyp, 1);
|
||||||
Xfertyp::Dmaen::set(xfertyp, 1); }
|
|
||||||
}
|
}
|
||||||
Xfertyp::Dtdsel::set(xfertyp,
|
Xfertyp::Dtdsel::set(xfertyp,
|
||||||
reading ? Xfertyp::Dtdsel::READ : Xfertyp::Dtdsel::WRITE);
|
reading ? Xfertyp::Dtdsel::READ : Xfertyp::Dtdsel::WRITE);
|
||||||
@ -75,21 +74,21 @@ bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Sdhc::_supported_host_version(Hostver::access_t hostver)
|
bool Driver::_supported_host_version(Hostver::access_t hostver)
|
||||||
{
|
{
|
||||||
return Hostver::Vvn::get(hostver) == 18 &&
|
return Hostver::Vvn::get(hostver) == 18 &&
|
||||||
Hostver::Svn::get(hostver) == 1;
|
Hostver::Svn::get(hostver) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_watermark_level(Wml::access_t &wml)
|
void Driver::_watermark_level(Wml::access_t &wml)
|
||||||
{
|
{
|
||||||
Wml::Wr_wml::set(wml, 16);
|
Wml::Wr_wml::set(wml, 16);
|
||||||
Wml::Wr_brst_len::set(wml, 8);
|
Wml::Wr_brst_len::set(wml, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_reset_amendments()
|
void Driver::_reset_amendments()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The SDHC specification says that a software reset shouldn't
|
* The SDHC specification says that a software reset shouldn't
|
||||||
@ -106,7 +105,7 @@ void Sdhc::_reset_amendments()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_clock_finish(Clock clock)
|
void Driver::_clock_finish(Clock clock)
|
||||||
{
|
{
|
||||||
Mmio::write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
|
Mmio::write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
|
||||||
switch (clock) {
|
switch (clock) {
|
||||||
@ -116,5 +115,5 @@ void Sdhc::_clock_finish(Clock clock)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_disable_clock_preparation() { }
|
void Driver::_disable_clock_preparation() { }
|
||||||
void Sdhc::_enable_clock_finish() { }
|
void Driver::_enable_clock_finish() { }
|
@ -12,13 +12,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* local includes */
|
/* local includes */
|
||||||
#include <sdhc.h>
|
#include <driver.h>
|
||||||
|
|
||||||
using namespace Sd_card;
|
using namespace Sd_card;
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
|
void Driver::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
|
||||||
{
|
{
|
||||||
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
|
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
|
||||||
Mixctrl::Dmaen::set(mixctrl, 1);
|
Mixctrl::Dmaen::set(mixctrl, 1);
|
||||||
@ -33,20 +33,20 @@ void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Sdhc::_wait_for_cmd_complete_mb_finish(bool const reading)
|
int Driver::_wait_for_cmd_complete_mb_finish(bool const reading)
|
||||||
{
|
{
|
||||||
/* we can't use the "Auto Command 12" feature as it does not work */
|
/* we can't use the "Auto Command 12" feature as it does not work */
|
||||||
return _stop_transmission() ? -1 : 0;
|
return _stop_transmission() ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &,
|
bool Driver::_issue_cmd_finish_xfertyp(Xfertyp::access_t &,
|
||||||
bool const transfer,
|
bool const transfer,
|
||||||
bool const multiblock,
|
bool const multiblock,
|
||||||
bool const reading)
|
bool const reading)
|
||||||
{
|
{
|
||||||
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
|
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
|
||||||
Mixctrl::Dmaen ::set(mixctrl, transfer && multiblock && _use_dma);
|
Mixctrl::Dmaen ::set(mixctrl, transfer && multiblock);
|
||||||
Mixctrl::Bcen ::set(mixctrl, transfer);
|
Mixctrl::Bcen ::set(mixctrl, transfer);
|
||||||
Mixctrl::Ac12en ::set(mixctrl, 0);
|
Mixctrl::Ac12en ::set(mixctrl, 0);
|
||||||
Mixctrl::Msbsel ::set(mixctrl, transfer);
|
Mixctrl::Msbsel ::set(mixctrl, transfer);
|
||||||
@ -64,21 +64,21 @@ bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Sdhc::_supported_host_version(Hostver::access_t hostver)
|
bool Driver::_supported_host_version(Hostver::access_t hostver)
|
||||||
{
|
{
|
||||||
return Hostver::Vvn::get(hostver) == 0 &&
|
return Hostver::Vvn::get(hostver) == 0 &&
|
||||||
Hostver::Svn::get(hostver) == 3;
|
Hostver::Svn::get(hostver) == 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_watermark_level(Wml::access_t &wml)
|
void Driver::_watermark_level(Wml::access_t &wml)
|
||||||
{
|
{
|
||||||
Wml::Wr_wml::set(wml, 64);
|
Wml::Wr_wml::set(wml, 64);
|
||||||
Wml::Wr_brst_len::set(wml, 16);
|
Wml::Wr_brst_len::set(wml, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_reset_amendments()
|
void Driver::_reset_amendments()
|
||||||
{
|
{
|
||||||
/* the USDHC doesn't reset the Mixer Control register automatically */
|
/* the USDHC doesn't reset the Mixer Control register automatically */
|
||||||
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
|
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
|
||||||
@ -95,7 +95,7 @@ void Sdhc::_reset_amendments()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_clock_finish(Clock clock)
|
void Driver::_clock_finish(Clock clock)
|
||||||
{
|
{
|
||||||
switch (clock) {
|
switch (clock) {
|
||||||
case CLOCK_INITIAL:
|
case CLOCK_INITIAL:
|
||||||
@ -111,6 +111,7 @@ void Sdhc::_clock_finish(Clock clock)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sdhc::_disable_clock_preparation() { Mmio::write<Vendspec::Frc_sdclk_on>(0); }
|
void Driver::_disable_clock_preparation() {
|
||||||
|
Mmio::write<Vendspec::Frc_sdclk_on>(0); }
|
||||||
|
|
||||||
void Sdhc::_enable_clock_finish() { Mmio::write<Vendspec::Frc_sdclk_on>(0); }
|
void Driver::_enable_clock_finish() { Mmio::write<Vendspec::Frc_sdclk_on>(0); }
|
443
repos/os/src/drivers/sd_card/spec/omap4/driver.cc
Normal file
443
repos/os/src/drivers/sd_card/spec/omap4/driver.cc
Normal file
@ -0,0 +1,443 @@
|
|||||||
|
/*
|
||||||
|
* \brief OMAP4-specific implementation of the Block::Driver interface
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2012-07-19
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2017 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <driver.h>
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
using namespace Sd_card;
|
||||||
|
|
||||||
|
|
||||||
|
Card_info Driver::_init()
|
||||||
|
{
|
||||||
|
Mmio::write<Sysconfig>(0x2015);
|
||||||
|
Mmio::write<Hctl>(0x0);
|
||||||
|
|
||||||
|
_set_bus_power(VOLTAGE_3_0);
|
||||||
|
|
||||||
|
if (!_sd_bus_power_on()) {
|
||||||
|
error("sd_bus_power failed");
|
||||||
|
}
|
||||||
|
_disable_irq();
|
||||||
|
|
||||||
|
_bus_width(BUS_WIDTH_1);
|
||||||
|
|
||||||
|
_delayer.usleep(10*1000);
|
||||||
|
|
||||||
|
_stop_clock();
|
||||||
|
if (!_set_and_enable_clock(CLOCK_DIV_240)) {
|
||||||
|
error("set_clock failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
if (!_init_stream()) {
|
||||||
|
error("sending the initialization stream failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
Mmio::write<Blk>(0);
|
||||||
|
_delayer.usleep(1000);
|
||||||
|
|
||||||
|
if (!issue_command(Go_idle_state())) {
|
||||||
|
error("Go_idle_state command failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
_delayer.usleep(2000);
|
||||||
|
|
||||||
|
if (!issue_command(Send_if_cond())) {
|
||||||
|
error("Send_if_cond command failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
if (Mmio::read<Rsp10>() != 0x1aa) {
|
||||||
|
error("unexpected response of Send_if_cond command");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to issue the same Sd_send_op_cond command multiple
|
||||||
|
* times. The first time, we receive the status information. On
|
||||||
|
* subsequent attempts, the response tells us that the card is
|
||||||
|
* busy. Usually, the command is issued twice. We give up if the
|
||||||
|
* card is not reaching busy state after one second.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int i = 1000;
|
||||||
|
for (; i > 0; --i) {
|
||||||
|
if (!issue_command(Sd_send_op_cond(0x18000, true))) {
|
||||||
|
warning("Sd_send_op_cond command failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Ocr::Busy::get(Mmio::read<Rsp10>()))
|
||||||
|
break;
|
||||||
|
|
||||||
|
_delayer.usleep(1000);
|
||||||
|
}
|
||||||
|
if (i == 0) {
|
||||||
|
error("Sd_send_op_cond timed out, could no power-on SD card");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
Card_info card_info = _detect();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Switch card to use 4 data signals
|
||||||
|
*/
|
||||||
|
if (!issue_command(Set_bus_width(Set_bus_width::Arg::Bus_width::FOUR_BITS),
|
||||||
|
card_info.rca())) {
|
||||||
|
warning("Set_bus_width(FOUR_BITS) command failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
_bus_width(BUS_WIDTH_4);
|
||||||
|
_delayer.usleep(10*1000);
|
||||||
|
|
||||||
|
_stop_clock();
|
||||||
|
if (!_set_and_enable_clock(CLOCK_DIV_0)) {
|
||||||
|
error("set_clock failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable IRQs */
|
||||||
|
Mmio::write<Ie::Tc_enable>(1);
|
||||||
|
Mmio::write<Ie::Cto_enable>(1);
|
||||||
|
Mmio::write<Ise::Tc_sigen>(1);
|
||||||
|
Mmio::write<Ise::Cto_sigen>(1);
|
||||||
|
|
||||||
|
return card_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Driver::_wait_for_bre()
|
||||||
|
{
|
||||||
|
if (!wait_for<Pstate::Bre>(1, _delayer, 1000*1000, 0)) {
|
||||||
|
if (!wait_for<Pstate::Bre>(1, _delayer)) {
|
||||||
|
error("Pstate::Bre timed out");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Driver::_wait_for_bwe()
|
||||||
|
{
|
||||||
|
if (!wait_for<Pstate::Bwe>(1, _delayer, 1000*1000, 0)) {
|
||||||
|
if (!wait_for<Pstate::Bwe>(1, _delayer)) {
|
||||||
|
error("Pstate::Bwe timed out");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_handle_irq()
|
||||||
|
{
|
||||||
|
_irq.ack_irq();
|
||||||
|
|
||||||
|
if (!_block_transfer.pending) {
|
||||||
|
return; }
|
||||||
|
|
||||||
|
if (Mmio::read<Stat::Tc>() != 1) {
|
||||||
|
warning("unexpected interrupt, Stat: ", Hex(Mmio::read<Stat>()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mmio::write<Stat::Tc>(1);
|
||||||
|
|
||||||
|
if (Mmio::read<Stat>() != 0) {
|
||||||
|
warning("unexpected state ("
|
||||||
|
"Stat: ", Hex(Mmio::read<Stat>()), " "
|
||||||
|
"Blen: ", Hex(Mmio::read<Blk::Blen>()), " "
|
||||||
|
"Nblk: ", Mmio::read<Blk::Nblk>());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_block_transfer.pending = false;
|
||||||
|
ack_packet(_block_transfer.packet, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Driver::_reset_cmd_line()
|
||||||
|
{
|
||||||
|
Mmio::write<Sysctl::Src>(1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We must poll quickly. If we waited too long until checking the
|
||||||
|
* bit, the polling would be infinite. Apparently the hardware
|
||||||
|
* depends on the timing here.
|
||||||
|
*/
|
||||||
|
if (!wait_for<Sysctl::Src>(1, _delayer, 1000, 0)) {
|
||||||
|
error("reset of cmd line timed out (src != 1)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!wait_for<Sysctl::Src>(0, _delayer, 1000, 0)) {
|
||||||
|
error("reset of cmd line timed out (src != 0)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_disable_irq()
|
||||||
|
{
|
||||||
|
Mmio::write<Ise>(0);
|
||||||
|
Mmio::write<Ie>(0);
|
||||||
|
Mmio::write<Stat>(~0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_bus_width(Bus_width bus_width)
|
||||||
|
{
|
||||||
|
switch (bus_width) {
|
||||||
|
case BUS_WIDTH_1:
|
||||||
|
Mmio::write<Con::Dw8>(0);
|
||||||
|
Mmio::write<Hctl::Dtw>(Hctl::Dtw::ONE_BIT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BUS_WIDTH_4:
|
||||||
|
Mmio::write<Con::Dw8>(0);
|
||||||
|
Mmio::write<Hctl::Dtw>(Hctl::Dtw::FOUR_BITS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Driver::_sd_bus_power_on()
|
||||||
|
{
|
||||||
|
Mmio::write<Hctl::Sdbp>(Hctl::Sdbp::POWER_ON);
|
||||||
|
|
||||||
|
if (!wait_for<Hctl::Sdbp>(1, _delayer)) {
|
||||||
|
error("setting Hctl::Sdbp timed out");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Driver::_set_and_enable_clock(enum Clock_divider divider)
|
||||||
|
{
|
||||||
|
Mmio::write<Sysctl::Dto>(Sysctl::Dto::TCF_2_POW_27);
|
||||||
|
|
||||||
|
switch (divider) {
|
||||||
|
case CLOCK_DIV_0: Mmio::write<Sysctl::Clkd>(0); break;
|
||||||
|
case CLOCK_DIV_240: Mmio::write<Sysctl::Clkd>(240); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mmio::write<Sysctl::Ice>(1);
|
||||||
|
|
||||||
|
/* wait for clock to become stable */
|
||||||
|
if (!wait_for<Sysctl::Ics>(1, _delayer)) {
|
||||||
|
error("clock enable timed out");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable clock */
|
||||||
|
Mmio::write<Sysctl::Ce>(1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_set_bus_power(Voltage voltage)
|
||||||
|
{
|
||||||
|
switch (voltage) {
|
||||||
|
case VOLTAGE_3_0:
|
||||||
|
Mmio::write<Hctl::Sdvs>(Hctl::Sdvs::VOLTAGE_3_0);
|
||||||
|
break;
|
||||||
|
case VOLTAGE_1_8:
|
||||||
|
Mmio::write<Hctl::Sdvs>(Hctl::Sdvs::VOLTAGE_1_8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Mmio::write<Capa::Vs18>(1);
|
||||||
|
|
||||||
|
if (voltage == VOLTAGE_3_0)
|
||||||
|
Mmio::write<Capa::Vs30>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Driver::_init_stream()
|
||||||
|
{
|
||||||
|
Mmio::write<Ie>(0x307f0033);
|
||||||
|
|
||||||
|
/* start initialization sequence */
|
||||||
|
Mmio::write<Con::Init>(1);
|
||||||
|
Mmio::write<Cmd>(0);
|
||||||
|
|
||||||
|
if (!wait_for<Stat::Cc>(1, _delayer, 1000*1000, 0)) {
|
||||||
|
error("init stream timed out");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* stop initialization sequence */
|
||||||
|
Mmio::write<Con::Init>(0);
|
||||||
|
Mmio::write<Stat>(~0);
|
||||||
|
Mmio::read<Stat>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Driver::_issue_command(Command_base const &command)
|
||||||
|
{
|
||||||
|
if (!wait_for<Pstate::Cmdi>(0, _delayer)) {
|
||||||
|
error("wait for Pstate::Cmdi timed out");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* write command argument */
|
||||||
|
Mmio::write<Arg>(command.arg);
|
||||||
|
|
||||||
|
/* assemble command register */
|
||||||
|
Cmd::access_t cmd = 0;
|
||||||
|
Cmd::Index::set(cmd, command.index);
|
||||||
|
if (command.transfer != TRANSFER_NONE) {
|
||||||
|
|
||||||
|
Cmd::Dp::set(cmd);
|
||||||
|
Cmd::Bce::set(cmd);
|
||||||
|
Cmd::Msbs::set(cmd);
|
||||||
|
|
||||||
|
if (command.index == Read_multiple_block::INDEX ||
|
||||||
|
command.index == Write_multiple_block::INDEX)
|
||||||
|
{
|
||||||
|
Cmd::Acen::set(cmd);
|
||||||
|
}
|
||||||
|
/* set data-direction bit depending on the command */
|
||||||
|
bool const read = command.transfer == TRANSFER_READ;
|
||||||
|
Cmd::Ddir::set(cmd, read ? Cmd::Ddir::READ : Cmd::Ddir::WRITE);
|
||||||
|
}
|
||||||
|
Cmd::access_t rsp_type = 0;
|
||||||
|
switch (command.rsp_type) {
|
||||||
|
case RESPONSE_NONE: rsp_type = Cmd::Rsp_type::RESPONSE_NONE; break;
|
||||||
|
case RESPONSE_136_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_136_BIT; break;
|
||||||
|
case RESPONSE_48_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_48_BIT; break;
|
||||||
|
case RESPONSE_48_BIT_WITH_BUSY: rsp_type = Cmd::Rsp_type::RESPONSE_48_BIT_WITH_BUSY; break;
|
||||||
|
}
|
||||||
|
Cmd::Rsp_type::set(cmd, rsp_type);
|
||||||
|
|
||||||
|
/* write command */
|
||||||
|
Mmio::write<Cmd>(cmd);
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
/* wait until command is completed, return false on timeout */
|
||||||
|
for (unsigned long i = 0; i < 1000*1000; i++) {
|
||||||
|
Stat::access_t const stat = Mmio::read<Stat>();
|
||||||
|
if (Stat::Erri::get(stat)) {
|
||||||
|
warning("SD command error");
|
||||||
|
if (Stat::Cto::get(stat))
|
||||||
|
warning("timeout");
|
||||||
|
|
||||||
|
_reset_cmd_line();
|
||||||
|
Mmio::write<Stat::Cc>(~0);
|
||||||
|
Mmio::read<Stat>();
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (Stat::Cc::get(stat) == 1) {
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* clear status of command-completed bit */
|
||||||
|
Mmio::write<Stat::Cc>(1);
|
||||||
|
Mmio::read<Stat>();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Cid Driver::_read_cid()
|
||||||
|
{
|
||||||
|
Cid cid;
|
||||||
|
cid.raw_0 = Mmio::read<Rsp10>();
|
||||||
|
cid.raw_1 = Mmio::read<Rsp32>();
|
||||||
|
cid.raw_2 = Mmio::read<Rsp54>();
|
||||||
|
cid.raw_3 = Mmio::read<Rsp76>();
|
||||||
|
return cid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Csd Driver::_read_csd()
|
||||||
|
{
|
||||||
|
Csd csd;
|
||||||
|
csd.csd0 = Mmio::read<Rsp10>();
|
||||||
|
csd.csd1 = Mmio::read<Rsp32>();
|
||||||
|
csd.csd2 = Mmio::read<Rsp54>();
|
||||||
|
csd.csd3 = Mmio::read<Rsp76>();
|
||||||
|
return csd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Driver::Driver(Env &env)
|
||||||
|
:
|
||||||
|
Attached_mmio(MMCHS1_MMIO_BASE, MMCHS1_MMIO_SIZE), _ep(env.ep())
|
||||||
|
{
|
||||||
|
_irq.sigh(_irq_handler);
|
||||||
|
_irq.ack_irq();
|
||||||
|
log("SD card detected");
|
||||||
|
log("capacity: ", _card_info.capacity_mb(), " MiB");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::read(Block::sector_t block_number,
|
||||||
|
size_t block_count,
|
||||||
|
char *buffer,
|
||||||
|
Block::Packet_descriptor &pkt)
|
||||||
|
{
|
||||||
|
if (_block_transfer.pending) {
|
||||||
|
throw Request_congestion(); }
|
||||||
|
|
||||||
|
Mmio::write<Blk::Blen>(block_size());
|
||||||
|
Mmio::write<Blk::Nblk>(block_count);
|
||||||
|
|
||||||
|
_block_transfer.packet = pkt;
|
||||||
|
_block_transfer.pending = true;
|
||||||
|
|
||||||
|
if (!issue_command(Read_multiple_block(block_number))) {
|
||||||
|
error("Read_multiple_block failed");
|
||||||
|
throw Io_error();
|
||||||
|
}
|
||||||
|
size_t const num_accesses = block_count * block_size() /
|
||||||
|
sizeof(Data::access_t);
|
||||||
|
Data::access_t *dst = (Data::access_t *)(buffer);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_accesses; i++) {
|
||||||
|
if (!_wait_for_bre())
|
||||||
|
throw Io_error();
|
||||||
|
|
||||||
|
*dst++ = Mmio::read<Data>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::write(Block::sector_t block_number,
|
||||||
|
size_t block_count,
|
||||||
|
char const *buffer,
|
||||||
|
Block::Packet_descriptor &pkt)
|
||||||
|
{
|
||||||
|
if (_block_transfer.pending) {
|
||||||
|
throw Request_congestion(); }
|
||||||
|
|
||||||
|
Mmio::write<Blk::Blen>(block_size());
|
||||||
|
Mmio::write<Blk::Nblk>(block_count);
|
||||||
|
|
||||||
|
_block_transfer.packet = pkt;
|
||||||
|
_block_transfer.pending = true;
|
||||||
|
|
||||||
|
if (!issue_command(Write_multiple_block(block_number))) {
|
||||||
|
error("Write_multiple_block failed");
|
||||||
|
throw Io_error();
|
||||||
|
}
|
||||||
|
size_t const num_accesses = block_count * block_size() /
|
||||||
|
sizeof(Data::access_t);
|
||||||
|
Data::access_t const *src = (Data::access_t const *)(buffer);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_accesses; i++) {
|
||||||
|
if (!_wait_for_bwe()) {
|
||||||
|
throw Io_error(); }
|
||||||
|
|
||||||
|
Mmio::write<Data>(*src++);
|
||||||
|
}
|
||||||
|
}
|
@ -5,137 +5,217 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2012-2015 Genode Labs GmbH
|
* Copyright (C) 2012-2017 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _DRIVERS__SD_CARD__SPEC__OMAP4__DRIVER_H_
|
#ifndef _DRIVER_H_
|
||||||
#define _DRIVERS__SD_CARD__SPEC__OMAP4__DRIVER_H_
|
#define _DRIVER_H_
|
||||||
|
|
||||||
#include <util/mmio.h>
|
/* Genode includes */
|
||||||
#include <os/attached_io_mem_dataspace.h>
|
#include <os/attached_mmio.h>
|
||||||
#include <base/log.h>
|
#include <drivers/board_base.h>
|
||||||
#include <timer_session/connection.h>
|
#include <timer_session/connection.h>
|
||||||
#include <os/server.h>
|
#include <irq_session/connection.h>
|
||||||
|
|
||||||
/* local includes */
|
/* local includes */
|
||||||
#include <mmchs.h>
|
#include <driver_base.h>
|
||||||
|
|
||||||
namespace Block {
|
namespace Sd_card { class Driver; }
|
||||||
using namespace Genode;
|
|
||||||
class Sdhci_driver;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Block::Sdhci_driver : public Block::Driver,
|
class Sd_card::Driver : public Driver_base,
|
||||||
public Sd_ack_handler
|
private Attached_mmio
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
struct Timer_delayer : Timer::Connection, Mmio::Delayer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Implementation of 'Delayer' interface
|
|
||||||
*/
|
|
||||||
void usleep(unsigned us) { Timer::Connection::usleep(us); }
|
|
||||||
} _delayer;
|
|
||||||
|
|
||||||
/* memory map */
|
|
||||||
enum {
|
enum {
|
||||||
MMCHS1_MMIO_BASE = 0x4809c000,
|
MMCHS1_MMIO_BASE = 0x4809c000,
|
||||||
MMCHS1_MMIO_SIZE = 0x00001000,
|
MMCHS1_MMIO_SIZE = 0x00001000,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* display sub system registers */
|
enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 };
|
||||||
Attached_io_mem_dataspace _mmchs1_mmio;
|
|
||||||
|
|
||||||
/* hsmmc controller instance */
|
enum Clock_divider { CLOCK_DIV_0, CLOCK_DIV_240 };
|
||||||
Omap4_hsmmc_controller _controller;
|
|
||||||
|
|
||||||
bool const _use_dma;
|
enum Voltage { VOLTAGE_3_0, VOLTAGE_1_8 };
|
||||||
|
|
||||||
|
struct Sysconfig : Register<0x110, 32> { };
|
||||||
|
|
||||||
|
struct Con : Register<0x12c, 32>
|
||||||
|
{
|
||||||
|
struct Init : Bitfield<1, 1> { };
|
||||||
|
struct Dw8 : Bitfield<5, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Cmd : Register<0x20c, 32>
|
||||||
|
{
|
||||||
|
struct Bce : Bitfield<1, 1> { };
|
||||||
|
struct Acen : Bitfield<2, 1> { };
|
||||||
|
struct Msbs : Bitfield<5, 1> { };
|
||||||
|
struct Index : Bitfield<24, 6> { };
|
||||||
|
struct Dp : Bitfield<21, 1> { };
|
||||||
|
|
||||||
|
struct Rsp_type : Bitfield<16, 2>
|
||||||
|
{
|
||||||
|
enum Response { RESPONSE_NONE = 0,
|
||||||
|
RESPONSE_136_BIT = 1,
|
||||||
|
RESPONSE_48_BIT = 2,
|
||||||
|
RESPONSE_48_BIT_WITH_BUSY = 3 };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Ddir : Bitfield<4, 1>
|
||||||
|
{
|
||||||
|
enum { WRITE = 0, READ = 1 };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Blk : Register<0x204, 32>
|
||||||
|
{
|
||||||
|
struct Blen : Bitfield<0, 12> { };
|
||||||
|
struct Nblk : Bitfield<16, 16> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Arg : Register<0x208, 32> { };
|
||||||
|
struct Rsp10 : Register<0x210, 32> { };
|
||||||
|
struct Rsp32 : Register<0x214, 32> { };
|
||||||
|
struct Rsp54 : Register<0x218, 32> { };
|
||||||
|
struct Rsp76 : Register<0x21c, 32> { };
|
||||||
|
struct Data : Register<0x220, 32> { };
|
||||||
|
|
||||||
|
struct Pstate : Register<0x224, 32>
|
||||||
|
{
|
||||||
|
struct Cmdi : Bitfield<0, 1> { };
|
||||||
|
struct Bwe : Bitfield<10, 1> { };
|
||||||
|
struct Bre : Bitfield<11, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Hctl : Register<0x228, 32>
|
||||||
|
{
|
||||||
|
struct Sdbp : Bitfield<8, 1>
|
||||||
|
{
|
||||||
|
enum { POWER_OFF = 0, POWER_ON = 1 };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Sdvs : Bitfield<9, 3>
|
||||||
|
{
|
||||||
|
enum Voltage { VOLTAGE_1_8 = 5,
|
||||||
|
VOLTAGE_3_0 = 6,
|
||||||
|
VOLTAGE_3_3 = 7 };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dtw : Bitfield<1, 1>
|
||||||
|
{
|
||||||
|
enum { ONE_BIT = 0, FOUR_BITS = 1 };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Sysctl : Register<0x22c, 32>
|
||||||
|
{
|
||||||
|
struct Ice : Bitfield<0, 1> { };
|
||||||
|
struct Ics : Bitfield<1, 1> { };
|
||||||
|
struct Ce : Bitfield<2, 1> { };
|
||||||
|
struct Clkd : Bitfield<6, 10> { };
|
||||||
|
struct Src : Bitfield<25, 1> { };
|
||||||
|
|
||||||
|
struct Dto : Bitfield<16, 4>
|
||||||
|
{
|
||||||
|
enum { TCF_2_POW_27 = 0xe };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Stat : Register<0x230, 32>
|
||||||
|
{
|
||||||
|
struct Tc : Bitfield<1, 1> { };
|
||||||
|
struct Cc : Bitfield<0, 1> { };
|
||||||
|
struct Erri : Bitfield<15, 1> { };
|
||||||
|
struct Cto : Bitfield<16, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Ie : Register<0x234, 32>
|
||||||
|
{
|
||||||
|
struct Tc_enable : Bitfield<1, 1> { };
|
||||||
|
struct Cto_enable : Bitfield<16, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Ise : Register<0x238, 32>
|
||||||
|
{
|
||||||
|
struct Tc_sigen : Bitfield<1, 1> { };
|
||||||
|
struct Cto_sigen : Bitfield<16, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Capa : Register<0x240, 32>
|
||||||
|
{
|
||||||
|
struct Vs30 : Bitfield<25, 1> { };
|
||||||
|
struct Vs18 : Bitfield<26, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Block_transfer
|
||||||
|
{
|
||||||
|
Block::Packet_descriptor packet;
|
||||||
|
bool pending = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Timer_delayer : Timer::Connection, Mmio::Delayer
|
||||||
|
{
|
||||||
|
void usleep(unsigned us) { Timer::Connection::usleep(us); }
|
||||||
|
};
|
||||||
|
|
||||||
|
Entrypoint &_ep;
|
||||||
|
Block_transfer _block_transfer;
|
||||||
|
Timer_delayer _delayer;
|
||||||
|
Signal_handler<Driver> _irq_handler { _ep, *this, &Driver::_handle_irq };
|
||||||
|
Irq_connection _irq { Board_base::HSMMC_IRQ };
|
||||||
|
Card_info _card_info { _init() };
|
||||||
|
|
||||||
|
Card_info _init();
|
||||||
|
bool _wait_for_bre();
|
||||||
|
bool _wait_for_bwe();
|
||||||
|
void _handle_irq();
|
||||||
|
bool _reset_cmd_line();
|
||||||
|
void _disable_irq();
|
||||||
|
void _bus_width(Bus_width bus_width);
|
||||||
|
bool _sd_bus_power_on();
|
||||||
|
bool _set_and_enable_clock(enum Clock_divider divider);
|
||||||
|
void _set_bus_power(Voltage voltage);
|
||||||
|
bool _init_stream();
|
||||||
|
|
||||||
|
void _stop_clock() { Mmio::write<Sysctl::Ce>(0); }
|
||||||
|
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
** Host_controller **
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
bool _issue_command(Command_base const &command) override;
|
||||||
|
Cid _read_cid() override;
|
||||||
|
Csd _read_csd() override;
|
||||||
|
|
||||||
|
Card_info card_info() const override { return _card_info; }
|
||||||
|
|
||||||
|
unsigned _read_rca() override {
|
||||||
|
return Send_relative_addr::Response::Rca::get(Mmio::read<Rsp10>()); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
struct Dma_not_supported : Exception { };
|
Driver(Env &env);
|
||||||
|
|
||||||
Sdhci_driver(Env &env)
|
|
||||||
:
|
|
||||||
_mmchs1_mmio(MMCHS1_MMIO_BASE, MMCHS1_MMIO_SIZE),
|
|
||||||
_controller(env.ep(), (addr_t)_mmchs1_mmio.local_addr<void>(),
|
|
||||||
_delayer, *this, false),
|
|
||||||
_use_dma(false)
|
|
||||||
{
|
|
||||||
if (_use_dma) { throw Dma_not_supported(); }
|
|
||||||
Sd_card::Card_info const card_info = _controller.card_info();
|
|
||||||
|
|
||||||
Genode::log("SD card detected");
|
|
||||||
Genode::log("capacity: ", card_info.capacity_mb(), " MiB");
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_ack(Block::Packet_descriptor pkt, bool success) {
|
|
||||||
ack_packet(pkt, success); }
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************
|
/*******************
|
||||||
** Block::Driver interface **
|
** Block::Driver **
|
||||||
*****************************/
|
*******************/
|
||||||
|
|
||||||
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,
|
void read(Block::sector_t block_number,
|
||||||
Genode::size_t block_count,
|
size_t block_count,
|
||||||
char *out_buffer,
|
char *buffer,
|
||||||
Packet_descriptor &packet)
|
Block::Packet_descriptor &pkt) override;
|
||||||
{
|
|
||||||
_controller.read_blocks(block_number, block_count, out_buffer, packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(Block::sector_t block_number,
|
void write(Block::sector_t block_number,
|
||||||
Genode::size_t block_count,
|
size_t block_count,
|
||||||
char const *buffer,
|
char const *buffer,
|
||||||
Packet_descriptor &packet)
|
Block::Packet_descriptor &pkt) override;
|
||||||
{
|
|
||||||
_controller.write_blocks(block_number, block_count, buffer, packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_dma(Block::sector_t block_number,
|
|
||||||
Genode::size_t block_count,
|
|
||||||
Genode::addr_t phys,
|
|
||||||
Packet_descriptor &packet)
|
|
||||||
{
|
|
||||||
_controller.read_blocks_dma(block_number, block_count, phys, packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_dma(Block::sector_t block_number,
|
|
||||||
Genode::size_t block_count,
|
|
||||||
Genode::addr_t phys,
|
|
||||||
Packet_descriptor &packet)
|
|
||||||
{
|
|
||||||
_controller.write_blocks_dma(block_number, block_count, phys, packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dma_enabled() { return _use_dma; }
|
|
||||||
|
|
||||||
Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size) {
|
|
||||||
return Genode::env()->ram_session()->alloc(size, UNCACHED); }
|
|
||||||
|
|
||||||
void free_dma_buffer(Genode::Ram_dataspace_capability c) {
|
|
||||||
return Genode::env()->ram_session()->free(c); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _DRIVERS__SD_CARD__SPEC__OMAP4__DRIVER_H_ */
|
#endif /* _DRIVER_H_ */
|
||||||
|
@ -1,999 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief OMAP4 MMCHS controller registers
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2012-07-03
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2012-2015 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU General Public License version 2.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _DRIVERS__SD_CARD__SPEC__OMAP4__MMCHS_H_
|
|
||||||
#define _DRIVERS__SD_CARD__SPEC__OMAP4__MMCHS_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <util/mmio.h>
|
|
||||||
#include <os/attached_ram_dataspace.h>
|
|
||||||
#include <irq_session/connection.h>
|
|
||||||
#include <drivers/board_base.h>
|
|
||||||
#include <block/component.h>
|
|
||||||
|
|
||||||
/* local includes */
|
|
||||||
#include <sd_card.h>
|
|
||||||
|
|
||||||
struct Mmchs : Genode::Mmio
|
|
||||||
{
|
|
||||||
enum { verbose = false };
|
|
||||||
|
|
||||||
typedef Genode::size_t size_t;
|
|
||||||
|
|
||||||
Mmchs(Genode::addr_t const mmio_base) : Genode::Mmio(mmio_base) { }
|
|
||||||
|
|
||||||
struct Sysconfig : Register<0x110, 32>
|
|
||||||
{
|
|
||||||
struct Autoidle : Bitfield<0, 1> { };
|
|
||||||
struct Softreset : Bitfield<1, 1> { };
|
|
||||||
struct Sidlemode : Bitfield<3, 2>
|
|
||||||
{
|
|
||||||
enum { NO_IDLE = 1 };
|
|
||||||
};
|
|
||||||
struct Clockactivity : Bitfield<8, 2>
|
|
||||||
{
|
|
||||||
enum { BOTH_ACTIVE = 3 };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Sysstatus : Register<0x114, 32>
|
|
||||||
{
|
|
||||||
struct Reset_done : Bitfield<0, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Con : Register<0x12c, 32>
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Open drain mode
|
|
||||||
*/
|
|
||||||
struct Od : Bitfield<0, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start initialization stream
|
|
||||||
*/
|
|
||||||
struct Init : Bitfield<1, 1> { };
|
|
||||||
|
|
||||||
struct Dw8 : Bitfield<5, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Master master slave selection (set if master DMA)
|
|
||||||
*/
|
|
||||||
struct Dma_mns : Bitfield<20, 1> { };
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Command register
|
|
||||||
*/
|
|
||||||
struct Cmd : Register<0x20c, 32>
|
|
||||||
{
|
|
||||||
struct Index : Bitfield<24, 6> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data present
|
|
||||||
*/
|
|
||||||
struct Dp : Bitfield<21, 1> { };
|
|
||||||
|
|
||||||
struct Rsp_type : Bitfield<16, 2>
|
|
||||||
{
|
|
||||||
enum Response { RESPONSE_NONE = 0,
|
|
||||||
RESPONSE_136_BIT = 1,
|
|
||||||
RESPONSE_48_BIT = 2,
|
|
||||||
RESPONSE_48_BIT_WITH_BUSY = 3 };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data direction
|
|
||||||
*/
|
|
||||||
struct Ddir : Bitfield<4, 1>
|
|
||||||
{
|
|
||||||
enum { WRITE = 0, READ = 1 };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Block count enable
|
|
||||||
*/
|
|
||||||
struct Bce : Bitfield<1, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Multiple block select
|
|
||||||
*/
|
|
||||||
struct Msbs : Bitfield<5, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto-CMD12 enable
|
|
||||||
*/
|
|
||||||
struct Acen : Bitfield<2, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DMA enable
|
|
||||||
*/
|
|
||||||
struct De : Bitfield<0, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transfer length configuration
|
|
||||||
*/
|
|
||||||
struct Blk : Register<0x204, 32>
|
|
||||||
{
|
|
||||||
struct Blen : Bitfield<0, 12> { };
|
|
||||||
struct Nblk : Bitfield<16, 16> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Command argument
|
|
||||||
*/
|
|
||||||
struct Arg : Register<0x208, 32> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response bits 0..31
|
|
||||||
*/
|
|
||||||
struct Rsp10 : Register<0x210, 32> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response bits 32..63
|
|
||||||
*/
|
|
||||||
struct Rsp32 : Register<0x214, 32> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response bits 64..95
|
|
||||||
*/
|
|
||||||
struct Rsp54 : Register<0x218, 32> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response bits 96..127
|
|
||||||
*/
|
|
||||||
struct Rsp76 : Register<0x21c, 32> { };
|
|
||||||
|
|
||||||
struct Data : Register<0x220, 32> { };
|
|
||||||
|
|
||||||
struct Pstate : Register<0x224, 32>
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Command inhibit
|
|
||||||
*/
|
|
||||||
struct Cmdi : Bitfield<0, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Buffer write enable status
|
|
||||||
*/
|
|
||||||
struct Bwe : Bitfield<10, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Buffer read enable status
|
|
||||||
*/
|
|
||||||
struct Bre : Bitfield<11, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Hctl : Register<0x228, 32>
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Wakeup event enable
|
|
||||||
*/
|
|
||||||
struct Iwe : Bitfield<24, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SD bus power
|
|
||||||
*/
|
|
||||||
struct Sdbp : Bitfield<8, 1>
|
|
||||||
{
|
|
||||||
enum { POWER_OFF = 0, POWER_ON = 1 };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SD voltage select
|
|
||||||
*/
|
|
||||||
struct Sdvs : Bitfield<9, 3>
|
|
||||||
{
|
|
||||||
enum Voltage { VOLTAGE_1_8 = 5,
|
|
||||||
VOLTAGE_3_0 = 6,
|
|
||||||
VOLTAGE_3_3 = 7 };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data transfer width
|
|
||||||
*/
|
|
||||||
struct Dtw : Bitfield<1, 1>
|
|
||||||
{
|
|
||||||
enum { ONE_BIT = 0, FOUR_BITS = 1 };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Sysctl : Register<0x22c, 32>
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Internal clock enable
|
|
||||||
*/
|
|
||||||
struct Ice : Bitfield<0, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal clock stable
|
|
||||||
*/
|
|
||||||
struct Ics : Bitfield<1, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clock enable
|
|
||||||
*/
|
|
||||||
struct Ce : Bitfield<2, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clock divider
|
|
||||||
*/
|
|
||||||
struct Clkd : Bitfield<6, 10> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Software reset all
|
|
||||||
*/
|
|
||||||
struct Sra : Bitfield<24, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Software reset for command line
|
|
||||||
*/
|
|
||||||
struct Src : Bitfield<25, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data timeout counter
|
|
||||||
*/
|
|
||||||
struct Dto : Bitfield<16, 4>
|
|
||||||
{
|
|
||||||
enum { TCF_2_POW_27 = 0xe };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Stat : Register<0x230, 32>
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Transfer completed
|
|
||||||
*/
|
|
||||||
struct Tc : Bitfield<1, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Command completed
|
|
||||||
*/
|
|
||||||
struct Cc : Bitfield<0, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Error
|
|
||||||
*/
|
|
||||||
struct Erri : Bitfield<15, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Command timed out
|
|
||||||
*/
|
|
||||||
struct Cto : Bitfield<16, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interrupt enable
|
|
||||||
*/
|
|
||||||
struct Ie : Register<0x234, 32>
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Command completed
|
|
||||||
*/
|
|
||||||
struct Cc_enable : Bitfield<0, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transfer completed
|
|
||||||
*/
|
|
||||||
struct Tc_enable : Bitfield<1, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Card interrupt
|
|
||||||
*/
|
|
||||||
struct Cirq_enable : Bitfield<8, 1> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Command timeout error
|
|
||||||
*/
|
|
||||||
struct Cto_enable : Bitfield<16, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Ise : Register<0x238, 32>
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* The naming follows the lines of the 'Ie' register
|
|
||||||
*/
|
|
||||||
struct Tc_sigen : Bitfield<1, 1> { };
|
|
||||||
struct Cto_sigen : Bitfield<16, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Capabilities
|
|
||||||
*/
|
|
||||||
struct Capa : Register<0x240, 32>
|
|
||||||
{
|
|
||||||
struct Vs30 : Bitfield<25, 1> { };
|
|
||||||
struct Vs18 : Bitfield<26, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ADMA system address
|
|
||||||
*
|
|
||||||
* Base address of the ADMA descriptor table
|
|
||||||
*/
|
|
||||||
struct Admasal : Register<0x258, 32> { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ADMA descriptor layout
|
|
||||||
*/
|
|
||||||
struct Adma_desc : Genode::Register<64>
|
|
||||||
{
|
|
||||||
struct Valid : Bitfield<0, 1> { };
|
|
||||||
struct Ent : Bitfield<1, 1> { };
|
|
||||||
struct Int : Bitfield<2, 1> { };
|
|
||||||
struct Act1 : Bitfield<4, 1> { };
|
|
||||||
struct Act2 : Bitfield<5, 1> { };
|
|
||||||
struct Length : Bitfield<16, 16> { };
|
|
||||||
struct Address : Bitfield<32, 32> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
bool reset_cmd_line(Delayer &delayer)
|
|
||||||
{
|
|
||||||
write<Sysctl::Src>(1);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We must poll quickly. If we waited too long until checking the bit,
|
|
||||||
* the polling would be infinite. Apparently the hardware depends on
|
|
||||||
* the timing here.
|
|
||||||
*/
|
|
||||||
if (!wait_for<Sysctl::Src>(1, delayer, 1000, 0)) {
|
|
||||||
Genode::error("reset of cmd line timed out (src != 1)");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wait_for<Sysctl::Src>(0, delayer, 1000, 0)) {
|
|
||||||
Genode::error("reset of cmd line timed out (src != 0)");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* XXX unused */
|
|
||||||
bool soft_reset_all(Delayer &delayer)
|
|
||||||
{
|
|
||||||
write<Sysctl::Sra>(1);
|
|
||||||
|
|
||||||
if (!wait_for<Sysctl::Sra>(1, delayer, 1000, 0)) {
|
|
||||||
Genode::error("soft reset all timed out (src != 1)");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void disable_irq()
|
|
||||||
{
|
|
||||||
write<Ise>(0);
|
|
||||||
write<Ie>(0);
|
|
||||||
write<Stat>(~0);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 };
|
|
||||||
|
|
||||||
void bus_width(Bus_width bus_width)
|
|
||||||
{
|
|
||||||
switch (bus_width) {
|
|
||||||
case BUS_WIDTH_1:
|
|
||||||
write<Con::Dw8>(0);
|
|
||||||
write<Hctl::Dtw>(Hctl::Dtw::ONE_BIT);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BUS_WIDTH_4:
|
|
||||||
write<Con::Dw8>(0);
|
|
||||||
write<Hctl::Dtw>(Hctl::Dtw::FOUR_BITS);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sd_bus_power_on(Delayer &delayer)
|
|
||||||
{
|
|
||||||
write<Hctl::Sdbp>(Hctl::Sdbp::POWER_ON);
|
|
||||||
|
|
||||||
if (!wait_for<Hctl::Sdbp>(1, delayer)) {
|
|
||||||
Genode::error("setting Hctl::Sdbp timed out");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void stop_clock()
|
|
||||||
{
|
|
||||||
write<Sysctl::Ce>(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Clock_divider { CLOCK_DIV_0, CLOCK_DIV_240 };
|
|
||||||
|
|
||||||
bool set_and_enable_clock(enum Clock_divider divider, Delayer &delayer)
|
|
||||||
{
|
|
||||||
write<Sysctl::Dto>(Sysctl::Dto::TCF_2_POW_27);
|
|
||||||
|
|
||||||
switch (divider) {
|
|
||||||
case CLOCK_DIV_0: write<Sysctl::Clkd>(0); break;
|
|
||||||
case CLOCK_DIV_240: write<Sysctl::Clkd>(240); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
write<Sysctl::Ice>(1);
|
|
||||||
|
|
||||||
/* wait for clock to become stable */
|
|
||||||
if (!wait_for<Sysctl::Ics>(1, delayer)) {
|
|
||||||
Genode::error("clock enable timed out");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* enable clock */
|
|
||||||
write<Sysctl::Ce>(1);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Voltage { VOLTAGE_3_0, VOLTAGE_1_8 };
|
|
||||||
|
|
||||||
void set_bus_power(Voltage voltage)
|
|
||||||
{
|
|
||||||
switch (voltage) {
|
|
||||||
case VOLTAGE_3_0:
|
|
||||||
write<Hctl::Sdvs>(Hctl::Sdvs::VOLTAGE_3_0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VOLTAGE_1_8:
|
|
||||||
write<Hctl::Sdvs>(Hctl::Sdvs::VOLTAGE_1_8);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
write<Capa::Vs18>(1);
|
|
||||||
|
|
||||||
if (voltage == VOLTAGE_3_0)
|
|
||||||
write<Capa::Vs30>(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool init_stream(Delayer &delayer)
|
|
||||||
{
|
|
||||||
write<Ie>(0x307f0033);
|
|
||||||
|
|
||||||
/* start initialization sequence */
|
|
||||||
write<Con::Init>(1);
|
|
||||||
|
|
||||||
write<Cmd>(0);
|
|
||||||
|
|
||||||
if (!wait_for<Stat::Cc>(1, delayer, 1000*1000, 0)) {
|
|
||||||
Genode::error("init stream timed out");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* stop initialization sequence */
|
|
||||||
write<Con::Init>(0);
|
|
||||||
|
|
||||||
write<Stat>(~0);
|
|
||||||
read<Stat>();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Hl_sysconfig : Register<0x10, 32> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct Sd_ack_handler {
|
|
||||||
virtual void handle_ack(Block::Packet_descriptor pkt, bool success) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
struct Block_transfer
|
|
||||||
{
|
|
||||||
Block::Packet_descriptor packet;
|
|
||||||
bool pending = false;
|
|
||||||
|
|
||||||
} _block_transfer;
|
|
||||||
|
|
||||||
Delayer &_delayer;
|
|
||||||
Sd_card::Card_info _card_info;
|
|
||||||
bool const _use_dma;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* DMA memory for holding the ADMA descriptor table
|
|
||||||
*/
|
|
||||||
enum { ADMA_DESC_MAX_ENTRIES = 1024 };
|
|
||||||
Genode::Attached_ram_dataspace _adma_desc_ds;
|
|
||||||
Adma_desc::access_t * const _adma_desc;
|
|
||||||
Genode::addr_t const _adma_desc_phys;
|
|
||||||
|
|
||||||
Sd_ack_handler &_ack_handler;
|
|
||||||
Genode::Signal_handler<Omap4_hsmmc_controller> _irq_handler;
|
|
||||||
Genode::Irq_connection _irq;
|
|
||||||
|
|
||||||
Sd_card::Card_info _init()
|
|
||||||
{
|
|
||||||
using namespace Sd_card;
|
|
||||||
|
|
||||||
write<Sysconfig>(0x2015);
|
|
||||||
write<Hctl>(0x0);
|
|
||||||
|
|
||||||
set_bus_power(VOLTAGE_3_0);
|
|
||||||
|
|
||||||
if (!sd_bus_power_on(_delayer)) {
|
|
||||||
Genode::error("sd_bus_power failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
disable_irq();
|
|
||||||
|
|
||||||
bus_width(BUS_WIDTH_1);
|
|
||||||
|
|
||||||
_delayer.usleep(10*1000);
|
|
||||||
|
|
||||||
stop_clock();
|
|
||||||
if (!set_and_enable_clock(CLOCK_DIV_240, _delayer)) {
|
|
||||||
Genode::error("set_clock failed");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!init_stream(_delayer)) {
|
|
||||||
Genode::error("sending the initialization stream failed");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
write<Blk>(0);
|
|
||||||
|
|
||||||
_delayer.usleep(1000);
|
|
||||||
|
|
||||||
if (!issue_command(Go_idle_state())) {
|
|
||||||
Genode::error("Go_idle_state command failed");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
_delayer.usleep(2000);
|
|
||||||
|
|
||||||
if (!issue_command(Send_if_cond())) {
|
|
||||||
Genode::error("Send_if_cond command failed");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (read<Rsp10>() != 0x1aa) {
|
|
||||||
Genode::error("unexpected response of Send_if_cond command");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We need to issue the same Sd_send_op_cond command multiple
|
|
||||||
* times. The first time, we receive the status information. On
|
|
||||||
* subsequent attempts, the response tells us that the card is
|
|
||||||
* busy. Usually, the command is issued twice. We give up if the
|
|
||||||
* card is not reaching busy state after one second.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int i = 1000;
|
|
||||||
for (; i > 0; --i) {
|
|
||||||
if (!issue_command(Sd_send_op_cond(0x18000, true))) {
|
|
||||||
Genode::warning("Sd_send_op_cond command failed");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Ocr::Busy::get(read<Rsp10>()))
|
|
||||||
break;
|
|
||||||
|
|
||||||
_delayer.usleep(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
Genode::error("Sd_send_op_cond timed out, could no power-on SD card");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
Card_info card_info = _detect();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Switch card to use 4 data signals
|
|
||||||
*/
|
|
||||||
if (!issue_command(Set_bus_width(Set_bus_width::Arg::Bus_width::FOUR_BITS),
|
|
||||||
card_info.rca())) {
|
|
||||||
Genode::warning("Set_bus_width(FOUR_BITS) command failed");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
bus_width(BUS_WIDTH_4);
|
|
||||||
|
|
||||||
_delayer.usleep(10*1000);
|
|
||||||
|
|
||||||
stop_clock();
|
|
||||||
if (!set_and_enable_clock(CLOCK_DIV_0, _delayer)) {
|
|
||||||
Genode::error("set_clock failed");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* enable master DMA */
|
|
||||||
write<Con::Dma_mns>(1);
|
|
||||||
|
|
||||||
/* enable IRQs */
|
|
||||||
write<Ie::Tc_enable>(1);
|
|
||||||
write<Ie::Cto_enable>(1);
|
|
||||||
write<Ise::Tc_sigen>(1);
|
|
||||||
write<Ise::Cto_sigen>(1);
|
|
||||||
|
|
||||||
return card_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marshal ADMA descriptors according to block request
|
|
||||||
*
|
|
||||||
* \return false if block request is too large
|
|
||||||
*/
|
|
||||||
bool _setup_adma_descriptor_table(size_t block_count,
|
|
||||||
Genode::addr_t out_buffer_phys)
|
|
||||||
{
|
|
||||||
using namespace Sd_card;
|
|
||||||
|
|
||||||
/* reset ADMA offset to first descriptor */
|
|
||||||
write<Admasal>(_adma_desc_phys);
|
|
||||||
|
|
||||||
enum { BLOCK_SIZE = 512 /* bytes */ };
|
|
||||||
|
|
||||||
size_t const max_adma_request_size = 64*1024 - 4; /* bytes */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* sanity check
|
|
||||||
*
|
|
||||||
* XXX An alternative to this sanity check would be to expose
|
|
||||||
* the maximum DMA transfer size to the driver and let the
|
|
||||||
* driver partition large requests into ones that are
|
|
||||||
* supported by the controller.
|
|
||||||
*/
|
|
||||||
if (block_count*BLOCK_SIZE > max_adma_request_size*ADMA_DESC_MAX_ENTRIES) {
|
|
||||||
Genode::error("Block request too large");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Each ADMA descriptor can transfer up to MAX_ADMA_REQUEST_SIZE
|
|
||||||
* bytes. If the request is larger, we generate a list of ADMA
|
|
||||||
* descriptors.
|
|
||||||
*/
|
|
||||||
|
|
||||||
size_t const total_bytes = block_count*BLOCK_SIZE;
|
|
||||||
|
|
||||||
/* number of bytes for which descriptors have been created */
|
|
||||||
addr_t consumed_bytes = 0;
|
|
||||||
|
|
||||||
for (int index = 0; consumed_bytes < total_bytes; index++) {
|
|
||||||
|
|
||||||
size_t const remaining_bytes = total_bytes - consumed_bytes;
|
|
||||||
|
|
||||||
/* clamp current request to maximum ADMA request size */
|
|
||||||
size_t const curr_bytes = Genode::min(max_adma_request_size,
|
|
||||||
remaining_bytes);
|
|
||||||
/*
|
|
||||||
* Assemble new ADMA descriptor
|
|
||||||
*/
|
|
||||||
Adma_desc::access_t desc = 0;
|
|
||||||
Adma_desc::Address::set(desc, out_buffer_phys + consumed_bytes);
|
|
||||||
Adma_desc::Length::set(desc, curr_bytes);
|
|
||||||
|
|
||||||
/* set action to transfer */
|
|
||||||
Adma_desc::Act1::set(desc, 0);
|
|
||||||
Adma_desc::Act2::set(desc, 1);
|
|
||||||
|
|
||||||
Adma_desc::Valid::set(desc, 1);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Let the last descriptor generate transfer-complete interrupt
|
|
||||||
*/
|
|
||||||
if (consumed_bytes + curr_bytes == total_bytes)
|
|
||||||
Adma_desc::Ent::set(desc, 1);
|
|
||||||
|
|
||||||
/* install descriptor into ADMA descriptor table */
|
|
||||||
_adma_desc[index] = desc;
|
|
||||||
|
|
||||||
consumed_bytes += curr_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _wait_for_bre()
|
|
||||||
{
|
|
||||||
if (!wait_for<Pstate::Bre>(1, _delayer, 1000*1000, 0)
|
|
||||||
&& !wait_for<Pstate::Bre>(1, _delayer)) {
|
|
||||||
Genode::error("Pstate::Bre timed out");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _wait_for_bwe()
|
|
||||||
{
|
|
||||||
if (!wait_for<Pstate::Bwe>(1, _delayer, 1000*1000, 0)
|
|
||||||
&& !wait_for<Pstate::Bwe>(1, _delayer)) {
|
|
||||||
Genode::error("Pstate::Bwe timed out");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handle_irq()
|
|
||||||
{
|
|
||||||
_irq.ack_irq();
|
|
||||||
|
|
||||||
if (!_block_transfer.pending) {
|
|
||||||
return; }
|
|
||||||
|
|
||||||
if (read<Stat::Tc>() != 1) {
|
|
||||||
Genode::warning("unexpected interrupt, Stat: ", Genode::Hex(read<Stat>()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
write<Stat::Tc>(1);
|
|
||||||
|
|
||||||
if (read<Stat>() != 0) {
|
|
||||||
Genode::warning("unexpected state ("
|
|
||||||
"Stat: ", Genode::Hex(read<Stat>()), " "
|
|
||||||
"Blen: ", Genode::Hex(read<Blk::Blen>()), " "
|
|
||||||
"Nblk: ", read<Blk::Nblk>());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_block_transfer.pending = false;
|
|
||||||
_ack_handler.handle_ack(_block_transfer.packet, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
enum { IRQ_NUMBER = Genode::Board_base::HSMMC_IRQ };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* \param mmio_base local base address of MMIO registers
|
|
||||||
*/
|
|
||||||
Omap4_hsmmc_controller(Genode::Entrypoint &ep,
|
|
||||||
Genode::addr_t const mmio_base, Delayer &delayer,
|
|
||||||
Sd_ack_handler &ack_handler, bool use_dma)
|
|
||||||
:
|
|
||||||
Mmchs(mmio_base), _delayer(delayer), _card_info(_init()),
|
|
||||||
_use_dma(use_dma),
|
|
||||||
_adma_desc_ds(Genode::env()->ram_session(),
|
|
||||||
ADMA_DESC_MAX_ENTRIES*sizeof(Adma_desc::access_t),
|
|
||||||
Genode::UNCACHED),
|
|
||||||
_adma_desc(_adma_desc_ds.local_addr<Adma_desc::access_t>()),
|
|
||||||
_adma_desc_phys(Genode::Dataspace_client(_adma_desc_ds.cap()).phys_addr()),
|
|
||||||
_ack_handler(ack_handler),
|
|
||||||
_irq_handler(ep, *this, &Omap4_hsmmc_controller::_handle_irq),
|
|
||||||
_irq(IRQ_NUMBER)
|
|
||||||
{
|
|
||||||
_irq.sigh(_irq_handler);
|
|
||||||
_irq.ack_irq();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************
|
|
||||||
** Sd_card::Host_controller interface **
|
|
||||||
****************************************/
|
|
||||||
|
|
||||||
bool _issue_command(Sd_card::Command_base const &command)
|
|
||||||
{
|
|
||||||
if (verbose)
|
|
||||||
Genode::log("-> ", command);
|
|
||||||
|
|
||||||
if (!wait_for<Pstate::Cmdi>(0, _delayer)) {
|
|
||||||
Genode::error("wait for Pstate::Cmdi timed out");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* write command argument */
|
|
||||||
write<Arg>(command.arg);
|
|
||||||
|
|
||||||
/* assemble command register */
|
|
||||||
Cmd::access_t cmd = 0;
|
|
||||||
Cmd::Index::set(cmd, command.index);
|
|
||||||
if (command.transfer != Sd_card::TRANSFER_NONE) {
|
|
||||||
|
|
||||||
Cmd::Dp::set(cmd);
|
|
||||||
Cmd::Bce::set(cmd);
|
|
||||||
Cmd::Msbs::set(cmd);
|
|
||||||
|
|
||||||
if (command.index == Sd_card::Read_multiple_block::INDEX
|
|
||||||
|| command.index == Sd_card::Write_multiple_block::INDEX) {
|
|
||||||
Cmd::Acen::set(cmd);
|
|
||||||
|
|
||||||
if (_use_dma)
|
|
||||||
Cmd::De::set(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set data-direction bit depending on the command */
|
|
||||||
bool const read = command.transfer == Sd_card::TRANSFER_READ;
|
|
||||||
Cmd::Ddir::set(cmd, read ? Cmd::Ddir::READ : Cmd::Ddir::WRITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
Cmd::access_t rsp_type = 0;
|
|
||||||
switch (command.rsp_type) {
|
|
||||||
case Sd_card::RESPONSE_NONE: rsp_type = Cmd::Rsp_type::RESPONSE_NONE; break;
|
|
||||||
case Sd_card::RESPONSE_136_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_136_BIT; break;
|
|
||||||
case Sd_card::RESPONSE_48_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_48_BIT; break;
|
|
||||||
case Sd_card::RESPONSE_48_BIT_WITH_BUSY: rsp_type = Cmd::Rsp_type::RESPONSE_48_BIT_WITH_BUSY; break;
|
|
||||||
}
|
|
||||||
Cmd::Rsp_type::set(cmd, rsp_type);
|
|
||||||
|
|
||||||
/* write command */
|
|
||||||
write<Cmd>(cmd);
|
|
||||||
|
|
||||||
bool result = false;
|
|
||||||
|
|
||||||
/* wait until command is completed, return false on timeout */
|
|
||||||
for (unsigned long i = 0; i < 1000*1000; i++) {
|
|
||||||
Stat::access_t const stat = read<Stat>();
|
|
||||||
if (Stat::Erri::get(stat)) {
|
|
||||||
Genode::warning("SD command error");
|
|
||||||
if (Stat::Cto::get(stat))
|
|
||||||
Genode::warning("timeout");
|
|
||||||
|
|
||||||
reset_cmd_line(_delayer);
|
|
||||||
write<Stat::Cc>(~0);
|
|
||||||
read<Stat>();
|
|
||||||
result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (Stat::Cc::get(stat) == 1) {
|
|
||||||
result = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
Genode::log("<- ", result ? "succeeded" : "timed out");
|
|
||||||
|
|
||||||
/* clear status of command-completed bit */
|
|
||||||
write<Stat::Cc>(1);
|
|
||||||
read<Stat>();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sd_card::Card_info card_info() const
|
|
||||||
{
|
|
||||||
return _card_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sd_card::Cid _read_cid()
|
|
||||||
{
|
|
||||||
Sd_card::Cid cid;
|
|
||||||
cid.raw_0 = read<Rsp10>();
|
|
||||||
cid.raw_1 = read<Rsp32>();
|
|
||||||
cid.raw_2 = read<Rsp54>();
|
|
||||||
cid.raw_3 = read<Rsp76>();
|
|
||||||
return cid;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sd_card::Csd _read_csd()
|
|
||||||
{
|
|
||||||
Sd_card::Csd csd;
|
|
||||||
csd.csd0 = read<Rsp10>();
|
|
||||||
csd.csd1 = read<Rsp32>();
|
|
||||||
csd.csd2 = read<Rsp54>();
|
|
||||||
csd.csd3 = read<Rsp76>();
|
|
||||||
return csd;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned _read_rca()
|
|
||||||
{
|
|
||||||
return Sd_card::Send_relative_addr::Response::Rca::get(read<Rsp10>());
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_blocks(size_t block_number, size_t block_count, char *out_buffer,
|
|
||||||
Block::Packet_descriptor pkt)
|
|
||||||
{
|
|
||||||
using namespace Sd_card;
|
|
||||||
|
|
||||||
if (_block_transfer.pending) {
|
|
||||||
throw Block::Driver::Request_congestion(); }
|
|
||||||
|
|
||||||
write<Blk::Blen>(0x200);
|
|
||||||
write<Blk::Nblk>(block_count);
|
|
||||||
|
|
||||||
_block_transfer.packet = pkt;
|
|
||||||
_block_transfer.pending = true;
|
|
||||||
|
|
||||||
if (!issue_command(Read_multiple_block(block_number))) {
|
|
||||||
Genode::error("Read_multiple_block failed");
|
|
||||||
throw Block::Driver::Io_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t const num_accesses = block_count*512/sizeof(Data::access_t);
|
|
||||||
Data::access_t *dst = (Data::access_t *)(out_buffer);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < num_accesses; i++) {
|
|
||||||
if (!_wait_for_bre())
|
|
||||||
throw Block::Driver::Io_error();
|
|
||||||
|
|
||||||
*dst++ = read<Data>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_blocks(size_t block_number, size_t block_count, char const *buffer,
|
|
||||||
Block::Packet_descriptor pkt)
|
|
||||||
{
|
|
||||||
using namespace Sd_card;
|
|
||||||
|
|
||||||
if (_block_transfer.pending) {
|
|
||||||
throw Block::Driver::Request_congestion(); }
|
|
||||||
|
|
||||||
write<Blk::Blen>(0x200);
|
|
||||||
write<Blk::Nblk>(block_count);
|
|
||||||
|
|
||||||
_block_transfer.packet = pkt;
|
|
||||||
_block_transfer.pending = true;
|
|
||||||
|
|
||||||
if (!issue_command(Write_multiple_block(block_number))) {
|
|
||||||
Genode::error("Write_multiple_block failed");
|
|
||||||
throw Block::Driver::Io_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t const num_accesses = block_count*512/sizeof(Data::access_t);
|
|
||||||
Data::access_t const *src = (Data::access_t const *)(buffer);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < num_accesses; i++) {
|
|
||||||
if (!_wait_for_bwe())
|
|
||||||
throw Block::Driver::Io_error();
|
|
||||||
|
|
||||||
write<Data>(*src++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_blocks_dma(size_t block_number, size_t block_count,
|
|
||||||
Genode::addr_t out_buffer_phys,
|
|
||||||
Block::Packet_descriptor pkt)
|
|
||||||
{
|
|
||||||
using namespace Sd_card;
|
|
||||||
|
|
||||||
if (_block_transfer.pending) {
|
|
||||||
throw Block::Driver::Request_congestion(); }
|
|
||||||
|
|
||||||
write<Blk::Blen>(0x200);
|
|
||||||
write<Blk::Nblk>(block_count);
|
|
||||||
|
|
||||||
_setup_adma_descriptor_table(block_count, out_buffer_phys);
|
|
||||||
|
|
||||||
_block_transfer.packet = pkt;
|
|
||||||
_block_transfer.pending = true;
|
|
||||||
|
|
||||||
if (!issue_command(Read_multiple_block(block_number))) {
|
|
||||||
Genode::error("Read_multiple_block failed");
|
|
||||||
throw Block::Driver::Io_error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_blocks_dma(size_t block_number, size_t block_count,
|
|
||||||
Genode::addr_t buffer_phys,
|
|
||||||
Block::Packet_descriptor pkt)
|
|
||||||
{
|
|
||||||
using namespace Sd_card;
|
|
||||||
|
|
||||||
if (_block_transfer.pending) {
|
|
||||||
throw Block::Driver::Request_congestion(); }
|
|
||||||
|
|
||||||
write<Blk::Blen>(0x200);
|
|
||||||
write<Blk::Nblk>(block_count);
|
|
||||||
|
|
||||||
_setup_adma_descriptor_table(block_count, buffer_phys);
|
|
||||||
|
|
||||||
_block_transfer.packet = pkt;
|
|
||||||
_block_transfer.pending = true;
|
|
||||||
|
|
||||||
if (!issue_command(Write_multiple_block(block_number))) {
|
|
||||||
Genode::error("Write_multiple_block failed");
|
|
||||||
throw Block::Driver::Io_error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _DRIVERS__SD_CARD__SPEC__OMAP4__MMCHS_H_ */
|
|
239
repos/os/src/drivers/sd_card/spec/pl180/driver.cc
Normal file
239
repos/os/src/drivers/sd_card/spec/pl180/driver.cc
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
/*
|
||||||
|
* \brief PL180-specific implementation of the Block::Driver interface
|
||||||
|
* \author Christian Helmuth
|
||||||
|
* \author Martin Stein
|
||||||
|
* \date 2011-05-19
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2017 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <driver.h>
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
using namespace Sd_card;
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_write_command(unsigned cmd_index, bool resp)
|
||||||
|
{
|
||||||
|
enum Bits {
|
||||||
|
CmdIndex = 0x3f,
|
||||||
|
Response = 1 << 6,
|
||||||
|
Enable = 1 << 10
|
||||||
|
};
|
||||||
|
cmd_index = (cmd_index & CmdIndex) | Enable;
|
||||||
|
cmd_index |= (resp ? Response : 0);
|
||||||
|
|
||||||
|
_write_reg(Command, cmd_index);
|
||||||
|
|
||||||
|
while (!_read_reg(Status) & (CmdRespEnd | CmdSent)) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_request(unsigned char cmd,
|
||||||
|
unsigned *out_resp)
|
||||||
|
{
|
||||||
|
_write_reg(Argument, 0);
|
||||||
|
_write_command(cmd, (out_resp != 0));
|
||||||
|
if (out_resp)
|
||||||
|
*out_resp = _read_reg(Response0);
|
||||||
|
_clear_status();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_request(unsigned char cmd,
|
||||||
|
unsigned arg,
|
||||||
|
unsigned *out_resp)
|
||||||
|
{
|
||||||
|
_write_reg(Argument, arg);
|
||||||
|
_write_command(cmd, (out_resp != 0));
|
||||||
|
if (out_resp)
|
||||||
|
*out_resp = _read_reg(Response0);
|
||||||
|
_clear_status();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_read_request(unsigned char cmd,
|
||||||
|
unsigned arg,
|
||||||
|
unsigned length,
|
||||||
|
unsigned *out_resp)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* FIXME on real hardware the blocksize must be written into
|
||||||
|
* DataCtrl:BlockSize.
|
||||||
|
*/
|
||||||
|
enum { CTRL_ENABLE = 0x01, CTRL_READ = 0x02 };
|
||||||
|
|
||||||
|
_write_reg(DataLength, length);
|
||||||
|
_write_reg(DataCtrl, CTRL_ENABLE | CTRL_READ);
|
||||||
|
|
||||||
|
_write_reg(Argument, arg);
|
||||||
|
_write_command(cmd, (out_resp != 0));
|
||||||
|
if (out_resp)
|
||||||
|
*out_resp = _read_reg(Response0);
|
||||||
|
_clear_status();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_write_request(unsigned char cmd,
|
||||||
|
unsigned arg,
|
||||||
|
unsigned length,
|
||||||
|
unsigned *out_resp)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* FIXME on real hardware the blocksize must be written into
|
||||||
|
* DataCtrl:BlockSize.
|
||||||
|
*/
|
||||||
|
enum { CTRL_ENABLE = 0x01 };
|
||||||
|
|
||||||
|
_write_reg(DataLength, length);
|
||||||
|
_write_reg(DataCtrl, CTRL_ENABLE);
|
||||||
|
|
||||||
|
_write_reg(Argument, arg);
|
||||||
|
_write_command(cmd, (out_resp != 0));
|
||||||
|
if (out_resp)
|
||||||
|
*out_resp = _read_reg(Response0);
|
||||||
|
_clear_status();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_read_data(unsigned length,
|
||||||
|
char *out_buffer)
|
||||||
|
{
|
||||||
|
unsigned *buf = reinterpret_cast<unsigned *>(out_buffer);
|
||||||
|
for (unsigned count = 0; count < length / 4; ) {
|
||||||
|
/*
|
||||||
|
* The FIFO contains at least 'remainder - FifoCnt' words.
|
||||||
|
*/
|
||||||
|
int chunk = length / 4 - count - _read_reg(FifoCnt);
|
||||||
|
count += chunk;
|
||||||
|
for ( ; chunk > 0; --chunk)
|
||||||
|
buf[count - chunk] = _read_reg(FIFO);
|
||||||
|
}
|
||||||
|
_clear_status();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_write_data(unsigned length,
|
||||||
|
char const *buffer)
|
||||||
|
{
|
||||||
|
enum { FIFO_SIZE = 16 };
|
||||||
|
|
||||||
|
unsigned const *buf = reinterpret_cast<unsigned const *>(buffer);
|
||||||
|
for (unsigned count = 0; count < length / 4; ) {
|
||||||
|
uint32_t status;
|
||||||
|
while (!((status = _read_reg(Status)) & TxFifoHalfEmpty)) ;
|
||||||
|
|
||||||
|
int chunk = (status & TxFifoEmpty) ? FIFO_SIZE : FIFO_SIZE / 2;
|
||||||
|
count += chunk;
|
||||||
|
for ( ; chunk > 0; --chunk)
|
||||||
|
_write_reg(FIFO, buf[count - chunk]);
|
||||||
|
}
|
||||||
|
_clear_status();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Driver::Driver(Env &env) : Attached_mmio(PL180_PHYS, PL180_SIZE), _timer(env)
|
||||||
|
{
|
||||||
|
enum { POWER_UP = 2, POWER_ON = 3 };
|
||||||
|
|
||||||
|
_write_reg(Power, POWER_UP);
|
||||||
|
_timer.msleep(10);
|
||||||
|
_write_reg(Power, POWER_ON);
|
||||||
|
_timer.msleep(10);
|
||||||
|
_clear_status();
|
||||||
|
|
||||||
|
/* CMD0: go idle state */
|
||||||
|
_request(0, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CMD8: send interface condition
|
||||||
|
*
|
||||||
|
* XXX only one hard-coded value currently.
|
||||||
|
*/
|
||||||
|
unsigned resp;
|
||||||
|
_request(8, 0x1aa, &resp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ACMD41: card send operating condition
|
||||||
|
*
|
||||||
|
* This is an application-specific command and, therefore, consists
|
||||||
|
* of prefix command CMD55 + CMD41.
|
||||||
|
*/
|
||||||
|
_request(55, 0, &resp);
|
||||||
|
_request(41, 0x4000, &resp);
|
||||||
|
|
||||||
|
/* CMD2: all send card identification (CID) */
|
||||||
|
_request(2, &resp);
|
||||||
|
|
||||||
|
/* CMD3: send relative card address (RCA) */
|
||||||
|
_request(3, &resp);
|
||||||
|
unsigned short rca = resp >> 16;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now, the card is in transfer mode...
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* CMD7: select card */
|
||||||
|
_request(7, rca << 16, &resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::read(Block::sector_t block_number,
|
||||||
|
size_t block_count,
|
||||||
|
char *buffer,
|
||||||
|
Block::Packet_descriptor &packet)
|
||||||
|
{
|
||||||
|
unsigned resp;
|
||||||
|
unsigned length = block_size();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < block_count; ++i) {
|
||||||
|
/*
|
||||||
|
* CMD17: read single block
|
||||||
|
*
|
||||||
|
* SDSC cards use a byte address as argument while SDHC/SDSC uses a
|
||||||
|
* block address here.
|
||||||
|
*/
|
||||||
|
_read_request(17, (block_number + i) * block_size(),
|
||||||
|
length, &resp);
|
||||||
|
_read_data(length, buffer + (i * block_size()));
|
||||||
|
}
|
||||||
|
ack_packet(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::write(Block::sector_t block_number,
|
||||||
|
size_t block_count,
|
||||||
|
char const *buffer,
|
||||||
|
Block::Packet_descriptor &packet)
|
||||||
|
{
|
||||||
|
unsigned resp;
|
||||||
|
unsigned length = block_size();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < block_count; ++i) {
|
||||||
|
/*
|
||||||
|
* CMD24: write single block
|
||||||
|
*
|
||||||
|
* SDSC cards use a byte address as argument while SDHC/SDSC uses a
|
||||||
|
* block address here.
|
||||||
|
*/
|
||||||
|
_write_request(24, (block_number + i) * block_size(),
|
||||||
|
length, &resp);
|
||||||
|
_write_data(length, buffer + (i * block_size()));
|
||||||
|
}
|
||||||
|
ack_packet(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Block::Session::Operations Driver::ops()
|
||||||
|
{
|
||||||
|
Block::Session::Operations ops;
|
||||||
|
ops.set_operation(Block::Packet_descriptor::READ);
|
||||||
|
ops.set_operation(Block::Packet_descriptor::WRITE);
|
||||||
|
return ops;
|
||||||
|
}
|
@ -1,11 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* \brief SD-card protocol
|
* \brief PL180-specific implementation of the Block::Driver interface
|
||||||
* \author Christian Helmuth
|
* \author Christian Helmuth
|
||||||
|
* \author Martin Stein
|
||||||
* \date 2011-05-19
|
* \date 2011-05-19
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2013 Genode Labs GmbH
|
* Copyright (C) 2011-2017 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
@ -14,131 +15,122 @@
|
|||||||
#ifndef _DRIVER_H_
|
#ifndef _DRIVER_H_
|
||||||
#define _DRIVER_H_
|
#define _DRIVER_H_
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <block/driver.h>
|
|
||||||
|
|
||||||
/* local includes */
|
/* local includes */
|
||||||
#include <pl180.h>
|
#include <os/attached_mmio.h>
|
||||||
|
#include <block/driver.h>
|
||||||
#include <pl180_defs.h>
|
#include <pl180_defs.h>
|
||||||
|
#include <timer_session/connection.h>
|
||||||
|
|
||||||
namespace Block {
|
namespace Sd_card {
|
||||||
|
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
class Sdhci_driver;
|
|
||||||
|
class Driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Block::Sdhci_driver : public Block::Driver
|
class Sd_card::Driver : public Block::Driver, private Attached_mmio
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Pl180 _hd;
|
enum Register {
|
||||||
|
Power = 0x000, /* power control */
|
||||||
|
Argument = 0x008, /* argument for command */
|
||||||
|
Command = 0x00c, /* command index and type */
|
||||||
|
Response0 = 0x014, /* command response (card status, read only) */
|
||||||
|
DataLength = 0x028, /* number of bytes in data transfer (block size) */
|
||||||
|
DataCtrl = 0x02c, /* data transfer control */
|
||||||
|
Status = 0x034, /* controller status flags (read only) */
|
||||||
|
Clear = 0x038, /* status clear (write only) */
|
||||||
|
Mask0 = 0x03c, /* interrupt 0 mask */
|
||||||
|
Mask1 = 0x040, /* interrupt 1 mask */
|
||||||
|
FifoCnt = 0x048, /* data FIFO counter (in words, read only) */
|
||||||
|
FIFO = 0x080, /* data FIFO */
|
||||||
|
};
|
||||||
|
|
||||||
enum { BLOCK_SIZE = 512 };
|
enum Flag {
|
||||||
|
CmdCrcFail = 0x000001, /* command response received (CRC failed) */
|
||||||
|
DataCrcFail = 0x000002, /* data block sent/received (CRC failed) */
|
||||||
|
CmdTimeOut = 0x000004, /* command response timeout */
|
||||||
|
DataTimeOut = 0x000008, /* data timeout */
|
||||||
|
TxUnderrun = 0x000010, /* tx fifo underrun */
|
||||||
|
RxUnderrun = 0x000020, /* rx fifo underrun */
|
||||||
|
CmdRespEnd = 0x000040, /* command response received (CRC ok) */
|
||||||
|
CmdSent = 0x000080, /* command sent (no response required) */
|
||||||
|
DataEnd = 0x000100, /* data counter zero */
|
||||||
|
StartBitErr = 0x000200, /* start bit not detected */
|
||||||
|
DataBlockEnd = 0x000400, /* data block sent/received (CRC ok) */
|
||||||
|
CmdActive = 0x000800, /* command transfer in progress */
|
||||||
|
TxActive = 0x001000, /* data tx in progress */
|
||||||
|
RxActive = 0x002000, /* data rx in progress */
|
||||||
|
TxFifoHalfEmpty = 0x004000,
|
||||||
|
RxFifoHalfFull = 0x008000,
|
||||||
|
TxFifoFull = 0x010000,
|
||||||
|
RxFifoFull = 0x020000,
|
||||||
|
TxFifoEmpty = 0x040000,
|
||||||
|
RxFifoEmpty = 0x080000,
|
||||||
|
TxDataAvlbl = 0x100000,
|
||||||
|
RxDataAvlbl = 0x200000,
|
||||||
|
};
|
||||||
|
|
||||||
|
Timer::Connection _timer;
|
||||||
|
uint32_t volatile *_base { local_addr<unsigned volatile>() };
|
||||||
|
|
||||||
|
uint32_t _read_reg(Register reg) const { return _base[reg >> 2]; }
|
||||||
|
|
||||||
|
void _write_reg(Register reg, uint32_t v) { _base[reg >> 2] = v; }
|
||||||
|
|
||||||
|
void _clear_status() { _write_reg(Clear, ~0); }
|
||||||
|
|
||||||
|
void _request(unsigned char cmd,
|
||||||
|
unsigned *out_resp);
|
||||||
|
|
||||||
|
void _request(unsigned char cmd,
|
||||||
|
unsigned arg,
|
||||||
|
unsigned *out_resp);
|
||||||
|
|
||||||
|
void _read_request(unsigned char cmd,
|
||||||
|
unsigned arg,
|
||||||
|
unsigned length,
|
||||||
|
unsigned *out_resp);
|
||||||
|
|
||||||
|
void _write_request(unsigned char cmd,
|
||||||
|
unsigned arg,
|
||||||
|
unsigned length,
|
||||||
|
unsigned *out_resp);
|
||||||
|
|
||||||
|
void _read_data(unsigned length, char *out_buffer);
|
||||||
|
void _write_data(unsigned length, char const *buffer);
|
||||||
|
void _write_command(unsigned cmd_index, bool resp);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Sdhci_driver(Env &) : _hd(PL180_PHYS, PL180_SIZE)
|
|
||||||
{
|
|
||||||
unsigned resp;
|
|
||||||
|
|
||||||
/* CMD0: go idle state */
|
Driver(Env &env);
|
||||||
_hd.request(0, 0);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* CMD8: send interface condition
|
|
||||||
*
|
|
||||||
* XXX only one hard-coded value currently.
|
|
||||||
*/
|
|
||||||
_hd.request(8, 0x1aa, &resp);
|
|
||||||
|
|
||||||
/*
|
/******************
|
||||||
* ACMD41: card send operating condition
|
** Block-driver **
|
||||||
*
|
******************/
|
||||||
* This is an application-specific command and, therefore, consists
|
|
||||||
* of prefix command CMD55 + CMD41.
|
|
||||||
*/
|
|
||||||
_hd.request(55, 0, &resp);
|
|
||||||
_hd.request(41, 0x4000, &resp);
|
|
||||||
|
|
||||||
/* CMD2: all send card identification (CID) */
|
Genode::size_t block_size() override { return 512; }
|
||||||
_hd.request(2, &resp);
|
Block::Session::Operations ops() override;
|
||||||
|
|
||||||
/* CMD3: send relative card address (RCA) */
|
void read(Block::sector_t block_number,
|
||||||
_hd.request(3, &resp);
|
size_t block_count,
|
||||||
unsigned short rca = resp >> 16;
|
char *buffer,
|
||||||
|
Block::Packet_descriptor &packet);
|
||||||
|
|
||||||
/*
|
void write(Block::sector_t block_number,
|
||||||
* Now, the card is in transfer mode...
|
size_t block_count,
|
||||||
*/
|
char const *buffer,
|
||||||
|
Block::Packet_descriptor &packet);
|
||||||
|
|
||||||
/* CMD7: select card */
|
|
||||||
_hd.request(7, rca << 16, &resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
Host_driver &host_driver() { return _hd; }
|
|
||||||
|
|
||||||
/****************************
|
|
||||||
** Block-driver interface **
|
|
||||||
****************************/
|
|
||||||
|
|
||||||
Genode::size_t block_size() { return BLOCK_SIZE; }
|
|
||||||
/*
|
/*
|
||||||
* TODO report (and support) real capacity not just 512M
|
* TODO report (and support) real capacity not just 512M
|
||||||
*/
|
*/
|
||||||
Block::sector_t block_count() { return 0x20000000 / BLOCK_SIZE; }
|
Block::sector_t block_count() override {
|
||||||
|
return 0x20000000 / block_size(); }
|
||||||
Block::Session::Operations ops()
|
|
||||||
{
|
|
||||||
Block::Session::Operations o;
|
|
||||||
o.set_operation(Block::Packet_descriptor::READ);
|
|
||||||
o.set_operation(Block::Packet_descriptor::WRITE);
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
void read(Block::sector_t block_number,
|
|
||||||
Genode::size_t block_count,
|
|
||||||
char *out_buffer,
|
|
||||||
Block::Packet_descriptor &packet)
|
|
||||||
{
|
|
||||||
unsigned resp;
|
|
||||||
unsigned length = BLOCK_SIZE;
|
|
||||||
|
|
||||||
for (Genode::size_t i = 0; i < block_count; ++i) {
|
|
||||||
/*
|
|
||||||
* CMD17: read single block
|
|
||||||
*
|
|
||||||
* SDSC cards use a byte address as argument while SDHC/SDSC uses a
|
|
||||||
* block address here.
|
|
||||||
*/
|
|
||||||
_hd.read_request(17, (block_number + i) * BLOCK_SIZE,
|
|
||||||
length, &resp);
|
|
||||||
_hd.read_data(length, out_buffer + (i * BLOCK_SIZE));
|
|
||||||
}
|
|
||||||
ack_packet(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(Block::sector_t block_number,
|
|
||||||
Genode::size_t block_count,
|
|
||||||
char const *buffer,
|
|
||||||
Block::Packet_descriptor &packet)
|
|
||||||
{
|
|
||||||
unsigned resp;
|
|
||||||
unsigned length = BLOCK_SIZE;
|
|
||||||
|
|
||||||
for (Genode::size_t i = 0; i < block_count; ++i) {
|
|
||||||
/*
|
|
||||||
* CMD24: write single block
|
|
||||||
*
|
|
||||||
* SDSC cards use a byte address as argument while SDHC/SDSC uses a
|
|
||||||
* block address here.
|
|
||||||
*/
|
|
||||||
_hd.write_request(24, (block_number + i) * BLOCK_SIZE,
|
|
||||||
length, &resp);
|
|
||||||
_hd.write_data(length, buffer + (i * BLOCK_SIZE));
|
|
||||||
}
|
|
||||||
ack_packet(packet);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _DRIVER_H_ */
|
#endif /* _DRIVER_H_ */
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief SD-card host-interface driver
|
|
||||||
* \author Christian Helmuth
|
|
||||||
* \date 2011-05-19
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2011-2013 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU General Public License version 2.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _HOST_DRIVER_H_
|
|
||||||
#define _HOST_DRIVER_H_
|
|
||||||
|
|
||||||
struct Host_driver
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Send request
|
|
||||||
*
|
|
||||||
* \param cmd command index
|
|
||||||
* \param out_resp response for command
|
|
||||||
* (set to 0 if not expected)
|
|
||||||
*/
|
|
||||||
virtual void request(unsigned char cmd,
|
|
||||||
unsigned *out_resp) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send request
|
|
||||||
*
|
|
||||||
* \param cmd command index
|
|
||||||
* \param arg command argument
|
|
||||||
* \param out_resp response for command
|
|
||||||
* (set to 0 if not expected)
|
|
||||||
*/
|
|
||||||
virtual void request(unsigned char cmd,
|
|
||||||
unsigned arg,
|
|
||||||
unsigned *out_resp) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send data-read request
|
|
||||||
*
|
|
||||||
* \param cmd command index
|
|
||||||
* \param arg command argument
|
|
||||||
* \param length number of bytes in data transfer
|
|
||||||
* \param out_resp response for command
|
|
||||||
* (set to 0 if not expected)
|
|
||||||
*/
|
|
||||||
virtual void read_request(unsigned char cmd,
|
|
||||||
unsigned arg,
|
|
||||||
unsigned length,
|
|
||||||
unsigned *out_resp) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send data-write request
|
|
||||||
*
|
|
||||||
* \param cmd command index
|
|
||||||
* \param arg command argument
|
|
||||||
* \param length number of bytes in data transfer
|
|
||||||
* \param out_resp response for command
|
|
||||||
* (set to 0 if not expected)
|
|
||||||
*/
|
|
||||||
virtual void write_request(unsigned char cmd,
|
|
||||||
unsigned arg,
|
|
||||||
unsigned length,
|
|
||||||
unsigned *out_resp) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read data
|
|
||||||
*
|
|
||||||
* Used to read the data after a successful data-read request.
|
|
||||||
*/
|
|
||||||
virtual void read_data(unsigned length,
|
|
||||||
char *out_buffer) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write data
|
|
||||||
*
|
|
||||||
* Used to write the data after a successful data-write request.
|
|
||||||
*/
|
|
||||||
virtual void write_data(unsigned length,
|
|
||||||
char const *buffer) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _HOST_DRIVER_H_ */
|
|
@ -1,232 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief PL180 MMCI driver
|
|
||||||
* \author Christian Helmuth
|
|
||||||
* \date 2011-05-19
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2011-2016 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU General Public License version 2.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _DRIVERS__SD_CARD__SPEC__PL180__PL180_H_
|
|
||||||
#define _DRIVERS__SD_CARD__SPEC__PL180__PL180_H_
|
|
||||||
|
|
||||||
#include <base/log.h>
|
|
||||||
#include <os/attached_io_mem_dataspace.h>
|
|
||||||
#include <timer_session/connection.h>
|
|
||||||
|
|
||||||
#include "host_driver.h"
|
|
||||||
|
|
||||||
class Pl180 : public Host_driver
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Constants for flags, registers, etc. only visible in this file are
|
|
||||||
* exceptionally named in mixed case to relieve correlation between
|
|
||||||
* implementation and specification.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register offsets
|
|
||||||
*
|
|
||||||
* Registers are read/writable unless explicitly stated.
|
|
||||||
*/
|
|
||||||
enum Register {
|
|
||||||
Power = 0x000, /* power control */
|
|
||||||
Argument = 0x008, /* argument for command */
|
|
||||||
Command = 0x00c, /* command index and type */
|
|
||||||
Response0 = 0x014, /* command response (card status, read only) */
|
|
||||||
DataLength = 0x028, /* number of bytes in data transfer (block size) */
|
|
||||||
DataCtrl = 0x02c, /* data transfer control */
|
|
||||||
Status = 0x034, /* controller status flags (read only) */
|
|
||||||
Clear = 0x038, /* status clear (write only) */
|
|
||||||
Mask0 = 0x03c, /* interrupt 0 mask */
|
|
||||||
Mask1 = 0x040, /* interrupt 1 mask */
|
|
||||||
FifoCnt = 0x048, /* data FIFO counter (in words, read only) */
|
|
||||||
FIFO = 0x080, /* data FIFO */
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flags
|
|
||||||
*/
|
|
||||||
enum Flag {
|
|
||||||
CmdCrcFail = 0x000001, /* command response received (CRC failed) */
|
|
||||||
DataCrcFail = 0x000002, /* data block sent/received (CRC failed) */
|
|
||||||
CmdTimeOut = 0x000004, /* command response timeout */
|
|
||||||
DataTimeOut = 0x000008, /* data timeout */
|
|
||||||
TxUnderrun = 0x000010, /* tx fifo underrun */
|
|
||||||
RxUnderrun = 0x000020, /* rx fifo underrun */
|
|
||||||
CmdRespEnd = 0x000040, /* command response received (CRC ok) */
|
|
||||||
CmdSent = 0x000080, /* command sent (no response required) */
|
|
||||||
DataEnd = 0x000100, /* data counter zero */
|
|
||||||
StartBitErr = 0x000200, /* start bit not detected */
|
|
||||||
DataBlockEnd = 0x000400, /* data block sent/received (CRC ok) */
|
|
||||||
CmdActive = 0x000800, /* command transfer in progress */
|
|
||||||
TxActive = 0x001000, /* data tx in progress */
|
|
||||||
RxActive = 0x002000, /* data rx in progress */
|
|
||||||
TxFifoHalfEmpty = 0x004000,
|
|
||||||
RxFifoHalfFull = 0x008000,
|
|
||||||
TxFifoFull = 0x010000,
|
|
||||||
RxFifoFull = 0x020000,
|
|
||||||
TxFifoEmpty = 0x040000,
|
|
||||||
RxFifoEmpty = 0x080000,
|
|
||||||
TxDataAvlbl = 0x100000,
|
|
||||||
RxDataAvlbl = 0x200000,
|
|
||||||
};
|
|
||||||
|
|
||||||
Timer::Connection _timer;
|
|
||||||
Genode::Attached_io_mem_dataspace _io_mem;
|
|
||||||
Genode::uint32_t volatile *_base;
|
|
||||||
|
|
||||||
Genode::uint32_t _read_reg(Register reg) const
|
|
||||||
{
|
|
||||||
return _base[reg >> 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
void _write_reg(Register reg, Genode::uint32_t v)
|
|
||||||
{
|
|
||||||
_base[reg >> 2] = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _write_command(unsigned cmd_index, bool resp)
|
|
||||||
{
|
|
||||||
enum Bits {
|
|
||||||
CmdIndex = 0x3f,
|
|
||||||
Response = 1 << 6,
|
|
||||||
Enable = 1 << 10
|
|
||||||
};
|
|
||||||
|
|
||||||
cmd_index = (cmd_index & CmdIndex) | Enable;
|
|
||||||
cmd_index |= (resp ? Response : 0);
|
|
||||||
|
|
||||||
_write_reg(Command, cmd_index);
|
|
||||||
|
|
||||||
while (!_read_reg(Status) & (CmdRespEnd | CmdSent)) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _clear_status() { _write_reg(Clear, ~0); }
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Pl180(Genode::addr_t mmio_base, Genode::size_t mmio_size)
|
|
||||||
:
|
|
||||||
_io_mem(mmio_base, mmio_size),
|
|
||||||
_base(_io_mem.local_addr<unsigned volatile>())
|
|
||||||
{
|
|
||||||
enum { POWER_UP = 2, POWER_ON = 3 };
|
|
||||||
|
|
||||||
_write_reg(Power, POWER_UP);
|
|
||||||
_timer.msleep(10);
|
|
||||||
_write_reg(Power, POWER_ON);
|
|
||||||
_timer.msleep(10);
|
|
||||||
_clear_status();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***************************
|
|
||||||
** Host driver interface **
|
|
||||||
***************************/
|
|
||||||
|
|
||||||
void request(unsigned char cmd,
|
|
||||||
unsigned *out_resp)
|
|
||||||
{
|
|
||||||
_write_reg(Argument, 0);
|
|
||||||
_write_command(cmd, (out_resp != 0));
|
|
||||||
if (out_resp)
|
|
||||||
*out_resp = _read_reg(Response0);
|
|
||||||
_clear_status();
|
|
||||||
}
|
|
||||||
|
|
||||||
void request(unsigned char cmd,
|
|
||||||
unsigned arg,
|
|
||||||
unsigned *out_resp)
|
|
||||||
{
|
|
||||||
_write_reg(Argument, arg);
|
|
||||||
_write_command(cmd, (out_resp != 0));
|
|
||||||
if (out_resp)
|
|
||||||
*out_resp = _read_reg(Response0);
|
|
||||||
_clear_status();
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_request(unsigned char cmd,
|
|
||||||
unsigned arg,
|
|
||||||
unsigned length,
|
|
||||||
unsigned *out_resp)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* FIXME on real hardware the blocksize must be written into
|
|
||||||
* DataCtrl:BlockSize.
|
|
||||||
*/
|
|
||||||
enum { CTRL_ENABLE = 0x01, CTRL_READ = 0x02 };
|
|
||||||
|
|
||||||
_write_reg(DataLength, length);
|
|
||||||
_write_reg(DataCtrl, CTRL_ENABLE | CTRL_READ);
|
|
||||||
|
|
||||||
_write_reg(Argument, arg);
|
|
||||||
_write_command(cmd, (out_resp != 0));
|
|
||||||
if (out_resp)
|
|
||||||
*out_resp = _read_reg(Response0);
|
|
||||||
_clear_status();
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_request(unsigned char cmd,
|
|
||||||
unsigned arg,
|
|
||||||
unsigned length,
|
|
||||||
unsigned *out_resp)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* FIXME on real hardware the blocksize must be written into
|
|
||||||
* DataCtrl:BlockSize.
|
|
||||||
*/
|
|
||||||
enum { CTRL_ENABLE = 0x01 };
|
|
||||||
|
|
||||||
_write_reg(DataLength, length);
|
|
||||||
_write_reg(DataCtrl, CTRL_ENABLE);
|
|
||||||
|
|
||||||
_write_reg(Argument, arg);
|
|
||||||
_write_command(cmd, (out_resp != 0));
|
|
||||||
if (out_resp)
|
|
||||||
*out_resp = _read_reg(Response0);
|
|
||||||
_clear_status();
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_data(unsigned length,
|
|
||||||
char *out_buffer)
|
|
||||||
{
|
|
||||||
unsigned *buf = reinterpret_cast<unsigned *>(out_buffer);
|
|
||||||
for (unsigned count = 0; count < length / 4; ) {
|
|
||||||
/*
|
|
||||||
* The FIFO contains at least 'remainder - FifoCnt' words.
|
|
||||||
*/
|
|
||||||
int chunk = length / 4 - count - _read_reg(FifoCnt);
|
|
||||||
count += chunk;
|
|
||||||
for ( ; chunk > 0; --chunk)
|
|
||||||
buf[count - chunk] = _read_reg(FIFO);
|
|
||||||
}
|
|
||||||
_clear_status();
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_data(unsigned length,
|
|
||||||
char const *buffer)
|
|
||||||
{
|
|
||||||
enum { FIFO_SIZE = 16 };
|
|
||||||
|
|
||||||
unsigned const *buf = reinterpret_cast<unsigned const *>(buffer);
|
|
||||||
for (unsigned count = 0; count < length / 4; ) {
|
|
||||||
Genode::uint32_t status;
|
|
||||||
while (!((status = _read_reg(Status)) & TxFifoHalfEmpty)) ;
|
|
||||||
|
|
||||||
int chunk = (status & TxFifoEmpty) ? FIFO_SIZE : FIFO_SIZE / 2;
|
|
||||||
count += chunk;
|
|
||||||
for ( ; chunk > 0; --chunk)
|
|
||||||
_write_reg(FIFO, buf[count - chunk]);
|
|
||||||
}
|
|
||||||
_clear_status();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _DRIVERS__SD_CARD__SPEC__PL180__PL180_H_ */
|
|
318
repos/os/src/drivers/sd_card/spec/rpi/driver.cc
Normal file
318
repos/os/src/drivers/sd_card/spec/rpi/driver.cc
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
/*
|
||||||
|
* \brief Raspberry Pi SDHCI driver
|
||||||
|
* \author Norman Feske
|
||||||
|
* \author Christian Helmuth
|
||||||
|
* \author Timo Wischer
|
||||||
|
* \author Martin Stein
|
||||||
|
* \date 2014-09-21
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014-2017 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <driver.h>
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
using namespace Sd_card;
|
||||||
|
|
||||||
|
|
||||||
|
Driver::Driver(Env &env)
|
||||||
|
:
|
||||||
|
Attached_mmio(Board_base::SDHCI_BASE, Board_base::SDHCI_SIZE),
|
||||||
|
_ram(env.ram())
|
||||||
|
{
|
||||||
|
log("SD card detected");
|
||||||
|
log("capacity: ", _card_info.capacity_mb(), " MiB");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_set_and_enable_clock(unsigned divider)
|
||||||
|
{
|
||||||
|
Control1::access_t ctl1 = Mmio::Mmio::read<Control1>();
|
||||||
|
Control1::Clk_freq8::set(ctl1, divider);
|
||||||
|
Control1::Clk_freq_ms2::set(ctl1, 0);
|
||||||
|
Control1::Clk_internal_en::set(ctl1, 1);
|
||||||
|
Mmio::write<Control1>(ctl1);
|
||||||
|
|
||||||
|
if (!wait_for<Control1::Clk_internal_stable>(1, _delayer)) {
|
||||||
|
error("could not set internal clock");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
Mmio::write<Control1::Clk_en>(1);
|
||||||
|
_delayer.usleep(10*1000);
|
||||||
|
|
||||||
|
/* data timeout unit exponent */
|
||||||
|
Mmio::write<Control1::Data_tounit>(0xe);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Card_info Driver::_init()
|
||||||
|
{
|
||||||
|
/* reset host controller */
|
||||||
|
Control1::access_t v = Mmio::read<Control1>();
|
||||||
|
Control1::Srst_hc::set(v);
|
||||||
|
Control1::Srst_data::set(v);
|
||||||
|
Mmio::write<Control1>(v);
|
||||||
|
|
||||||
|
if (!wait_for<Control1::Srst_hc>(0, _delayer)) {
|
||||||
|
error("host-controller soft reset timed out");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
log("SDHCI version: ", Mmio::read<Host_version::Vendor>(), " "
|
||||||
|
"(specification ", Mmio::read<Host_version::Spec>() + 1, ".0)");
|
||||||
|
|
||||||
|
/* Enable sd card power */
|
||||||
|
Mmio::write<Host_ctrl>(Host_ctrl::Power::bits(1)
|
||||||
|
| Host_ctrl::Voltage::bits(Host_ctrl::Voltage::V33));
|
||||||
|
|
||||||
|
/* enable interrupt status reporting */
|
||||||
|
Mmio::write<Irpt_mask>(~0UL);
|
||||||
|
Mmio::write<Irpt_en>(~0UL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't read the capability register as the BCM2835 always
|
||||||
|
* returns all bits set to zero.
|
||||||
|
*/
|
||||||
|
|
||||||
|
_set_and_enable_clock(240);
|
||||||
|
|
||||||
|
if (!issue_command(Go_idle_state())) {
|
||||||
|
warning("Go_idle_state command failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
_delayer.usleep(2000);
|
||||||
|
|
||||||
|
if (!issue_command(Send_if_cond())) {
|
||||||
|
warning("Send_if_cond command failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
if (Mmio::read<Resp0>() != 0x1aa) {
|
||||||
|
error("unexpected response of Send_if_cond command");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to issue the same Sd_send_op_cond command multiple
|
||||||
|
* times. The first time, we receive the status information. On
|
||||||
|
* subsequent attempts, the response tells us that the card is
|
||||||
|
* busy. Usually, the command is issued twice. We give up if the
|
||||||
|
* card is not reaching busy state after one second.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int i = 1000;
|
||||||
|
for (; i > 0; --i) {
|
||||||
|
if (!issue_command(Sd_send_op_cond(0x18000, true))) {
|
||||||
|
warning("Sd_send_op_cond command failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
if (Ocr::Busy::get(Mmio::read<Resp0>()))
|
||||||
|
break;
|
||||||
|
|
||||||
|
_delayer.usleep(1000);
|
||||||
|
}
|
||||||
|
if (i == 0) {
|
||||||
|
error("Sd_send_op_cond timed out, could no power-on SD card");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
Card_info card_info = _detect();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Switch card to use 4 data signals
|
||||||
|
*/
|
||||||
|
if (!issue_command(Set_bus_width(Set_bus_width::Arg::Bus_width::FOUR_BITS),
|
||||||
|
card_info.rca())) {
|
||||||
|
warning("Set_bus_width(FOUR_BITS) command failed");
|
||||||
|
throw Detection_failed();
|
||||||
|
}
|
||||||
|
/* switch host controller to use 4 data signals */
|
||||||
|
Control0::access_t ctl0 = Mmio::read<Control0>();
|
||||||
|
Control0::Hctl_dwidth::set(ctl0);
|
||||||
|
Control0::Hctl_hs_en::set(ctl0);
|
||||||
|
Mmio::write<Control0>(ctl0);
|
||||||
|
_delayer.usleep(10*1000);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Accelerate clock, the divider is hard-coded for now.
|
||||||
|
*
|
||||||
|
* The Raspberry Pi report as clock of 250 MHz. According to the
|
||||||
|
* SDHCI specification, it is possible to driver SD cards with
|
||||||
|
* 50 MHz in high-speed mode (Hctl_hs_en).
|
||||||
|
*/
|
||||||
|
_set_and_enable_clock(5);
|
||||||
|
return card_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::_set_block_count(size_t block_count)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The 'Blksizecnt' register must be written in one step. If we
|
||||||
|
* used subsequent writes for the 'Blkcnt' and 'Blksize' bitfields,
|
||||||
|
* the host controller of the BCM2835 would fail to recognize any
|
||||||
|
* but the first write operation.
|
||||||
|
*/
|
||||||
|
Blksizecnt::access_t v = Mmio::read<Blksizecnt>();
|
||||||
|
Blksizecnt::Blkcnt::set(v, block_count);
|
||||||
|
Blksizecnt::Blksize::set(v, block_size());
|
||||||
|
Mmio::write<Blksizecnt>(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t Driver::_block_to_command_address(const size_t block_number)
|
||||||
|
{
|
||||||
|
/* use byte position for addressing with standard cards */
|
||||||
|
if (_card_info.version() == Csd3::Version::STANDARD_CAPACITY) {
|
||||||
|
return block_number * block_size();
|
||||||
|
}
|
||||||
|
return block_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Driver::_issue_command(Command_base const &command)
|
||||||
|
{
|
||||||
|
if (!_poll_and_wait_for<Status::Inhibit>(0)) {
|
||||||
|
error("controller inhibits issueing commands");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* write command argument */
|
||||||
|
Mmio::write<Arg1>(command.arg);
|
||||||
|
|
||||||
|
/* assemble command register */
|
||||||
|
Cmdtm::access_t cmd = 0;
|
||||||
|
Cmdtm::Index::set(cmd, command.index);
|
||||||
|
if (command.transfer != TRANSFER_NONE) {
|
||||||
|
|
||||||
|
Cmdtm::Isdata::set(cmd);
|
||||||
|
Cmdtm::Tm_blkcnt_en::set(cmd);
|
||||||
|
Cmdtm::Tm_multi_block::set(cmd);
|
||||||
|
|
||||||
|
if (command.index == Read_multiple_block::INDEX ||
|
||||||
|
command.index == Write_multiple_block::INDEX)
|
||||||
|
{
|
||||||
|
Cmdtm::Tm_auto_cmd_en::set(cmd, Cmdtm::Tm_auto_cmd_en::CMD12);
|
||||||
|
}
|
||||||
|
/* set data-direction bit depending on the command */
|
||||||
|
bool const read = command.transfer == TRANSFER_READ;
|
||||||
|
Cmdtm::Tm_dat_dir::set(cmd, read ? Cmdtm::Tm_dat_dir::READ
|
||||||
|
: Cmdtm::Tm_dat_dir::WRITE);
|
||||||
|
}
|
||||||
|
Cmdtm::access_t rsp_type = 0;
|
||||||
|
switch (command.rsp_type) {
|
||||||
|
case RESPONSE_NONE: rsp_type = Cmdtm::Rsp_type::RESPONSE_NONE; break;
|
||||||
|
case RESPONSE_136_BIT: rsp_type = Cmdtm::Rsp_type::RESPONSE_136_BIT; break;
|
||||||
|
case RESPONSE_48_BIT: rsp_type = Cmdtm::Rsp_type::RESPONSE_48_BIT; break;
|
||||||
|
case RESPONSE_48_BIT_WITH_BUSY: rsp_type = Cmdtm::Rsp_type::RESPONSE_48_BIT_WITH_BUSY; break;
|
||||||
|
}
|
||||||
|
Cmdtm::Rsp_type::set(cmd, rsp_type);
|
||||||
|
|
||||||
|
/* write command */
|
||||||
|
Mmio::write<Cmdtm>(cmd);
|
||||||
|
|
||||||
|
if (!_poll_and_wait_for<Interrupt::Cmd_done>(1)) {
|
||||||
|
error("command timed out");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* clear interrupt state */
|
||||||
|
Mmio::write<Interrupt::Cmd_done>(1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Cid Driver::_read_cid()
|
||||||
|
{
|
||||||
|
Cid cid;
|
||||||
|
cid.raw_0 = Mmio::read<Resp0_136>();
|
||||||
|
cid.raw_1 = Mmio::read<Resp1_136>();
|
||||||
|
cid.raw_2 = Mmio::read<Resp2_136>();
|
||||||
|
cid.raw_3 = Mmio::read<Resp3_136>();
|
||||||
|
return cid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Csd Driver::_read_csd()
|
||||||
|
{
|
||||||
|
Csd csd;
|
||||||
|
csd.csd0 = Mmio::read<Resp0_136>();
|
||||||
|
csd.csd1 = Mmio::read<Resp1_136>();
|
||||||
|
csd.csd2 = Mmio::read<Resp2_136>();
|
||||||
|
csd.csd3 = Mmio::read<Resp3_136>();
|
||||||
|
return csd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::read(Block::sector_t block_number,
|
||||||
|
size_t block_count,
|
||||||
|
char *out_buffer,
|
||||||
|
Block::Packet_descriptor &packet)
|
||||||
|
{
|
||||||
|
_set_block_count(block_count);
|
||||||
|
|
||||||
|
if (!issue_command(Read_multiple_block(_block_to_command_address(block_number)))) {
|
||||||
|
error("Read_multiple_block failed");
|
||||||
|
throw Io_error();
|
||||||
|
}
|
||||||
|
Data::access_t *dst = (Data::access_t *)(out_buffer);
|
||||||
|
for (size_t i = 0; i < block_count; i++) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check for buffer-read enable bit for each block
|
||||||
|
*
|
||||||
|
* According to the BCM2835 documentation, this bit is
|
||||||
|
* reserved but it actually corresponds to the bre status
|
||||||
|
* bit as described in the SDHCI specification.
|
||||||
|
*/
|
||||||
|
if (!_poll_and_wait_for<Status::Bre>(1)) {
|
||||||
|
throw Io_error(); }
|
||||||
|
|
||||||
|
/* read data from sdhci buffer */
|
||||||
|
for (size_t j = 0; j < block_size() / sizeof(Data::access_t); j++)
|
||||||
|
*dst++ = Mmio::read<Data>();
|
||||||
|
}
|
||||||
|
if (!_poll_and_wait_for<Interrupt::Data_done>(1)) {
|
||||||
|
error("completion of read request failed");
|
||||||
|
throw Io_error();
|
||||||
|
}
|
||||||
|
/* clear interrupt state */
|
||||||
|
Mmio::write<Interrupt::Data_done>(1);
|
||||||
|
|
||||||
|
ack_packet(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Driver::write(Block::sector_t block_number,
|
||||||
|
size_t block_count,
|
||||||
|
char const *buffer,
|
||||||
|
Block::Packet_descriptor &packet)
|
||||||
|
{
|
||||||
|
_set_block_count(block_count);
|
||||||
|
|
||||||
|
if (!issue_command(Write_multiple_block(_block_to_command_address(block_number)))) {
|
||||||
|
error("Write_multiple_block failed");
|
||||||
|
throw Io_error();
|
||||||
|
}
|
||||||
|
Data::access_t const *src = (Data::access_t const *)(buffer);
|
||||||
|
for (size_t i = 0; i < block_count; i++) {
|
||||||
|
|
||||||
|
/* check for buffer-write enable bit for each block */
|
||||||
|
if (!_poll_and_wait_for<Status::Bwe>(1)) {
|
||||||
|
throw Io_error(); }
|
||||||
|
|
||||||
|
/* write data into sdhci buffer */
|
||||||
|
for (size_t j = 0; j < block_size() / sizeof(Data::access_t); j++)
|
||||||
|
Mmio::write<Data>(*src++);
|
||||||
|
}
|
||||||
|
if (!_poll_and_wait_for<Interrupt::Data_done>(1)) {
|
||||||
|
error("completion of write request failed");
|
||||||
|
throw Io_error();
|
||||||
|
}
|
||||||
|
/* clear interrupt state */
|
||||||
|
Mmio::write<Interrupt::Data_done>(1);
|
||||||
|
|
||||||
|
ack_packet(packet);
|
||||||
|
}
|
@ -1,138 +1,216 @@
|
|||||||
/*
|
/*
|
||||||
* \brief Raspberry Pi implementation of the Block::Driver interface
|
* \brief Raspberry Pi SDHCI driver
|
||||||
* \author Norman Feske
|
* \author Norman Feske
|
||||||
* \author Timo Wischer
|
* \author Timo Wischer
|
||||||
|
* \author Martin Stein
|
||||||
* \date 2014-09-21
|
* \date 2014-09-21
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2014 Genode Labs GmbH
|
* Copyright (C) 2014-2017 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _DRIVERS__SD_CARD__SPEC__RPI__DRIVER_H_
|
#ifndef _DRIVER_H_
|
||||||
#define _DRIVERS__SD_CARD__SPEC__RPI__DRIVER_H_
|
#define _DRIVER_H_
|
||||||
|
|
||||||
#include <util/mmio.h>
|
/* Genode includes */
|
||||||
#include <os/attached_io_mem_dataspace.h>
|
|
||||||
#include <base/log.h>
|
|
||||||
#include <timer_session/connection.h>
|
#include <timer_session/connection.h>
|
||||||
#include <block/component.h>
|
|
||||||
#include <drivers/board_base.h>
|
#include <drivers/board_base.h>
|
||||||
|
#include <os/attached_mmio.h>
|
||||||
|
#include <irq_session/connection.h>
|
||||||
|
|
||||||
/* local includes */
|
/* local includes */
|
||||||
#include <sdhci.h>
|
#include <driver_base.h>
|
||||||
|
|
||||||
namespace Block {
|
namespace Sd_card { class Driver; }
|
||||||
using namespace Genode;
|
|
||||||
class Sdhci_driver;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Block::Sdhci_driver : public Block::Driver
|
class Sd_card::Driver : public Driver_base,
|
||||||
|
private Attached_mmio
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
struct Blksizecnt : Register<0x4, 32>
|
||||||
|
{
|
||||||
|
struct Blkcnt : Bitfield<16, 16> { };
|
||||||
|
struct Blksize : Bitfield<0, 10> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Resp0 : Register<0x10, 32> { };
|
||||||
|
struct Resp1 : Register<0x14, 32> { };
|
||||||
|
struct Resp2 : Register<0x18, 32> { };
|
||||||
|
struct Resp3 : Register<0x1c, 32> { };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle the SDHCI quirk that responses of 136-bit requests are
|
||||||
|
* shifted by 8 bits.
|
||||||
|
*/
|
||||||
|
template <off_t OFFSET>
|
||||||
|
struct Cmdresp_tpl : Register<OFFSET, 32>
|
||||||
|
{
|
||||||
|
struct Resp_8_24 : Register<OFFSET, 32>::template Bitfield<0, 24> { };
|
||||||
|
struct Resp_0_8 : Register<OFFSET, 32>::template Bitfield<24, 8> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Cmdresp0 : Cmdresp_tpl<0x10> { };
|
||||||
|
struct Cmdresp1 : Cmdresp_tpl<0x14> { };
|
||||||
|
struct Cmdresp2 : Cmdresp_tpl<0x18> { };
|
||||||
|
struct Cmdresp3 : Cmdresp_tpl<0x1c> { };
|
||||||
|
|
||||||
|
struct Resp0_136 : Bitset_2<Cmdresp3::Resp_0_8, Cmdresp0::Resp_8_24> { };
|
||||||
|
struct Resp1_136 : Bitset_2<Cmdresp0::Resp_0_8, Cmdresp1::Resp_8_24> { };
|
||||||
|
struct Resp2_136 : Bitset_2<Cmdresp1::Resp_0_8, Cmdresp2::Resp_8_24> { };
|
||||||
|
struct Resp3_136 : Bitset_2<Cmdresp2::Resp_0_8, Cmdresp3::Resp_8_24> { };
|
||||||
|
|
||||||
|
struct Data : Register<0x20, 32> { };
|
||||||
|
|
||||||
|
struct Control0 : Register<0x28, 32>
|
||||||
|
{
|
||||||
|
struct Hctl_dwidth : Bitfield<1, 1> { };
|
||||||
|
struct Hctl_hs_en : Bitfield<2, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Control1 : Register<0x2c, 32>
|
||||||
|
{
|
||||||
|
struct Clk_internal_en : Bitfield<0, 1> { };
|
||||||
|
struct Clk_internal_stable : Bitfield<1, 1> { };
|
||||||
|
struct Clk_en : Bitfield<2, 1> { };
|
||||||
|
|
||||||
|
struct Clk_freq8 : Bitfield<8, 8> { };
|
||||||
|
struct Clk_freq_ms2 : Bitfield<6, 2> { };
|
||||||
|
struct Data_tounit : Bitfield<16, 4> { };
|
||||||
|
struct Srst_hc : Bitfield<24, 1> { };
|
||||||
|
struct Srst_cmd : Bitfield<25, 1> { };
|
||||||
|
struct Srst_data : Bitfield<26, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Status : Register<0x24, 32>
|
||||||
|
{
|
||||||
|
struct Inhibit : Bitfield<0, 2> { };
|
||||||
|
struct Bwe : Bitfield<10, 1> { };
|
||||||
|
struct Bre : Bitfield<11, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Host_ctrl : Register<0x28, 32>
|
||||||
|
{
|
||||||
|
struct Voltage : Bitfield<9, 3> {
|
||||||
|
enum {
|
||||||
|
V18 = 0b101,
|
||||||
|
V30 = 0b110,
|
||||||
|
V33 = 0b111,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
struct Power : Bitfield<8, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Arg1 : Register<0x8, 32> { };
|
||||||
|
|
||||||
|
struct Cmdtm : Register<0xc, 32>
|
||||||
|
{
|
||||||
|
struct Index : Bitfield<24, 6> { };
|
||||||
|
struct Isdata : Bitfield<21, 1> { };
|
||||||
|
struct Tm_blkcnt_en : Bitfield<1, 1> { };
|
||||||
|
struct Tm_multi_block : Bitfield<5, 1> { };
|
||||||
|
struct Tm_auto_cmd_en : Bitfield<2, 2>
|
||||||
|
{
|
||||||
|
enum { CMD12 = 1 };
|
||||||
|
};
|
||||||
|
struct Tm_dat_dir : Bitfield<4, 1>
|
||||||
|
{
|
||||||
|
enum { WRITE = 0, READ = 1 };
|
||||||
|
};
|
||||||
|
struct Rsp_type : Bitfield<16, 2>
|
||||||
|
{
|
||||||
|
enum Response { RESPONSE_NONE = 0,
|
||||||
|
RESPONSE_136_BIT = 1,
|
||||||
|
RESPONSE_48_BIT = 2,
|
||||||
|
RESPONSE_48_BIT_WITH_BUSY = 3 };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Interrupt : Register<0x30, 32>
|
||||||
|
{
|
||||||
|
struct Cmd_done : Bitfield<0, 1> { };
|
||||||
|
struct Data_done : Bitfield<1, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Irpt_mask : Register<0x34, 32> { };
|
||||||
|
struct Irpt_en : Register<0x38, 32> { };
|
||||||
|
|
||||||
|
struct Capabilities : Register<0x40, 32> { };
|
||||||
|
|
||||||
|
struct Host_version : Register<0xFE, 16>
|
||||||
|
{
|
||||||
|
struct Spec : Bitfield<0, 8> { };
|
||||||
|
struct Vendor : Bitfield<8, 8> { };
|
||||||
|
};
|
||||||
|
|
||||||
struct Timer_delayer : Timer::Connection, Mmio::Delayer
|
struct Timer_delayer : Timer::Connection, Mmio::Delayer
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Implementation of 'Delayer' interface
|
|
||||||
*/
|
|
||||||
void usleep(unsigned us) { Timer::Connection::usleep(us); }
|
void usleep(unsigned us) { Timer::Connection::usleep(us); }
|
||||||
|
};
|
||||||
|
|
||||||
} _delayer;
|
Ram_session &_ram;
|
||||||
|
Timer_delayer _delayer;
|
||||||
|
Irq_connection _irq { Board_base::SDHCI_IRQ };
|
||||||
|
Card_info _card_info { _init() };
|
||||||
|
|
||||||
/* display sub system registers */
|
template <typename REG>
|
||||||
Attached_io_mem_dataspace _sdhci_mmio { Board_base::SDHCI_BASE, Board_base::SDHCI_SIZE };
|
bool _poll_and_wait_for(unsigned value)
|
||||||
|
{
|
||||||
|
/* poll for a while */
|
||||||
|
if (!wait_for<REG>(value, _delayer, 5000, 0)) {
|
||||||
|
|
||||||
/* host-controller instance */
|
/* if the value was not reached while polling, start sleeping */
|
||||||
Sdhci_controller _controller;
|
if (!wait_for<REG>(value, _delayer)) {
|
||||||
|
return false; }
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool const _use_dma;
|
Card_info _init();
|
||||||
|
void _set_and_enable_clock(unsigned divider);
|
||||||
|
void _set_block_count(size_t block_count);
|
||||||
|
size_t _block_to_command_address(size_t block_number);
|
||||||
|
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
** Host_controller **
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
bool _issue_command(Command_base const &command) override;
|
||||||
|
Cid _read_cid() override;
|
||||||
|
Csd _read_csd() override;
|
||||||
|
|
||||||
|
Card_info card_info() const override { return _card_info; }
|
||||||
|
|
||||||
|
unsigned _read_rca() override {
|
||||||
|
return Send_relative_addr::Response::Rca::get(Mmio::read<Resp0>()); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Sdhci_driver(Env &)
|
Driver(Env &env);
|
||||||
:
|
|
||||||
_controller((addr_t)_sdhci_mmio.local_addr<void>(),
|
|
||||||
_delayer, Board_base::SDHCI_IRQ, false, true),
|
|
||||||
_use_dma(false)
|
|
||||||
{
|
|
||||||
Sd_card::Card_info const card_info = _controller.card_info();
|
|
||||||
|
|
||||||
Genode::log("SD card detected");
|
|
||||||
Genode::log("capacity: ", card_info.capacity_mb(), " MiB");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************
|
/*******************
|
||||||
** Block::Driver interface **
|
** Block::Driver **
|
||||||
*****************************/
|
*******************/
|
||||||
|
|
||||||
Genode::size_t block_size() { return Sdhci_controller::Block_size; }
|
|
||||||
|
|
||||||
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,
|
void read(Block::sector_t block_number,
|
||||||
Genode::size_t block_count,
|
size_t block_count,
|
||||||
char *out_buffer,
|
char *buffer,
|
||||||
Packet_descriptor &packet)
|
Block::Packet_descriptor &packet) override;
|
||||||
{
|
|
||||||
if (!_controller.read_blocks(block_number, block_count, out_buffer))
|
|
||||||
throw Io_error();
|
|
||||||
ack_packet(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(Block::sector_t block_number,
|
void write(Block::sector_t block_number,
|
||||||
Genode::size_t block_count,
|
size_t block_count,
|
||||||
char const *buffer,
|
char const *buffer,
|
||||||
Packet_descriptor &packet)
|
Block::Packet_descriptor &packet) override;
|
||||||
{
|
|
||||||
if (!_controller.write_blocks(block_number, block_count, buffer))
|
|
||||||
throw Io_error();
|
|
||||||
ack_packet(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_dma(Block::sector_t block_number,
|
Ram_dataspace_capability alloc_dma_buffer(size_t size) override {
|
||||||
Genode::size_t block_count,
|
return _ram.alloc(size, UNCACHED); }
|
||||||
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 /* _DRIVERS__SD_CARD__SPEC__RPI__DRIVER_H_ */
|
#endif /* _DRIVER_H_ */
|
||||||
|
@ -1,561 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief SDHCI controller driver
|
|
||||||
* \author Norman Feske
|
|
||||||
* \author Christian Helmuth
|
|
||||||
* \author Timo Wischer
|
|
||||||
* \date 2014-09-21
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2014 Genode Labs GmbH
|
|
||||||
*
|
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
|
||||||
* under the terms of the GNU General Public License version 2.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _DRIVERS__SD_CARD__SPEC__RPI__SDHCI_H_
|
|
||||||
#define _DRIVERS__SD_CARD__SPEC__RPI__SDHCI_H_
|
|
||||||
|
|
||||||
/* Genode includes */
|
|
||||||
#include <util/mmio.h>
|
|
||||||
#include <os/attached_ram_dataspace.h>
|
|
||||||
#include <irq_session/connection.h>
|
|
||||||
#include <drivers/board_base.h>
|
|
||||||
|
|
||||||
/* local includes */
|
|
||||||
#include <sd_card.h>
|
|
||||||
|
|
||||||
|
|
||||||
struct Sdhci : Genode::Mmio
|
|
||||||
{
|
|
||||||
enum { verbose = false };
|
|
||||||
|
|
||||||
typedef Genode::size_t size_t;
|
|
||||||
|
|
||||||
struct Blksizecnt : Register<0x4, 32>
|
|
||||||
{
|
|
||||||
struct Blkcnt : Bitfield<16, 16> { };
|
|
||||||
struct Blksize : Bitfield<0, 10> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Resp0 : Register<0x10, 32> { };
|
|
||||||
struct Resp1 : Register<0x14, 32> { };
|
|
||||||
struct Resp2 : Register<0x18, 32> { };
|
|
||||||
struct Resp3 : Register<0x1c, 32> { };
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Handle the SDHCI quirk that responses of 136-bit requests are shifted
|
|
||||||
* by 8 bits.
|
|
||||||
*/
|
|
||||||
template <Genode::off_t OFFSET>
|
|
||||||
struct Cmdresp_tpl : Register<OFFSET, 32>
|
|
||||||
{
|
|
||||||
struct Resp_8_24 : Register<OFFSET, 32>::template Bitfield<0, 24> { };
|
|
||||||
struct Resp_0_8 : Register<OFFSET, 32>::template Bitfield<24, 8> { };
|
|
||||||
};
|
|
||||||
struct Cmdresp0 : Cmdresp_tpl<0x10> { };
|
|
||||||
struct Cmdresp1 : Cmdresp_tpl<0x14> { };
|
|
||||||
struct Cmdresp2 : Cmdresp_tpl<0x18> { };
|
|
||||||
struct Cmdresp3 : Cmdresp_tpl<0x1c> { };
|
|
||||||
|
|
||||||
struct Resp0_136 : Genode::Bitset_2<Cmdresp3::Resp_0_8, Cmdresp0::Resp_8_24> { };
|
|
||||||
struct Resp1_136 : Genode::Bitset_2<Cmdresp0::Resp_0_8, Cmdresp1::Resp_8_24> { };
|
|
||||||
struct Resp2_136 : Genode::Bitset_2<Cmdresp1::Resp_0_8, Cmdresp2::Resp_8_24> { };
|
|
||||||
struct Resp3_136 : Genode::Bitset_2<Cmdresp2::Resp_0_8, Cmdresp3::Resp_8_24> { };
|
|
||||||
|
|
||||||
struct Data : Register<0x20, 32> { };
|
|
||||||
|
|
||||||
struct Control0 : Register<0x28, 32>
|
|
||||||
{
|
|
||||||
struct Hctl_dwidth : Bitfield<1, 1> { };
|
|
||||||
struct Hctl_hs_en : Bitfield<2, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Control1 : Register<0x2c, 32>
|
|
||||||
{
|
|
||||||
struct Clk_internal_en : Bitfield<0, 1> { };
|
|
||||||
struct Clk_internal_stable : Bitfield<1, 1> { };
|
|
||||||
struct Clk_en : Bitfield<2, 1> { };
|
|
||||||
|
|
||||||
struct Clk_freq8 : Bitfield<8, 8> { };
|
|
||||||
struct Clk_freq_ms2 : Bitfield<6, 2> { };
|
|
||||||
struct Data_tounit : Bitfield<16, 4> { };
|
|
||||||
struct Srst_hc : Bitfield<24, 1> { };
|
|
||||||
struct Srst_cmd : Bitfield<25, 1> { };
|
|
||||||
struct Srst_data : Bitfield<26, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Status : Register<0x24, 32>
|
|
||||||
{
|
|
||||||
struct Inhibit : Bitfield<0, 2> { };
|
|
||||||
struct Bwe : Bitfield<10, 1> { };
|
|
||||||
struct Bre : Bitfield<11, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Host_ctrl : Register<0x28, 32>
|
|
||||||
{
|
|
||||||
struct Voltage : Bitfield<9, 3> {
|
|
||||||
enum {
|
|
||||||
V18 = 0b101,
|
|
||||||
V30 = 0b110,
|
|
||||||
V33 = 0b111,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
struct Power : Bitfield<8, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Arg1 : Register<0x8, 32> { };
|
|
||||||
|
|
||||||
struct Cmdtm : Register<0xc, 32>
|
|
||||||
{
|
|
||||||
struct Index : Bitfield<24, 6> { };
|
|
||||||
struct Isdata : Bitfield<21, 1> { };
|
|
||||||
struct Tm_blkcnt_en : Bitfield<1, 1> { };
|
|
||||||
struct Tm_multi_block : Bitfield<5, 1> { };
|
|
||||||
struct Tm_auto_cmd_en : Bitfield<2, 2>
|
|
||||||
{
|
|
||||||
enum { CMD12 = 1 };
|
|
||||||
};
|
|
||||||
struct Tm_dat_dir : Bitfield<4, 1>
|
|
||||||
{
|
|
||||||
enum { WRITE = 0, READ = 1 };
|
|
||||||
};
|
|
||||||
struct Rsp_type : Bitfield<16, 2>
|
|
||||||
{
|
|
||||||
enum Response { RESPONSE_NONE = 0,
|
|
||||||
RESPONSE_136_BIT = 1,
|
|
||||||
RESPONSE_48_BIT = 2,
|
|
||||||
RESPONSE_48_BIT_WITH_BUSY = 3 };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Interrupt : Register<0x30, 32>
|
|
||||||
{
|
|
||||||
struct Cmd_done : Bitfield<0, 1> { };
|
|
||||||
struct Data_done : Bitfield<1, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Irpt_mask : Register<0x34, 32> { };
|
|
||||||
struct Irpt_en : Register<0x38, 32> { };
|
|
||||||
|
|
||||||
struct Capabilities : Register<0x40, 32> { };
|
|
||||||
|
|
||||||
struct Host_version : Register<0xFE, 16>
|
|
||||||
{
|
|
||||||
struct Spec : Bitfield<0, 8> { };
|
|
||||||
struct Vendor : Bitfield<8, 8> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
Sdhci(Genode::addr_t const mmio_base) : Genode::Mmio(mmio_base) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct Sdhci_controller : private Sdhci, public Sd_card::Host_controller
|
|
||||||
{
|
|
||||||
enum { Block_size = 0x200 };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
Delayer &_delayer;
|
|
||||||
Sd_card::Card_info _card_info;
|
|
||||||
|
|
||||||
Genode::Irq_connection _irq;
|
|
||||||
|
|
||||||
void _set_and_enable_clock(unsigned divider)
|
|
||||||
{
|
|
||||||
Control1::access_t v = read<Control1>();
|
|
||||||
Control1::Clk_freq8::set(v, divider);
|
|
||||||
Control1::Clk_freq_ms2::set(v, 0);
|
|
||||||
Control1::Clk_internal_en::set(v, 1);
|
|
||||||
|
|
||||||
write<Control1>(v);
|
|
||||||
|
|
||||||
if (!wait_for<Control1::Clk_internal_stable>(1, _delayer)) {
|
|
||||||
Genode::error("could not set internal clock");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
write<Control1::Clk_en>(1);
|
|
||||||
|
|
||||||
_delayer.usleep(10*1000);
|
|
||||||
|
|
||||||
/* data timeout unit exponent */
|
|
||||||
write<Control1::Data_tounit>(0xe);
|
|
||||||
}
|
|
||||||
|
|
||||||
Sd_card::Card_info _init(const bool set_voltage)
|
|
||||||
{
|
|
||||||
using namespace Sd_card;
|
|
||||||
|
|
||||||
/* reset host controller */
|
|
||||||
{
|
|
||||||
Control1::access_t v = read<Control1>();
|
|
||||||
Control1::Srst_hc::set(v);
|
|
||||||
Control1::Srst_data::set(v);
|
|
||||||
write<Control1>(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wait_for<Control1::Srst_hc>(0, _delayer)) {
|
|
||||||
Genode::error("host-controller soft reset timed out");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
Genode::log("SDHCI version: ", read<Host_version::Vendor>(), " "
|
|
||||||
"(specification ", read<Host_version::Spec>() + 1, ".0)");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Raspberry Pi (BCM2835) does not need to
|
|
||||||
* set the sd card voltage and
|
|
||||||
* power up the host controller.
|
|
||||||
* This registers are reserved and
|
|
||||||
* always have to be written to 0.
|
|
||||||
*/
|
|
||||||
if (set_voltage) {
|
|
||||||
/* Enable sd card power */
|
|
||||||
write<Host_ctrl>(Host_ctrl::Power::bits(1)
|
|
||||||
| Host_ctrl::Voltage::bits(Host_ctrl::Voltage::V33));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* enable interrupt status reporting */
|
|
||||||
write<Irpt_mask>(~0UL);
|
|
||||||
write<Irpt_en>(~0UL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We don't read the capability register as the BCM2835 always
|
|
||||||
* returns all bits set to zero.
|
|
||||||
*/
|
|
||||||
|
|
||||||
_set_and_enable_clock(240);
|
|
||||||
|
|
||||||
if (!issue_command(Go_idle_state())) {
|
|
||||||
Genode::warning("Go_idle_state command failed");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
_delayer.usleep(2000);
|
|
||||||
|
|
||||||
if (!issue_command(Send_if_cond())) {
|
|
||||||
Genode::warning("Send_if_cond command failed");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (read<Resp0>() != 0x1aa) {
|
|
||||||
Genode::error("unexpected response of Send_if_cond command");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We need to issue the same Sd_send_op_cond command multiple
|
|
||||||
* times. The first time, we receive the status information. On
|
|
||||||
* subsequent attempts, the response tells us that the card is
|
|
||||||
* busy. Usually, the command is issued twice. We give up if the
|
|
||||||
* card is not reaching busy state after one second.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int i = 1000;
|
|
||||||
for (; i > 0; --i) {
|
|
||||||
if (!issue_command(Sd_send_op_cond(0x18000, true))) {
|
|
||||||
Genode::warning("Sd_send_op_cond command failed");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Sd_card::Ocr::Busy::get(read<Resp0>()))
|
|
||||||
break;
|
|
||||||
|
|
||||||
_delayer.usleep(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
Genode::error("Sd_send_op_cond timed out, could no power-on SD card");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
Card_info card_info = _detect();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Switch card to use 4 data signals
|
|
||||||
*/
|
|
||||||
if (!issue_command(Set_bus_width(Set_bus_width::Arg::Bus_width::FOUR_BITS),
|
|
||||||
card_info.rca())) {
|
|
||||||
Genode::warning("Set_bus_width(FOUR_BITS) command failed");
|
|
||||||
throw Detection_failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* switch host controller to use 4 data signals */
|
|
||||||
{
|
|
||||||
Control0::access_t v = read<Control0>();
|
|
||||||
Control0::Hctl_dwidth::set(v);
|
|
||||||
Control0::Hctl_hs_en::set(v);
|
|
||||||
write<Control0>(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
_delayer.usleep(10*1000);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Accelerate clock, the divider is hard-coded for now.
|
|
||||||
*
|
|
||||||
* The Raspberry Pi report as clock of 250 MHz. According to the
|
|
||||||
* SDHCI specification, it is possible to driver SD cards with
|
|
||||||
* 50 MHz in high-speed mode (Hctl_hs_en).
|
|
||||||
*/
|
|
||||||
_set_and_enable_clock(5);
|
|
||||||
|
|
||||||
return card_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the block count for the next data transfer
|
|
||||||
*/
|
|
||||||
void _set_block_count(size_t block_count)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* The 'Blksizecnt' register must be written in one step. If we
|
|
||||||
* used subsequent writes for the 'Blkcnt' and 'Blksize' bitfields,
|
|
||||||
* the host controller of the BCM2835 would fail to recognize any
|
|
||||||
* but the first write operation.
|
|
||||||
*/
|
|
||||||
Blksizecnt::access_t v = read<Blksizecnt>();
|
|
||||||
Blksizecnt::Blkcnt::set(v, block_count);
|
|
||||||
Blksizecnt::Blksize::set(v, Block_size);
|
|
||||||
write<Blksizecnt>(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename REG>
|
|
||||||
bool _poll_and_wait_for(unsigned value)
|
|
||||||
{
|
|
||||||
/* poll for a while */
|
|
||||||
if (wait_for<REG>(value, _delayer, 5000, 0))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
/* if the value were not reached while polling, start sleeping */
|
|
||||||
return wait_for<REG>(value, _delayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* \param mmio_base local base address of MMIO registers
|
|
||||||
*/
|
|
||||||
Sdhci_controller(Genode::addr_t const mmio_base, Delayer &delayer,
|
|
||||||
unsigned irq, bool use_dma, const bool set_voltage = false)
|
|
||||||
:
|
|
||||||
Sdhci(mmio_base), _delayer(delayer), _card_info(_init(set_voltage)), _irq(irq)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************
|
|
||||||
** Sd_card::Host_controller interface **
|
|
||||||
****************************************/
|
|
||||||
|
|
||||||
bool _issue_command(Sd_card::Command_base const &command)
|
|
||||||
{
|
|
||||||
if (verbose)
|
|
||||||
Genode::log("-> ", command);
|
|
||||||
|
|
||||||
if (!_poll_and_wait_for<Status::Inhibit>(0)) {
|
|
||||||
Genode::error("controller inhibits issueing commands");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* write command argument */
|
|
||||||
write<Arg1>(command.arg);
|
|
||||||
|
|
||||||
/* assemble command register */
|
|
||||||
Cmdtm::access_t cmd = 0;
|
|
||||||
Cmdtm::Index::set(cmd, command.index);
|
|
||||||
if (command.transfer != Sd_card::TRANSFER_NONE) {
|
|
||||||
|
|
||||||
Cmdtm::Isdata::set(cmd);
|
|
||||||
Cmdtm::Tm_blkcnt_en::set(cmd);
|
|
||||||
Cmdtm::Tm_multi_block::set(cmd);
|
|
||||||
|
|
||||||
if (command.index == Sd_card::Read_multiple_block::INDEX
|
|
||||||
|| command.index == Sd_card::Write_multiple_block::INDEX) {
|
|
||||||
Cmdtm::Tm_auto_cmd_en::set(cmd, Cmdtm::Tm_auto_cmd_en::CMD12);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set data-direction bit depending on the command */
|
|
||||||
bool const read = command.transfer == Sd_card::TRANSFER_READ;
|
|
||||||
Cmdtm::Tm_dat_dir::set(cmd, read ? Cmdtm::Tm_dat_dir::READ
|
|
||||||
: Cmdtm::Tm_dat_dir::WRITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
Cmdtm::access_t rsp_type = 0;
|
|
||||||
switch (command.rsp_type) {
|
|
||||||
case Sd_card::RESPONSE_NONE: rsp_type = Cmdtm::Rsp_type::RESPONSE_NONE; break;
|
|
||||||
case Sd_card::RESPONSE_136_BIT: rsp_type = Cmdtm::Rsp_type::RESPONSE_136_BIT; break;
|
|
||||||
case Sd_card::RESPONSE_48_BIT: rsp_type = Cmdtm::Rsp_type::RESPONSE_48_BIT; break;
|
|
||||||
case Sd_card::RESPONSE_48_BIT_WITH_BUSY: rsp_type = Cmdtm::Rsp_type::RESPONSE_48_BIT_WITH_BUSY; break;
|
|
||||||
}
|
|
||||||
Cmdtm::Rsp_type::set(cmd, rsp_type);
|
|
||||||
|
|
||||||
/* write command */
|
|
||||||
write<Cmdtm>(cmd);
|
|
||||||
|
|
||||||
if (!_poll_and_wait_for<Interrupt::Cmd_done>(1)) {
|
|
||||||
Genode::error("command timed out");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clear interrupt state */
|
|
||||||
write<Interrupt::Cmd_done>(1);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sd_card::Card_info card_info() const
|
|
||||||
{
|
|
||||||
return _card_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sd_card::Cid _read_cid()
|
|
||||||
{
|
|
||||||
Sd_card::Cid cid;
|
|
||||||
cid.raw_0 = read<Resp0_136>();
|
|
||||||
cid.raw_1 = read<Resp1_136>();
|
|
||||||
cid.raw_2 = read<Resp2_136>();
|
|
||||||
cid.raw_3 = read<Resp3_136>();
|
|
||||||
return cid;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sd_card::Csd _read_csd()
|
|
||||||
{
|
|
||||||
Sd_card::Csd csd;
|
|
||||||
csd.csd0 = read<Resp0_136>();
|
|
||||||
csd.csd1 = read<Resp1_136>();
|
|
||||||
csd.csd2 = read<Resp2_136>();
|
|
||||||
csd.csd3 = read<Resp3_136>();
|
|
||||||
return csd;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned _read_rca()
|
|
||||||
{
|
|
||||||
return Sd_card::Send_relative_addr::Response::Rca::get(read<Resp0>());
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t _block_to_command_address(const size_t block_number)
|
|
||||||
{
|
|
||||||
/* use byte position for addressing with standard cards */
|
|
||||||
if (_card_info.version() == Sd_card::Csd3::Version::STANDARD_CAPACITY) {
|
|
||||||
return block_number * Block_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return block_number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read data blocks from SD card
|
|
||||||
*
|
|
||||||
* \return true on success
|
|
||||||
*/
|
|
||||||
bool read_blocks(size_t block_number, size_t block_count, char *out_buffer)
|
|
||||||
{
|
|
||||||
using namespace Sd_card;
|
|
||||||
|
|
||||||
_set_block_count(block_count);
|
|
||||||
|
|
||||||
if (!issue_command(Read_multiple_block(_block_to_command_address(block_number)))) {
|
|
||||||
Genode::error("Read_multiple_block failed, Status: ",
|
|
||||||
Genode::Hex(read<Status>()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Data::access_t *dst = (Data::access_t *)(out_buffer);
|
|
||||||
for (size_t i = 0; i < block_count; i++) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check for buffer-read enable bit for each block
|
|
||||||
*
|
|
||||||
* According to the BCM2835 documentation, this bit is
|
|
||||||
* reserved but it actually corresponds to the bre status
|
|
||||||
* bit as described in the SDHCI specification.
|
|
||||||
*/
|
|
||||||
if (!_poll_and_wait_for<Status::Bre>(1))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* read data from sdhci buffer */
|
|
||||||
for (size_t j = 0; j < 512/sizeof(Data::access_t); j++)
|
|
||||||
*dst++ = read<Data>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_poll_and_wait_for<Interrupt::Data_done>(1)) {
|
|
||||||
Genode::error("completion of read request failed (interrupt "
|
|
||||||
"status ", Genode::Hex(read<Interrupt>()), ")");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clear interrupt state */
|
|
||||||
write<Interrupt::Data_done>(1);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write data blocks to SD card
|
|
||||||
*
|
|
||||||
* \return true on success
|
|
||||||
*/
|
|
||||||
bool write_blocks(size_t block_number, size_t block_count, char const *buffer)
|
|
||||||
{
|
|
||||||
using namespace Sd_card;
|
|
||||||
|
|
||||||
_set_block_count(block_count);
|
|
||||||
|
|
||||||
if (!issue_command(Write_multiple_block(_block_to_command_address(block_number)))) {
|
|
||||||
Genode::error("Write_multiple_block failed, Status: ",
|
|
||||||
Genode::Hex(read<Status>()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Data::access_t const *src = (Data::access_t const *)(buffer);
|
|
||||||
for (size_t i = 0; i < block_count; i++) {
|
|
||||||
|
|
||||||
/* check for buffer-write enable bit for each block */
|
|
||||||
if (!_poll_and_wait_for<Status::Bwe>(1))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* write data into sdhci buffer */
|
|
||||||
for (size_t j = 0; j < 512/sizeof(Data::access_t); j++)
|
|
||||||
write<Data>(*src++);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_poll_and_wait_for<Interrupt::Data_done>(1)) {
|
|
||||||
Genode::error("completion of write request failed (interrupt "
|
|
||||||
"status ", read<Interrupt>(), ")");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clear interrupt state */
|
|
||||||
write<Interrupt::Data_done>(1);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read data blocks from SD card via master DMA
|
|
||||||
*
|
|
||||||
* \return true on success
|
|
||||||
*/
|
|
||||||
bool read_blocks_dma(size_t block_number, size_t block_count,
|
|
||||||
Genode::addr_t out_buffer_phys)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write data blocks to SD card via master DMA
|
|
||||||
*
|
|
||||||
* \return true on success
|
|
||||||
*/
|
|
||||||
bool write_blocks_dma(size_t block_number, size_t block_count,
|
|
||||||
Genode::addr_t buffer_phys)
|
|
||||||
{
|
|
||||||
using namespace Sd_card;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _DRIVERS__SD_CARD__SPEC__RPI__SDHCI_H_ */
|
|
@ -55,7 +55,7 @@ struct Main
|
|||||||
Operation operation { READ };
|
Operation operation { READ };
|
||||||
Signal_handler<Main> ack_handler { env.ep(), *this, &Main::update_state };
|
Signal_handler<Main> ack_handler { env.ep(), *this, &Main::update_state };
|
||||||
Driver_session drv_session { ack_handler };
|
Driver_session drv_session { ack_handler };
|
||||||
Block::Sdhci_driver drv { env };
|
Sd_card::Driver drv { env };
|
||||||
size_t const buf_size_kib { config()->xml_node()
|
size_t const buf_size_kib { config()->xml_node()
|
||||||
.attribute_value("buffer_size_kib",
|
.attribute_value("buffer_size_kib",
|
||||||
(size_t)0) };
|
(size_t)0) };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user