/* * \brief ESDHCv2 host controller * \author Martin Stein * \date 2015-02-05 */ /* * 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 _ESDHCV2_H_ #define _ESDHCV2_H_ /* Genode includes */ #include #include #include #include /* local includes */ #include namespace Adma2 { using namespace Genode; /** * Descriptor layout */ struct Desc : Register<64> { static size_t constexpr max_size = 64 * 1024 - 4; 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> { }; 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::max_size; 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::max_size, 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; } return true; } /* * Accessors */ addr_t base_phys() const { return _base_phys; } }; } /** * Freescale eSDHCv2 host MMIO */ struct Esdhcv2 : Genode::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 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 : Genode::Bitset_2 { }; struct Rsp136_1 : Genode::Bitset_2 { }; struct Rsp136_2 : Genode::Bitset_2 { }; struct Rsp136_3 : Genode::Bitset_2 { }; struct Xfertyp : Register<0xc, 32> { struct Dmaen : Bitfield<0, 1> { }; struct Bcen : Bitfield<1, 1> { }; struct Ac12en : Bitfield<2, 1> { }; struct Dtdsel : Bitfield<4, 1> { enum { WRITE = 0, READ = 1, }; }; struct Msbsel : Bitfield<5, 1> { }; struct Rsptyp : Bitfield<16, 2> { enum { _0BIT = 0, _136BIT = 1, _48BIT = 2, _48BIT_BUSY = 3, }; }; struct Dpsel : Bitfield<21, 1> { }; struct Cmdtyp : Bitfield<22, 2> { enum { ABORT_CMD12 = 3 }; }; struct Cmdinx : Bitfield<24, 6> { }; }; struct Datport : Register<0x20, 32> { }; struct Prsstat_lhw : Register<0x24, 16> { struct Sdstb : Bitfield<3, 1> { }; static constexpr access_t cmd_allowed() { return Sdstb::reg_mask(); } }; 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_27 = 0xe }; }; 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<03, 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> { }; }; /** * 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); } }; struct Esdhcv2_controller : private Esdhcv2, public Sd_card::Host_controller { private: enum { BLOCK_SIZE = 512, WATERMARK_WORDS = 16, 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; /** * Print 'err' and throw detection-error exception */ void _detect_err(char const * const err) { PERR("%s", err); throw Detection_failed(); } /** * Initialize host and card and return basic card information */ Sd_card::Card_info _init() { /* configure host for initialization stage */ using namespace Sd_card; if (!reset_all(_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. */ 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"); } if (read() != 0xff8000) { _detect_err("Unexpected response of Sd_send_op_cond command"); } _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; } /** * 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::Rsptyp::set(xfertyp, Xfertyp::Rsptyp::_48BIT); 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_cc_tc = Irq::Cc::reg_mask() | Irq::Tc::reg_mask(); switch (irq) { case Irq::Cc::reg_mask(): case Irq::Tc::reg_mask(): /* poll for the missing signal */ if (!wait_for(irq_cc_tc, _delayer)) { PERR("completion host signal timed out"); return false; } case irq_cc_tc: /* acknowledge both completion signals */ write(irq_cc_tc); /* * 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. */ return r ? true : _abort_transmission(); default: PERR("received unexpected host signal"); return false; } } /** * 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::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); } public: /** * Constructor * * \param mmio_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) { _irq.sigh(_irq_rec.manage(&_irq_ctx)); } ~Esdhcv2_controller() { _irq_rec.dissolve(&_irq_ctx); } /**************************************** ** 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(); } 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 /* _ESDHCV2_H_ */