diff --git a/repos/os/lib/mk/spec/exynos5/sd_card_bench.mk b/repos/os/lib/mk/spec/exynos5/sd_card_bench.mk index 1007f6c24f..d8f3a6f490 100644 --- a/repos/os/lib/mk/spec/exynos5/sd_card_bench.mk +++ b/repos/os/lib/mk/spec/exynos5/sd_card_bench.mk @@ -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 diff --git a/repos/os/lib/mk/spec/exynos5/sd_card_drv.mk b/repos/os/lib/mk/spec/exynos5/sd_card_drv.mk index b7657581d0..a2356fa505 100644 --- a/repos/os/lib/mk/spec/exynos5/sd_card_drv.mk +++ b/repos/os/lib/mk/spec/exynos5/sd_card_drv.mk @@ -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 diff --git a/repos/os/lib/mk/spec/imx53/sd_card_bench.mk b/repos/os/lib/mk/spec/imx53/sd_card_bench.mk index 1542020f2a..c551fd4c82 100644 --- a/repos/os/lib/mk/spec/imx53/sd_card_bench.mk +++ b/repos/os/lib/mk/spec/imx53/sd_card_bench.mk @@ -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 diff --git a/repos/os/lib/mk/spec/imx53/sd_card_drv.mk b/repos/os/lib/mk/spec/imx53/sd_card_drv.mk index 784a82fef3..54fb7eb653 100644 --- a/repos/os/lib/mk/spec/imx53/sd_card_drv.mk +++ b/repos/os/lib/mk/spec/imx53/sd_card_drv.mk @@ -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 diff --git a/repos/os/lib/mk/spec/imx6/sd_card_bench.mk b/repos/os/lib/mk/spec/imx6/sd_card_bench.mk index f25ea8b120..e2e3fb3d9b 100644 --- a/repos/os/lib/mk/spec/imx6/sd_card_bench.mk +++ b/repos/os/lib/mk/spec/imx6/sd_card_bench.mk @@ -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 diff --git a/repos/os/lib/mk/spec/imx6/sd_card_drv.mk b/repos/os/lib/mk/spec/imx6/sd_card_drv.mk index c5cdcf54f5..dc586ee169 100644 --- a/repos/os/lib/mk/spec/imx6/sd_card_drv.mk +++ b/repos/os/lib/mk/spec/imx6/sd_card_drv.mk @@ -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 diff --git a/repos/os/lib/mk/spec/omap4/sd_card_bench.mk b/repos/os/lib/mk/spec/omap4/sd_card_bench.mk index a91ddc6e06..a3d1134068 100644 --- a/repos/os/lib/mk/spec/omap4/sd_card_bench.mk +++ b/repos/os/lib/mk/spec/omap4/sd_card_bench.mk @@ -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 diff --git a/repos/os/lib/mk/spec/omap4/sd_card_drv.mk b/repos/os/lib/mk/spec/omap4/sd_card_drv.mk index 5fdd391f8d..0f65c5db3d 100644 --- a/repos/os/lib/mk/spec/omap4/sd_card_drv.mk +++ b/repos/os/lib/mk/spec/omap4/sd_card_drv.mk @@ -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 diff --git a/repos/os/lib/mk/spec/pl180/sd_card_bench.mk b/repos/os/lib/mk/spec/pl180/sd_card_bench.mk index 0ed74cda51..bad767bbe3 100644 --- a/repos/os/lib/mk/spec/pl180/sd_card_bench.mk +++ b/repos/os/lib/mk/spec/pl180/sd_card_bench.mk @@ -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 diff --git a/repos/os/lib/mk/spec/pl180/sd_card_drv.mk b/repos/os/lib/mk/spec/pl180/sd_card_drv.mk index b4b6183617..401fc7c825 100644 --- a/repos/os/lib/mk/spec/pl180/sd_card_drv.mk +++ b/repos/os/lib/mk/spec/pl180/sd_card_drv.mk @@ -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 diff --git a/repos/os/lib/mk/spec/rpi/sd_card_bench.mk b/repos/os/lib/mk/spec/rpi/sd_card_bench.mk index 9bb3d5f948..9bcb592b70 100644 --- a/repos/os/lib/mk/spec/rpi/sd_card_bench.mk +++ b/repos/os/lib/mk/spec/rpi/sd_card_bench.mk @@ -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 diff --git a/repos/os/lib/mk/spec/rpi/sd_card_drv.mk b/repos/os/lib/mk/spec/rpi/sd_card_drv.mk index b2c9b61867..f93c1716a8 100644 --- a/repos/os/lib/mk/spec/rpi/sd_card_drv.mk +++ b/repos/os/lib/mk/spec/rpi/sd_card_drv.mk @@ -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 diff --git a/repos/os/src/drivers/sd_card/spec/imx/adma2.cc b/repos/os/src/drivers/sd_card/adma2.cc similarity index 96% rename from repos/os/src/drivers/sd_card/spec/imx/adma2.cc rename to repos/os/src/drivers/sd_card/adma2.cc index 815ef700d3..f1736bd8f3 100644 --- a/repos/os/src/drivers/sd_card/spec/imx/adma2.cc +++ b/repos/os/src/drivers/sd_card/adma2.cc @@ -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()), _base_phys(Dataspace_client(_ds.cap()).phys_addr()) { } diff --git a/repos/os/src/drivers/sd_card/spec/imx/adma2.h b/repos/os/src/drivers/sd_card/adma2.h similarity index 98% rename from repos/os/src/drivers/sd_card/spec/imx/adma2.h rename to repos/os/src/drivers/sd_card/adma2.h index df11e5b1b5..7f80fbed42 100644 --- a/repos/os/src/drivers/sd_card/spec/imx/adma2.h +++ b/repos/os/src/drivers/sd_card/adma2.h @@ -66,7 +66,7 @@ class Adma2::Table public: - Table(); + Table(Ram_session &ram); /** * Marshal descriptors according to block request diff --git a/repos/os/src/drivers/sd_card/driver_base.h b/repos/os/src/drivers/sd_card/driver_base.h new file mode 100644 index 0000000000..a846427e13 --- /dev/null +++ b/repos/os/src/drivers/sd_card/driver_base.h @@ -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 + +/* local includes */ +#include + +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_ */ diff --git a/repos/os/src/drivers/sd_card/main.cc b/repos/os/src/drivers/sd_card/main.cc index db80f23e44..c14cfbf4dc 100644 --- a/repos/os/src/drivers/sd_card/main.cc +++ b/repos/os/src/drivers/sd_card/main.cc @@ -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(driver)); } + Genode::destroy(&heap, static_cast(driver)); } } factory { env, heap }; diff --git a/repos/os/src/drivers/sd_card/sd_card.h b/repos/os/src/drivers/sd_card/sd_card.h index 1e59eb362f..9d6b69183d 100644 --- a/repos/os/src/drivers/sd_card/sd_card.h +++ b/repos/os/src/drivers/sd_card/sd_card.h @@ -16,7 +16,7 @@ #define _SD_CARD_H_ /* Genode includes */ -#include +#include namespace Sd_card { diff --git a/repos/os/src/drivers/sd_card/spec/exynos5/driver.cc b/repos/os/src/drivers/sd_card/spec/exynos5/driver.cc new file mode 100644 index 0000000000..f99d629326 --- /dev/null +++ b/repos/os/src/drivers/sd_card/spec/exynos5/driver.cc @@ -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 + +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(0x7); + + if (!wait_for(0, _delayer, 100, 1000)) { + error("Could not reset host contoller"); + return false; + } + return true; +} + + +void Driver::_reset_fifo() +{ + Mmio::write(0x2); + + if (!wait_for(0, _delayer, 100, 1000)) { + error("Could not reset fifo"); } +} + + +void Driver::_disable_irq() +{ + Mmio::write(~0U); + Mmio::write(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); + + if (!wait_for(0, _delayer)) { + error("Update clock registers failed"); + return false; + } + return true; +} + + +bool Driver::_setup_bus(unsigned clock_div) +{ + /* set host clock divider */ + Mmio::write(clock_div); + + if (!_update_clock_registers()) + return false; + + /* enable clock for card 1 */ + Mmio::write(0x1); + + if (!_update_clock_registers()) + return false; + + _delayer.usleep(10 * 1000); + return true; +} + + +Card_info Driver::_init() +{ + Mmio::write(1); + if (!_reset()) + throw Detection_failed(); + + Mmio::write(0x1); + + _disable_irq(); + + Mmio::write(~0U); + Mmio::write(0); + Mmio::write(1); + Mmio::write(0); + Mmio::write(0x203f0040); + + /* set to one bit transfer Bit */ + if (!_setup_bus(CLK_DIV_400Khz)) + throw Detection_failed(); + + Mmio::write(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() & 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(); + arg = (voltages & (arg & 0x007FFF80)) | (arg & 0x60000000); + + _delayer.usleep(1000); + + if (Ocr::Busy::get(Mmio::read())) + 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(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(0x28a); + Mmio::write(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(_idmac_desc_phys); + + Mmio::write(1); + Mmio::write(1); + + Mmio::write(1); + Mmio::write(1); + + Mmio::write(block_size()); + Mmio::write(block_size() * block_count); + + Mmio::write(1); + + return true; +} + + +void Driver::_handle_irq() +{ + _irq.ack_irq(); + + if (!_block_transfer.pending) { + return; } + + bool success = false; + if (Mmio::read()) { + error("Response error"); + } + if (Mmio::read()) { + error("Data read timeout"); + } + if (Mmio::read()) { + error("CRC error"); + } + if (Mmio::read()) { + Mmio::write(~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(0, _delayer, 10000, 100)) { + error("wait for State::Data_busy timed out ", + Hex(Mmio::read())); + return false; + } + + Mmio::write(~0UL); + + /* write command argument */ + Mmio::write(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); + + if (!wait_for(1, _delayer, 10000, 100)) { + error("command failed " + "Rintst: ", Mmio::read(), " " + "Mintst: ", Mmio::read(), " " + "Status: ", Mmio::read()); + + if (Mmio::read()) + warning("timeout"); + + if (Mmio::read()) + warning("repsonse error"); + + return false; + } + + /* acknowledge interrupt */ + Mmio::write(1); + + _delayer.usleep(100); + + return true; +} + + +Cid Driver::_read_cid() +{ + Cid cid; + cid.raw_0 = Mmio::read(); + cid.raw_1 = Mmio::read(); + cid.raw_2 = Mmio::read(); + cid.raw_3 = Mmio::read(); + return cid; +} + + +Csd Driver::_read_csd() +{ + Csd csd; + csd.csd0 = Mmio::read(); + csd.csd1 = Mmio::read(); + csd.csd2 = Mmio::read(); + csd.csd3 = Mmio::read(); + 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(1, _delayer, 10000, 100)) { + error("cannot retrieve extented CSD"); + throw Detection_failed(); + } + /* clear IRQ */ + Mmio::write(1); + + /* contruct extented CSD */ + Ext_csd csd((addr_t)ds.local_addr()); + + /* read revision */ + if (csd.Mmio::read() < 2) { + error("extented CSD revision is < 2"); + throw Detection_failed(); + } + + /* return sector count */ + uint64_t capacity = csd.Mmio::read() * 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; +} diff --git a/repos/os/src/drivers/sd_card/spec/exynos5/driver.h b/repos/os/src/drivers/sd_card/spec/exynos5/driver.h index 3edfe5dfdf..0000e3dc48 100644 --- a/repos/os/src/drivers/sd_card/spec/exynos5/driver.h +++ b/repos/os/src/drivers/sd_card/spec/exynos5/driver.h @@ -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 -#include -#include +#include #include -#include +#include #include +#include +#include /* local includes */ -#include +#include -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 + struct Register : Mmio::Register { }; + + 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 _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() }; + 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(), - _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_ */ diff --git a/repos/os/src/drivers/sd_card/spec/exynos5/dwmmc.h b/repos/os/src/drivers/sd_card/spec/exynos5/dwmmc.h deleted file mode 100644 index d6d16b6ce8..0000000000 --- a/repos/os/src/drivers/sd_card/spec/exynos5/dwmmc.h +++ /dev/null @@ -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 -#include -#include -#include -#include - -#include - -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 - struct Register : Genode::Mmio::Register { }; - - /** - * 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(1); - } - - bool reset(Delayer &delayer) - { - /* set all three bits */ - write(0x7); - - if (!wait_for(0, delayer, 100, 1000)) { - Genode::error("Could not reset host contoller"); - return false; - } - - return true; - } - - void reset_fifo(Delayer &delayer) - { - write(0x2); - - if (!wait_for(0, delayer, 100, 1000)) - Genode::error("Could not reset fifo"); - } - - void disable_irq() - { - write(~0U); - write(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(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); - - if (!wait_for(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(clock_div); - - if (!update_clock_registers(delayer)) - return false; - - /* enable clock for card 1 */ - write(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 _irq_handler; - Genode::Irq_connection _irq; - - Sd_card::Card_info _init() - { - using namespace Sd_card; - - powerup(); - - if (!reset(_delayer)) - throw Detection_failed(); - - write(0x1); - - disable_irq(); - - write(~0U); - write(0); - write(1); - write(0); - - write(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() & 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(); - arg = (voltages & (arg & 0x007FFF80)) | (arg & 0x60000000); - - _delayer.usleep(1000); - - if (Ocr::Busy::get(read())) - 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(0x28a); - write(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(_idmac_desc_phys); - - write(1); - write(1); - - write(1); - write(1); - - write(BLOCK_SIZE); - write(BLOCK_SIZE * block_count); - - write(1); - - return true; - } - - void _handle_irq() - { - _irq.ack_irq(); - - if (!_block_transfer.pending) { - return; } - - bool success = false; - if (read()) { - Genode::error("Response error"); - } - if (read()) { - Genode::error("Data read timeout"); - } - if (read()) { - Genode::error("CRC error"); - } - if (read()) { - write(~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_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(0, _delayer, 10000, 100)) { - Genode::error("wait for State::Data_busy timed out ", - Genode::Hex(read())); - return false; - } - - write(~0UL); - - /* write command argument */ - write(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); - - if (!wait_for(1, _delayer, 10000, 100)) { - Genode::error("command failed " - "Rintst: ", read(), " " - "Mintst: ", read(), " " - "Status: ", read()); - - if (read()) - Genode::warning("timeout"); - - if (read()) - Genode::warning("repsonse error"); - - return false; - } - - /* acknowledge interrupt */ - write(1); - - _delayer.usleep(100); - - return true; - } - - Sd_card::Cid _read_cid() - { - Sd_card::Cid cid; - cid.raw_0 = read(); - cid.raw_1 = read(); - cid.raw_2 = read(); - cid.raw_3 = read(); - return cid; - } - - Sd_card::Csd _read_csd() - { - Sd_card::Csd csd; - csd.csd0 = read(); - csd.csd1 = read(); - csd.csd2 = read(); - csd.csd3 = read(); - 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(1, _delayer)) { - Genode::error("cannot retrieve extented CSD"); - throw Detection_failed(); - } - /* clear IRQ */ - write(1); - - /* contruct extented CSD */ - Sd_card::Ext_csd csd((addr_t)ds.local_addr()); - - /* read revision */ - if (csd.read() < 2) { - Genode::error("extented CSD revision is < 2"); - throw Detection_failed(); - } - - /* return sector count */ - uint64_t capacity = csd.read() * 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())); - 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())); - throw Block::Driver::Io_error(); - } - } -}; - - -#endif /* _DRIVERS__SD_CARD__SPEC__EXYNOS5__DWMMC_H_ */ diff --git a/repos/os/src/drivers/sd_card/spec/imx/sdhc.cc b/repos/os/src/drivers/sd_card/spec/imx/driver.cc similarity index 85% rename from repos/os/src/drivers/sd_card/spec/imx/sdhc.cc rename to repos/os/src/drivers/sd_card/spec/imx/driver.cc index e02a6b3514..5c4e8c0a6c 100644 --- a/repos/os/src/drivers/sd_card/spec/imx/sdhc.cc +++ b/repos/os/src/drivers/sd_card/spec/imx/driver.cc @@ -12,13 +12,13 @@ */ /* local includes */ -#include +#include 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(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(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(); @@ -189,7 +189,7 @@ Cid Sdhc::_read_cid() } -Csd Sdhc::_read_csd() +Csd Driver::_read_csd() { Csd csd; csd.csd0 = Mmio::read(); @@ -200,76 +200,65 @@ Csd Sdhc::_read_csd() } -unsigned Sdhc::_read_rca() +unsigned Driver::_read_rca() { Cmdrsp0::access_t const rsp0 = Mmio::read(); 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(_adma2_table.base_phys()); - Mmio::write(BLOCK_SIZE); + Mmio::write(block_size()); Mmio::write(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(1); @@ -454,14 +443,14 @@ int Sdhc::_reset(Delayer &delayer) } -void Sdhc::_disable_irqs() +void Driver::_disable_irqs() { Mmio::write(0); Mmio::write(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::_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(); @@ -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::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(1, _delayer); _disable_clock(); diff --git a/repos/os/src/drivers/sd_card/spec/imx/driver.h b/repos/os/src/drivers/sd_card/spec/imx/driver.h index 7d64b68a67..e7f9b1151a 100644 --- a/repos/os/src/drivers/sd_card/spec/imx/driver.h +++ b/repos/os/src/drivers/sd_card/spec/imx/driver.h @@ -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 +#include +#include +#include + /* local includes */ -#include +#include +#include + +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 + struct Cmdrsp_tpl : Register + { + struct Rsp136_8_24 : Register::template Bitfield<0, 24> { }; + struct Rsp136_0_8 : Register::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 { }; + struct Rsp136_1 : Bitset_2 { }; + struct Rsp136_2 : Bitset_2 { }; + struct Rsp136_3 : Bitset_2 { }; + + template + struct Xfertyp_base : Register + { + struct Dmaen : Register::template Bitfield<0, 1> { }; + struct Bcen : Register::template Bitfield<1, 1> { }; + struct Ac12en : Register::template Bitfield<2, 1> { }; + struct Dtdsel : Register::template Bitfield<4, 1> + { + enum { WRITE = 0, READ = 1, }; + }; + struct Msbsel : Register::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 + struct Irq_tpl : Register + { + struct Cc : Register::template Bitfield<0, 1> { }; + struct Tc : Register::template Bitfield<1, 1> { }; + struct Dint : Register::template Bitfield<3, 1> { }; + struct Ctoe : Register::template Bitfield<16, 1> { }; + struct Cce : Register::template Bitfield<17, 1> { }; + struct Cebe : Register::template Bitfield<18, 1> { }; + struct Cie : Register::template Bitfield<19, 1> { }; + struct Dtoe : Register::template Bitfield<20, 1> { }; + struct Dce : Register::template Bitfield<21, 1> { }; + struct Debe : Register::template Bitfield<22, 1> { }; + struct Ac12e : Register::template Bitfield<24, 1> { }; + struct Dmae : Register::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 _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_ */ diff --git a/repos/os/src/drivers/sd_card/spec/imx/sdhc.h b/repos/os/src/drivers/sd_card/spec/imx/sdhc.h deleted file mode 100644 index cb8150722b..0000000000 --- a/repos/os/src/drivers/sd_card/spec/imx/sdhc.h +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* local includes */ -#include -#include - -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 - struct Cmdrsp_tpl : Register - { - struct Rsp136_8_24 : Register::template Bitfield<0, 24> { }; - struct Rsp136_0_8 : Register::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 { }; - struct Rsp136_1 : Bitset_2 { }; - struct Rsp136_2 : Bitset_2 { }; - struct Rsp136_3 : Bitset_2 { }; - - template - struct Xfertyp_base : Register - { - struct Dmaen : Register::template Bitfield<0, 1> { }; - struct Bcen : Register::template Bitfield<1, 1> { }; - struct Ac12en : Register::template Bitfield<2, 1> { }; - struct Dtdsel : Register::template Bitfield<4, 1> - { - enum { WRITE = 0, READ = 1, }; - }; - struct Msbsel : Register::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 - struct Irq_tpl : Register - { - struct Cc : Register::template Bitfield<0, 1> { }; - struct Tc : Register::template Bitfield<1, 1> { }; - struct Dint : Register::template Bitfield<3, 1> { }; - struct Ctoe : Register::template Bitfield<16, 1> { }; - struct Cce : Register::template Bitfield<17, 1> { }; - struct Cebe : Register::template Bitfield<18, 1> { }; - struct Cie : Register::template Bitfield<19, 1> { }; - struct Dtoe : Register::template Bitfield<20, 1> { }; - struct Dce : Register::template Bitfield<21, 1> { }; - struct Debe : Register::template Bitfield<22, 1> { }; - struct Ac12e : Register::template Bitfield<24, 1> { }; - struct Dmae : Register::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 _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_ */ diff --git a/repos/os/src/drivers/sd_card/spec/imx53/sdhc.cc b/repos/os/src/drivers/sd_card/spec/imx53/driver.cc similarity index 82% rename from repos/os/src/drivers/sd_card/spec/imx53/sdhc.cc rename to repos/os/src/drivers/sd_card/spec/imx53/driver.cc index 2de562c59a..08aefd6145 100644 --- a/repos/os/src/drivers/sd_card/spec/imx53/sdhc.cc +++ b/repos/os/src/drivers/sd_card/spec/imx53/driver.cc @@ -12,13 +12,13 @@ */ /* local includes */ -#include +#include 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::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() { } diff --git a/repos/os/src/drivers/sd_card/spec/imx6/sdhc.cc b/repos/os/src/drivers/sd_card/spec/imx6/driver.cc similarity index 80% rename from repos/os/src/drivers/sd_card/spec/imx6/sdhc.cc rename to repos/os/src/drivers/sd_card/spec/imx6/driver.cc index e89f313baf..24d91fb405 100644 --- a/repos/os/src/drivers/sd_card/spec/imx6/sdhc.cc +++ b/repos/os/src/drivers/sd_card/spec/imx6/driver.cc @@ -12,13 +12,13 @@ */ /* local includes */ -#include +#include 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::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::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(); @@ -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(0); } +void Driver::_disable_clock_preparation() { + Mmio::write(0); } -void Sdhc::_enable_clock_finish() { Mmio::write(0); } +void Driver::_enable_clock_finish() { Mmio::write(0); } diff --git a/repos/os/src/drivers/sd_card/spec/omap4/driver.cc b/repos/os/src/drivers/sd_card/spec/omap4/driver.cc new file mode 100644 index 0000000000..4a1d4fc287 --- /dev/null +++ b/repos/os/src/drivers/sd_card/spec/omap4/driver.cc @@ -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 + +using namespace Genode; +using namespace Sd_card; + + +Card_info Driver::_init() +{ + Mmio::write(0x2015); + Mmio::write(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(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() != 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())) + 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(1); + Mmio::write(1); + Mmio::write(1); + Mmio::write(1); + + return card_info; +} + + +bool Driver::_wait_for_bre() +{ + if (!wait_for(1, _delayer, 1000*1000, 0)) { + if (!wait_for(1, _delayer)) { + error("Pstate::Bre timed out"); + return false; + } + } + return true; +} + + +bool Driver::_wait_for_bwe() +{ + if (!wait_for(1, _delayer, 1000*1000, 0)) { + if (!wait_for(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() != 1) { + warning("unexpected interrupt, Stat: ", Hex(Mmio::read())); + return; + } + + Mmio::write(1); + + if (Mmio::read() != 0) { + warning("unexpected state (" + "Stat: ", Hex(Mmio::read()), " " + "Blen: ", Hex(Mmio::read()), " " + "Nblk: ", Mmio::read()); + return; + } + _block_transfer.pending = false; + ack_packet(_block_transfer.packet, true); +} + + +bool Driver::_reset_cmd_line() +{ + Mmio::write(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(1, _delayer, 1000, 0)) { + error("reset of cmd line timed out (src != 1)"); + return false; + } + if (!wait_for(0, _delayer, 1000, 0)) { + error("reset of cmd line timed out (src != 0)"); + return false; + } + return true; +} + + +void Driver::_disable_irq() +{ + Mmio::write(0); + Mmio::write(0); + Mmio::write(~0); +} + + +void Driver::_bus_width(Bus_width bus_width) +{ + switch (bus_width) { + case BUS_WIDTH_1: + Mmio::write(0); + Mmio::write(Hctl::Dtw::ONE_BIT); + break; + + case BUS_WIDTH_4: + Mmio::write(0); + Mmio::write(Hctl::Dtw::FOUR_BITS); + break; + } +} + + +bool Driver::_sd_bus_power_on() +{ + Mmio::write(Hctl::Sdbp::POWER_ON); + + if (!wait_for(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::TCF_2_POW_27); + + switch (divider) { + case CLOCK_DIV_0: Mmio::write(0); break; + case CLOCK_DIV_240: Mmio::write(240); break; + } + + Mmio::write(1); + + /* wait for clock to become stable */ + if (!wait_for(1, _delayer)) { + error("clock enable timed out"); + return false; + } + + /* enable clock */ + Mmio::write(1); + + return true; +} + + +void Driver::_set_bus_power(Voltage voltage) +{ + switch (voltage) { + case VOLTAGE_3_0: + Mmio::write(Hctl::Sdvs::VOLTAGE_3_0); + break; + case VOLTAGE_1_8: + Mmio::write(Hctl::Sdvs::VOLTAGE_1_8); + break; + } + Mmio::write(1); + + if (voltage == VOLTAGE_3_0) + Mmio::write(1); +} + + +bool Driver::_init_stream() +{ + Mmio::write(0x307f0033); + + /* start initialization sequence */ + Mmio::write(1); + Mmio::write(0); + + if (!wait_for(1, _delayer, 1000*1000, 0)) { + error("init stream timed out"); + return false; + } + /* stop initialization sequence */ + Mmio::write(0); + Mmio::write(~0); + Mmio::read(); + return true; +} + + +bool Driver::_issue_command(Command_base const &command) +{ + if (!wait_for(0, _delayer)) { + error("wait for Pstate::Cmdi timed out"); + return false; + } + /* write command argument */ + Mmio::write(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); + 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(); + if (Stat::Erri::get(stat)) { + warning("SD command error"); + if (Stat::Cto::get(stat)) + warning("timeout"); + + _reset_cmd_line(); + Mmio::write(~0); + Mmio::read(); + result = false; + break; + } + if (Stat::Cc::get(stat) == 1) { + result = true; + break; + } + } + /* clear status of command-completed bit */ + Mmio::write(1); + Mmio::read(); + return result; +} + + +Cid Driver::_read_cid() +{ + Cid cid; + cid.raw_0 = Mmio::read(); + cid.raw_1 = Mmio::read(); + cid.raw_2 = Mmio::read(); + cid.raw_3 = Mmio::read(); + return cid; +} + + +Csd Driver::_read_csd() +{ + Csd csd; + csd.csd0 = Mmio::read(); + csd.csd1 = Mmio::read(); + csd.csd2 = Mmio::read(); + csd.csd3 = Mmio::read(); + 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(block_size()); + Mmio::write(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(); + } +} + + +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(block_size()); + Mmio::write(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(*src++); + } +} diff --git a/repos/os/src/drivers/sd_card/spec/omap4/driver.h b/repos/os/src/drivers/sd_card/spec/omap4/driver.h index e11b9d38ab..9605ddd7b9 100644 --- a/repos/os/src/drivers/sd_card/spec/omap4/driver.h +++ b/repos/os/src/drivers/sd_card/spec/omap4/driver.h @@ -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 -#include -#include +/* Genode includes */ +#include +#include #include -#include +#include /* local includes */ -#include +#include -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 _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(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()); } 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(), - _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_ */ diff --git a/repos/os/src/drivers/sd_card/spec/omap4/mmchs.h b/repos/os/src/drivers/sd_card/spec/omap4/mmchs.h deleted file mode 100644 index 64ecb6f162..0000000000 --- a/repos/os/src/drivers/sd_card/spec/omap4/mmchs.h +++ /dev/null @@ -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 -#include -#include -#include -#include - -/* local includes */ -#include - -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(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(1, delayer, 1000, 0)) { - Genode::error("reset of cmd line timed out (src != 1)"); - return false; - } - - if (!wait_for(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(1); - - if (!wait_for(1, delayer, 1000, 0)) { - Genode::error("soft reset all timed out (src != 1)"); - return false; - } - - return true; - } - - void disable_irq() - { - write(0); - write(0); - write(~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(0); - write(Hctl::Dtw::ONE_BIT); - break; - - case BUS_WIDTH_4: - write(0); - write(Hctl::Dtw::FOUR_BITS); - break; - } - } - - bool sd_bus_power_on(Delayer &delayer) - { - write(Hctl::Sdbp::POWER_ON); - - if (!wait_for(1, delayer)) { - Genode::error("setting Hctl::Sdbp timed out"); - return false; - } - return true; - } - - void stop_clock() - { - write(0); - } - - enum Clock_divider { CLOCK_DIV_0, CLOCK_DIV_240 }; - - bool set_and_enable_clock(enum Clock_divider divider, Delayer &delayer) - { - write(Sysctl::Dto::TCF_2_POW_27); - - switch (divider) { - case CLOCK_DIV_0: write(0); break; - case CLOCK_DIV_240: write(240); break; - } - - write(1); - - /* wait for clock to become stable */ - if (!wait_for(1, delayer)) { - Genode::error("clock enable timed out"); - return false; - } - - /* enable clock */ - write(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::VOLTAGE_3_0); - break; - - case VOLTAGE_1_8: - write(Hctl::Sdvs::VOLTAGE_1_8); - break; - } - - write(1); - - if (voltage == VOLTAGE_3_0) - write(1); - } - - bool init_stream(Delayer &delayer) - { - write(0x307f0033); - - /* start initialization sequence */ - write(1); - - write(0); - - if (!wait_for(1, delayer, 1000*1000, 0)) { - Genode::error("init stream timed out"); - return false; - } - - /* stop initialization sequence */ - write(0); - - write(~0); - read(); - - 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 _irq_handler; - Genode::Irq_connection _irq; - - Sd_card::Card_info _init() - { - using namespace Sd_card; - - write(0x2015); - write(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(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() != 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())) - 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(1); - - /* enable IRQs */ - write(1); - write(1); - write(1); - write(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(_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(1, _delayer, 1000*1000, 0) - && !wait_for(1, _delayer)) { - Genode::error("Pstate::Bre timed out"); - return false; - } - return true; - } - - bool _wait_for_bwe() - { - if (!wait_for(1, _delayer, 1000*1000, 0) - && !wait_for(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() != 1) { - Genode::warning("unexpected interrupt, Stat: ", Genode::Hex(read())); - return; - } - - write(1); - - if (read() != 0) { - Genode::warning("unexpected state (" - "Stat: ", Genode::Hex(read()), " " - "Blen: ", Genode::Hex(read()), " " - "Nblk: ", read()); - 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_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(0, _delayer)) { - Genode::error("wait for Pstate::Cmdi timed out"); - return false; - } - - /* write command argument */ - write(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); - - 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(); - if (Stat::Erri::get(stat)) { - Genode::warning("SD command error"); - if (Stat::Cto::get(stat)) - Genode::warning("timeout"); - - reset_cmd_line(_delayer); - write(~0); - read(); - 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(1); - read(); - - 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(); - cid.raw_1 = read(); - cid.raw_2 = read(); - cid.raw_3 = read(); - return cid; - } - - Sd_card::Csd _read_csd() - { - Sd_card::Csd csd; - csd.csd0 = read(); - csd.csd1 = read(); - csd.csd2 = read(); - csd.csd3 = read(); - return csd; - } - - unsigned _read_rca() - { - return Sd_card::Send_relative_addr::Response::Rca::get(read()); - } - - 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(0x200); - write(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(); - } - } - - 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(0x200); - write(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(*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(0x200); - write(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(0x200); - write(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_ */ diff --git a/repos/os/src/drivers/sd_card/spec/pl180/driver.cc b/repos/os/src/drivers/sd_card/spec/pl180/driver.cc new file mode 100644 index 0000000000..73f758469a --- /dev/null +++ b/repos/os/src/drivers/sd_card/spec/pl180/driver.cc @@ -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 + +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(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(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; +} diff --git a/repos/os/src/drivers/sd_card/spec/pl180/driver.h b/repos/os/src/drivers/sd_card/spec/pl180/driver.h index 4574b2186d..455084fe63 100644 --- a/repos/os/src/drivers/sd_card/spec/pl180/driver.h +++ b/repos/os/src/drivers/sd_card/spec/pl180/driver.h @@ -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 - /* local includes */ -#include +#include +#include #include +#include -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() }; + + 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_ */ diff --git a/repos/os/src/drivers/sd_card/spec/pl180/host_driver.h b/repos/os/src/drivers/sd_card/spec/pl180/host_driver.h deleted file mode 100644 index 8d1c2ba979..0000000000 --- a/repos/os/src/drivers/sd_card/spec/pl180/host_driver.h +++ /dev/null @@ -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_ */ diff --git a/repos/os/src/drivers/sd_card/spec/pl180/pl180.h b/repos/os/src/drivers/sd_card/spec/pl180/pl180.h deleted file mode 100644 index dd4b05ef44..0000000000 --- a/repos/os/src/drivers/sd_card/spec/pl180/pl180.h +++ /dev/null @@ -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 -#include -#include - -#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()) - { - 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(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(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_ */ diff --git a/repos/os/src/drivers/sd_card/spec/rpi/driver.cc b/repos/os/src/drivers/sd_card/spec/rpi/driver.cc new file mode 100644 index 0000000000..f9821d80d9 --- /dev/null +++ b/repos/os/src/drivers/sd_card/spec/rpi/driver.cc @@ -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 + +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::Clk_freq8::set(ctl1, divider); + Control1::Clk_freq_ms2::set(ctl1, 0); + Control1::Clk_internal_en::set(ctl1, 1); + Mmio::write(ctl1); + + if (!wait_for(1, _delayer)) { + error("could not set internal clock"); + throw Detection_failed(); + } + Mmio::write(1); + _delayer.usleep(10*1000); + + /* data timeout unit exponent */ + Mmio::write(0xe); +} + + +Card_info Driver::_init() +{ + /* reset host controller */ + Control1::access_t v = Mmio::read(); + Control1::Srst_hc::set(v); + Control1::Srst_data::set(v); + Mmio::write(v); + + if (!wait_for(0, _delayer)) { + error("host-controller soft reset timed out"); + throw Detection_failed(); + } + log("SDHCI version: ", Mmio::read(), " " + "(specification ", Mmio::read() + 1, ".0)"); + + /* Enable sd card power */ + Mmio::write(Host_ctrl::Power::bits(1) + | Host_ctrl::Voltage::bits(Host_ctrl::Voltage::V33)); + + /* enable interrupt status reporting */ + Mmio::write(~0UL); + Mmio::write(~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() != 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())) + 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::Hctl_dwidth::set(ctl0); + Control0::Hctl_hs_en::set(ctl0); + Mmio::write(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::Blkcnt::set(v, block_count); + Blksizecnt::Blksize::set(v, block_size()); + Mmio::write(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(0)) { + error("controller inhibits issueing commands"); + return false; + } + /* write command argument */ + Mmio::write(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(cmd); + + if (!_poll_and_wait_for(1)) { + error("command timed out"); + return false; + } + /* clear interrupt state */ + Mmio::write(1); + + return true; +} + + +Cid Driver::_read_cid() +{ + Cid cid; + cid.raw_0 = Mmio::read(); + cid.raw_1 = Mmio::read(); + cid.raw_2 = Mmio::read(); + cid.raw_3 = Mmio::read(); + return cid; +} + + +Csd Driver::_read_csd() +{ + Csd csd; + csd.csd0 = Mmio::read(); + csd.csd1 = Mmio::read(); + csd.csd2 = Mmio::read(); + csd.csd3 = Mmio::read(); + 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(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(); + } + if (!_poll_and_wait_for(1)) { + error("completion of read request failed"); + throw Io_error(); + } + /* clear interrupt state */ + Mmio::write(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(1)) { + throw Io_error(); } + + /* write data into sdhci buffer */ + for (size_t j = 0; j < block_size() / sizeof(Data::access_t); j++) + Mmio::write(*src++); + } + if (!_poll_and_wait_for(1)) { + error("completion of write request failed"); + throw Io_error(); + } + /* clear interrupt state */ + Mmio::write(1); + + ack_packet(packet); +} diff --git a/repos/os/src/drivers/sd_card/spec/rpi/driver.h b/repos/os/src/drivers/sd_card/spec/rpi/driver.h index 10f66de9e1..b662990726 100644 --- a/repos/os/src/drivers/sd_card/spec/rpi/driver.h +++ b/repos/os/src/drivers/sd_card/spec/rpi/driver.h @@ -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 -#include -#include +/* Genode includes */ #include -#include #include +#include +#include /* local includes */ -#include +#include -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 + struct Cmdresp_tpl : Register + { + struct Resp_8_24 : Register::template Bitfield<0, 24> { }; + struct Resp_0_8 : Register::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 { }; + struct Resp1_136 : Bitset_2 { }; + struct Resp2_136 : Bitset_2 { }; + struct Resp3_136 : Bitset_2 { }; + + 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 + bool _poll_and_wait_for(unsigned value) + { + /* poll for a while */ + if (!wait_for(value, _delayer, 5000, 0)) { - /* host-controller instance */ - Sdhci_controller _controller; + /* if the value was not reached while polling, start sleeping */ + if (!wait_for(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()); } public: - Sdhci_driver(Env &) - : - _controller((addr_t)_sdhci_mmio.local_addr(), - _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_ */ diff --git a/repos/os/src/drivers/sd_card/spec/rpi/sdhci.h b/repos/os/src/drivers/sd_card/spec/rpi/sdhci.h deleted file mode 100644 index 0978e50ac5..0000000000 --- a/repos/os/src/drivers/sd_card/spec/rpi/sdhci.h +++ /dev/null @@ -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 -#include -#include -#include - -/* local includes */ -#include - - -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 - struct Cmdresp_tpl : Register - { - struct Resp_8_24 : Register::template Bitfield<0, 24> { }; - struct Resp_0_8 : Register::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 { }; - struct Resp1_136 : Genode::Bitset_2 { }; - struct Resp2_136 : Genode::Bitset_2 { }; - struct Resp3_136 : Genode::Bitset_2 { }; - - 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::Clk_freq8::set(v, divider); - Control1::Clk_freq_ms2::set(v, 0); - Control1::Clk_internal_en::set(v, 1); - - write(v); - - if (!wait_for(1, _delayer)) { - Genode::error("could not set internal clock"); - throw Detection_failed(); - } - - write(1); - - _delayer.usleep(10*1000); - - /* data timeout unit exponent */ - write(0xe); - } - - Sd_card::Card_info _init(const bool set_voltage) - { - using namespace Sd_card; - - /* reset host controller */ - { - Control1::access_t v = read(); - Control1::Srst_hc::set(v); - Control1::Srst_data::set(v); - write(v); - } - - if (!wait_for(0, _delayer)) { - Genode::error("host-controller soft reset timed out"); - throw Detection_failed(); - } - - Genode::log("SDHCI version: ", read(), " " - "(specification ", read() + 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::Power::bits(1) - | Host_ctrl::Voltage::bits(Host_ctrl::Voltage::V33)); - } - - /* enable interrupt status reporting */ - write(~0UL); - write(~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() != 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())) - 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::Hctl_dwidth::set(v); - Control0::Hctl_hs_en::set(v); - write(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::Blkcnt::set(v, block_count); - Blksizecnt::Blksize::set(v, Block_size); - write(v); - } - - template - bool _poll_and_wait_for(unsigned value) - { - /* poll for a while */ - if (wait_for(value, _delayer, 5000, 0)) - return true; - - /* if the value were not reached while polling, start sleeping */ - return wait_for(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(0)) { - Genode::error("controller inhibits issueing commands"); - return false; - } - - /* write command argument */ - write(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(cmd); - - if (!_poll_and_wait_for(1)) { - Genode::error("command timed out"); - return false; - } - - /* clear interrupt state */ - write(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(); - cid.raw_1 = read(); - cid.raw_2 = read(); - cid.raw_3 = read(); - return cid; - } - - Sd_card::Csd _read_csd() - { - Sd_card::Csd csd; - csd.csd0 = read(); - csd.csd1 = read(); - csd.csd2 = read(); - csd.csd3 = read(); - return csd; - } - - unsigned _read_rca() - { - return Sd_card::Send_relative_addr::Response::Rca::get(read()); - } - - 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())); - 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(1)) - return false; - - /* read data from sdhci buffer */ - for (size_t j = 0; j < 512/sizeof(Data::access_t); j++) - *dst++ = read(); - } - - if (!_poll_and_wait_for(1)) { - Genode::error("completion of read request failed (interrupt " - "status ", Genode::Hex(read()), ")"); - return false; - } - - /* clear interrupt state */ - write(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())); - 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(1)) - return false; - - /* write data into sdhci buffer */ - for (size_t j = 0; j < 512/sizeof(Data::access_t); j++) - write(*src++); - } - - if (!_poll_and_wait_for(1)) { - Genode::error("completion of write request failed (interrupt " - "status ", read(), ")"); - return false; - } - - /* clear interrupt state */ - write(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_ */ diff --git a/repos/os/src/test/sd_card_bench/main.cc b/repos/os/src/test/sd_card_bench/main.cc index 83c45b98fb..46ab6340ec 100644 --- a/repos/os/src/test/sd_card_bench/main.cc +++ b/repos/os/src/test/sd_card_bench/main.cc @@ -55,7 +55,7 @@ struct Main Operation operation { READ }; Signal_handler
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) };