From 8c22e5d53590decc2ce71f0805914667a4a6a1e3 Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Thu, 22 Oct 2015 18:17:41 +0200 Subject: [PATCH] sd_card & imx53: refactor and clean-up Move ADMA2 stuff to extra header and unit. Move ESDHCv2 implementations to extra unit. Use exceptions instead of error codes. Clean-up documentation. Ref #1497 --- .../src/drivers/sd_card/spec/imx53/adma2.cc | 65 ++ .../os/src/drivers/sd_card/spec/imx53/adma2.h | 89 ++ .../sd_card/spec/imx53/bench/target.mk | 15 +- .../src/drivers/sd_card/spec/imx53/esdhcv2.cc | 555 ++++++++++++ .../src/drivers/sd_card/spec/imx53/esdhcv2.h | 795 ++---------------- .../src/drivers/sd_card/spec/imx53/target.mk | 14 +- 6 files changed, 789 insertions(+), 744 deletions(-) create mode 100644 repos/os/src/drivers/sd_card/spec/imx53/adma2.cc create mode 100644 repos/os/src/drivers/sd_card/spec/imx53/adma2.h create mode 100644 repos/os/src/drivers/sd_card/spec/imx53/esdhcv2.cc diff --git a/repos/os/src/drivers/sd_card/spec/imx53/adma2.cc b/repos/os/src/drivers/sd_card/spec/imx53/adma2.cc new file mode 100644 index 000000000..0146c0fa5 --- /dev/null +++ b/repos/os/src/drivers/sd_card/spec/imx53/adma2.cc @@ -0,0 +1,65 @@ +/* + * \brief Advanced DMA 2 + * \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. + */ + +/* Genode includes */ +#include + +/* local includes */ +#include + +using namespace Adma2; + + +Table::Table() +: + _ds(env()->ram_session(), _ds_size, UNCACHED), + _base_virt(_ds.local_addr()), + _base_phys(Dataspace_client(_ds.cap()).phys_addr()) +{ } + + +int Table::setup_request(size_t const size, addr_t const buffer_phys) +{ + /* sanity check */ + static size_t constexpr max_size = _max_desc * Desc::Length::max; + if (size > max_size) { + PERR("Block request too large"); + return -1; + } + /* install new descriptors till they cover all requested bytes */ + addr_t consumed = 0; + for (int index = 0; consumed < size; index++) { + + /* clamp current request to maximum request size */ + size_t const remaining = size - consumed; + size_t const curr = min(Desc::Length::max, remaining); + + /* assemble new descriptor */ + Desc::access_t desc = 0; + Desc::Address::set(desc, buffer_phys + consumed); + Desc::Length::set(desc, curr); + Desc::Act1::set(desc, 0); + Desc::Act2::set(desc, 1); + Desc::Valid::set(desc, 1); + + /* let last descriptor generate transfer-complete signal */ + if (consumed + curr == size) { Desc::End::set(desc, 1); } + + /* install and account descriptor */ + _base_virt[index] = desc; + consumed += curr; + } + /* ensure that all descriptor writes were actually executed */ + asm volatile ("dsb"); + return 0; +} diff --git a/repos/os/src/drivers/sd_card/spec/imx53/adma2.h b/repos/os/src/drivers/sd_card/spec/imx53/adma2.h new file mode 100644 index 000000000..df11e5b1b --- /dev/null +++ b/repos/os/src/drivers/sd_card/spec/imx53/adma2.h @@ -0,0 +1,89 @@ +/* + * \brief Advanced DMA 2 + * \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 _ADMA2_H_ +#define _ADMA2_H_ + +/* Genode includes */ +#include +#include + +namespace Adma2 +{ + using namespace Genode; + + class Desc; + class Table; +} + +/** + * Descriptor layout + */ +struct Adma2::Desc : Register<64> +{ + struct Valid : Bitfield<0, 1> { }; + struct End : Bitfield<1, 1> { }; + struct Int : Bitfield<2, 1> { }; + struct Act1 : Bitfield<4, 1> { }; + struct Act2 : Bitfield<5, 1> { }; + struct Length : Bitfield<16, 16> + { + /* + * According to the 'SD Specifications, Part A2, SD Host + * Controller, Simplified Specification, Version 2.00, February 8, + * 2007, Table 1-10', a maximum length of 65536 bytes is achieved + * by value 0. However, if we do so, the completion host-signal + * times out now and then. Thus, we use the next lower possible + * value. + */ + static constexpr addr_t align_log2 = 2; + static constexpr size_t max = (1 << WIDTH) - (1 << align_log2); + }; + struct Address : Bitfield<32, 32> { }; +}; + +/** + * Descriptor table + */ +class Adma2::Table +{ + static size_t constexpr _max_desc = 1024; + static size_t constexpr _ds_size = _max_desc * sizeof(Desc::access_t); + + Attached_ram_dataspace _ds; + Desc::access_t * const _base_virt; + addr_t const _base_phys; + + public: + + Table(); + + /** + * Marshal descriptors according to block request + * + * \param size request size in bytes + * \param buffer_phys physical base of transfer buffer + * + * \retval 0 success + * \retval -1 error + */ + int setup_request(size_t const size, addr_t const buffer_phys); + + /* + * Accessors + */ + + addr_t base_phys() const { return _base_phys; } +}; + +#endif /* _ADMA2_H_ */ diff --git a/repos/os/src/drivers/sd_card/spec/imx53/bench/target.mk b/repos/os/src/drivers/sd_card/spec/imx53/bench/target.mk index c60ae1272..b465d5e75 100644 --- a/repos/os/src/drivers/sd_card/spec/imx53/bench/target.mk +++ b/repos/os/src/drivers/sd_card/spec/imx53/bench/target.mk @@ -1,6 +1,9 @@ -TARGET = sd_card_bench -REQUIRES = imx53 -SRC_CC = main.cc -LIBS = base server -INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx53 -INC_DIR += $(REP_DIR)/src/drivers/sd_card +TARGET = sd_card_bench +REQUIRES += imx53 +SRC_CC += main.cc +SRC_CC += ../adma2.cc +SRC_CC += ../esdhcv2.cc +LIBS += base +LIBS += server +INC_DIR += $(PRG_DIR)/.. +INC_DIR += $(PRG_DIR)/../../.. diff --git a/repos/os/src/drivers/sd_card/spec/imx53/esdhcv2.cc b/repos/os/src/drivers/sd_card/spec/imx53/esdhcv2.cc new file mode 100644 index 000000000..d19bbafb9 --- /dev/null +++ b/repos/os/src/drivers/sd_card/spec/imx53/esdhcv2.cc @@ -0,0 +1,555 @@ +/* + * \brief Freescale Enhanced Secured Digital Host Controller Version 2 + * \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. + */ + +/* local includes */ +#include + +using namespace Sd_card; +using namespace Genode; + + +int Esdhcv2_controller::_wait_for_card_ready_mbw() +{ + /* + * Poll card status + * + * The maximum number of attempts and the delay between two attempts are + * freely chosen. + */ + unsigned attempts = 5; + unsigned constexpr attempts_delay_us = 100000; + while (1) { + if (!attempts) { + PERR("Reading card status after multiblock write failed"); + return -1; + } + /* assemble argument register value */ + Send_status::Arg::access_t cmdarg = 0; + Send_status::Arg::Rca::set(cmdarg, _card_info.rca()); + + /* assemble command register value */ + Xfertyp::access_t xfertyp = 0; + Xfertyp::Cmdinx::set(xfertyp, Send_status::INDEX); + Xfertyp::Cicen::set(xfertyp, 1); + Xfertyp::Cccen::set(xfertyp, 1); + Xfertyp::Rsptyp::set(xfertyp, Xfertyp::Rsptyp::_48BIT); + Xfertyp::Msbsel::set(xfertyp, 1); + Xfertyp::Bcen::set(xfertyp, 1); + Xfertyp::Dmaen::set(xfertyp, 1); + + /* send command as soon as the host allows it */ + if (_wait_for_cmd_allowed()) { return -1; } + write(cmdarg); + write(xfertyp); + + /* wait for command completion */ + if (_wait_for_cmd_complete()) { return -1; } + + /* check for errors */ + R1_response_0::access_t const resp = read(); + if (R1_response_0::Error::get(resp)) { + PERR("Reading card status after multiblock write failed"); + return -1; + } + /* if card is in a ready state, return success, retry otherwise */ + if (R1_response_0::card_ready(resp)) { break; } + _delayer.usleep(attempts_delay_us); + } + return 0; +} + + +int Esdhcv2_controller::_stop_transmission_mbw() +{ + /* write argument register */ + write(0); + + /* write command register */ + Xfertyp::access_t xfertyp = 0; + Xfertyp::Cmdinx::set(xfertyp, Stop_transmission::INDEX); + Xfertyp::Cmdtyp::set(xfertyp, Xfertyp::Cmdtyp::ABORT_CMD12); + Xfertyp::Cccen::set(xfertyp, 1); + Xfertyp::Cicen::set(xfertyp, 1); + Xfertyp::Rsptyp::set(xfertyp, Xfertyp::Rsptyp::_48BIT_BUSY); + Xfertyp::Msbsel::set(xfertyp, 1); + Xfertyp::Bcen::set(xfertyp, 1); + Xfertyp::Dmaen::set(xfertyp, 1); + write(xfertyp); + + /* wait for command completion */ + if (_wait_for_cmd_complete()) { return -1; } + return 0; +} + + +int Esdhcv2_controller::_wait_for_cmd_complete_mb(bool const r) +{ + /* + * The ESDHC signals on multi-block transfers seem to be broken. + * Synchronizing to "Transfer Complete" before returning from transfers + * and to "Command Inhibit" before sending further commands - as it is + * done with other controllers - isn't sufficient. Instead, both "Transfer + * Complete" and "Command Complete" must be gathered. + */ + Irqstat::access_t constexpr irq_goal = + Irq::Cc::reg_mask() | Irq::Tc::reg_mask(); + + /* wait for a first signal */ + _wait_for_irq(); + Irqstat::access_t const irq = read(); + + /* + * Poll for missing signal because interrupts are edge-triggered + * and could thus got lost in the meantime. + */ + if (irq != irq_goal) { + if (!wait_for(irq_goal, _delayer)) { + PERR("Completion host signal timed out"); + return -1; + } + } + /* acknowledge completion signals */ + write(irq_goal); + if (!r) { + + /* + * The "Auto Command 12" feature of the ESDHC seems to be + * broken for multi-block writes as it causes command- + * timeout errors sometimes. Thus, we stop such transfers + * manually. + */ + if (_stop_transmission_mbw()) { return -1; } + + /* + * The manual termination of multi-block writes seems to leave + * the card in a busy state sometimes. This causes + * errors on subsequent commands. Thus, we have to synchronize + * manually with the card-internal state. + */ + if (_wait_for_card_ready_mbw()) { return -1; } + } + return 0; +} + + +int Esdhcv2_controller::_wait_for_cmd_complete() +{ + /* wait for "Command Completion" signal and acknowledge it */ + _wait_for_irq(); + if (read() != Irqstat::Cc::reg_mask()) { + PERR("Received unexpected host signal"); + return -1; + } + write(Irqstat::Cc::reg_mask()); + return 0; +} + + +bool Esdhcv2_controller::_issue_command(Command_base const & command) +{ + /* detect if command is a multi-block transfer and whether it reads */ + bool const r = command.transfer == TRANSFER_READ; + bool const mb = + command.index == Read_multiple_block::INDEX || + command.index == Write_multiple_block::INDEX; + + /* assemble command register value */ + Xfertyp::access_t cmd = 0; + Xfertyp::Cmdinx::set(cmd, command.index); + if (command.transfer != TRANSFER_NONE) { + Xfertyp::Dpsel::set(cmd); + Xfertyp::Bcen::set(cmd); + Xfertyp::Msbsel::set(cmd); + if (mb) { + /* + * The "Auto Command 12" feature of the ESDHC seems to be + * broken for multi-block writes as it causes command- + * timeout errors sometimes. + */ + if (r) { Xfertyp::Ac12en::set(cmd); } + if (_use_dma) { Xfertyp::Dmaen::set(cmd); } + } + Xfertyp::Dtdsel::set(cmd, + r ? Xfertyp::Dtdsel::READ : Xfertyp::Dtdsel::WRITE); + } + typedef Xfertyp::Rsptyp Rsptyp; + Xfertyp::access_t rt = 0; + switch (command.rsp_type) { + case RESPONSE_NONE: rt = Rsptyp::_0BIT; break; + case RESPONSE_136_BIT: rt = Rsptyp::_136BIT; break; + case RESPONSE_48_BIT: rt = Rsptyp::_48BIT; break; + case RESPONSE_48_BIT_WITH_BUSY: rt = Rsptyp::_48BIT_BUSY; break; + } + Xfertyp::Rsptyp::set(cmd, rt); + + /* send command as soon as the host allows it */ + if (_wait_for_cmd_allowed()) { return false; } + write(command.arg); + write(cmd); + + /* wait for completion */ + return mb ? !_wait_for_cmd_complete_mb(r) : !_wait_for_cmd_complete(); +} + + +Cid Esdhcv2_controller::_read_cid() +{ + Cid cid; + cid.raw_0 = read(); + cid.raw_1 = read(); + cid.raw_2 = read(); + cid.raw_3 = read(); + return cid; +} + + +Csd Esdhcv2_controller::_read_csd() +{ + Csd csd; + csd.csd0 = read(); + csd.csd1 = read(); + csd.csd2 = read(); + csd.csd3 = read(); + return csd; +} + + +unsigned Esdhcv2_controller::_read_rca() +{ + Cmdrsp0::access_t const rsp0 = read(); + return Send_relative_addr::Response::Rca::get(rsp0); +} + + +bool Esdhcv2_controller::read_blocks(size_t, size_t, char *) +{ + PERR("Block transfer without DMA not supported by now"); + return false; +} + + +bool Esdhcv2_controller::write_blocks(size_t, size_t, char const *) +{ + PERR("Block transfer without DMA not supported by now"); + return false; +} + + +bool Esdhcv2_controller::read_blocks_dma(size_t blk_nr, size_t blk_cnt, + addr_t buf_phys) +{ + if (_prepare_dma_mb(blk_cnt, buf_phys)) { return false; } + return issue_command(Read_multiple_block(blk_nr)); +} + + +bool Esdhcv2_controller::write_blocks_dma(size_t blk_nr, size_t blk_cnt, + addr_t buf_phys) +{ + if (_prepare_dma_mb(blk_cnt, buf_phys)) { return false; } + return issue_command(Write_multiple_block(blk_nr)); +} + + +Esdhcv2_controller::Esdhcv2_controller(addr_t const base, unsigned const irq, + Delayer & delayer, bool const use_dma) +: + Esdhcv2(base), _irq(irq), _delayer(delayer), _card_info(_init()), + _use_dma(use_dma) +{ } + + +int Esdhcv2_controller::_prepare_dma_mb(size_t blk_cnt, addr_t buf_phys) +{ + /* write ADMA2 table to DMA */ + size_t const req_size = blk_cnt * BLOCK_SIZE; + if (_adma2_table.setup_request(req_size, buf_phys)) { return -1; } + + /* configure DMA at host */ + write(_adma2_table.base_phys()); + write(BLOCK_SIZE); + write(blk_cnt); + return 0; +} + + +int Esdhcv2_controller::_wait_for_cmd_allowed() +{ + /* + * At least after multi-block writes with the fix for the broken "Auto + * Command 12", waiting only for "Command Inhibit" isn't sufficient as + * "Data Line Active" and "Data Inhibit" may also be active. + */ + if (!wait_for(Prsstat_lhw::cmd_allowed(), _delayer)) { + PERR("Wait till issuing a new command is allowed timed out"); + return -1; + } + return 0; +} + + +void Esdhcv2_controller::_wait_for_irq() +{ + /* acknowledge IRQ first, to activate IRQ propagation initially */ + _irq.ack_irq(); + _irq_rec.wait_for_signal(); +} + + +Card_info Esdhcv2_controller::_init() +{ + /* install IRQ signal */ + _irq.sigh(_irq_rec.manage(&_irq_ctx)); + + /* configure host for initialization stage */ + if (_reset(_delayer)) { _detect_err("Host reset failed"); } + _disable_irqs(); + + /* check host version */ + Hostver::access_t const hostver = read(); + if (Hostver::Vvn::get(hostver) != 18) { + _detect_err("Unexpected Vendor Version Number"); } + if (Hostver::Svn::get(hostver) != 1) { + _detect_err("Unexpected Specification Version Number"); } + + /* + * We should check host capabilities at this point if we want to + * support other versions of the ESDHC. For the i.MX53 ESDHCv2 we + * know that the capabilities fit our requirements. + */ + + /* configure IRQs, bus width, and clock for initialization */ + _enable_irqs(); + _bus_width(BUS_WIDTH_1); + _delayer.usleep(10000); + _clock(CLOCK_DIV_512, _delayer); + + /* + * Initialize card + */ + + /* + * At this point we should do an SDIO card reset if we later want + * to detect the unwanted case of an SDIO card beeing inserted. + * The reset would be done via 2 differently configured + * Io_rw_direct commands. + */ + + _delayer.usleep(1000); + if (!issue_command(Go_idle_state())) { + _detect_err("Go_idle_state command failed"); } + + _delayer.usleep(2000); + if (!issue_command(Send_if_cond())) { + _detect_err("Send_if_cond command failed"); } + + if (read() != 0x1aa) { + _detect_err("Unexpected response of Send_if_cond command"); } + + /* + * At this point we could detect the unwanted case of an SDIO card + * beeing inserted by issuing 4 Io_send_op_cond commands at an + * interval of 10 ms (they should time out on SD). + */ + + if (!issue_command(Sd_send_op_cond(0, false))) { + _detect_err("Sd_send_op_cond command failed"); } + + _delayer.usleep(1000); + if (!issue_command(Go_idle_state())) { + _detect_err("Go_idle_state command failed"); } + + _delayer.usleep(2000); + if (!issue_command(Send_if_cond())) { + _detect_err("Send_if_cond failed"); } + + if (read() != 0x1aa) { + _detect_err("Unexpected response of Send_if_cond command"); } + + /* + * Power on card + * + * 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(0x200000, true))) { + _detect_err("Sd_send_op_cond command failed"); } + + if (Ocr::Busy::get(read())) { break; } + _delayer.usleep(1000); + } + if (!i) { _detect_err("Could not power-on SD card"); } + + /* get basic information about the card */ + Card_info card_info = _detect(); + + /* + * Configure working clock of host + * + * Host and card may be driven with a higher clock rate but + * checks (maybe read SSR/SCR, read switch, try frequencies) are + * necessary for that. + */ + _clock(CLOCK_DIV_8, _delayer); + + /* + * Configure card and host to use 4 data signals + * + * Host and card may be driven with a higher bus width but + * further checks (read SCR) are necessary for that. + */ + if (!issue_command(Set_bus_width(Set_bus_width::Arg::Bus_width::FOUR_BITS), + card_info.rca())) + { + _detect_err("Set_bus_width(FOUR_BITS) command failed"); + } + _bus_width(BUS_WIDTH_4); + _delayer.usleep(10000); + + /* configure card to use given block size */ + if (!issue_command(Set_blocklen(BLOCK_SIZE))) { + _detect_err("Set_blocklen command failed"); } + + /* configure host buffer */ + Wml::access_t wml = read(); + Wml::Rd_wml::set(wml, WATERMARK_WORDS); + Wml::Rd_brst_len::set(wml, BURST_WORDS); + Wml::Wr_wml::set(wml, WATERMARK_WORDS); + Wml::Wr_brst_len::set(wml, BURST_WORDS); + write(wml); + + /* configure ADMA */ + write(Proctl::Dmas::ADMA2); + + /* configure interrupts for operational mode */ + _disable_irqs(); + write(~0); + _enable_irqs(); + return card_info; +} + + +void Esdhcv2_controller::_detect_err(char const * const err) +{ + PERR("%s", err); + throw Detection_failed(); +} + + +int Esdhcv2_controller::_reset(Delayer & delayer) +{ + /* start reset */ + write(1); + + /* + * The SDHC specification says that a software reset shouldn't + * have an effect on the the card detection circuit. The ESDHC + * clears Sysctl::Ipgen, Sysctl::Hcken, and Sysctl::Peren + * nonetheless which disables clocks that card detection relies + * on. + */ + Sysctl::access_t sysctl = read(); + Sysctl::Ipgen::set(sysctl, 1); + Sysctl::Hcken::set(sysctl, 1); + Sysctl::Peren::set(sysctl, 1); + write(sysctl); + + /* wait for reset completion */ + if (!wait_for(0, delayer)) { + PERR("Reset timed out"); + return -1; + } + return 0; +} + + +void Esdhcv2_controller::_disable_irqs() +{ + write(0); + write(0); +} + + +void Esdhcv2_controller::_enable_irqs() +{ + Irq::access_t irq = 0; + Irq::Cc::set(irq, 1); + Irq::Tc::set(irq, 1); + Irq::Dint::set(irq, 1); + Irq::Ctoe::set(irq, 1); + Irq::Cce::set(irq, 1); + Irq::Cebe::set(irq, 1); + Irq::Cie::set(irq, 1); + Irq::Dtoe::set(irq, 1); + Irq::Dce::set(irq, 1); + Irq::Debe::set(irq, 1); + Irq::Ac12e::set(irq, 1); + Irq::Dmae::set(irq, 1); + write(irq); + write(irq); +} + + +void Esdhcv2_controller::_bus_width(Bus_width bus_width) +{ + switch (bus_width) { + case BUS_WIDTH_1: write(Proctl::Dtw::_1BIT); break; + case BUS_WIDTH_4: write(Proctl::Dtw::_4BIT); break; + } +} + + +void Esdhcv2_controller::_disable_clock() +{ + Sysctl::access_t sysctl = read(); + Sysctl::Ipgen::set(sysctl, 0); + Sysctl::Hcken::set(sysctl, 0); + Sysctl::Peren::set(sysctl, 0); + Sysctl::Dvs::set(sysctl, Sysctl::Dvs::DIV1); + Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV1); + write(sysctl); +} + + +void Esdhcv2_controller::_enable_clock(Clock_divider divider, Delayer &delayer) +{ + Sysctl::access_t sysctl = read(); + Sysctl::Ipgen::set(sysctl, 1); + Sysctl::Hcken::set(sysctl, 1); + Sysctl::Peren::set(sysctl, 1); + switch (divider) { + case CLOCK_DIV_8: + Sysctl::Dvs::set(sysctl, Sysctl::Dvs::DIV4); + Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV2); + break; + case CLOCK_DIV_512: + Sysctl::Dvs::set(sysctl, Sysctl::Dvs::DIV16); + Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV32); + break; + } + write(sysctl); + delayer.usleep(1000); +} + + +void Esdhcv2_controller::_clock(enum Clock_divider divider, Delayer &delayer) +{ + _disable_clock(); + write(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27); + _enable_clock(divider, delayer); +} diff --git a/repos/os/src/drivers/sd_card/spec/imx53/esdhcv2.h b/repos/os/src/drivers/sd_card/spec/imx53/esdhcv2.h index 177de1ed1..a87bb4abd 100644 --- a/repos/os/src/drivers/sd_card/spec/imx53/esdhcv2.h +++ b/repos/os/src/drivers/sd_card/spec/imx53/esdhcv2.h @@ -1,159 +1,45 @@ /* - * \brief ESDHCv2 host controller + * \brief Freescale Enhanced Secured Digital Host Controller Version 2 * \author Martin Stein * \date 2015-02-05 */ /* - * Copyright (C) 2012-2015 Genode Labs GmbH + * 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__IMX53__ESDHCV2_H_ -#define _DRIVERS__SD_CARD__SPEC__IMX53__ESDHCV2_H_ +#ifndef _ESDHCV2_H_ +#define _ESDHCV2_H_ /* Genode includes */ #include -#include #include #include /* local includes */ #include +#include -namespace Adma2 +namespace Genode { - using namespace Genode; - - /** - * Descriptor layout - */ - struct Desc : Register<64> - { - struct Valid : Bitfield<0, 1> { }; - struct End : Bitfield<1, 1> { }; - struct Int : Bitfield<2, 1> { }; - struct Act1 : Bitfield<4, 1> { }; - struct Act2 : Bitfield<5, 1> { }; - struct Length : Bitfield<16, 16> - { - /* - * According to the 'SD Specifications, Part A2, SD Host - * Controller, Simplified Specification, Version 2.00, February 8, - * 2007, Table 1-10', a maximum length of 65536 bytes is achieved - * by value 0. However, if we do so, the completion host-signal - * times out now and then. Thus, we use the next lower possible - * value. - */ - static constexpr addr_t align_log2 = 2; - static constexpr size_t max = (1 << WIDTH) - (1 << align_log2); - }; - struct Address : Bitfield<32, 32> { }; - }; - - /** - * Descriptor table - */ - class Table - { - static size_t constexpr _max_desc = 1024; - static size_t constexpr _ds_size = _max_desc * sizeof(Desc::access_t); - - Attached_ram_dataspace _ds; - Desc::access_t * const _base_virt; - addr_t const _base_phys; - - public: - - /** - * Constructor - */ - Table() : - _ds(env()->ram_session(), _ds_size, UNCACHED), - _base_virt(_ds.local_addr()), - _base_phys(Dataspace_client(_ds.cap()).phys_addr()) - { } - - /** - * Marshal descriptors according to block request - * - * \param size request size in bytes - * \param buffer_phys physical base of transfer buffer - * - * \return true on success - */ - bool setup_req(size_t const size, addr_t const buffer_phys) - { - /* - * Sanity check - * - * FIXME An alternative to this sanity check would be to - * expose the maximum transfer size to the driver and let - * the driver partition large requests into ones that are - * supported. - */ - static size_t constexpr max_size = _max_desc * Desc::Length::max; - if (size > max_size) { - PERR("Block request too large"); - return false; - } - /* install new descriptors till they cover all requested bytes */ - addr_t consumed = 0; - for (int index = 0; consumed < size; index++) { - - /* clamp current request to maximum request size */ - size_t const remaining = size - consumed; - size_t const curr = min(Desc::Length::max, remaining); - - /* assemble new descriptor */ - Desc::access_t desc = 0; - Desc::Address::set(desc, buffer_phys + consumed); - Desc::Length::set(desc, curr); - Desc::Act1::set(desc, 0); - Desc::Act2::set(desc, 1); - Desc::Valid::set(desc, 1); - - /* let last descriptor generate transfer-complete signal */ - if (consumed + curr == size) { Desc::End::set(desc, 1); } - - /* install and account descriptor */ - _base_virt[index] = desc; - consumed += curr; - } - /* ensure that all descriptor writes were actually executed */ - asm volatile ("dsb"); - return true; - } - - /* - * Accessors - */ - - addr_t base_phys() const { return _base_phys; } - }; - + struct Esdhcv2; + class Esdhcv2_controller; } /** - * Freescale eSDHCv2 host MMIO + * MMIO structure of a Freescale ESDHCv2 */ -struct Esdhcv2 : Genode::Mmio +struct Genode::Esdhcv2 : Mmio { - typedef Genode::size_t size_t; - typedef Genode::addr_t addr_t; - - /* - * MMIO structure - */ - struct Blkattr : Register<0x4, 32> { struct Blksize : Bitfield<0, 13> { }; struct Blkcnt : Bitfield<16, 16> { }; }; - template + template struct Cmdrsp_tpl : Register { struct Rsp136_8_24 : Register::template Bitfield<0, 24> { }; @@ -164,10 +50,10 @@ struct Esdhcv2 : Genode::Mmio struct Cmdrsp1 : Cmdrsp_tpl<0x14> { }; struct Cmdrsp2 : Cmdrsp_tpl<0x18> { }; struct Cmdrsp3 : Cmdrsp_tpl<0x1c> { }; - struct Rsp136_0 : Genode::Bitset_2 { }; - struct Rsp136_1 : Genode::Bitset_2 { }; - struct Rsp136_2 : Genode::Bitset_2 { }; - struct Rsp136_3 : Genode::Bitset_2 { }; + struct Rsp136_0 : Bitset_2 { }; + struct Rsp136_1 : Bitset_2 { }; + struct Rsp136_2 : Bitset_2 { }; + struct Rsp136_3 : Bitset_2 { }; struct Xfertyp : Register<0xc, 32> { struct Dmaen : Bitfield<0, 1> { }; @@ -196,7 +82,7 @@ struct Esdhcv2 : Genode::Mmio }; struct Cmdinx : Bitfield<24, 6> { }; }; - struct Datport : Register<0x20, 32> { }; + struct Prsstat : Register<0x24, 32> { }; struct Prsstat_lhw : Register<0x24, 16> { struct Sdstb : Bitfield<3, 1> { }; @@ -233,12 +119,12 @@ struct Esdhcv2 : Genode::Mmio struct Rstd : Bitfield<26, 1> { }; }; - template + template struct Irq_tpl : Register { struct Cc : Register::template Bitfield<0, 1> { }; struct Tc : Register::template Bitfield<1, 1> { }; - struct Dint : Register::template Bitfield<03, 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> { }; @@ -268,157 +154,15 @@ struct Esdhcv2 : Genode::Mmio struct Wr_brst_len : Bitfield<24, 5> { }; }; - /** - * Constructor - */ - Esdhcv2(addr_t const mmio_base) : Genode::Mmio(mmio_base) { } - - /** - * Reset command line circuit of host controller - */ - bool reset_command(Delayer & delayer) - { - write(1); - if (!wait_for(0, delayer)) { - PERR("Reset of command circuit failed"); - return false; - } - return true; - } - - /** - * Reset data circuit of host controller - */ - bool reset_data(Delayer & delayer) - { - write(1); - if (!wait_for(0, delayer)) { - PERR("Reset of data circuit failed"); - return false; - } - return true; - } - - /** - * Full host reset - */ - bool reset_all(Delayer & delayer) - { - /* start reset */ - write(1); - - /* - * The SDHC specification says that a software reset shouldn't - * have an effect on the the card detection circuit. The ESDHC - * clears Sysctl::Ipgen, Sysctl::Hcken, and Sysctl::Peren - * nonetheless which disables clocks that card detection relies - * on. - */ - Sysctl::access_t sysctl = read(); - Sysctl::Ipgen::set(sysctl, 1); - Sysctl::Hcken::set(sysctl, 1); - Sysctl::Peren::set(sysctl, 1); - write(sysctl); - - /* wait for reset completion */ - return wait_for(0, delayer); - } - - /** - * Disable all interrupts - */ - void disable_irqs() - { - write(0); - write(0); - } - - /** - * Enable desired interrupts - */ - void enable_irqs() - { - Irq::access_t irq = 0; - Irq::Cc::set(irq, 1); - Irq::Tc::set(irq, 1); - Irq::Dint::set(irq, 1); - Irq::Ctoe::set(irq, 1); - Irq::Cce::set(irq, 1); - Irq::Cebe::set(irq, 1); - Irq::Cie::set(irq, 1); - Irq::Dtoe::set(irq, 1); - Irq::Dce::set(irq, 1); - Irq::Debe::set(irq, 1); - Irq::Ac12e::set(irq, 1); - Irq::Dmae::set(irq, 1); - write(irq); - write(irq); - } - - enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 }; - - /** - * Set bus width - */ - void bus_width(Bus_width bus_width) - { - switch (bus_width) { - case BUS_WIDTH_1: write(Proctl::Dtw::_1BIT); break; - case BUS_WIDTH_4: write(Proctl::Dtw::_4BIT); break; - } - } - - /** - * Disable host clock - */ - void disable_clock() - { - Sysctl::access_t sysctl = read(); - Sysctl::Ipgen::set(sysctl, 0); - Sysctl::Hcken::set(sysctl, 0); - Sysctl::Peren::set(sysctl, 0); - Sysctl::Dvs::set(sysctl, Sysctl::Dvs::DIV1); - Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV1); - write(sysctl); - } - - enum Clock_divider { CLOCK_DIV_8, CLOCK_DIV_512 }; - - /** - * Enable host clock and configure it to fullfill a given divider - */ - void enable_clock(Clock_divider divider, Delayer &delayer) - { - Sysctl::access_t sysctl = read(); - Sysctl::Ipgen::set(sysctl, 1); - Sysctl::Hcken::set(sysctl, 1); - Sysctl::Peren::set(sysctl, 1); - switch (divider) { - case CLOCK_DIV_8: - Sysctl::Dvs::set(sysctl, Sysctl::Dvs::DIV4); - Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV2); - break; - case CLOCK_DIV_512: - Sysctl::Dvs::set(sysctl, Sysctl::Dvs::DIV16); - Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV32); - break; - } - write(sysctl); - delayer.usleep(1000); - } - - /** - * Set clock to fulfill a given divider - */ - void clock(enum Clock_divider divider, Delayer &delayer) - { - disable_clock(); - write(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27); - enable_clock(divider, delayer); - } + Esdhcv2(addr_t const mmio_base) : Mmio(mmio_base) { } }; -struct Esdhcv2_controller : private Esdhcv2, public Sd_card::Host_controller +/** + * Implementation of the SD host-controller interface for the ESDHCv2 + */ +struct Genode::Esdhcv2_controller +: + private Esdhcv2, public Sd_card::Host_controller { private: @@ -428,404 +172,58 @@ struct Esdhcv2_controller : private Esdhcv2, public Sd_card::Host_controller BURST_WORDS = 8, }; - Genode::Irq_connection _irq; - Genode::Signal_receiver _irq_rec; - Genode::Signal_context _irq_ctx; - Delayer & _delayer; - Sd_card::Card_info _card_info; - bool const _use_dma; - Adma2::Table _adma2_table; + enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 }; - /** - * Print 'err' and throw detection-error exception - */ - void _detect_err(char const * const err) - { - PERR("%s", err); - throw Detection_failed(); - } + enum Clock_divider { CLOCK_DIV_8, CLOCK_DIV_512 }; - /** - * Initialize host and card and return basic card information - */ - Sd_card::Card_info _init() - { - /* install IRQ signal */ - _irq.sigh(_irq_rec.manage(&_irq_ctx)); + Irq_connection _irq; + Signal_receiver _irq_rec; + Signal_context _irq_ctx; + Delayer & _delayer; + Sd_card::Card_info _card_info; + bool const _use_dma; + Adma2::Table _adma2_table; - /* configure host for initialization stage */ - using namespace Sd_card; - if (!reset_all(_delayer)) { _detect_err("Host reset failed"); } - disable_irqs(); + void _detect_err(char const * const err); + void _disable_irqs(); + void _enable_irqs(); + void _bus_width(Bus_width bus_width); + void _disable_clock(); + void _enable_clock(Clock_divider divider, Delayer &delayer); + void _clock(enum Clock_divider divider, Delayer &delayer); + void _wait_for_irq(); + int _reset(Delayer & delayer); + int _wait_for_cmd_allowed(); + int _wait_for_cmd_complete(); + int _wait_for_card_ready_mbw(); + int _stop_transmission_mbw(); + int _wait_for_cmd_complete_mb(bool const r); + int _prepare_dma_mb(size_t blk_cnt, addr_t buf_phys); - /* check host version */ - Hostver::access_t const hostver = read(); - if (Hostver::Vvn::get(hostver) != 18) { - _detect_err("Unexpected Vendor Version Number"); } - if (Hostver::Svn::get(hostver) != 1) { - _detect_err("Unexpected Specification Version Number"); } - - /* - * We should check host capabilities at this point if we want to - * support other versions of the ESDHC. For the i.MX53 ESDHCv2 we - * know that the capabilities fit our requirements. - */ - - enable_irqs(); - bus_width(BUS_WIDTH_1); - _delayer.usleep(10000); - clock(CLOCK_DIV_512, _delayer); - - /* - * Initialize card - */ - - /* - * At this point we should do an SDIO card reset if we later want - * to detect the unwanted case of an SDIO card beeing inserted. - * The reset would be done via 2 differently configured - * Io_rw_direct commands. - */ - - _delayer.usleep(1000); - if (!issue_command(Go_idle_state())) { - _detect_err("Go_idle_state command failed"); } - - _delayer.usleep(2000); - if (!issue_command(Send_if_cond())) { - _detect_err("Send_if_cond command failed"); } - - if (read() != 0x1aa) { - _detect_err("Unexpected response of Send_if_cond command"); } - - /* - * At this point we could detect the unwanted case of an SDIO card - * beeing inserted by issuing 4 Io_send_op_cond commands at an - * interval of 10 ms (they should time out on SD). - */ - - if (!issue_command(Sd_send_op_cond(0, false))) { - _detect_err("Sd_send_op_cond command failed"); } - - _delayer.usleep(1000); - if (!issue_command(Go_idle_state())) { - _detect_err("Go_idle_state command failed"); } - - _delayer.usleep(2000); - if (!issue_command(Send_if_cond())) { - _detect_err("Send_if_cond failed"); } - - if (read() != 0x1aa) { - _detect_err("Unexpected response of Send_if_cond command"); } - - /* - * Power on card - * - * 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(0x200000, true))) { - _detect_err("Sd_send_op_cond command failed"); } - - if (Ocr::Busy::get(read())) { break; } - _delayer.usleep(1000); - } - if (!i) { _detect_err("Could not power-on SD card"); } - - /* get basic information about the card */ - Card_info card_info = _detect(); - - /* - * Configure working clock of host - * - * FIXME Host and card may be driven with a higher clock rate but - * checks (maybe read SSR/SCR, read switch, try frequencies) are - * necessary for that. - */ - clock(CLOCK_DIV_8, _delayer); - - /* - * Configure card and host to use 4 data signals - * - * FIXME Host and card may be driven with a higher bus width but - * further checks (read SCR) are necessary for that. - */ - if (!issue_command( - Set_bus_width(Set_bus_width::Arg::Bus_width::FOUR_BITS), - card_info.rca())) - { _detect_err("Set_bus_width(FOUR_BITS) command failed"); } - - bus_width(BUS_WIDTH_4); - _delayer.usleep(10000); - - /* configure card to use our block size */ - if (!issue_command(Set_blocklen(BLOCK_SIZE))) { - _detect_err("Set_blocklen command failed"); } - - /* configure host buffer */ - Wml::access_t wml = read(); - Wml::Rd_wml::set(wml, WATERMARK_WORDS); - Wml::Rd_brst_len::set(wml, BURST_WORDS); - Wml::Wr_wml::set(wml, WATERMARK_WORDS); - Wml::Wr_brst_len::set(wml, BURST_WORDS); - write(wml); - - /* configure ADMA */ - write(Proctl::Dmas::ADMA2); - - /* configure interrupts for operational mode */ - disable_irqs(); - write(~0); - enable_irqs(); - return card_info; - } - - /** - * Wait until we received the IRQ signal - */ - void _wait_for_irq() - { - /* - * Acknowledge the IRQ first to implicitly activate - * receiving of further IRQ signals on the first usage - * of this method. - */ - _irq.ack_irq(); - _irq_rec.wait_for_signal(); - } - - /* - * Wait till sending a new command is allowed - * - * \return true on success - */ - bool _wait_for_cmd_allowed() - { - /* - * At least after multi-block writes with our - * "Broken Auto Command 12" fix, waiting only for Prsstat::Cihb - * isn't sufficient as Prsstat::Dla and Prsstat::Cdihb may also - * be active. - */ - if (!wait_for(Prsstat_lhw::cmd_allowed(), _delayer)) { - PERR("wait till issuing a new command is allowed timed out"); - return false; - } - return true; - } - - /** - * Wait for the completion of a non-multiple-block command - * - * \return true on success - */ - bool _wait_for_cmd_complete() - { - _wait_for_irq(); - if (read() != Irqstat::Cc::reg_mask()) { - PWRN("received unexpected host signal"); - reset_command(_delayer); - reset_data(_delayer); - enable_irqs(); - return false; - } - write(Irqstat::Cc::reg_mask()); - return true; - } - - int _wait_for_card_ready_mbw() - { - /* - * Poll card status - * - * The maximum number of attempts and the delay between two attempts are - * freely chosen. - */ - using namespace Sd_card; - unsigned attempts = 5; - unsigned constexpr attempts_delay_us = 100000; - while (1) { - if (!attempts) { - PERR("Reading card status after multiblock write failed"); - return -1; - } - /* assemble argument register value */ - Send_status::Arg::access_t cmdarg = 0; - Send_status::Arg::Rca::set(cmdarg, _card_info.rca()); - - /* assemble command register value */ - Xfertyp::access_t xfertyp = 0; - Xfertyp::Cmdinx::set(xfertyp, Send_status::INDEX); - Xfertyp::Cicen::set(xfertyp, 1); - Xfertyp::Cccen::set(xfertyp, 1); - Xfertyp::Rsptyp::set(xfertyp, Xfertyp::Rsptyp::_48BIT); - Xfertyp::Msbsel::set(xfertyp, 1); - Xfertyp::Bcen::set(xfertyp, 1); - Xfertyp::Dmaen::set(xfertyp, 1); - - /* send command as soon as the host allows it */ - if (_wait_for_cmd_allowed()) { return -1; } - write(cmdarg); - write(xfertyp); - - /* wait for command completion */ - if (_wait_for_cmd_complete()) { return -1; } - - /* check for errors */ - R1_response_0::access_t const resp = read(); - if (R1_response_0::Error::get(resp)) { - PERR("Reading card status after multiblock write failed"); - return -1; - } - /* if card is in a ready state, return success, retry otherwise */ - if (R1_response_0::card_ready(resp)) { break; } - _delayer.usleep(attempts_delay_us); - } - return 0; - } - - /** - * Abort transmission by manually issuing stop command - * - * \return true on success - */ - bool _abort_transmission() - { - write(0); - Xfertyp::access_t xfertyp = 0; - Xfertyp::Cmdinx::set(xfertyp, Sd_card::Stop_transmission::INDEX); - Xfertyp::Cmdtyp::set(xfertyp, Xfertyp::Cmdtyp::ABORT_CMD12); - Xfertyp::Cccen::set(xfertyp, 1); - Xfertyp::Cicen::set(xfertyp, 1); - Xfertyp::Rsptyp::set(xfertyp, Xfertyp::Rsptyp::_48BIT_BUSY); - Xfertyp::Msbsel::set(xfertyp, 1); - Xfertyp::Bcen::set(xfertyp, 1); - Xfertyp::Dmaen::set(xfertyp, 1); - write(xfertyp); - return _wait_for_cmd_complete(); - } - - /** - * Wait for the completion of a multiple-block command - * - * \param r wether the transfer reads from card to host - * - * \return true on success - */ - bool _wait_for_mbc_complete(bool const r) - { - /* wait for a signal */ - _wait_for_irq(); - Irqstat::access_t const irq = read(); - - /* - * The ESDHC signals on multi-block transfers seem to be broken. - * Synchronizing to Irqstat::Tc before returning from transfer - * requests and to Prsstat::Cihb before sending the next command, - * as it is done with other SDHCs, isn't sufficient. Instead, both - * completion signals must be gathered while dealing with the fact - * that the interrupt triggers only on signal edges and we can - * therefore not wait for a second IRQ when we've received a - * single signal the first time. - */ - Irqstat::access_t constexpr irq_goal = - Irq::Cc::reg_mask() | Irq::Tc::reg_mask(); - - /* poll for the missing signal */ - if (irq != irq_goal) { - if (!wait_for(irq_goal, _delayer)) { - PERR("completion host signal timed out"); - return false; - } - } - /* acknowledge both completion signals */ - write(irq_goal); - if (!r) { - - /* - * The "Auto Command 12" feature of the ESDHC seems to be - * broken for multi-block writes as it causes command- - * timeout errors sometimes. Thus, we end such transfers - * manually. - */ - if (!_abort_transmission()) { return false; } - if (_wait_for_card_ready_mbw()) { return false; } - } - return true; - } - - /** - * Prepare multi-block-transfer command with DMA - * - * \param blk_cnt number of transferred blocks block - * \param buf_phys physical base of transfer buffer - * - * \return true on success - */ - bool _prepare_dma_mbc(size_t blk_cnt, addr_t buf_phys) - { - size_t const req_size = blk_cnt * BLOCK_SIZE; - if (!_adma2_table.setup_req(req_size, buf_phys)) { - PERR("Setup ADMA2 table failed"); - return false; - } - write(_adma2_table.base_phys()); - write(BLOCK_SIZE); - write(blk_cnt); - return true; - } + Sd_card::Card_info _init(); /**************************************** ** Sd_card::Host_controller interface ** ****************************************/ - 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() - { - Cmdrsp0::access_t const rsp0 = read(); - return Sd_card::Send_relative_addr::Response::Rca::get(rsp0); - } + Sd_card::Cid _read_cid(); + Sd_card::Csd _read_csd(); + unsigned _read_rca(); + bool _issue_command(Sd_card::Command_base const & command); public: /** * Constructor * - * \param mmio_base local base address of MMIO registers + * \param base local base address of MMIO registers * \param irq host-interrupt ID * \param delayer delayer timing of MMIO accesses * \param use_dma wether to use DMA or direct IO for transfers */ - Esdhcv2_controller(addr_t const mmio_base, unsigned const irq, - Delayer & delayer, bool const use_dma) - : - Esdhcv2(mmio_base), _irq(irq), - _delayer(delayer), _card_info(_init()), _use_dma(use_dma) { } + Esdhcv2_controller(addr_t const base, unsigned const irq, + Delayer & delayer, bool const use_dma); ~Esdhcv2_controller() { _irq_rec.dissolve(&_irq_ctx); } @@ -834,81 +232,12 @@ struct Esdhcv2_controller : private Esdhcv2, public Sd_card::Host_controller ** Sd_card::Host_controller interface ** ****************************************/ - bool _issue_command(Sd_card::Command_base const & command) - { - /* detect if command is a multi-block transfer and if it reads */ - using namespace Sd_card; - bool const r = command.transfer == Sd_card::TRANSFER_READ; - bool const mb = - command.index == Sd_card::Read_multiple_block::INDEX || - command.index == Sd_card::Write_multiple_block::INDEX; - - /* assemble comsendcode */ - Xfertyp::access_t cmd = 0; - Xfertyp::Cmdinx::set(cmd, command.index); - if (command.transfer != Sd_card::TRANSFER_NONE) { - Xfertyp::Dpsel::set(cmd); - Xfertyp::Bcen::set(cmd); - Xfertyp::Msbsel::set(cmd); - if (mb) { - /* - * The "Auto Command 12" feature of the ESDHC seems to be - * broken for multi-block writes as it causes command- - * timeout errors sometimes. Thus, we end such transfers - * manually. - */ - if (r) { Xfertyp::Ac12en::set(cmd); } - if (_use_dma) { Xfertyp::Dmaen::set(cmd); } - } - Xfertyp::Dtdsel::set(cmd, - r ? Xfertyp::Dtdsel::READ : Xfertyp::Dtdsel::WRITE); - } - typedef Xfertyp::Rsptyp Rsptyp; - Xfertyp::access_t rt = 0; - switch (command.rsp_type) { - case RESPONSE_NONE: rt = Rsptyp::_0BIT; break; - case RESPONSE_136_BIT: rt = Rsptyp::_136BIT; break; - case RESPONSE_48_BIT: rt = Rsptyp::_48BIT; break; - case RESPONSE_48_BIT_WITH_BUSY: rt = Rsptyp::_48BIT_BUSY; break; - } - Xfertyp::Rsptyp::set(cmd, rt); - - /* send command as soon as the host allows it */ - if (!_wait_for_cmd_allowed()) { return false; } - write(command.arg); - write(cmd); - - /* wait for completion */ - return mb ? _wait_for_mbc_complete(r) : _wait_for_cmd_complete(); - } + bool read_blocks(size_t, size_t, char *); + bool write_blocks(size_t, size_t, char const *); + bool read_blocks_dma(size_t blk_nr, size_t blk_cnt, addr_t buf_phys); + bool write_blocks_dma(size_t blk_nr, size_t blk_cnt, addr_t buf_phys); Sd_card::Card_info card_info() const { return _card_info; } - - bool read_blocks(size_t const block_number, size_t const block_count, - char * out_buffer_phys) - { - PERR("block transfer without DMA not supported by now"); - return false; - } - - bool write_blocks(size_t block_number, size_t block_count, - char const *buffer_phys) - { - PERR("block transfer without DMA not supported by now"); - return false; - } - - bool read_blocks_dma(size_t blk_nr, size_t blk_cnt, addr_t buf_phys) - { - if (!_prepare_dma_mbc(blk_cnt, buf_phys)) { return false; } - return issue_command(Sd_card::Read_multiple_block(blk_nr)); - } - - bool write_blocks_dma(size_t blk_nr, size_t blk_cnt, addr_t buf_phys) - { - if (!_prepare_dma_mbc(blk_cnt, buf_phys)) { return false; } - return issue_command(Sd_card::Write_multiple_block(blk_nr)); - } }; -#endif /* _DRIVERS__SD_CARD__SPEC__IMX53__ESDHCV2_H_ */ +#endif /* _ESDHCV2_H_ */ diff --git a/repos/os/src/drivers/sd_card/spec/imx53/target.mk b/repos/os/src/drivers/sd_card/spec/imx53/target.mk index 3ebf06054..f50458a8d 100644 --- a/repos/os/src/drivers/sd_card/spec/imx53/target.mk +++ b/repos/os/src/drivers/sd_card/spec/imx53/target.mk @@ -1,5 +1,9 @@ -TARGET = sd_card_drv -REQUIRES = imx53 -SRC_CC = main.cc -LIBS = base server -INC_DIR += $(PRG_DIR) $(REP_DIR)/src/drivers/sd_card +TARGET = sd_card_drv +REQUIRES += imx53 +SRC_CC += main.cc +SRC_CC += adma2.cc +SRC_CC += esdhcv2.cc +LIBS += base +LIBS += server +INC_DIR += $(PRG_DIR) +INC_DIR += $(PRG_DIR)/../../