mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-18 10:46:25 +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
|
||||
SRC_CC += spec/exynos5/driver.cc
|
||||
|
||||
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/exynos5
|
||||
SRC_CC += spec/exynos5/driver.cc
|
||||
|
||||
include $(REP_DIR)/lib/mk/sd_card.inc
|
||||
|
@ -1,7 +1,7 @@
|
||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
|
||||
SRC_CC += spec/imx/adma2.cc
|
||||
SRC_CC += spec/imx/sdhc.cc
|
||||
SRC_CC += spec/imx53/sdhc.cc
|
||||
SRC_CC += adma2.cc
|
||||
SRC_CC += spec/imx/driver.cc
|
||||
SRC_CC += spec/imx53/driver.cc
|
||||
|
||||
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
|
||||
SRC_CC += spec/imx/adma2.cc
|
||||
SRC_CC += spec/imx/sdhc.cc
|
||||
SRC_CC += spec/imx53/sdhc.cc
|
||||
SRC_CC += adma2.cc
|
||||
SRC_CC += spec/imx/driver.cc
|
||||
SRC_CC += spec/imx53/driver.cc
|
||||
|
||||
include $(REP_DIR)/lib/mk/sd_card.inc
|
||||
|
@ -1,7 +1,7 @@
|
||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
|
||||
SRC_CC += spec/imx/adma2.cc
|
||||
SRC_CC += spec/imx/sdhc.cc
|
||||
SRC_CC += spec/imx6/sdhc.cc
|
||||
SRC_CC += adma2.cc
|
||||
SRC_CC += spec/imx/driver.cc
|
||||
SRC_CC += spec/imx6/driver.cc
|
||||
|
||||
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx
|
||||
SRC_CC += spec/imx/adma2.cc
|
||||
SRC_CC += spec/imx/sdhc.cc
|
||||
SRC_CC += spec/imx6/sdhc.cc
|
||||
SRC_CC += adma2.cc
|
||||
SRC_CC += spec/imx/driver.cc
|
||||
SRC_CC += spec/imx6/driver.cc
|
||||
|
||||
include $(REP_DIR)/lib/mk/sd_card.inc
|
||||
|
@ -1,4 +1,5 @@
|
||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/omap4
|
||||
SRC_CC += spec/omap4/driver.cc
|
||||
|
||||
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/omap4
|
||||
SRC_CC += spec/omap4/driver.cc
|
||||
|
||||
include $(REP_DIR)/lib/mk/sd_card.inc
|
||||
|
@ -1,4 +1,5 @@
|
||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/pl180
|
||||
SRC_CC += spec/pl180/driver.cc
|
||||
|
||||
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/pl180
|
||||
SRC_CC += spec/pl180/driver.cc
|
||||
|
||||
include $(REP_DIR)/lib/mk/sd_card.inc
|
||||
|
@ -1,4 +1,5 @@
|
||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/rpi
|
||||
SRC_CC += spec/rpi/driver.cc
|
||||
|
||||
vpath main.cc $(REP_DIR)/src/test/sd_card_bench
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/rpi
|
||||
SRC_CC += spec/rpi/driver.cc
|
||||
|
||||
include $(REP_DIR)/lib/mk/sd_card.inc
|
||||
|
@ -20,9 +20,9 @@
|
||||
using namespace Adma2;
|
||||
|
||||
|
||||
Table::Table()
|
||||
Table::Table(Ram_session &ram)
|
||||
:
|
||||
_ds(env()->ram_session(), _ds_size, UNCACHED),
|
||||
_ds(&ram, _ds_size, UNCACHED),
|
||||
_base_virt(_ds.local_addr<Desc::access_t>()),
|
||||
_base_phys(Dataspace_client(_ds.cap()).phys_addr())
|
||||
{ }
|
@ -66,7 +66,7 @@ class Adma2::Table
|
||||
|
||||
public:
|
||||
|
||||
Table();
|
||||
Table(Ram_session &ram);
|
||||
|
||||
/**
|
||||
* 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) { }
|
||||
|
||||
Block::Driver *create() {
|
||||
return new (&heap) Block::Sdhci_driver(env); }
|
||||
return new (&heap) Sd_card::Driver(env); }
|
||||
|
||||
void destroy(Block::Driver *driver) {
|
||||
Genode::destroy(&heap, static_cast<Block::Sdhci_driver*>(driver)); }
|
||||
Genode::destroy(&heap, static_cast<Sd_card::Driver*>(driver)); }
|
||||
|
||||
} factory { env, heap };
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
#define _SD_CARD_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/register.h>
|
||||
#include <util/mmio.h>
|
||||
|
||||
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
|
||||
* \author Sebastian Sumpf
|
||||
* \author Martin Stein
|
||||
* \date 2013-03-22
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _DRIVERS__SD_CARD__SPEC__EXYNOS5__DRIVER_H_
|
||||
#define _DRIVERS__SD_CARD__SPEC__EXYNOS5__DRIVER_H_
|
||||
#ifndef _DRIVER_H_
|
||||
#define _DRIVER_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/mmio.h>
|
||||
#include <os/attached_io_mem_dataspace.h>
|
||||
#include <base/log.h>
|
||||
#include <os/attached_mmio.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <os/server.h>
|
||||
#include <drivers/board_base.h>
|
||||
#include <regulator_session/connection.h>
|
||||
#include <irq_session/connection.h>
|
||||
#include <os/attached_ram_dataspace.h>
|
||||
|
||||
/* local includes */
|
||||
#include <dwmmc.h>
|
||||
#include <driver_base.h>
|
||||
|
||||
namespace Block {
|
||||
using namespace Genode;
|
||||
class Sdhci_driver;
|
||||
}
|
||||
namespace Sd_card { class Driver; }
|
||||
|
||||
|
||||
class Block::Sdhci_driver : public Block::Driver,
|
||||
public Sd_ack_handler
|
||||
class Sd_card::Driver : public Driver_base,
|
||||
private Attached_mmio
|
||||
{
|
||||
private:
|
||||
|
||||
struct Timer_delayer : Timer::Connection, Mmio::Delayer
|
||||
{
|
||||
/**
|
||||
* Implementation of 'Delayer' interface
|
||||
*/
|
||||
void usleep(unsigned us)
|
||||
{
|
||||
/* polling */
|
||||
if (us == 0)
|
||||
return;
|
||||
|
||||
Timer::Connection::usleep(us);
|
||||
}
|
||||
} _delayer;
|
||||
|
||||
enum {
|
||||
MSH_BASE = 0x12200000, /* host controller base for eMMC */
|
||||
MSH_SIZE = 0x10000,
|
||||
HOST_FREQ = 52000000,
|
||||
CLK_FREQ = 400000000,
|
||||
CLK_DIV_52Mhz = 4,
|
||||
CLK_DIV_400Khz = 0xff,
|
||||
MSH_BASE = 0x12200000,
|
||||
MSH_SIZE = 0x10000,
|
||||
IDMAC_DESC_MAX_ENTRIES = 1024
|
||||
};
|
||||
|
||||
enum Bus_width {
|
||||
BUS_WIDTH_1 = 0,
|
||||
BUS_WIDTH_4 = 1,
|
||||
BUS_WIDTH_8 = 1 << 16,
|
||||
};
|
||||
|
||||
template <off_t OFFSET, bool STRICT_WRITE = false>
|
||||
struct Register : Mmio::Register<OFFSET, 32, STRICT_WRITE> { };
|
||||
|
||||
struct Ctrl : Register<0x0>
|
||||
{
|
||||
struct Reset : Bitfield<0, 3> { };
|
||||
struct Global_interrupt : Bitfield<4, 1> { };
|
||||
struct Dma_enable : Bitfield<5, 1> { };
|
||||
struct Use_internal_dmac : Bitfield<25, 1> { };
|
||||
};
|
||||
|
||||
struct Pwren : Register<0x4> { };
|
||||
struct Clkdiv : Register<0x8> { };
|
||||
struct Clkena : Register<0x10> { };
|
||||
struct Tmout : Register<0x14> { };
|
||||
struct Ctype : Register<0x18, true> { };
|
||||
struct Blksize : Register<0x1c> { };
|
||||
struct Bytcnt : Register<0x20> { };
|
||||
struct Intmask : Register<0x24> { };
|
||||
struct Cmdarg : Register<0x28> { };
|
||||
|
||||
struct Cmd : Register<0x2c>
|
||||
{
|
||||
struct Index : Bitfield<0, 6> { };
|
||||
struct Rsp_type : Bitfield<6, 3>
|
||||
{
|
||||
enum Response { RESPONSE_NONE = 0,
|
||||
RESPONSE_48_BIT = 1,
|
||||
RESPONSE_48_BIT_WITH_BUSY = 5,
|
||||
RESPONSE_136_BIT = 7,
|
||||
};
|
||||
};
|
||||
struct Data_expected : Bitfield<9, 1> { };
|
||||
struct Write : Bitfield<10, 1> { };
|
||||
struct Wait_prvdata_complete : Bitfield<13, 1> { };
|
||||
struct Init_sequence : Bitfield<15, 1> { };
|
||||
struct Update_clock_registers_only : Bitfield<21, 1> { };
|
||||
struct Use_hold_reg : Bitfield<29, 1> { };
|
||||
struct Start_cmd : Bitfield<31, 1> { };
|
||||
};
|
||||
|
||||
struct Rsp0 : Register<0x30> { };
|
||||
struct Rsp1 : Register<0x34> { };
|
||||
struct Rsp2 : Register<0x38> { };
|
||||
struct Rsp3 : Register<0x3c> { };
|
||||
|
||||
struct Mintsts : Register<0x40> { };
|
||||
struct Rintsts : Register<0x44, true>
|
||||
{
|
||||
struct Response_error : Bitfield<1, 1> { };
|
||||
struct Data_transfer_over : Bitfield<3, 1> { };
|
||||
struct Command_done : Bitfield<2, 1> { };
|
||||
struct Data_crc_error : Bitfield<7, 1> { };
|
||||
struct Response_timeout : Bitfield<8, 1> { };
|
||||
struct Data_read_timeout : Bitfield<9, 1> { };
|
||||
};
|
||||
|
||||
struct Status : Register<0x48>
|
||||
{
|
||||
struct Data_busy : Bitfield<9, 1> { };
|
||||
};
|
||||
|
||||
struct Fifoth : Register<0x4c> { };
|
||||
|
||||
struct Bmod : Register<0x80, true>
|
||||
{
|
||||
struct Fixed_burst : Bitfield<1, 1> { };
|
||||
struct Idmac_enable : Bitfield<7, 1> { };
|
||||
};
|
||||
|
||||
struct Pldmnd : Register<0x84> { };
|
||||
struct Idsts : Register<0x8c> { };
|
||||
struct Idinten : Register<0x90, true> { };
|
||||
struct Dbaddr : Register<0x88> { };
|
||||
struct Clksel : Register<0x9c> { };
|
||||
struct Emmc_ddr_req : Register<0x10c, true> { };
|
||||
|
||||
struct Idmac_desc
|
||||
{
|
||||
enum Flags {
|
||||
NONE = 0,
|
||||
DIC = 1 << 1,
|
||||
LD = 1 << 2,
|
||||
FS = 1 << 3,
|
||||
CH = 1 << 4,
|
||||
ER = 1 << 5,
|
||||
OWN = 1 << 31,
|
||||
};
|
||||
|
||||
unsigned flags;
|
||||
unsigned bytes;
|
||||
unsigned addr;
|
||||
unsigned next;
|
||||
|
||||
size_t set(size_t block_count,
|
||||
size_t block_size,
|
||||
addr_t phys_addr,
|
||||
Flags flag);
|
||||
};
|
||||
|
||||
struct Clock_regulator
|
||||
@ -62,74 +156,84 @@ class Block::Sdhci_driver : public Block::Driver,
|
||||
|
||||
Clock_regulator(Env &env) : regulator(env, Regulator::CLK_MMC0) {
|
||||
regulator.state(true); }
|
||||
};
|
||||
|
||||
} _clock_regulator;
|
||||
struct Timer_delayer : Timer::Connection, Mmio::Delayer
|
||||
{
|
||||
void usleep(unsigned us) {
|
||||
Timer::Connection::usleep(us); }
|
||||
};
|
||||
|
||||
struct Block_transfer
|
||||
{
|
||||
Block::Packet_descriptor packet;
|
||||
bool pending = false;
|
||||
};
|
||||
|
||||
Env &_env;
|
||||
Timer_delayer _delayer;
|
||||
Block_transfer _block_transfer;
|
||||
Clock_regulator _clock_regulator { _env };
|
||||
Signal_handler<Driver> _irq_handler { _env.ep(), *this, &Driver::_handle_irq };
|
||||
Irq_connection _irq { Board_base::SDMMC0_IRQ };
|
||||
Attached_ram_dataspace _idmac_desc_ds { &_env.ram(),
|
||||
IDMAC_DESC_MAX_ENTRIES * sizeof(Idmac_desc),
|
||||
UNCACHED };
|
||||
Idmac_desc *const _idmac_desc { _idmac_desc_ds.local_addr<Idmac_desc>() };
|
||||
addr_t const _idmac_desc_phys { Dataspace_client(_idmac_desc_ds.cap())
|
||||
.phys_addr() };
|
||||
Card_info _card_info { _init() };
|
||||
|
||||
bool _reset();
|
||||
void _reset_fifo();
|
||||
void _disable_irq();
|
||||
bool _update_clock_registers();
|
||||
bool _setup_bus(unsigned clock_div);
|
||||
void _handle_irq();
|
||||
Card_info _init();
|
||||
|
||||
bool _setup_idmac_descriptor_table(size_t block_count,
|
||||
addr_t phys_addr);
|
||||
|
||||
|
||||
/* display sub system registers */
|
||||
Attached_io_mem_dataspace _mmio;
|
||||
/*********************
|
||||
** Host_controller **
|
||||
*********************/
|
||||
|
||||
/* mobile storage host controller instance */
|
||||
Exynos5_msh_controller _controller;
|
||||
bool _issue_command(Command_base const &command) override;
|
||||
Cid _read_cid() override ;
|
||||
Csd _read_csd() override ;
|
||||
size_t _read_ext_csd() override;
|
||||
unsigned _read_rca() override { return 0; }
|
||||
|
||||
bool const _use_dma;
|
||||
Card_info card_info() const override { return _card_info; }
|
||||
|
||||
public:
|
||||
|
||||
Sdhci_driver(Env &env)
|
||||
:
|
||||
_clock_regulator(env),
|
||||
_mmio(MSH_BASE, MSH_SIZE),
|
||||
_controller(env.ep(), (addr_t)_mmio.local_addr<void>(),
|
||||
_delayer, *this, true),
|
||||
_use_dma(true)
|
||||
{
|
||||
Sd_card::Card_info const card_info = _controller.card_info();
|
||||
using Block::Driver::read;
|
||||
using Block::Driver::write;
|
||||
|
||||
Genode::log("SD/MMC card detected");
|
||||
Genode::log("capacity: ", card_info.capacity_mb(), " MiB");
|
||||
}
|
||||
|
||||
void handle_ack(Block::Packet_descriptor pkt, bool success) {
|
||||
ack_packet(pkt, success); }
|
||||
Driver(Env &env);
|
||||
|
||||
|
||||
/*****************************
|
||||
** Block::Driver interface **
|
||||
*****************************/
|
||||
/*******************
|
||||
** Block::Driver **
|
||||
*******************/
|
||||
|
||||
Genode::size_t block_size() { return 512; }
|
||||
void read_dma(Block::sector_t block_number,
|
||||
size_t block_count,
|
||||
addr_t buf_phys,
|
||||
Block::Packet_descriptor &pkt) override;
|
||||
|
||||
virtual Block::sector_t block_count()
|
||||
{
|
||||
return _controller.card_info().capacity_mb() * 1024 * 2;
|
||||
}
|
||||
void write_dma(Block::sector_t block_number,
|
||||
size_t block_count,
|
||||
addr_t buf_phys,
|
||||
Block::Packet_descriptor &pkt) override;
|
||||
|
||||
Block::Session::Operations ops()
|
||||
{
|
||||
Block::Session::Operations o;
|
||||
o.set_operation(Block::Packet_descriptor::READ);
|
||||
o.set_operation(Block::Packet_descriptor::WRITE);
|
||||
return o;
|
||||
}
|
||||
bool dma_enabled() override { return true; }
|
||||
|
||||
void read_dma(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
Genode::addr_t phys,
|
||||
Packet_descriptor &packet)
|
||||
{
|
||||
_controller.read_blocks_dma(block_number, block_count, phys, packet);
|
||||
}
|
||||
|
||||
void write_dma(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
Genode::addr_t phys,
|
||||
Packet_descriptor &packet)
|
||||
{
|
||||
_controller.write_blocks_dma(block_number, block_count, phys, packet);
|
||||
}
|
||||
|
||||
bool dma_enabled() { return _use_dma; }
|
||||
Ram_dataspace_capability alloc_dma_buffer(size_t size) override {
|
||||
return _env.ram().alloc(size, UNCACHED); }
|
||||
};
|
||||
|
||||
#endif /* _DRIVERS__SD_CARD__SPEC__EXYNOS5__DRIVER_H_ */
|
||||
#endif /* _DRIVER_H_ */
|
||||
|
@ -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 */
|
||||
#include <sdhc.h>
|
||||
#include <driver.h>
|
||||
|
||||
using namespace Sd_card;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
int Sdhc::_wait_for_card_ready_mbw()
|
||||
int Driver::_wait_for_card_ready_mbw()
|
||||
{
|
||||
/*
|
||||
* Poll card status
|
||||
@ -69,7 +69,7 @@ int Sdhc::_wait_for_card_ready_mbw()
|
||||
}
|
||||
|
||||
|
||||
int Sdhc::_stop_transmission()
|
||||
int Driver::_stop_transmission()
|
||||
{
|
||||
/* write argument register */
|
||||
Mmio::write<Cmdarg>(0);
|
||||
@ -90,7 +90,7 @@ int Sdhc::_stop_transmission()
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_handle_irq()
|
||||
void Driver::_handle_irq()
|
||||
{
|
||||
_irq.ack_irq();
|
||||
|
||||
@ -125,7 +125,7 @@ void Sdhc::_handle_irq()
|
||||
}
|
||||
|
||||
|
||||
int Sdhc::_wait_for_cmd_complete()
|
||||
int Driver::_wait_for_cmd_complete()
|
||||
{
|
||||
if (!wait_for<Irqstat::Cc>(1, _delayer, 200, 5000)) {
|
||||
error("command timed out");
|
||||
@ -136,7 +136,7 @@ int Sdhc::_wait_for_cmd_complete()
|
||||
}
|
||||
|
||||
|
||||
bool Sdhc::_issue_command(Command_base const & command)
|
||||
bool Driver::_issue_command(Command_base const & command)
|
||||
{
|
||||
/* get command characteristics */
|
||||
bool const transfer = command.transfer != TRANSFER_NONE;
|
||||
@ -178,7 +178,7 @@ bool Sdhc::_issue_command(Command_base const & command)
|
||||
}
|
||||
|
||||
|
||||
Cid Sdhc::_read_cid()
|
||||
Cid Driver::_read_cid()
|
||||
{
|
||||
Cid cid;
|
||||
cid.raw_0 = Mmio::read<Rsp136_0>();
|
||||
@ -189,7 +189,7 @@ Cid Sdhc::_read_cid()
|
||||
}
|
||||
|
||||
|
||||
Csd Sdhc::_read_csd()
|
||||
Csd Driver::_read_csd()
|
||||
{
|
||||
Csd csd;
|
||||
csd.csd0 = Mmio::read<Rsp136_0>();
|
||||
@ -200,76 +200,65 @@ Csd Sdhc::_read_csd()
|
||||
}
|
||||
|
||||
|
||||
unsigned Sdhc::_read_rca()
|
||||
unsigned Driver::_read_rca()
|
||||
{
|
||||
Cmdrsp0::access_t const rsp0 = Mmio::read<Cmdrsp0>();
|
||||
return Send_relative_addr::Response::Rca::get(rsp0);
|
||||
}
|
||||
|
||||
|
||||
bool Sdhc::read_blocks(size_t, size_t, char *)
|
||||
void Driver::read_dma(Block::sector_t blk_nr,
|
||||
size_t blk_cnt,
|
||||
addr_t buf_phys,
|
||||
Block::Packet_descriptor &packet)
|
||||
{
|
||||
error("block transfer without DMA not supported");
|
||||
return false;
|
||||
if (_prepare_dma_mb(packet, true, blk_cnt, buf_phys)) {
|
||||
throw Io_error(); }
|
||||
|
||||
if (!issue_command(Read_multiple_block(blk_nr))) {
|
||||
throw Io_error(); }
|
||||
}
|
||||
|
||||
|
||||
bool Sdhc::write_blocks(size_t, size_t, char const *)
|
||||
void Driver::write_dma(Block::sector_t blk_nr,
|
||||
size_t blk_cnt,
|
||||
addr_t buf_phys,
|
||||
Block::Packet_descriptor &packet)
|
||||
{
|
||||
error("block transfer without DMA not supported");
|
||||
return false;
|
||||
if (_prepare_dma_mb(packet, false, blk_cnt, buf_phys)) {
|
||||
throw Io_error(); }
|
||||
|
||||
if (!issue_command(Write_multiple_block(blk_nr))) {
|
||||
throw Io_error(); }
|
||||
}
|
||||
|
||||
|
||||
bool Sdhc::read_blocks_dma(Block::Packet_descriptor packet,
|
||||
size_t blk_nr,
|
||||
size_t blk_cnt,
|
||||
addr_t buf_phys)
|
||||
{
|
||||
if (_prepare_dma_mb(packet, true, blk_cnt, buf_phys)) { return false; }
|
||||
|
||||
return issue_command(Read_multiple_block(blk_nr));
|
||||
}
|
||||
|
||||
|
||||
bool Sdhc::write_blocks_dma(Block::Packet_descriptor packet,
|
||||
size_t blk_nr,
|
||||
size_t blk_cnt,
|
||||
addr_t buf_phys)
|
||||
{
|
||||
if (_prepare_dma_mb(packet, false, blk_cnt, buf_phys)) { return false; }
|
||||
|
||||
return issue_command(Write_multiple_block(blk_nr));
|
||||
}
|
||||
|
||||
|
||||
Sdhc::Sdhc(Env &env)
|
||||
Driver::Driver(Env &env)
|
||||
:
|
||||
Attached_mmio(Board_base::SDHC_MMIO_BASE, Board_base::SDHC_MMIO_SIZE),
|
||||
_irq_handler(env.ep(), *this, &Sdhc::_handle_irq), _irq(Board_base::SDHC_IRQ),
|
||||
_card_info(_init()), _use_dma(true)
|
||||
_env(env)
|
||||
{
|
||||
log("SD card detected");
|
||||
log("capacity: ", card_info().capacity_mb(), " MiB");
|
||||
}
|
||||
|
||||
|
||||
int Sdhc::_prepare_dma_mb(Block::Packet_descriptor packet,
|
||||
int Driver::_prepare_dma_mb(Block::Packet_descriptor packet,
|
||||
bool reading,
|
||||
size_t blk_cnt,
|
||||
addr_t buf_phys)
|
||||
{
|
||||
if (_block_transfer.pending) {
|
||||
throw Block::Driver::Request_congestion(); }
|
||||
throw Request_congestion(); }
|
||||
|
||||
|
||||
/* write ADMA2 table to DMA */
|
||||
size_t const req_size = blk_cnt * BLOCK_SIZE;
|
||||
size_t const req_size = blk_cnt * block_size();
|
||||
if (_adma2_table.setup_request(req_size, buf_phys)) { return -1; }
|
||||
|
||||
/* configure DMA at host */
|
||||
Mmio::write<Adsaddr>(_adma2_table.base_phys());
|
||||
Mmio::write<Blkattr::Blksize>(BLOCK_SIZE);
|
||||
Mmio::write<Blkattr::Blksize>(block_size());
|
||||
Mmio::write<Blkattr::Blkcnt>(blk_cnt);
|
||||
|
||||
_block_transfer.read = reading;
|
||||
@ -279,7 +268,7 @@ int Sdhc::_prepare_dma_mb(Block::Packet_descriptor packet,
|
||||
}
|
||||
|
||||
|
||||
int Sdhc::_wait_for_cmd_allowed()
|
||||
int Driver::_wait_for_cmd_allowed()
|
||||
{
|
||||
/*
|
||||
* At least after multi-block writes on i.MX53 with the fix for the broken
|
||||
@ -298,7 +287,7 @@ int Sdhc::_wait_for_cmd_allowed()
|
||||
}
|
||||
|
||||
|
||||
Card_info Sdhc::_init()
|
||||
Card_info Driver::_init()
|
||||
{
|
||||
/* install IRQ signal */
|
||||
_irq.sigh(_irq_handler);
|
||||
@ -413,7 +402,7 @@ Card_info Sdhc::_init()
|
||||
_delayer.usleep(10000);
|
||||
|
||||
/* configure card to use given block size */
|
||||
if (!issue_command(Set_blocklen(BLOCK_SIZE))) {
|
||||
if (!issue_command(Set_blocklen(block_size()))) {
|
||||
_detect_err("Set_blocklen command failed"); }
|
||||
|
||||
/* configure host buffer */
|
||||
@ -432,14 +421,14 @@ Card_info Sdhc::_init()
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_detect_err(char const * const err)
|
||||
void Driver::_detect_err(char const * const err)
|
||||
{
|
||||
error(err);
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
|
||||
int Sdhc::_reset(Delayer &delayer)
|
||||
int Driver::_reset(Delayer &delayer)
|
||||
{
|
||||
/* start reset */
|
||||
Mmio::write<Sysctl::Rsta>(1);
|
||||
@ -454,14 +443,14 @@ int Sdhc::_reset(Delayer &delayer)
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_disable_irqs()
|
||||
void Driver::_disable_irqs()
|
||||
{
|
||||
Mmio::write<Irqstaten>(0);
|
||||
Mmio::write<Irqsigen>(0);
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_enable_irqs()
|
||||
void Driver::_enable_irqs()
|
||||
{
|
||||
Irq::access_t irq = 0;
|
||||
Irq::Cc::set(irq, 1);
|
||||
@ -481,7 +470,7 @@ void Sdhc::_enable_irqs()
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_bus_width(Bus_width bus_width)
|
||||
void Driver::_bus_width(Bus_width bus_width)
|
||||
{
|
||||
switch (bus_width) {
|
||||
case BUS_WIDTH_1: Mmio::write<Proctl::Dtw>(Proctl::Dtw::_1BIT); break;
|
||||
@ -490,7 +479,7 @@ void Sdhc::_bus_width(Bus_width bus_width)
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_disable_clock()
|
||||
void Driver::_disable_clock()
|
||||
{
|
||||
_disable_clock_preparation();
|
||||
Sysctl::access_t sysctl = Mmio::read<Sysctl>();
|
||||
@ -503,7 +492,7 @@ void Sdhc::_disable_clock()
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_enable_clock(Clock_divider divider)
|
||||
void Driver::_enable_clock(Clock_divider divider)
|
||||
{
|
||||
Sysctl::access_t sysctl = Mmio::read<Sysctl>();
|
||||
Sysctl::Ipgen::set(sysctl, 1);
|
||||
@ -529,7 +518,7 @@ void Sdhc::_enable_clock(Clock_divider divider)
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_clock(Clock clock)
|
||||
void Driver::_clock(Clock clock)
|
||||
{
|
||||
wait_for<Prsstat::Sdstb>(1, _delayer);
|
||||
_disable_clock();
|
@ -1,11 +1,11 @@
|
||||
/*
|
||||
* \brief Implementation of the Block::Driver interface
|
||||
* \brief Secured Digital Host Controller
|
||||
* \author Martin Stein
|
||||
* \date 2015-02-04
|
||||
* \date 2015-02-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2016 Genode Labs GmbH
|
||||
* Copyright (C) 2015-2016 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
@ -14,7 +14,285 @@
|
||||
#ifndef _DRIVER_H_
|
||||
#define _DRIVER_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <drivers/board_base.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <irq_session/connection.h>
|
||||
#include <os/attached_mmio.h>
|
||||
|
||||
/* local includes */
|
||||
#include <sdhc.h>
|
||||
#include <driver_base.h>
|
||||
#include <adma2.h>
|
||||
|
||||
namespace Sd_card { class Driver; }
|
||||
|
||||
|
||||
class Sd_card::Driver : public Driver_base,
|
||||
private Attached_mmio
|
||||
{
|
||||
private:
|
||||
|
||||
enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 };
|
||||
|
||||
enum Clock { CLOCK_INITIAL, CLOCK_OPERATIONAL };
|
||||
|
||||
enum Clock_divider { CLOCK_DIV_4, CLOCK_DIV_8, CLOCK_DIV_512 };
|
||||
|
||||
|
||||
/********************
|
||||
** MMIO structure **
|
||||
********************/
|
||||
|
||||
struct Blkattr : Register<0x4, 32>
|
||||
{
|
||||
struct Blksize : Bitfield<0, 13> { };
|
||||
struct Blkcnt : Bitfield<16, 16> { };
|
||||
};
|
||||
template <off_t OFFSET>
|
||||
struct Cmdrsp_tpl : Register<OFFSET, 32>
|
||||
{
|
||||
struct Rsp136_8_24 : Register<OFFSET, 32>::template Bitfield<0, 24> { };
|
||||
struct Rsp136_0_8 : Register<OFFSET, 32>::template Bitfield<24, 8> { };
|
||||
};
|
||||
|
||||
struct Cmdarg : Register<0x8, 32> { };
|
||||
struct Cmdrsp0 : Cmdrsp_tpl<0x10> { };
|
||||
struct Cmdrsp1 : Cmdrsp_tpl<0x14> { };
|
||||
struct Cmdrsp2 : Cmdrsp_tpl<0x18> { };
|
||||
struct Cmdrsp3 : Cmdrsp_tpl<0x1c> { };
|
||||
struct Rsp136_0 : Bitset_2<Cmdrsp3::Rsp136_0_8, Cmdrsp0::Rsp136_8_24> { };
|
||||
struct Rsp136_1 : Bitset_2<Cmdrsp0::Rsp136_0_8, Cmdrsp1::Rsp136_8_24> { };
|
||||
struct Rsp136_2 : Bitset_2<Cmdrsp1::Rsp136_0_8, Cmdrsp2::Rsp136_8_24> { };
|
||||
struct Rsp136_3 : Bitset_2<Cmdrsp2::Rsp136_0_8, Cmdrsp3::Rsp136_8_24> { };
|
||||
|
||||
template <off_t OFFSET>
|
||||
struct Xfertyp_base : Register<OFFSET, 32>
|
||||
{
|
||||
struct Dmaen : Register<OFFSET, 32>::template Bitfield<0, 1> { };
|
||||
struct Bcen : Register<OFFSET, 32>::template Bitfield<1, 1> { };
|
||||
struct Ac12en : Register<OFFSET, 32>::template Bitfield<2, 1> { };
|
||||
struct Dtdsel : Register<OFFSET, 32>::template Bitfield<4, 1>
|
||||
{
|
||||
enum { WRITE = 0, READ = 1, };
|
||||
};
|
||||
struct Msbsel : Register<OFFSET, 32>::template Bitfield<5, 1> { };
|
||||
};
|
||||
|
||||
struct Mixctrl : Xfertyp_base<0x48>
|
||||
{
|
||||
struct Ddren : Bitfield<3, 1> { };
|
||||
struct Nibblepos : Bitfield<6, 1> { };
|
||||
struct Ac23en : Bitfield<7, 1> { };
|
||||
struct Always_ones : Bitfield<31, 1> { };
|
||||
};
|
||||
|
||||
struct Xfertyp : Xfertyp_base<0xc>
|
||||
{
|
||||
struct Rsptyp : Bitfield<16, 2>
|
||||
{
|
||||
enum {
|
||||
_0BIT = 0,
|
||||
_136BIT = 1,
|
||||
_48BIT = 2,
|
||||
_48BIT_BUSY = 3,
|
||||
};
|
||||
};
|
||||
struct Cccen : Bitfield<19, 1> { };
|
||||
struct Cicen : Bitfield<20, 1> { };
|
||||
struct Dpsel : Bitfield<21, 1> { };
|
||||
struct Cmdtyp : Bitfield<22, 2>
|
||||
{
|
||||
enum { ABORT_CMD12 = 3 };
|
||||
};
|
||||
struct Cmdinx : Bitfield<24, 6> { };
|
||||
};
|
||||
|
||||
struct Prsstat : Register<0x24, 32>
|
||||
{
|
||||
struct Cihb : Bitfield<0, 1> { };
|
||||
struct Cdihb : Bitfield<1, 1> { };
|
||||
struct Dla : Bitfield<2, 1> { };
|
||||
struct Sdstb : Bitfield<3, 1> { };
|
||||
};
|
||||
|
||||
struct Proctl : Register<0x28, 32>
|
||||
{
|
||||
struct Dtw : Bitfield<1, 2>
|
||||
{
|
||||
enum { _1BIT = 0, _4BIT = 1 };
|
||||
};
|
||||
struct Dmas : Bitfield<8, 2> { enum { ADMA2 = 2 }; };
|
||||
};
|
||||
|
||||
struct Sysctl : Register<0x2c, 32>
|
||||
{
|
||||
struct Ipgen : Bitfield<0, 1> { };
|
||||
struct Hcken : Bitfield<1, 1> { };
|
||||
struct Peren : Bitfield<2, 1> { };
|
||||
struct Dvs : Bitfield<4, 4>
|
||||
{
|
||||
enum { DIV1 = 0x0, DIV4 = 0x3, DIV16 = 0xf, };
|
||||
};
|
||||
struct Sdclkfs : Bitfield<8, 8>
|
||||
{
|
||||
enum { DIV1 = 0x00, DIV2 = 0x01, DIV32 = 0x10, };
|
||||
};
|
||||
struct Dtocv : Bitfield<16, 4>
|
||||
{
|
||||
enum {
|
||||
SDCLK_TIMES_2_POW_28 = 0xf,
|
||||
SDCLK_TIMES_2_POW_27 = 0xe,
|
||||
SDCLK_TIMES_2_POW_13 = 0x0,
|
||||
};
|
||||
};
|
||||
struct Ipp_rst_n : Bitfield<23, 1> { };
|
||||
struct Rsta : Bitfield<24, 1> { };
|
||||
struct Rstc : Bitfield<25, 1> { };
|
||||
struct Rstd : Bitfield<26, 1> { };
|
||||
};
|
||||
|
||||
template <off_t OFFSET>
|
||||
struct Irq_tpl : Register<OFFSET, 32>
|
||||
{
|
||||
struct Cc : Register<OFFSET, 32>::template Bitfield<0, 1> { };
|
||||
struct Tc : Register<OFFSET, 32>::template Bitfield<1, 1> { };
|
||||
struct Dint : Register<OFFSET, 32>::template Bitfield<3, 1> { };
|
||||
struct Ctoe : Register<OFFSET, 32>::template Bitfield<16, 1> { };
|
||||
struct Cce : Register<OFFSET, 32>::template Bitfield<17, 1> { };
|
||||
struct Cebe : Register<OFFSET, 32>::template Bitfield<18, 1> { };
|
||||
struct Cie : Register<OFFSET, 32>::template Bitfield<19, 1> { };
|
||||
struct Dtoe : Register<OFFSET, 32>::template Bitfield<20, 1> { };
|
||||
struct Dce : Register<OFFSET, 32>::template Bitfield<21, 1> { };
|
||||
struct Debe : Register<OFFSET, 32>::template Bitfield<22, 1> { };
|
||||
struct Ac12e : Register<OFFSET, 32>::template Bitfield<24, 1> { };
|
||||
struct Dmae : Register<OFFSET, 32>::template Bitfield<28, 1> { };
|
||||
};
|
||||
|
||||
struct Irq : Irq_tpl<0> { };
|
||||
struct Irqstat : Irq_tpl<0x30> { };
|
||||
struct Irqstaten : Irq_tpl<0x34> { };
|
||||
struct Irqsigen : Irq_tpl<0x38> { };
|
||||
struct Maxcurrent : Register<0x48, 32> { };
|
||||
struct Adsaddr : Register<0x58, 32> { };
|
||||
|
||||
struct Hostver : Register<0xfc, 32>
|
||||
{
|
||||
struct Svn : Bitfield<0, 8> { };
|
||||
struct Vvn : Bitfield<8, 8> { };
|
||||
};
|
||||
|
||||
struct Wml : Register<0x44, 32>
|
||||
{
|
||||
struct Rd_wml : Bitfield<0, 8> { };
|
||||
struct Rd_brst_len : Bitfield<8, 5> { };
|
||||
struct Wr_wml : Bitfield<16, 8> { };
|
||||
struct Wr_brst_len : Bitfield<24, 5> { };
|
||||
};
|
||||
|
||||
struct Vendspec : Register<0xc0, 32>
|
||||
{
|
||||
struct Frc_sdclk_on : Bitfield<8, 1> { };
|
||||
};
|
||||
|
||||
|
||||
/************************
|
||||
** Utility structures **
|
||||
************************/
|
||||
|
||||
struct Timer_delayer : Timer::Connection, Mmio::Delayer
|
||||
{
|
||||
void usleep(unsigned us) { Timer::Connection::usleep(us); }
|
||||
};
|
||||
|
||||
struct Block_transfer
|
||||
{
|
||||
Block::Packet_descriptor packet;
|
||||
bool pending = false;
|
||||
bool read;
|
||||
};
|
||||
|
||||
Env &_env;
|
||||
Block_transfer _block_transfer;
|
||||
Timer_delayer _delayer;
|
||||
Signal_handler<Driver> _irq_handler { _env.ep(), *this,
|
||||
&Driver::_handle_irq };
|
||||
Irq_connection _irq { Board_base::SDHC_IRQ };
|
||||
Card_info _card_info { _init() };
|
||||
Adma2::Table _adma2_table { _env.ram() };
|
||||
|
||||
static bool _supported_host_version(Hostver::access_t hostver);
|
||||
static void _watermark_level(Wml::access_t &wml);
|
||||
|
||||
void _handle_irq();
|
||||
void _detect_err(char const * const err);
|
||||
void _disable_irqs();
|
||||
void _enable_irqs();
|
||||
void _bus_width(Bus_width bus_width);
|
||||
void _disable_clock();
|
||||
void _disable_clock_preparation();
|
||||
void _enable_clock(Clock_divider divider);
|
||||
void _enable_clock_finish();
|
||||
void _clock(Clock clock);
|
||||
void _clock_finish(Clock clock);
|
||||
int _reset(Delayer & delayer);
|
||||
void _reset_amendments();
|
||||
int _wait_for_cmd_allowed();
|
||||
int _wait_for_cmd_complete();
|
||||
int _wait_for_card_ready_mbw();
|
||||
int _stop_transmission();
|
||||
void _stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp);
|
||||
int _wait_for_cmd_complete_mb_finish(bool const reading);
|
||||
|
||||
int _prepare_dma_mb(Block::Packet_descriptor packet,
|
||||
bool reading,
|
||||
size_t blk_cnt,
|
||||
addr_t buf_phys);
|
||||
|
||||
bool _issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
|
||||
bool const transfer,
|
||||
bool const multiblock,
|
||||
bool const reading);
|
||||
|
||||
Card_info _init();
|
||||
|
||||
|
||||
/*********************
|
||||
** Host_controller **
|
||||
*********************/
|
||||
|
||||
Cid _read_cid() override;
|
||||
Csd _read_csd() override;
|
||||
unsigned _read_rca() override;
|
||||
bool _issue_command(Command_base const & cmd) override;
|
||||
|
||||
Card_info card_info() const override { return _card_info; }
|
||||
|
||||
public:
|
||||
|
||||
using Block::Driver::read;
|
||||
using Block::Driver::write;
|
||||
|
||||
Driver(Env &env);
|
||||
|
||||
|
||||
/*******************
|
||||
** Block::Driver **
|
||||
*******************/
|
||||
|
||||
void read_dma(Block::sector_t block_number,
|
||||
size_t block_count,
|
||||
addr_t phys,
|
||||
Block::Packet_descriptor &packet) override;
|
||||
|
||||
void write_dma(Block::sector_t block_number,
|
||||
size_t block_count,
|
||||
addr_t phys,
|
||||
Block::Packet_descriptor &packet) override;
|
||||
|
||||
bool dma_enabled() override { return true; }
|
||||
|
||||
Ram_dataspace_capability alloc_dma_buffer(size_t size) override {
|
||||
return _env.ram().alloc(size, UNCACHED); }
|
||||
};
|
||||
|
||||
#endif /* _DRIVER_H_ */
|
||||
|
@ -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 */
|
||||
#include <sdhc.h>
|
||||
#include <driver.h>
|
||||
|
||||
using namespace Sd_card;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
|
||||
void Driver::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
|
||||
{
|
||||
Xfertyp::Msbsel::set(xfertyp, 1);
|
||||
Xfertyp::Bcen::set(xfertyp, 1);
|
||||
@ -26,7 +26,7 @@ void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
|
||||
}
|
||||
|
||||
|
||||
int Sdhc::_wait_for_cmd_complete_mb_finish(bool const reading)
|
||||
int Driver::_wait_for_cmd_complete_mb_finish(bool const reading)
|
||||
{
|
||||
if (reading) { return 0; }
|
||||
|
||||
@ -48,7 +48,7 @@ int Sdhc::_wait_for_cmd_complete_mb_finish(bool const reading)
|
||||
}
|
||||
|
||||
|
||||
bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
|
||||
bool Driver::_issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
|
||||
bool const transfer,
|
||||
bool const multiblock,
|
||||
bool const reading)
|
||||
@ -65,8 +65,7 @@ bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
|
||||
if (reading) {
|
||||
Xfertyp::Ac12en::set(xfertyp, 1); }
|
||||
|
||||
if (_use_dma) {
|
||||
Xfertyp::Dmaen::set(xfertyp, 1); }
|
||||
Xfertyp::Dmaen::set(xfertyp, 1);
|
||||
}
|
||||
Xfertyp::Dtdsel::set(xfertyp,
|
||||
reading ? Xfertyp::Dtdsel::READ : Xfertyp::Dtdsel::WRITE);
|
||||
@ -75,21 +74,21 @@ bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
|
||||
}
|
||||
|
||||
|
||||
bool Sdhc::_supported_host_version(Hostver::access_t hostver)
|
||||
bool Driver::_supported_host_version(Hostver::access_t hostver)
|
||||
{
|
||||
return Hostver::Vvn::get(hostver) == 18 &&
|
||||
Hostver::Svn::get(hostver) == 1;
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_watermark_level(Wml::access_t &wml)
|
||||
void Driver::_watermark_level(Wml::access_t &wml)
|
||||
{
|
||||
Wml::Wr_wml::set(wml, 16);
|
||||
Wml::Wr_brst_len::set(wml, 8);
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_reset_amendments()
|
||||
void Driver::_reset_amendments()
|
||||
{
|
||||
/*
|
||||
* The SDHC specification says that a software reset shouldn't
|
||||
@ -106,7 +105,7 @@ void Sdhc::_reset_amendments()
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_clock_finish(Clock clock)
|
||||
void Driver::_clock_finish(Clock clock)
|
||||
{
|
||||
Mmio::write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
|
||||
switch (clock) {
|
||||
@ -116,5 +115,5 @@ void Sdhc::_clock_finish(Clock clock)
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_disable_clock_preparation() { }
|
||||
void Sdhc::_enable_clock_finish() { }
|
||||
void Driver::_disable_clock_preparation() { }
|
||||
void Driver::_enable_clock_finish() { }
|
@ -12,13 +12,13 @@
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <sdhc.h>
|
||||
#include <driver.h>
|
||||
|
||||
using namespace Sd_card;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
|
||||
void Driver::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
|
||||
{
|
||||
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
|
||||
Mixctrl::Dmaen::set(mixctrl, 1);
|
||||
@ -33,20 +33,20 @@ void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
|
||||
}
|
||||
|
||||
|
||||
int Sdhc::_wait_for_cmd_complete_mb_finish(bool const reading)
|
||||
int Driver::_wait_for_cmd_complete_mb_finish(bool const reading)
|
||||
{
|
||||
/* we can't use the "Auto Command 12" feature as it does not work */
|
||||
return _stop_transmission() ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &,
|
||||
bool Driver::_issue_cmd_finish_xfertyp(Xfertyp::access_t &,
|
||||
bool const transfer,
|
||||
bool const multiblock,
|
||||
bool const reading)
|
||||
{
|
||||
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
|
||||
Mixctrl::Dmaen ::set(mixctrl, transfer && multiblock && _use_dma);
|
||||
Mixctrl::Dmaen ::set(mixctrl, transfer && multiblock);
|
||||
Mixctrl::Bcen ::set(mixctrl, transfer);
|
||||
Mixctrl::Ac12en ::set(mixctrl, 0);
|
||||
Mixctrl::Msbsel ::set(mixctrl, transfer);
|
||||
@ -64,21 +64,21 @@ bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &,
|
||||
}
|
||||
|
||||
|
||||
bool Sdhc::_supported_host_version(Hostver::access_t hostver)
|
||||
bool Driver::_supported_host_version(Hostver::access_t hostver)
|
||||
{
|
||||
return Hostver::Vvn::get(hostver) == 0 &&
|
||||
Hostver::Svn::get(hostver) == 3;
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_watermark_level(Wml::access_t &wml)
|
||||
void Driver::_watermark_level(Wml::access_t &wml)
|
||||
{
|
||||
Wml::Wr_wml::set(wml, 64);
|
||||
Wml::Wr_brst_len::set(wml, 16);
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_reset_amendments()
|
||||
void Driver::_reset_amendments()
|
||||
{
|
||||
/* the USDHC doesn't reset the Mixer Control register automatically */
|
||||
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
|
||||
@ -95,7 +95,7 @@ void Sdhc::_reset_amendments()
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_clock_finish(Clock clock)
|
||||
void Driver::_clock_finish(Clock clock)
|
||||
{
|
||||
switch (clock) {
|
||||
case CLOCK_INITIAL:
|
||||
@ -111,6 +111,7 @@ void Sdhc::_clock_finish(Clock clock)
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_disable_clock_preparation() { Mmio::write<Vendspec::Frc_sdclk_on>(0); }
|
||||
void Driver::_disable_clock_preparation() {
|
||||
Mmio::write<Vendspec::Frc_sdclk_on>(0); }
|
||||
|
||||
void Sdhc::_enable_clock_finish() { Mmio::write<Vendspec::Frc_sdclk_on>(0); }
|
||||
void Driver::_enable_clock_finish() { Mmio::write<Vendspec::Frc_sdclk_on>(0); }
|
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
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _DRIVERS__SD_CARD__SPEC__OMAP4__DRIVER_H_
|
||||
#define _DRIVERS__SD_CARD__SPEC__OMAP4__DRIVER_H_
|
||||
#ifndef _DRIVER_H_
|
||||
#define _DRIVER_H_
|
||||
|
||||
#include <util/mmio.h>
|
||||
#include <os/attached_io_mem_dataspace.h>
|
||||
#include <base/log.h>
|
||||
/* Genode includes */
|
||||
#include <os/attached_mmio.h>
|
||||
#include <drivers/board_base.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <os/server.h>
|
||||
#include <irq_session/connection.h>
|
||||
|
||||
/* local includes */
|
||||
#include <mmchs.h>
|
||||
#include <driver_base.h>
|
||||
|
||||
namespace Block {
|
||||
using namespace Genode;
|
||||
class Sdhci_driver;
|
||||
}
|
||||
namespace Sd_card { class Driver; }
|
||||
|
||||
|
||||
class Block::Sdhci_driver : public Block::Driver,
|
||||
public Sd_ack_handler
|
||||
class Sd_card::Driver : public Driver_base,
|
||||
private Attached_mmio
|
||||
{
|
||||
private:
|
||||
|
||||
struct Timer_delayer : Timer::Connection, Mmio::Delayer
|
||||
{
|
||||
/**
|
||||
* Implementation of 'Delayer' interface
|
||||
*/
|
||||
void usleep(unsigned us) { Timer::Connection::usleep(us); }
|
||||
} _delayer;
|
||||
|
||||
/* memory map */
|
||||
enum {
|
||||
MMCHS1_MMIO_BASE = 0x4809c000,
|
||||
MMCHS1_MMIO_SIZE = 0x00001000,
|
||||
};
|
||||
|
||||
/* display sub system registers */
|
||||
Attached_io_mem_dataspace _mmchs1_mmio;
|
||||
enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 };
|
||||
|
||||
/* hsmmc controller instance */
|
||||
Omap4_hsmmc_controller _controller;
|
||||
enum Clock_divider { CLOCK_DIV_0, CLOCK_DIV_240 };
|
||||
|
||||
bool const _use_dma;
|
||||
enum Voltage { VOLTAGE_3_0, VOLTAGE_1_8 };
|
||||
|
||||
struct Sysconfig : Register<0x110, 32> { };
|
||||
|
||||
struct Con : Register<0x12c, 32>
|
||||
{
|
||||
struct Init : Bitfield<1, 1> { };
|
||||
struct Dw8 : Bitfield<5, 1> { };
|
||||
};
|
||||
|
||||
struct Cmd : Register<0x20c, 32>
|
||||
{
|
||||
struct Bce : Bitfield<1, 1> { };
|
||||
struct Acen : Bitfield<2, 1> { };
|
||||
struct Msbs : Bitfield<5, 1> { };
|
||||
struct Index : Bitfield<24, 6> { };
|
||||
struct Dp : Bitfield<21, 1> { };
|
||||
|
||||
struct Rsp_type : Bitfield<16, 2>
|
||||
{
|
||||
enum Response { RESPONSE_NONE = 0,
|
||||
RESPONSE_136_BIT = 1,
|
||||
RESPONSE_48_BIT = 2,
|
||||
RESPONSE_48_BIT_WITH_BUSY = 3 };
|
||||
};
|
||||
|
||||
struct Ddir : Bitfield<4, 1>
|
||||
{
|
||||
enum { WRITE = 0, READ = 1 };
|
||||
};
|
||||
};
|
||||
|
||||
struct Blk : Register<0x204, 32>
|
||||
{
|
||||
struct Blen : Bitfield<0, 12> { };
|
||||
struct Nblk : Bitfield<16, 16> { };
|
||||
};
|
||||
|
||||
struct Arg : Register<0x208, 32> { };
|
||||
struct Rsp10 : Register<0x210, 32> { };
|
||||
struct Rsp32 : Register<0x214, 32> { };
|
||||
struct Rsp54 : Register<0x218, 32> { };
|
||||
struct Rsp76 : Register<0x21c, 32> { };
|
||||
struct Data : Register<0x220, 32> { };
|
||||
|
||||
struct Pstate : Register<0x224, 32>
|
||||
{
|
||||
struct Cmdi : Bitfield<0, 1> { };
|
||||
struct Bwe : Bitfield<10, 1> { };
|
||||
struct Bre : Bitfield<11, 1> { };
|
||||
};
|
||||
|
||||
struct Hctl : Register<0x228, 32>
|
||||
{
|
||||
struct Sdbp : Bitfield<8, 1>
|
||||
{
|
||||
enum { POWER_OFF = 0, POWER_ON = 1 };
|
||||
};
|
||||
|
||||
struct Sdvs : Bitfield<9, 3>
|
||||
{
|
||||
enum Voltage { VOLTAGE_1_8 = 5,
|
||||
VOLTAGE_3_0 = 6,
|
||||
VOLTAGE_3_3 = 7 };
|
||||
};
|
||||
|
||||
struct Dtw : Bitfield<1, 1>
|
||||
{
|
||||
enum { ONE_BIT = 0, FOUR_BITS = 1 };
|
||||
};
|
||||
};
|
||||
|
||||
struct Sysctl : Register<0x22c, 32>
|
||||
{
|
||||
struct Ice : Bitfield<0, 1> { };
|
||||
struct Ics : Bitfield<1, 1> { };
|
||||
struct Ce : Bitfield<2, 1> { };
|
||||
struct Clkd : Bitfield<6, 10> { };
|
||||
struct Src : Bitfield<25, 1> { };
|
||||
|
||||
struct Dto : Bitfield<16, 4>
|
||||
{
|
||||
enum { TCF_2_POW_27 = 0xe };
|
||||
};
|
||||
};
|
||||
|
||||
struct Stat : Register<0x230, 32>
|
||||
{
|
||||
struct Tc : Bitfield<1, 1> { };
|
||||
struct Cc : Bitfield<0, 1> { };
|
||||
struct Erri : Bitfield<15, 1> { };
|
||||
struct Cto : Bitfield<16, 1> { };
|
||||
};
|
||||
|
||||
struct Ie : Register<0x234, 32>
|
||||
{
|
||||
struct Tc_enable : Bitfield<1, 1> { };
|
||||
struct Cto_enable : Bitfield<16, 1> { };
|
||||
};
|
||||
|
||||
struct Ise : Register<0x238, 32>
|
||||
{
|
||||
struct Tc_sigen : Bitfield<1, 1> { };
|
||||
struct Cto_sigen : Bitfield<16, 1> { };
|
||||
};
|
||||
|
||||
struct Capa : Register<0x240, 32>
|
||||
{
|
||||
struct Vs30 : Bitfield<25, 1> { };
|
||||
struct Vs18 : Bitfield<26, 1> { };
|
||||
};
|
||||
|
||||
struct Block_transfer
|
||||
{
|
||||
Block::Packet_descriptor packet;
|
||||
bool pending = false;
|
||||
};
|
||||
|
||||
struct Timer_delayer : Timer::Connection, Mmio::Delayer
|
||||
{
|
||||
void usleep(unsigned us) { Timer::Connection::usleep(us); }
|
||||
};
|
||||
|
||||
Entrypoint &_ep;
|
||||
Block_transfer _block_transfer;
|
||||
Timer_delayer _delayer;
|
||||
Signal_handler<Driver> _irq_handler { _ep, *this, &Driver::_handle_irq };
|
||||
Irq_connection _irq { Board_base::HSMMC_IRQ };
|
||||
Card_info _card_info { _init() };
|
||||
|
||||
Card_info _init();
|
||||
bool _wait_for_bre();
|
||||
bool _wait_for_bwe();
|
||||
void _handle_irq();
|
||||
bool _reset_cmd_line();
|
||||
void _disable_irq();
|
||||
void _bus_width(Bus_width bus_width);
|
||||
bool _sd_bus_power_on();
|
||||
bool _set_and_enable_clock(enum Clock_divider divider);
|
||||
void _set_bus_power(Voltage voltage);
|
||||
bool _init_stream();
|
||||
|
||||
void _stop_clock() { Mmio::write<Sysctl::Ce>(0); }
|
||||
|
||||
|
||||
/*********************
|
||||
** Host_controller **
|
||||
*********************/
|
||||
|
||||
bool _issue_command(Command_base const &command) override;
|
||||
Cid _read_cid() override;
|
||||
Csd _read_csd() override;
|
||||
|
||||
Card_info card_info() const override { return _card_info; }
|
||||
|
||||
unsigned _read_rca() override {
|
||||
return Send_relative_addr::Response::Rca::get(Mmio::read<Rsp10>()); }
|
||||
|
||||
public:
|
||||
|
||||
struct Dma_not_supported : Exception { };
|
||||
|
||||
Sdhci_driver(Env &env)
|
||||
:
|
||||
_mmchs1_mmio(MMCHS1_MMIO_BASE, MMCHS1_MMIO_SIZE),
|
||||
_controller(env.ep(), (addr_t)_mmchs1_mmio.local_addr<void>(),
|
||||
_delayer, *this, false),
|
||||
_use_dma(false)
|
||||
{
|
||||
if (_use_dma) { throw Dma_not_supported(); }
|
||||
Sd_card::Card_info const card_info = _controller.card_info();
|
||||
|
||||
Genode::log("SD card detected");
|
||||
Genode::log("capacity: ", card_info.capacity_mb(), " MiB");
|
||||
}
|
||||
|
||||
void handle_ack(Block::Packet_descriptor pkt, bool success) {
|
||||
ack_packet(pkt, success); }
|
||||
Driver(Env &env);
|
||||
|
||||
|
||||
/*****************************
|
||||
** Block::Driver interface **
|
||||
*****************************/
|
||||
/*******************
|
||||
** Block::Driver **
|
||||
*******************/
|
||||
|
||||
Genode::size_t block_size() { return 512; }
|
||||
void read(Block::sector_t block_number,
|
||||
size_t block_count,
|
||||
char *buffer,
|
||||
Block::Packet_descriptor &pkt) override;
|
||||
|
||||
virtual Block::sector_t block_count()
|
||||
{
|
||||
return _controller.card_info().capacity_mb() * 1024 * 2;
|
||||
}
|
||||
|
||||
Block::Session::Operations ops()
|
||||
{
|
||||
Block::Session::Operations o;
|
||||
o.set_operation(Block::Packet_descriptor::READ);
|
||||
o.set_operation(Block::Packet_descriptor::WRITE);
|
||||
return o;
|
||||
}
|
||||
|
||||
void read(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char *out_buffer,
|
||||
Packet_descriptor &packet)
|
||||
{
|
||||
_controller.read_blocks(block_number, block_count, out_buffer, packet);
|
||||
}
|
||||
|
||||
void write(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char const *buffer,
|
||||
Packet_descriptor &packet)
|
||||
{
|
||||
_controller.write_blocks(block_number, block_count, buffer, packet);
|
||||
}
|
||||
|
||||
void read_dma(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
Genode::addr_t phys,
|
||||
Packet_descriptor &packet)
|
||||
{
|
||||
_controller.read_blocks_dma(block_number, block_count, phys, packet);
|
||||
}
|
||||
|
||||
void write_dma(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
Genode::addr_t phys,
|
||||
Packet_descriptor &packet)
|
||||
{
|
||||
_controller.write_blocks_dma(block_number, block_count, phys, packet);
|
||||
}
|
||||
|
||||
bool dma_enabled() { return _use_dma; }
|
||||
|
||||
Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size) {
|
||||
return Genode::env()->ram_session()->alloc(size, UNCACHED); }
|
||||
|
||||
void free_dma_buffer(Genode::Ram_dataspace_capability c) {
|
||||
return Genode::env()->ram_session()->free(c); }
|
||||
void write(Block::sector_t block_number,
|
||||
size_t block_count,
|
||||
char const *buffer,
|
||||
Block::Packet_descriptor &pkt) override;
|
||||
};
|
||||
|
||||
#endif /* _DRIVERS__SD_CARD__SPEC__OMAP4__DRIVER_H_ */
|
||||
#endif /* _DRIVER_H_ */
|
||||
|
@ -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 Martin Stein
|
||||
* \date 2011-05-19
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2013 Genode Labs GmbH
|
||||
* Copyright (C) 2011-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
@ -14,131 +15,122 @@
|
||||
#ifndef _DRIVER_H_
|
||||
#define _DRIVER_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <block/driver.h>
|
||||
|
||||
/* local includes */
|
||||
#include <pl180.h>
|
||||
#include <os/attached_mmio.h>
|
||||
#include <block/driver.h>
|
||||
#include <pl180_defs.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
namespace Block {
|
||||
namespace Sd_card {
|
||||
|
||||
using namespace Genode;
|
||||
class Sdhci_driver;
|
||||
|
||||
class Driver;
|
||||
}
|
||||
|
||||
|
||||
class Block::Sdhci_driver : public Block::Driver
|
||||
class Sd_card::Driver : public Block::Driver, private Attached_mmio
|
||||
{
|
||||
private:
|
||||
|
||||
Pl180 _hd;
|
||||
enum Register {
|
||||
Power = 0x000, /* power control */
|
||||
Argument = 0x008, /* argument for command */
|
||||
Command = 0x00c, /* command index and type */
|
||||
Response0 = 0x014, /* command response (card status, read only) */
|
||||
DataLength = 0x028, /* number of bytes in data transfer (block size) */
|
||||
DataCtrl = 0x02c, /* data transfer control */
|
||||
Status = 0x034, /* controller status flags (read only) */
|
||||
Clear = 0x038, /* status clear (write only) */
|
||||
Mask0 = 0x03c, /* interrupt 0 mask */
|
||||
Mask1 = 0x040, /* interrupt 1 mask */
|
||||
FifoCnt = 0x048, /* data FIFO counter (in words, read only) */
|
||||
FIFO = 0x080, /* data FIFO */
|
||||
};
|
||||
|
||||
enum { BLOCK_SIZE = 512 };
|
||||
enum Flag {
|
||||
CmdCrcFail = 0x000001, /* command response received (CRC failed) */
|
||||
DataCrcFail = 0x000002, /* data block sent/received (CRC failed) */
|
||||
CmdTimeOut = 0x000004, /* command response timeout */
|
||||
DataTimeOut = 0x000008, /* data timeout */
|
||||
TxUnderrun = 0x000010, /* tx fifo underrun */
|
||||
RxUnderrun = 0x000020, /* rx fifo underrun */
|
||||
CmdRespEnd = 0x000040, /* command response received (CRC ok) */
|
||||
CmdSent = 0x000080, /* command sent (no response required) */
|
||||
DataEnd = 0x000100, /* data counter zero */
|
||||
StartBitErr = 0x000200, /* start bit not detected */
|
||||
DataBlockEnd = 0x000400, /* data block sent/received (CRC ok) */
|
||||
CmdActive = 0x000800, /* command transfer in progress */
|
||||
TxActive = 0x001000, /* data tx in progress */
|
||||
RxActive = 0x002000, /* data rx in progress */
|
||||
TxFifoHalfEmpty = 0x004000,
|
||||
RxFifoHalfFull = 0x008000,
|
||||
TxFifoFull = 0x010000,
|
||||
RxFifoFull = 0x020000,
|
||||
TxFifoEmpty = 0x040000,
|
||||
RxFifoEmpty = 0x080000,
|
||||
TxDataAvlbl = 0x100000,
|
||||
RxDataAvlbl = 0x200000,
|
||||
};
|
||||
|
||||
Timer::Connection _timer;
|
||||
uint32_t volatile *_base { local_addr<unsigned volatile>() };
|
||||
|
||||
uint32_t _read_reg(Register reg) const { return _base[reg >> 2]; }
|
||||
|
||||
void _write_reg(Register reg, uint32_t v) { _base[reg >> 2] = v; }
|
||||
|
||||
void _clear_status() { _write_reg(Clear, ~0); }
|
||||
|
||||
void _request(unsigned char cmd,
|
||||
unsigned *out_resp);
|
||||
|
||||
void _request(unsigned char cmd,
|
||||
unsigned arg,
|
||||
unsigned *out_resp);
|
||||
|
||||
void _read_request(unsigned char cmd,
|
||||
unsigned arg,
|
||||
unsigned length,
|
||||
unsigned *out_resp);
|
||||
|
||||
void _write_request(unsigned char cmd,
|
||||
unsigned arg,
|
||||
unsigned length,
|
||||
unsigned *out_resp);
|
||||
|
||||
void _read_data(unsigned length, char *out_buffer);
|
||||
void _write_data(unsigned length, char const *buffer);
|
||||
void _write_command(unsigned cmd_index, bool resp);
|
||||
|
||||
public:
|
||||
|
||||
Sdhci_driver(Env &) : _hd(PL180_PHYS, PL180_SIZE)
|
||||
{
|
||||
unsigned resp;
|
||||
|
||||
/* CMD0: go idle state */
|
||||
_hd.request(0, 0);
|
||||
Driver(Env &env);
|
||||
|
||||
/*
|
||||
* CMD8: send interface condition
|
||||
*
|
||||
* XXX only one hard-coded value currently.
|
||||
*/
|
||||
_hd.request(8, 0x1aa, &resp);
|
||||
|
||||
/*
|
||||
* ACMD41: card send operating condition
|
||||
*
|
||||
* This is an application-specific command and, therefore, consists
|
||||
* of prefix command CMD55 + CMD41.
|
||||
*/
|
||||
_hd.request(55, 0, &resp);
|
||||
_hd.request(41, 0x4000, &resp);
|
||||
/******************
|
||||
** Block-driver **
|
||||
******************/
|
||||
|
||||
/* CMD2: all send card identification (CID) */
|
||||
_hd.request(2, &resp);
|
||||
Genode::size_t block_size() override { return 512; }
|
||||
Block::Session::Operations ops() override;
|
||||
|
||||
/* CMD3: send relative card address (RCA) */
|
||||
_hd.request(3, &resp);
|
||||
unsigned short rca = resp >> 16;
|
||||
void read(Block::sector_t block_number,
|
||||
size_t block_count,
|
||||
char *buffer,
|
||||
Block::Packet_descriptor &packet);
|
||||
|
||||
/*
|
||||
* Now, the card is in transfer mode...
|
||||
*/
|
||||
void write(Block::sector_t block_number,
|
||||
size_t block_count,
|
||||
char const *buffer,
|
||||
Block::Packet_descriptor &packet);
|
||||
|
||||
/* CMD7: select card */
|
||||
_hd.request(7, rca << 16, &resp);
|
||||
}
|
||||
|
||||
Host_driver &host_driver() { return _hd; }
|
||||
|
||||
/****************************
|
||||
** Block-driver interface **
|
||||
****************************/
|
||||
|
||||
Genode::size_t block_size() { return BLOCK_SIZE; }
|
||||
/*
|
||||
* TODO report (and support) real capacity not just 512M
|
||||
*/
|
||||
Block::sector_t block_count() { return 0x20000000 / BLOCK_SIZE; }
|
||||
|
||||
Block::Session::Operations ops()
|
||||
{
|
||||
Block::Session::Operations o;
|
||||
o.set_operation(Block::Packet_descriptor::READ);
|
||||
o.set_operation(Block::Packet_descriptor::WRITE);
|
||||
return o;
|
||||
}
|
||||
|
||||
void read(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char *out_buffer,
|
||||
Block::Packet_descriptor &packet)
|
||||
{
|
||||
unsigned resp;
|
||||
unsigned length = BLOCK_SIZE;
|
||||
|
||||
for (Genode::size_t i = 0; i < block_count; ++i) {
|
||||
/*
|
||||
* CMD17: read single block
|
||||
*
|
||||
* SDSC cards use a byte address as argument while SDHC/SDSC uses a
|
||||
* block address here.
|
||||
*/
|
||||
_hd.read_request(17, (block_number + i) * BLOCK_SIZE,
|
||||
length, &resp);
|
||||
_hd.read_data(length, out_buffer + (i * BLOCK_SIZE));
|
||||
}
|
||||
ack_packet(packet);
|
||||
}
|
||||
|
||||
void write(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char const *buffer,
|
||||
Block::Packet_descriptor &packet)
|
||||
{
|
||||
unsigned resp;
|
||||
unsigned length = BLOCK_SIZE;
|
||||
|
||||
for (Genode::size_t i = 0; i < block_count; ++i) {
|
||||
/*
|
||||
* CMD24: write single block
|
||||
*
|
||||
* SDSC cards use a byte address as argument while SDHC/SDSC uses a
|
||||
* block address here.
|
||||
*/
|
||||
_hd.write_request(24, (block_number + i) * BLOCK_SIZE,
|
||||
length, &resp);
|
||||
_hd.write_data(length, buffer + (i * BLOCK_SIZE));
|
||||
}
|
||||
ack_packet(packet);
|
||||
}
|
||||
Block::sector_t block_count() override {
|
||||
return 0x20000000 / block_size(); }
|
||||
};
|
||||
|
||||
#endif /* _DRIVER_H_ */
|
||||
|
@ -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 Timo Wischer
|
||||
* \author Martin Stein
|
||||
* \date 2014-09-21
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Genode Labs GmbH
|
||||
* Copyright (C) 2014-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _DRIVERS__SD_CARD__SPEC__RPI__DRIVER_H_
|
||||
#define _DRIVERS__SD_CARD__SPEC__RPI__DRIVER_H_
|
||||
#ifndef _DRIVER_H_
|
||||
#define _DRIVER_H_
|
||||
|
||||
#include <util/mmio.h>
|
||||
#include <os/attached_io_mem_dataspace.h>
|
||||
#include <base/log.h>
|
||||
/* Genode includes */
|
||||
#include <timer_session/connection.h>
|
||||
#include <block/component.h>
|
||||
#include <drivers/board_base.h>
|
||||
#include <os/attached_mmio.h>
|
||||
#include <irq_session/connection.h>
|
||||
|
||||
/* local includes */
|
||||
#include <sdhci.h>
|
||||
#include <driver_base.h>
|
||||
|
||||
namespace Block {
|
||||
using namespace Genode;
|
||||
class Sdhci_driver;
|
||||
}
|
||||
namespace Sd_card { class Driver; }
|
||||
|
||||
|
||||
class Block::Sdhci_driver : public Block::Driver
|
||||
class Sd_card::Driver : public Driver_base,
|
||||
private Attached_mmio
|
||||
{
|
||||
private:
|
||||
|
||||
struct Blksizecnt : Register<0x4, 32>
|
||||
{
|
||||
struct Blkcnt : Bitfield<16, 16> { };
|
||||
struct Blksize : Bitfield<0, 10> { };
|
||||
};
|
||||
|
||||
struct Resp0 : Register<0x10, 32> { };
|
||||
struct Resp1 : Register<0x14, 32> { };
|
||||
struct Resp2 : Register<0x18, 32> { };
|
||||
struct Resp3 : Register<0x1c, 32> { };
|
||||
|
||||
/*
|
||||
* Handle the SDHCI quirk that responses of 136-bit requests are
|
||||
* shifted by 8 bits.
|
||||
*/
|
||||
template <off_t OFFSET>
|
||||
struct Cmdresp_tpl : Register<OFFSET, 32>
|
||||
{
|
||||
struct Resp_8_24 : Register<OFFSET, 32>::template Bitfield<0, 24> { };
|
||||
struct Resp_0_8 : Register<OFFSET, 32>::template Bitfield<24, 8> { };
|
||||
};
|
||||
|
||||
struct Cmdresp0 : Cmdresp_tpl<0x10> { };
|
||||
struct Cmdresp1 : Cmdresp_tpl<0x14> { };
|
||||
struct Cmdresp2 : Cmdresp_tpl<0x18> { };
|
||||
struct Cmdresp3 : Cmdresp_tpl<0x1c> { };
|
||||
|
||||
struct Resp0_136 : Bitset_2<Cmdresp3::Resp_0_8, Cmdresp0::Resp_8_24> { };
|
||||
struct Resp1_136 : Bitset_2<Cmdresp0::Resp_0_8, Cmdresp1::Resp_8_24> { };
|
||||
struct Resp2_136 : Bitset_2<Cmdresp1::Resp_0_8, Cmdresp2::Resp_8_24> { };
|
||||
struct Resp3_136 : Bitset_2<Cmdresp2::Resp_0_8, Cmdresp3::Resp_8_24> { };
|
||||
|
||||
struct Data : Register<0x20, 32> { };
|
||||
|
||||
struct Control0 : Register<0x28, 32>
|
||||
{
|
||||
struct Hctl_dwidth : Bitfield<1, 1> { };
|
||||
struct Hctl_hs_en : Bitfield<2, 1> { };
|
||||
};
|
||||
|
||||
struct Control1 : Register<0x2c, 32>
|
||||
{
|
||||
struct Clk_internal_en : Bitfield<0, 1> { };
|
||||
struct Clk_internal_stable : Bitfield<1, 1> { };
|
||||
struct Clk_en : Bitfield<2, 1> { };
|
||||
|
||||
struct Clk_freq8 : Bitfield<8, 8> { };
|
||||
struct Clk_freq_ms2 : Bitfield<6, 2> { };
|
||||
struct Data_tounit : Bitfield<16, 4> { };
|
||||
struct Srst_hc : Bitfield<24, 1> { };
|
||||
struct Srst_cmd : Bitfield<25, 1> { };
|
||||
struct Srst_data : Bitfield<26, 1> { };
|
||||
};
|
||||
|
||||
struct Status : Register<0x24, 32>
|
||||
{
|
||||
struct Inhibit : Bitfield<0, 2> { };
|
||||
struct Bwe : Bitfield<10, 1> { };
|
||||
struct Bre : Bitfield<11, 1> { };
|
||||
};
|
||||
|
||||
struct Host_ctrl : Register<0x28, 32>
|
||||
{
|
||||
struct Voltage : Bitfield<9, 3> {
|
||||
enum {
|
||||
V18 = 0b101,
|
||||
V30 = 0b110,
|
||||
V33 = 0b111,
|
||||
};
|
||||
};
|
||||
struct Power : Bitfield<8, 1> { };
|
||||
};
|
||||
|
||||
struct Arg1 : Register<0x8, 32> { };
|
||||
|
||||
struct Cmdtm : Register<0xc, 32>
|
||||
{
|
||||
struct Index : Bitfield<24, 6> { };
|
||||
struct Isdata : Bitfield<21, 1> { };
|
||||
struct Tm_blkcnt_en : Bitfield<1, 1> { };
|
||||
struct Tm_multi_block : Bitfield<5, 1> { };
|
||||
struct Tm_auto_cmd_en : Bitfield<2, 2>
|
||||
{
|
||||
enum { CMD12 = 1 };
|
||||
};
|
||||
struct Tm_dat_dir : Bitfield<4, 1>
|
||||
{
|
||||
enum { WRITE = 0, READ = 1 };
|
||||
};
|
||||
struct Rsp_type : Bitfield<16, 2>
|
||||
{
|
||||
enum Response { RESPONSE_NONE = 0,
|
||||
RESPONSE_136_BIT = 1,
|
||||
RESPONSE_48_BIT = 2,
|
||||
RESPONSE_48_BIT_WITH_BUSY = 3 };
|
||||
};
|
||||
};
|
||||
|
||||
struct Interrupt : Register<0x30, 32>
|
||||
{
|
||||
struct Cmd_done : Bitfield<0, 1> { };
|
||||
struct Data_done : Bitfield<1, 1> { };
|
||||
};
|
||||
|
||||
struct Irpt_mask : Register<0x34, 32> { };
|
||||
struct Irpt_en : Register<0x38, 32> { };
|
||||
|
||||
struct Capabilities : Register<0x40, 32> { };
|
||||
|
||||
struct Host_version : Register<0xFE, 16>
|
||||
{
|
||||
struct Spec : Bitfield<0, 8> { };
|
||||
struct Vendor : Bitfield<8, 8> { };
|
||||
};
|
||||
|
||||
struct Timer_delayer : Timer::Connection, Mmio::Delayer
|
||||
{
|
||||
/**
|
||||
* Implementation of 'Delayer' interface
|
||||
*/
|
||||
void usleep(unsigned us) { Timer::Connection::usleep(us); }
|
||||
};
|
||||
|
||||
} _delayer;
|
||||
Ram_session &_ram;
|
||||
Timer_delayer _delayer;
|
||||
Irq_connection _irq { Board_base::SDHCI_IRQ };
|
||||
Card_info _card_info { _init() };
|
||||
|
||||
/* display sub system registers */
|
||||
Attached_io_mem_dataspace _sdhci_mmio { Board_base::SDHCI_BASE, Board_base::SDHCI_SIZE };
|
||||
template <typename REG>
|
||||
bool _poll_and_wait_for(unsigned value)
|
||||
{
|
||||
/* poll for a while */
|
||||
if (!wait_for<REG>(value, _delayer, 5000, 0)) {
|
||||
|
||||
/* host-controller instance */
|
||||
Sdhci_controller _controller;
|
||||
/* if the value was not reached while polling, start sleeping */
|
||||
if (!wait_for<REG>(value, _delayer)) {
|
||||
return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool const _use_dma;
|
||||
Card_info _init();
|
||||
void _set_and_enable_clock(unsigned divider);
|
||||
void _set_block_count(size_t block_count);
|
||||
size_t _block_to_command_address(size_t block_number);
|
||||
|
||||
|
||||
/*********************
|
||||
** Host_controller **
|
||||
*********************/
|
||||
|
||||
bool _issue_command(Command_base const &command) override;
|
||||
Cid _read_cid() override;
|
||||
Csd _read_csd() override;
|
||||
|
||||
Card_info card_info() const override { return _card_info; }
|
||||
|
||||
unsigned _read_rca() override {
|
||||
return Send_relative_addr::Response::Rca::get(Mmio::read<Resp0>()); }
|
||||
|
||||
public:
|
||||
|
||||
Sdhci_driver(Env &)
|
||||
:
|
||||
_controller((addr_t)_sdhci_mmio.local_addr<void>(),
|
||||
_delayer, Board_base::SDHCI_IRQ, false, true),
|
||||
_use_dma(false)
|
||||
{
|
||||
Sd_card::Card_info const card_info = _controller.card_info();
|
||||
|
||||
Genode::log("SD card detected");
|
||||
Genode::log("capacity: ", card_info.capacity_mb(), " MiB");
|
||||
}
|
||||
Driver(Env &env);
|
||||
|
||||
|
||||
/*****************************
|
||||
** Block::Driver interface **
|
||||
*****************************/
|
||||
/*******************
|
||||
** Block::Driver **
|
||||
*******************/
|
||||
|
||||
Genode::size_t block_size() { return Sdhci_controller::Block_size; }
|
||||
void read(Block::sector_t block_number,
|
||||
size_t block_count,
|
||||
char *buffer,
|
||||
Block::Packet_descriptor &packet) override;
|
||||
|
||||
virtual Block::sector_t block_count()
|
||||
{
|
||||
return _controller.card_info().capacity_mb() * 1024 * 2;
|
||||
}
|
||||
void write(Block::sector_t block_number,
|
||||
size_t block_count,
|
||||
char const *buffer,
|
||||
Block::Packet_descriptor &packet) override;
|
||||
|
||||
Block::Session::Operations ops()
|
||||
{
|
||||
Block::Session::Operations o;
|
||||
o.set_operation(Block::Packet_descriptor::READ);
|
||||
o.set_operation(Block::Packet_descriptor::WRITE);
|
||||
return o;
|
||||
}
|
||||
|
||||
void read(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char *out_buffer,
|
||||
Packet_descriptor &packet)
|
||||
{
|
||||
if (!_controller.read_blocks(block_number, block_count, out_buffer))
|
||||
throw Io_error();
|
||||
ack_packet(packet);
|
||||
}
|
||||
|
||||
void write(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
char const *buffer,
|
||||
Packet_descriptor &packet)
|
||||
{
|
||||
if (!_controller.write_blocks(block_number, block_count, buffer))
|
||||
throw Io_error();
|
||||
ack_packet(packet);
|
||||
}
|
||||
|
||||
void read_dma(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
Genode::addr_t phys,
|
||||
Packet_descriptor &packet)
|
||||
{
|
||||
if (!_controller.read_blocks_dma(block_number, block_count, phys))
|
||||
throw Io_error();
|
||||
ack_packet(packet);
|
||||
}
|
||||
|
||||
void write_dma(Block::sector_t block_number,
|
||||
Genode::size_t block_count,
|
||||
Genode::addr_t phys,
|
||||
Packet_descriptor &packet)
|
||||
{
|
||||
if (!_controller.write_blocks_dma(block_number, block_count, phys))
|
||||
throw Io_error();
|
||||
ack_packet(packet);
|
||||
}
|
||||
|
||||
bool dma_enabled() { return _use_dma; }
|
||||
|
||||
Genode::Ram_dataspace_capability alloc_dma_buffer(Genode::size_t size) {
|
||||
return Genode::env()->ram_session()->alloc(size, UNCACHED); }
|
||||
|
||||
void free_dma_buffer(Genode::Ram_dataspace_capability c) {
|
||||
return Genode::env()->ram_session()->free(c); }
|
||||
Ram_dataspace_capability alloc_dma_buffer(size_t size) override {
|
||||
return _ram.alloc(size, UNCACHED); }
|
||||
};
|
||||
|
||||
#endif /* _DRIVERS__SD_CARD__SPEC__RPI__DRIVER_H_ */
|
||||
#endif /* _DRIVER_H_ */
|
||||
|
@ -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 };
|
||||
Signal_handler<Main> ack_handler { env.ep(), *this, &Main::update_state };
|
||||
Driver_session drv_session { ack_handler };
|
||||
Block::Sdhci_driver drv { env };
|
||||
Sd_card::Driver drv { env };
|
||||
size_t const buf_size_kib { config()->xml_node()
|
||||
.attribute_value("buffer_size_kib",
|
||||
(size_t)0) };
|
||||
|
Loading…
Reference in New Issue
Block a user