mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-07 19:34:56 +00:00
sd_card: i.MX6 support
The i.MX6 driver shares most of its code with the i.MX53 driver. Ref #2206
This commit is contained in:
parent
4ae4f7f605
commit
613f4171f3
@ -33,7 +33,7 @@ bool secure_irq(unsigned const i)
|
||||
if (i == Board::EPIT_2_IRQ) return true;
|
||||
if (i == Board::I2C_2_IRQ) return SECURE_I2C;
|
||||
if (i == Board::I2C_3_IRQ) return SECURE_I2C;
|
||||
if (i == Board::ESDHCV2_1_IRQ) return SECURE_ESDHC;
|
||||
if (i == Board::SDHC_IRQ) return SECURE_ESDHC;
|
||||
if (i >= Board::GPIO1_IRQL && i <= Board::GPIO4_IRQH) return SECURE_GPIO;
|
||||
if (i >= Board::GPIO5_IRQL && i <= Board::GPIO7_IRQH) return SECURE_GPIO;
|
||||
return false;
|
||||
|
@ -26,9 +26,9 @@ struct Imx53::Board_base
|
||||
MMIO_BASE = 0x0,
|
||||
MMIO_SIZE = 0x70000000,
|
||||
|
||||
ESDHCV2_1_IRQ = 1,
|
||||
ESDHCV2_1_MMIO_BASE = 0x50004000,
|
||||
ESDHCV2_1_MMIO_SIZE = 0x00004000,
|
||||
SDHC_IRQ = 1,
|
||||
SDHC_MMIO_BASE = 0x50004000,
|
||||
SDHC_MMIO_SIZE = 0x00004000,
|
||||
|
||||
UART_1_IRQ = 31,
|
||||
UART_1_MMIO_BASE = 0x53fbc000,
|
||||
|
@ -40,6 +40,11 @@ struct Genode::Board_base
|
||||
UART_1_MMIO_BASE = 0x02020000,
|
||||
UART_1_MMIO_SIZE = 0x00004000,
|
||||
|
||||
/* SD host controller */
|
||||
SDHC_IRQ = 54,
|
||||
SDHC_MMIO_BASE = 0x02190000,
|
||||
SDHC_MMIO_SIZE = 0x00004000,
|
||||
|
||||
/* timer */
|
||||
EPIT_2_IRQ = 89,
|
||||
EPIT_2_MMIO_BASE = 0x020d4000,
|
||||
|
@ -1,4 +1,5 @@
|
||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx53
|
||||
SRC_CC += spec/imx53/adma2.cc
|
||||
SRC_CC += spec/imx53/esdhcv2.cc
|
||||
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
|
||||
include $(REP_DIR)/lib/mk/sd_card_bench.inc
|
||||
|
@ -1,5 +1,6 @@
|
||||
INC_DIR += $(REP_DIR)/src/drivers/sd_card/spec/imx53
|
||||
SRC_CC += spec/imx53/adma2.cc
|
||||
SRC_CC += spec/imx53/esdhcv2.cc
|
||||
SRC_CC += spec/imx53/main.cc
|
||||
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/imx/main.cc
|
||||
SRC_CC += spec/imx53/sdhc.cc
|
||||
include $(REP_DIR)/lib/mk/sd_card.inc
|
||||
|
5
repos/os/lib/mk/spec/imx6/sd_card_bench.mk
Normal file
5
repos/os/lib/mk/spec/imx6/sd_card_bench.mk
Normal file
@ -0,0 +1,5 @@
|
||||
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
|
||||
include $(REP_DIR)/lib/mk/sd_card_bench.inc
|
6
repos/os/lib/mk/spec/imx6/sd_card_drv.mk
Normal file
6
repos/os/lib/mk/spec/imx6/sd_card_drv.mk
Normal file
@ -0,0 +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/imx/main.cc
|
||||
SRC_CC += spec/imx6/sdhc.cc
|
||||
include $(REP_DIR)/lib/mk/sd_card.inc
|
@ -1,19 +1,20 @@
|
||||
/*
|
||||
* \brief Imx53-specific implementation of the Block::Driver interface
|
||||
* \brief Implementation of the Block::Driver interface
|
||||
* \author Martin Stein
|
||||
* \date 2015-02-04
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2015 Genode Labs GmbH
|
||||
* Copyright (C) 2012-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__IMX53__DRIVER_H_
|
||||
#define _DRIVERS__SD_CARD__SPEC__IMX53__DRIVER_H_
|
||||
#ifndef _DRIVER_H_
|
||||
#define _DRIVER_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/mmio.h>
|
||||
#include <os/attached_io_mem_dataspace.h>
|
||||
#include <base/log.h>
|
||||
@ -23,7 +24,7 @@
|
||||
#include <os/server.h>
|
||||
|
||||
/* local includes */
|
||||
#include <esdhcv2.h>
|
||||
#include <sdhc.h>
|
||||
|
||||
namespace Block {
|
||||
using namespace Genode;
|
||||
@ -43,8 +44,8 @@ class Block::Sdhci_driver : public Block::Driver
|
||||
void usleep(unsigned us) { Timer::Connection::usleep(us); }
|
||||
} _delayer;
|
||||
|
||||
Attached_io_mem_dataspace _esdhcv2_1_mmio;
|
||||
Esdhcv2_controller _controller;
|
||||
Attached_io_mem_dataspace _sdhc_mmio;
|
||||
Sdhc _controller;
|
||||
|
||||
bool const _use_dma;
|
||||
|
||||
@ -52,10 +53,9 @@ class Block::Sdhci_driver : public Block::Driver
|
||||
|
||||
Sdhci_driver(Entrypoint &, bool use_dma)
|
||||
:
|
||||
_esdhcv2_1_mmio(Genode::Board_base::ESDHCV2_1_MMIO_BASE,
|
||||
Genode::Board_base::ESDHCV2_1_MMIO_SIZE),
|
||||
_controller((addr_t)_esdhcv2_1_mmio.local_addr<void>(),
|
||||
Genode::Board_base::ESDHCV2_1_IRQ, _delayer, use_dma),
|
||||
_sdhc_mmio(Board_base::SDHC_MMIO_BASE, Board_base::SDHC_MMIO_SIZE),
|
||||
_controller((addr_t)_sdhc_mmio.local_addr<void>(),
|
||||
Board_base::SDHC_IRQ, _delayer, use_dma),
|
||||
_use_dma(use_dma)
|
||||
{
|
||||
Sd_card::Card_info const card_info = _controller.card_info();
|
||||
@ -133,4 +133,4 @@ class Block::Sdhci_driver : public Block::Driver
|
||||
return Genode::env()->ram_session()->free(c); }
|
||||
};
|
||||
|
||||
#endif /* _DRIVERS__SD_CARD__SPEC__IMX53__DRIVER_H_ */
|
||||
#endif /* _DRIVER_H_ */
|
@ -1,24 +1,24 @@
|
||||
/*
|
||||
* \brief Freescale Enhanced Secured Digital Host Controller Version 2
|
||||
* \brief Secured Digital Host Controller
|
||||
* \author Martin Stein
|
||||
* \date 2015-02-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <esdhcv2.h>
|
||||
#include <sdhc.h>
|
||||
|
||||
using namespace Sd_card;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
int Esdhcv2_controller::_wait_for_card_ready_mbw()
|
||||
int Sdhc::_wait_for_card_ready_mbw()
|
||||
{
|
||||
/*
|
||||
* Poll card status
|
||||
@ -69,7 +69,7 @@ int Esdhcv2_controller::_wait_for_card_ready_mbw()
|
||||
}
|
||||
|
||||
|
||||
int Esdhcv2_controller::_stop_transmission_mbw()
|
||||
int Sdhc::_stop_transmission()
|
||||
{
|
||||
/* write argument register */
|
||||
write<Cmdarg>(0);
|
||||
@ -81,9 +81,7 @@ int Esdhcv2_controller::_stop_transmission_mbw()
|
||||
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);
|
||||
_stop_transmission_finish_xfertyp(xfertyp);
|
||||
write<Xfertyp>(xfertyp);
|
||||
|
||||
/* wait for command completion */
|
||||
@ -92,10 +90,10 @@ int Esdhcv2_controller::_stop_transmission_mbw()
|
||||
}
|
||||
|
||||
|
||||
int Esdhcv2_controller::_wait_for_cmd_complete_mb(bool const r)
|
||||
int Sdhc::_wait_for_cmd_complete_mb(bool const r)
|
||||
{
|
||||
/*
|
||||
* The ESDHC signals on multi-block transfers seem to be broken.
|
||||
* The host 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
|
||||
@ -120,29 +118,11 @@ int Esdhcv2_controller::_wait_for_cmd_complete_mb(bool const r)
|
||||
}
|
||||
/* acknowledge completion signals */
|
||||
write<Irqstat>(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;
|
||||
return _wait_for_cmd_complete_mb_finish(r);
|
||||
}
|
||||
|
||||
|
||||
int Esdhcv2_controller::_wait_for_cmd_complete()
|
||||
int Sdhc::_wait_for_cmd_complete()
|
||||
{
|
||||
/* wait for "Command Completion" signal and acknowledge it */
|
||||
_wait_for_irq();
|
||||
@ -155,54 +135,50 @@ int Esdhcv2_controller::_wait_for_cmd_complete()
|
||||
}
|
||||
|
||||
|
||||
bool Esdhcv2_controller::_issue_command(Command_base const & command)
|
||||
bool Sdhc::_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 =
|
||||
/* get command characteristics */
|
||||
bool const transfer = command.transfer != TRANSFER_NONE;
|
||||
bool const reading = command.transfer == TRANSFER_READ;
|
||||
bool const multiblock =
|
||||
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);
|
||||
/* set command index */
|
||||
Xfertyp::access_t xfertyp = 0;
|
||||
Xfertyp::Cmdinx::set(xfertyp, command.index);
|
||||
|
||||
/* send command as soon as the host allows it */
|
||||
if (_wait_for_cmd_allowed()) { return false; }
|
||||
/* select response type */
|
||||
typedef Xfertyp::Rsptyp Rsptyp;
|
||||
Xfertyp::access_t rsptyp = 0;
|
||||
switch (command.rsp_type) {
|
||||
case RESPONSE_NONE: rsptyp = Rsptyp::_0BIT; break;
|
||||
case RESPONSE_136_BIT: rsptyp = Rsptyp::_136BIT; break;
|
||||
case RESPONSE_48_BIT: rsptyp = Rsptyp::_48BIT; break;
|
||||
case RESPONSE_48_BIT_WITH_BUSY: rsptyp = Rsptyp::_48BIT_BUSY; break;
|
||||
}
|
||||
Xfertyp::Rsptyp::set(xfertyp, rsptyp);
|
||||
|
||||
/* generic transfer settings */
|
||||
if (command.transfer != TRANSFER_NONE) {
|
||||
Xfertyp::Dpsel::set(xfertyp);
|
||||
if (multiblock) {
|
||||
Xfertyp::Cicen::set(xfertyp, 1);
|
||||
Xfertyp::Cccen::set(xfertyp, 1);
|
||||
}
|
||||
}
|
||||
/* version-dependent transfer settings and issue command */
|
||||
_issue_cmd_finish_xfertyp(xfertyp, transfer, multiblock, reading);
|
||||
write<Cmdarg>(command.arg);
|
||||
write<Xfertyp>(cmd);
|
||||
write<Xfertyp>(xfertyp);
|
||||
|
||||
/* wait for completion */
|
||||
return mb ? !_wait_for_cmd_complete_mb(r) : !_wait_for_cmd_complete();
|
||||
return multiblock ? !_wait_for_cmd_complete_mb(reading) :
|
||||
!_wait_for_cmd_complete();
|
||||
}
|
||||
|
||||
|
||||
Cid Esdhcv2_controller::_read_cid()
|
||||
Cid Sdhc::_read_cid()
|
||||
{
|
||||
Cid cid;
|
||||
cid.raw_0 = read<Rsp136_0>();
|
||||
@ -213,7 +189,7 @@ Cid Esdhcv2_controller::_read_cid()
|
||||
}
|
||||
|
||||
|
||||
Csd Esdhcv2_controller::_read_csd()
|
||||
Csd Sdhc::_read_csd()
|
||||
{
|
||||
Csd csd;
|
||||
csd.csd0 = read<Rsp136_0>();
|
||||
@ -224,36 +200,35 @@ Csd Esdhcv2_controller::_read_csd()
|
||||
}
|
||||
|
||||
|
||||
unsigned Esdhcv2_controller::_read_rca()
|
||||
unsigned Sdhc::_read_rca()
|
||||
{
|
||||
Cmdrsp0::access_t const rsp0 = read<Cmdrsp0>();
|
||||
return Send_relative_addr::Response::Rca::get(rsp0);
|
||||
}
|
||||
|
||||
|
||||
bool Esdhcv2_controller::read_blocks(size_t, size_t, char *)
|
||||
bool Sdhc::read_blocks(size_t, size_t, char *)
|
||||
{
|
||||
error("block transfer without DMA not supported by now");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Esdhcv2_controller::write_blocks(size_t, size_t, char const *)
|
||||
bool Sdhc::write_blocks(size_t, size_t, char const *)
|
||||
{
|
||||
error("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)
|
||||
bool Sdhc::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,
|
||||
bool Sdhc::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; }
|
||||
@ -261,15 +236,17 @@ bool Esdhcv2_controller::write_blocks_dma(size_t blk_nr, size_t blk_cnt,
|
||||
}
|
||||
|
||||
|
||||
Esdhcv2_controller::Esdhcv2_controller(addr_t const base, unsigned const irq,
|
||||
Delayer & delayer, bool const use_dma)
|
||||
Sdhc::Sdhc(addr_t const base,
|
||||
unsigned const irq,
|
||||
Delayer &delayer,
|
||||
bool const use_dma)
|
||||
:
|
||||
Esdhcv2(base), _irq(irq), _delayer(delayer), _card_info(_init()),
|
||||
Mmio(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)
|
||||
int Sdhc::_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;
|
||||
@ -283,14 +260,18 @@ int Esdhcv2_controller::_prepare_dma_mb(size_t blk_cnt, addr_t buf_phys)
|
||||
}
|
||||
|
||||
|
||||
int Esdhcv2_controller::_wait_for_cmd_allowed()
|
||||
int Sdhc::_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.
|
||||
* At least after multi-block writes on i.MX53 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>(Prsstat_lhw::cmd_allowed(), _delayer)) {
|
||||
if (!wait_for<Prsstat::Dla>(0, _delayer) ||
|
||||
!wait_for<Prsstat::Sdstb>(1, _delayer) ||
|
||||
!wait_for<Prsstat::Cihb>(0, _delayer) ||
|
||||
!wait_for<Prsstat::Cdihb>(0, _delayer))
|
||||
{
|
||||
error("wait till issuing a new command is allowed timed out");
|
||||
return -1;
|
||||
}
|
||||
@ -298,7 +279,7 @@ int Esdhcv2_controller::_wait_for_cmd_allowed()
|
||||
}
|
||||
|
||||
|
||||
void Esdhcv2_controller::_wait_for_irq()
|
||||
void Sdhc::_wait_for_irq()
|
||||
{
|
||||
/* acknowledge IRQ first, to activate IRQ propagation initially */
|
||||
_irq.ack_irq();
|
||||
@ -306,7 +287,7 @@ void Esdhcv2_controller::_wait_for_irq()
|
||||
}
|
||||
|
||||
|
||||
Card_info Esdhcv2_controller::_init()
|
||||
Card_info Sdhc::_init()
|
||||
{
|
||||
/* install IRQ signal */
|
||||
_irq.sigh(_irq_rec.manage(&_irq_ctx));
|
||||
@ -315,24 +296,22 @@ Card_info Esdhcv2_controller::_init()
|
||||
if (_reset(_delayer)) { _detect_err("Host reset failed"); }
|
||||
_disable_irqs();
|
||||
|
||||
/* check host version */
|
||||
Hostver::access_t const hostver = read<Hostver>();
|
||||
if (Hostver::Vvn::get(hostver) != 18) {
|
||||
_detect_err("Unexpected Vendor Version Number"); }
|
||||
if (Hostver::Svn::get(hostver) != 1) {
|
||||
_detect_err("Unexpected Specification Version Number"); }
|
||||
if (!_supported_host_version(read<Hostver>())) {
|
||||
error("host version not supported");
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* support other versions of the SDHC. For the already supported
|
||||
* versions 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);
|
||||
_clock(CLOCK_INITIAL);
|
||||
|
||||
/*
|
||||
* Initialize card
|
||||
@ -405,7 +384,7 @@ Card_info Esdhcv2_controller::_init()
|
||||
* checks (maybe read SSR/SCR, read switch, try frequencies) are
|
||||
* necessary for that.
|
||||
*/
|
||||
_clock(CLOCK_DIV_8, _delayer);
|
||||
_clock(CLOCK_OPERATIONAL);
|
||||
|
||||
/*
|
||||
* Configure card and host to use 4 data signals
|
||||
@ -427,10 +406,7 @@ Card_info Esdhcv2_controller::_init()
|
||||
|
||||
/* configure host buffer */
|
||||
Wml::access_t wml = read<Wml>();
|
||||
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);
|
||||
_watermark_level(wml);
|
||||
write<Wml>(wml);
|
||||
|
||||
/* configure ADMA */
|
||||
@ -444,30 +420,18 @@ Card_info Esdhcv2_controller::_init()
|
||||
}
|
||||
|
||||
|
||||
void Esdhcv2_controller::_detect_err(char const * const err)
|
||||
void Sdhc::_detect_err(char const * const err)
|
||||
{
|
||||
error(err);
|
||||
throw Detection_failed();
|
||||
}
|
||||
|
||||
|
||||
int Esdhcv2_controller::_reset(Delayer & delayer)
|
||||
int Sdhc::_reset(Delayer &delayer)
|
||||
{
|
||||
/* start reset */
|
||||
write<Sysctl::Rsta>(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>();
|
||||
Sysctl::Ipgen::set(sysctl, 1);
|
||||
Sysctl::Hcken::set(sysctl, 1);
|
||||
Sysctl::Peren::set(sysctl, 1);
|
||||
write<Sysctl>(sysctl);
|
||||
_reset_amendments();
|
||||
|
||||
/* wait for reset completion */
|
||||
if (!wait_for<Sysctl::Rsta>(0, delayer)) {
|
||||
@ -478,14 +442,14 @@ int Esdhcv2_controller::_reset(Delayer & delayer)
|
||||
}
|
||||
|
||||
|
||||
void Esdhcv2_controller::_disable_irqs()
|
||||
void Sdhc::_disable_irqs()
|
||||
{
|
||||
write<Irqstaten>(0);
|
||||
write<Irqsigen>(0);
|
||||
}
|
||||
|
||||
|
||||
void Esdhcv2_controller::_enable_irqs()
|
||||
void Sdhc::_enable_irqs()
|
||||
{
|
||||
Irq::access_t irq = 0;
|
||||
Irq::Cc::set(irq, 1);
|
||||
@ -505,7 +469,7 @@ void Esdhcv2_controller::_enable_irqs()
|
||||
}
|
||||
|
||||
|
||||
void Esdhcv2_controller::_bus_width(Bus_width bus_width)
|
||||
void Sdhc::_bus_width(Bus_width bus_width)
|
||||
{
|
||||
switch (bus_width) {
|
||||
case BUS_WIDTH_1: write<Proctl::Dtw>(Proctl::Dtw::_1BIT); break;
|
||||
@ -514,8 +478,9 @@ void Esdhcv2_controller::_bus_width(Bus_width bus_width)
|
||||
}
|
||||
|
||||
|
||||
void Esdhcv2_controller::_disable_clock()
|
||||
void Sdhc::_disable_clock()
|
||||
{
|
||||
_disable_clock_preparation();
|
||||
Sysctl::access_t sysctl = read<Sysctl>();
|
||||
Sysctl::Ipgen::set(sysctl, 0);
|
||||
Sysctl::Hcken::set(sysctl, 0);
|
||||
@ -526,13 +491,17 @@ void Esdhcv2_controller::_disable_clock()
|
||||
}
|
||||
|
||||
|
||||
void Esdhcv2_controller::_enable_clock(Clock_divider divider, Delayer &delayer)
|
||||
void Sdhc::_enable_clock(Clock_divider divider)
|
||||
{
|
||||
Sysctl::access_t sysctl = read<Sysctl>();
|
||||
Sysctl::Ipgen::set(sysctl, 1);
|
||||
Sysctl::Hcken::set(sysctl, 1);
|
||||
Sysctl::Peren::set(sysctl, 1);
|
||||
switch (divider) {
|
||||
case CLOCK_DIV_4:
|
||||
Sysctl::Dvs::set(sysctl, Sysctl::Dvs::DIV4);
|
||||
Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV1);
|
||||
break;
|
||||
case CLOCK_DIV_8:
|
||||
Sysctl::Dvs::set(sysctl, Sysctl::Dvs::DIV4);
|
||||
Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV2);
|
||||
@ -543,13 +512,14 @@ void Esdhcv2_controller::_enable_clock(Clock_divider divider, Delayer &delayer)
|
||||
break;
|
||||
}
|
||||
write<Sysctl>(sysctl);
|
||||
delayer.usleep(1000);
|
||||
_enable_clock_finish();
|
||||
_delayer.usleep(1000);
|
||||
}
|
||||
|
||||
|
||||
void Esdhcv2_controller::_clock(enum Clock_divider divider, Delayer &delayer)
|
||||
void Sdhc::_clock(Clock clock)
|
||||
{
|
||||
wait_for<Prsstat::Sdstb>(1, _delayer);
|
||||
_disable_clock();
|
||||
write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
|
||||
_enable_clock(divider, delayer);
|
||||
_clock_finish(clock);
|
||||
}
|
261
repos/os/src/drivers/sd_card/spec/imx/sdhc.h
Normal file
261
repos/os/src/drivers/sd_card/spec/imx/sdhc.h
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* \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 <util/mmio.h>
|
||||
#include <irq_session/connection.h>
|
||||
#include <base/sleep.h>
|
||||
|
||||
/* local includes */
|
||||
#include <sd_card.h>
|
||||
#include <adma2.h>
|
||||
|
||||
namespace Genode { class Sdhc; }
|
||||
|
||||
|
||||
class Genode::Sdhc : public Sd_card::Host_controller, public Mmio
|
||||
{
|
||||
private:
|
||||
|
||||
struct Blkattr : Register<0x4, 32>
|
||||
{
|
||||
struct Blksize : Bitfield<0, 13> { };
|
||||
struct Blkcnt : Bitfield<16, 16> { };
|
||||
};
|
||||
template <off_t OFFSET>
|
||||
struct Cmdrsp_tpl : Register<OFFSET, 32>
|
||||
{
|
||||
struct Rsp136_8_24 : Register<OFFSET, 32>::template Bitfield<0, 24> { };
|
||||
struct Rsp136_0_8 : Register<OFFSET, 32>::template Bitfield<24, 8> { };
|
||||
};
|
||||
struct Cmdarg : Register<0x8, 32> { };
|
||||
struct Cmdrsp0 : Cmdrsp_tpl<0x10> { };
|
||||
struct Cmdrsp1 : Cmdrsp_tpl<0x14> { };
|
||||
struct Cmdrsp2 : Cmdrsp_tpl<0x18> { };
|
||||
struct Cmdrsp3 : Cmdrsp_tpl<0x1c> { };
|
||||
struct Rsp136_0 : Bitset_2<Cmdrsp3::Rsp136_0_8, Cmdrsp0::Rsp136_8_24> { };
|
||||
struct Rsp136_1 : Bitset_2<Cmdrsp0::Rsp136_0_8, Cmdrsp1::Rsp136_8_24> { };
|
||||
struct Rsp136_2 : Bitset_2<Cmdrsp1::Rsp136_0_8, Cmdrsp2::Rsp136_8_24> { };
|
||||
struct Rsp136_3 : Bitset_2<Cmdrsp2::Rsp136_0_8, Cmdrsp3::Rsp136_8_24> { };
|
||||
|
||||
template <off_t OFFSET>
|
||||
struct Xfertyp_base : Register<OFFSET, 32>
|
||||
{
|
||||
struct Dmaen : Register<OFFSET, 32>::template Bitfield<0, 1> { };
|
||||
struct Bcen : Register<OFFSET, 32>::template Bitfield<1, 1> { };
|
||||
struct Ac12en : Register<OFFSET, 32>::template Bitfield<2, 1> { };
|
||||
struct Dtdsel : Register<OFFSET, 32>::template Bitfield<4, 1>
|
||||
{
|
||||
enum { WRITE = 0, READ = 1, };
|
||||
};
|
||||
struct Msbsel : Register<OFFSET, 32>::template Bitfield<5, 1> { };
|
||||
};
|
||||
struct Mixctrl : Xfertyp_base<0x48>
|
||||
{
|
||||
struct Ddren : Bitfield<3, 1> { };
|
||||
struct Nibblepos : Bitfield<6, 1> { };
|
||||
struct Ac23en : Bitfield<7, 1> { };
|
||||
struct Always_ones : Bitfield<31, 1> { };
|
||||
};
|
||||
struct Xfertyp : Xfertyp_base<0xc>
|
||||
{
|
||||
struct Rsptyp : Bitfield<16, 2>
|
||||
{
|
||||
enum {
|
||||
_0BIT = 0,
|
||||
_136BIT = 1,
|
||||
_48BIT = 2,
|
||||
_48BIT_BUSY = 3,
|
||||
};
|
||||
};
|
||||
struct Cccen : Bitfield<19, 1> { };
|
||||
struct Cicen : Bitfield<20, 1> { };
|
||||
struct Dpsel : Bitfield<21, 1> { };
|
||||
struct Cmdtyp : Bitfield<22, 2>
|
||||
{
|
||||
enum { ABORT_CMD12 = 3 };
|
||||
};
|
||||
struct Cmdinx : Bitfield<24, 6> { };
|
||||
};
|
||||
struct Prsstat : Register<0x24, 32>
|
||||
{
|
||||
struct Cihb : Bitfield<0, 1> { };
|
||||
struct Cdihb : Bitfield<1, 1> { };
|
||||
struct Dla : Bitfield<2, 1> { };
|
||||
struct Sdstb : Bitfield<3, 1> { };
|
||||
};
|
||||
struct Proctl : Register<0x28, 32>
|
||||
{
|
||||
struct Dtw : Bitfield<1, 2>
|
||||
{
|
||||
enum { _1BIT = 0, _4BIT = 1 };
|
||||
};
|
||||
struct Dmas : Bitfield<8, 2> { enum { ADMA2 = 2 }; };
|
||||
};
|
||||
struct Sysctl : Register<0x2c, 32>
|
||||
{
|
||||
struct Ipgen : Bitfield<0, 1> { };
|
||||
struct Hcken : Bitfield<1, 1> { };
|
||||
struct Peren : Bitfield<2, 1> { };
|
||||
struct Dvs : Bitfield<4, 4>
|
||||
{
|
||||
enum { DIV1 = 0x0, DIV4 = 0x3, DIV16 = 0xf, };
|
||||
};
|
||||
struct Sdclkfs : Bitfield<8, 8>
|
||||
{
|
||||
enum { DIV1 = 0x00, DIV2 = 0x01, DIV32 = 0x10, };
|
||||
};
|
||||
struct Dtocv : Bitfield<16, 4>
|
||||
{
|
||||
enum {
|
||||
SDCLK_TIMES_2_POW_28 = 0xf,
|
||||
SDCLK_TIMES_2_POW_27 = 0xe,
|
||||
SDCLK_TIMES_2_POW_13 = 0x0,
|
||||
};
|
||||
};
|
||||
struct Ipp_rst_n : Bitfield<23, 1> { };
|
||||
struct Rsta : Bitfield<24, 1> { };
|
||||
struct Rstc : Bitfield<25, 1> { };
|
||||
struct Rstd : Bitfield<26, 1> { };
|
||||
};
|
||||
|
||||
template <off_t OFFSET>
|
||||
struct Irq_tpl : Register<OFFSET, 32>
|
||||
{
|
||||
struct Cc : Register<OFFSET, 32>::template Bitfield<0, 1> { };
|
||||
struct Tc : Register<OFFSET, 32>::template Bitfield<1, 1> { };
|
||||
struct Dint : Register<OFFSET, 32>::template Bitfield<3, 1> { };
|
||||
struct Ctoe : Register<OFFSET, 32>::template Bitfield<16, 1> { };
|
||||
struct Cce : Register<OFFSET, 32>::template Bitfield<17, 1> { };
|
||||
struct Cebe : Register<OFFSET, 32>::template Bitfield<18, 1> { };
|
||||
struct Cie : Register<OFFSET, 32>::template Bitfield<19, 1> { };
|
||||
struct Dtoe : Register<OFFSET, 32>::template Bitfield<20, 1> { };
|
||||
struct Dce : Register<OFFSET, 32>::template Bitfield<21, 1> { };
|
||||
struct Debe : Register<OFFSET, 32>::template Bitfield<22, 1> { };
|
||||
struct Ac12e : Register<OFFSET, 32>::template Bitfield<24, 1> { };
|
||||
struct Dmae : Register<OFFSET, 32>::template Bitfield<28, 1> { };
|
||||
};
|
||||
struct Irq : Irq_tpl<0> { };
|
||||
struct Irqstat : Irq_tpl<0x30> { };
|
||||
struct Irqstaten : Irq_tpl<0x34> { };
|
||||
struct Irqsigen : Irq_tpl<0x38> { };
|
||||
struct Maxcurrent : Register<0x48, 32> { };
|
||||
struct Adsaddr : Register<0x58, 32> { };
|
||||
struct Hostver : Register<0xfc, 32>
|
||||
{
|
||||
struct Svn : Bitfield<0, 8> { };
|
||||
struct Vvn : Bitfield<8, 8> { };
|
||||
};
|
||||
struct Wml : Register<0x44, 32>
|
||||
{
|
||||
struct Rd_wml : Bitfield<0, 8> { };
|
||||
struct Rd_brst_len : Bitfield<8, 5> { };
|
||||
struct Wr_wml : Bitfield<16, 8> { };
|
||||
struct Wr_brst_len : Bitfield<24, 5> { };
|
||||
};
|
||||
struct Vendspec : Register<0xc0, 32>
|
||||
{
|
||||
struct Frc_sdclk_on : Bitfield<8, 1> { };
|
||||
};
|
||||
|
||||
enum { BLOCK_SIZE = 512 };
|
||||
|
||||
enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 };
|
||||
|
||||
enum Clock { CLOCK_INITIAL, CLOCK_OPERATIONAL };
|
||||
|
||||
enum Clock_divider { CLOCK_DIV_4, CLOCK_DIV_8, CLOCK_DIV_512 };
|
||||
|
||||
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;
|
||||
|
||||
static bool _supported_host_version(Hostver::access_t hostver);
|
||||
static void _watermark_level(Wml::access_t &wml);
|
||||
|
||||
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);
|
||||
void _wait_for_irq();
|
||||
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(bool const r);
|
||||
int _wait_for_cmd_complete_mb_finish(bool const reading);
|
||||
int _prepare_dma_mb(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
|
||||
*
|
||||
* \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
|
||||
*/
|
||||
Sdhc(addr_t const base,
|
||||
unsigned const irq,
|
||||
Delayer &delayer,
|
||||
bool const use_dma);
|
||||
|
||||
~Sdhc() { _irq_rec.dissolve(&_irq_ctx); }
|
||||
|
||||
|
||||
/****************************************
|
||||
** 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(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; }
|
||||
};
|
||||
|
||||
#endif /* _SDHC_H_ */
|
@ -1,243 +0,0 @@
|
||||
/*
|
||||
* \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.
|
||||
*/
|
||||
|
||||
#ifndef _ESDHCV2_H_
|
||||
#define _ESDHCV2_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/mmio.h>
|
||||
#include <irq_session/connection.h>
|
||||
#include <base/sleep.h>
|
||||
|
||||
/* local includes */
|
||||
#include <sd_card.h>
|
||||
#include <adma2.h>
|
||||
|
||||
namespace Genode
|
||||
{
|
||||
struct Esdhcv2;
|
||||
class Esdhcv2_controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* MMIO structure of a Freescale ESDHCv2
|
||||
*/
|
||||
struct Genode::Esdhcv2 : Mmio
|
||||
{
|
||||
struct Blkattr : Register<0x4, 32>
|
||||
{
|
||||
struct Blksize : Bitfield<0, 13> { };
|
||||
struct Blkcnt : Bitfield<16, 16> { };
|
||||
};
|
||||
template <off_t OFFSET>
|
||||
struct Cmdrsp_tpl : Register<OFFSET, 32>
|
||||
{
|
||||
struct Rsp136_8_24 : Register<OFFSET, 32>::template Bitfield<0, 24> { };
|
||||
struct Rsp136_0_8 : Register<OFFSET, 32>::template Bitfield<24, 8> { };
|
||||
};
|
||||
struct Cmdarg : Register<0x8, 32> { };
|
||||
struct Cmdrsp0 : Cmdrsp_tpl<0x10> { };
|
||||
struct Cmdrsp1 : Cmdrsp_tpl<0x14> { };
|
||||
struct Cmdrsp2 : Cmdrsp_tpl<0x18> { };
|
||||
struct Cmdrsp3 : Cmdrsp_tpl<0x1c> { };
|
||||
struct Rsp136_0 : Bitset_2<Cmdrsp3::Rsp136_0_8, Cmdrsp0::Rsp136_8_24> { };
|
||||
struct Rsp136_1 : Bitset_2<Cmdrsp0::Rsp136_0_8, Cmdrsp1::Rsp136_8_24> { };
|
||||
struct Rsp136_2 : Bitset_2<Cmdrsp1::Rsp136_0_8, Cmdrsp2::Rsp136_8_24> { };
|
||||
struct Rsp136_3 : Bitset_2<Cmdrsp2::Rsp136_0_8, Cmdrsp3::Rsp136_8_24> { };
|
||||
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 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 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 <off_t OFFSET>
|
||||
struct Irq_tpl : Register<OFFSET, 32>
|
||||
{
|
||||
struct Cc : Register<OFFSET, 32>::template Bitfield<0, 1> { };
|
||||
struct Tc : Register<OFFSET, 32>::template Bitfield<1, 1> { };
|
||||
struct Dint : Register<OFFSET, 32>::template Bitfield<3, 1> { };
|
||||
struct Ctoe : Register<OFFSET, 32>::template Bitfield<16, 1> { };
|
||||
struct Cce : Register<OFFSET, 32>::template Bitfield<17, 1> { };
|
||||
struct Cebe : Register<OFFSET, 32>::template Bitfield<18, 1> { };
|
||||
struct Cie : Register<OFFSET, 32>::template Bitfield<19, 1> { };
|
||||
struct Dtoe : Register<OFFSET, 32>::template Bitfield<20, 1> { };
|
||||
struct Dce : Register<OFFSET, 32>::template Bitfield<21, 1> { };
|
||||
struct Debe : Register<OFFSET, 32>::template Bitfield<22, 1> { };
|
||||
struct Ac12e : Register<OFFSET, 32>::template Bitfield<24, 1> { };
|
||||
struct Dmae : Register<OFFSET, 32>::template Bitfield<28, 1> { };
|
||||
};
|
||||
struct Irq : Irq_tpl<0> { };
|
||||
struct Irqstat : Irq_tpl<0x30> { };
|
||||
struct Irqstaten : Irq_tpl<0x34> { };
|
||||
struct Irqsigen : Irq_tpl<0x38> { };
|
||||
struct Maxcurrent : Register<0x48, 32> { };
|
||||
struct Adsaddr : Register<0x58, 32> { };
|
||||
struct Hostver : Register<0xfc, 32>
|
||||
{
|
||||
struct Svn : Bitfield<0, 8> { };
|
||||
struct Vvn : Bitfield<8, 8> { };
|
||||
};
|
||||
struct Wml : Register<0x44, 32>
|
||||
{
|
||||
struct Rd_wml : Bitfield<0, 8> { };
|
||||
struct Rd_brst_len : Bitfield<8, 5> { };
|
||||
struct Wr_wml : Bitfield<16, 8> { };
|
||||
struct Wr_brst_len : Bitfield<24, 5> { };
|
||||
};
|
||||
|
||||
Esdhcv2(addr_t const mmio_base) : Mmio(mmio_base) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of the SD host-controller interface for the ESDHCv2
|
||||
*/
|
||||
struct Genode::Esdhcv2_controller
|
||||
:
|
||||
private Esdhcv2, public Sd_card::Host_controller
|
||||
{
|
||||
private:
|
||||
|
||||
enum {
|
||||
BLOCK_SIZE = 512,
|
||||
WATERMARK_WORDS = 16,
|
||||
BURST_WORDS = 8,
|
||||
};
|
||||
|
||||
enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 };
|
||||
|
||||
enum Clock_divider { CLOCK_DIV_8, CLOCK_DIV_512 };
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
*
|
||||
* \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 base, unsigned const irq,
|
||||
Delayer & delayer, bool const use_dma);
|
||||
|
||||
~Esdhcv2_controller() { _irq_rec.dissolve(&_irq_ctx); }
|
||||
|
||||
|
||||
/****************************************
|
||||
** 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(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; }
|
||||
};
|
||||
|
||||
#endif /* _ESDHCV2_H_ */
|
120
repos/os/src/drivers/sd_card/spec/imx53/sdhc.cc
Normal file
120
repos/os/src/drivers/sd_card/spec/imx53/sdhc.cc
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* \brief Secured Digital Host Controller
|
||||
* \author Martin Stein
|
||||
* \date 2016-12-13
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <sdhc.h>
|
||||
|
||||
using namespace Sd_card;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
|
||||
{
|
||||
Xfertyp::Msbsel::set(xfertyp, 1);
|
||||
Xfertyp::Bcen::set(xfertyp, 1);
|
||||
Xfertyp::Dmaen::set(xfertyp, 1);
|
||||
}
|
||||
|
||||
|
||||
int Sdhc::_wait_for_cmd_complete_mb_finish(bool const reading)
|
||||
{
|
||||
if (reading) { return 0; }
|
||||
|
||||
/*
|
||||
* 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()) { 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.
|
||||
*/
|
||||
return _wait_for_card_ready_mbw() ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
bool Sdhc::_issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
|
||||
bool const transfer,
|
||||
bool const multiblock,
|
||||
bool const reading)
|
||||
{
|
||||
if (transfer) {
|
||||
Xfertyp::Bcen::set(xfertyp, 1);
|
||||
Xfertyp::Msbsel::set(xfertyp, 1);
|
||||
if (multiblock) {
|
||||
/*
|
||||
* The "Auto Command 12" feature of the ESDHC seems to be
|
||||
* broken for multi-block writes as it causes command-
|
||||
* timeout errors sometimes.
|
||||
*/
|
||||
if (reading) {
|
||||
Xfertyp::Ac12en::set(xfertyp, 1); }
|
||||
|
||||
if (_use_dma) {
|
||||
Xfertyp::Dmaen::set(xfertyp, 1); }
|
||||
}
|
||||
Xfertyp::Dtdsel::set(xfertyp,
|
||||
reading ? Xfertyp::Dtdsel::READ : Xfertyp::Dtdsel::WRITE);
|
||||
}
|
||||
return _wait_for_cmd_allowed() ? false : true;
|
||||
}
|
||||
|
||||
|
||||
bool Sdhc::_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)
|
||||
{
|
||||
Wml::Wr_wml::set(wml, 16);
|
||||
Wml::Wr_brst_len::set(wml, 8);
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_reset_amendments()
|
||||
{
|
||||
/*
|
||||
* 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>();
|
||||
Sysctl::Ipgen::set(sysctl, 1);
|
||||
Sysctl::Hcken::set(sysctl, 1);
|
||||
Sysctl::Peren::set(sysctl, 1);
|
||||
write<Sysctl>(sysctl);
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_clock_finish(Clock clock)
|
||||
{
|
||||
write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
|
||||
switch (clock) {
|
||||
case CLOCK_INITIAL: _enable_clock(CLOCK_DIV_512); break;
|
||||
case CLOCK_OPERATIONAL: _enable_clock(CLOCK_DIV_8); break; }
|
||||
write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_disable_clock_preparation() { }
|
||||
void Sdhc::_enable_clock_finish() { }
|
116
repos/os/src/drivers/sd_card/spec/imx6/sdhc.cc
Normal file
116
repos/os/src/drivers/sd_card/spec/imx6/sdhc.cc
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* \brief Secured Digital Host Controller
|
||||
* \author Martin Stein
|
||||
* \date 2016-12-13
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <sdhc.h>
|
||||
|
||||
using namespace Sd_card;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
void Sdhc::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
|
||||
{
|
||||
Mixctrl::access_t mixctrl = read<Mixctrl>();
|
||||
Mixctrl::Dmaen::set(mixctrl, 1);
|
||||
Mixctrl::Bcen::set(mixctrl, 1);
|
||||
Mixctrl::Ac12en::set(mixctrl, 0);
|
||||
Mixctrl::Ddren::set(mixctrl, 0);
|
||||
Mixctrl::Dtdsel::set(mixctrl, Mixctrl::Dtdsel::READ);
|
||||
Mixctrl::Msbsel::set(mixctrl, 1);
|
||||
Mixctrl::Nibblepos::set(mixctrl, 0);
|
||||
Mixctrl::Ac23en::set(mixctrl, 0);
|
||||
write<Mixctrl>(mixctrl);
|
||||
}
|
||||
|
||||
|
||||
int Sdhc::_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 const transfer,
|
||||
bool const multiblock,
|
||||
bool const reading)
|
||||
{
|
||||
Mixctrl::access_t mixctrl = read<Mixctrl>();
|
||||
Mixctrl::Dmaen ::set(mixctrl, transfer && multiblock && _use_dma);
|
||||
Mixctrl::Bcen ::set(mixctrl, transfer);
|
||||
Mixctrl::Ac12en ::set(mixctrl, 0);
|
||||
Mixctrl::Msbsel ::set(mixctrl, transfer);
|
||||
Mixctrl::Ddren ::set(mixctrl, 0);
|
||||
Mixctrl::Nibblepos::set(mixctrl, 0);
|
||||
Mixctrl::Ac23en ::set(mixctrl, 0);
|
||||
Mixctrl::Dtdsel ::set(mixctrl, reading ? Mixctrl::Dtdsel::READ :
|
||||
Mixctrl::Dtdsel::WRITE);
|
||||
|
||||
if (_wait_for_cmd_allowed()) {
|
||||
return false; }
|
||||
|
||||
write<Mixctrl>(mixctrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Sdhc::_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)
|
||||
{
|
||||
Wml::Wr_wml::set(wml, 64);
|
||||
Wml::Wr_brst_len::set(wml, 16);
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_reset_amendments()
|
||||
{
|
||||
/* the USDHC doesn't reset the Mixer Control register automatically */
|
||||
Mixctrl::access_t mixctrl = read<Mixctrl>();
|
||||
Mixctrl::Dmaen::set(mixctrl, 0);
|
||||
Mixctrl::Bcen::set(mixctrl, 0);
|
||||
Mixctrl::Ac12en::set(mixctrl, 0);
|
||||
Mixctrl::Ddren::set(mixctrl, 0);
|
||||
Mixctrl::Dtdsel::set(mixctrl, 0);
|
||||
Mixctrl::Msbsel::set(mixctrl, 0);
|
||||
Mixctrl::Nibblepos::set(mixctrl, 0);
|
||||
Mixctrl::Ac23en::set(mixctrl, 0);
|
||||
Mixctrl::Always_ones::set(mixctrl, 1);
|
||||
write<Mixctrl>(mixctrl);
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_clock_finish(Clock clock)
|
||||
{
|
||||
switch (clock) {
|
||||
case CLOCK_INITIAL:
|
||||
write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_13);
|
||||
_enable_clock(CLOCK_DIV_512);
|
||||
break;
|
||||
case CLOCK_OPERATIONAL:
|
||||
write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_28);
|
||||
write<Sysctl::Ipp_rst_n>(0);
|
||||
_enable_clock(CLOCK_DIV_4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Sdhc::_disable_clock_preparation() { write<Vendspec::Frc_sdclk_on>(0); }
|
||||
|
||||
void Sdhc::_enable_clock_finish() { write<Vendspec::Frc_sdclk_on>(0); }
|
Loading…
x
Reference in New Issue
Block a user