os: move imx sd_card driver to imx repository

Ref genodelabs/genode#5252
This commit is contained in:
Stefan Kalkowski 2024-06-19 14:15:58 +02:00 committed by Norman Feske
parent d1123ebe4c
commit 0c8abf9b50
6 changed files with 0 additions and 1097 deletions

View File

@ -1,536 +0,0 @@
/*
* \brief Secured Digital Host Controller
* \author Martin Stein
* \date 2015-02-05
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* local includes */
#include <driver.h>
using namespace Sd_card;
using namespace Genode;
int Driver::_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;
uint64_t constexpr attempts_delay_us = 100000;
while (1) {
if (!attempts) {
error("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; }
Mmio::write<Cmdarg>(cmdarg);
Mmio::write<Xfertyp>(xfertyp);
/* wait for command completion */
if (_wait_for_cmd_complete()) { return -1; }
/* check for errors */
R1_response_0::access_t const resp = Mmio::read<Cmdrsp0>();
if (R1_response_0::Error::get(resp)) {
error("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 Driver::_stop_transmission()
{
/* write argument register */
Mmio::write<Cmdarg>(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);
_stop_transmission_finish_xfertyp(xfertyp);
Mmio::write<Xfertyp>(xfertyp);
/* wait for command completion */
if (_wait_for_cmd_complete()) { return -1; }
return 0;
}
void Driver::_handle_irq()
{
_irq.ack();
/* the handler is only for block transfers, on other commands we poll */
if (!_block_transfer.pending) {
return; }
/*
* 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
* Complete" and "Command Complete" must be gathered.
*/
try {
wait_for(Attempts(1000), Microseconds(1000), _delayer,
Irqstat::Cc::Equal(1), Irqstat::Tc::Equal(1)); }
catch (Polling_timeout) {
error("Completion host signal timed out");
throw -1;
}
/* acknowledge completion signals */
Irqstat::access_t irqstat = 0;
Irqstat::Cc::set(irqstat, 1);
Irqstat::Tc::set(irqstat, 1);
Mmio::write<Irqstat>(irqstat);
if (_wait_for_cmd_complete_mb_finish(_block_transfer.read)) {
throw -1; }
_block_transfer.pending = false;
ack_packet(_block_transfer.packet, true);
}
int Driver::_wait_for_cmd_complete()
{
try { wait_for(Attempts(200), Microseconds(5000), _delayer,
Irqstat::Cc::Equal(1)); }
catch (Polling_timeout) {
error("command timed out");
return -1;
}
Mmio::write<Irqstat>(Irqstat::Cc::reg_mask());
return 0;
}
bool Driver::_issue_command(Command_base const & command)
{
/* 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;
/* set command index */
Xfertyp::access_t xfertyp = 0;
Xfertyp::Cmdinx::set(xfertyp, command.index);
/* 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);
Mmio::write<Cmdarg>(command.arg);
Mmio::write<Xfertyp>(xfertyp);
/* for block transfers there's a signal handler, on other commands poll */
return transfer ? true : !_wait_for_cmd_complete();
}
Cid Driver::_read_cid()
{
Cid cid;
cid.raw_0 = Mmio::read<Rsp136_0>();
cid.raw_1 = Mmio::read<Rsp136_1>();
cid.raw_2 = Mmio::read<Rsp136_2>();
cid.raw_3 = Mmio::read<Rsp136_3>();
return cid;
}
Csd Driver::_read_csd()
{
Csd csd;
csd.csd0 = Mmio::read<Rsp136_0>();
csd.csd1 = Mmio::read<Rsp136_1>();
csd.csd2 = Mmio::read<Rsp136_2>();
csd.csd3 = Mmio::read<Rsp136_3>();
return csd;
}
unsigned Driver::_read_rca()
{
Cmdrsp0::access_t const rsp0 = Mmio::read<Cmdrsp0>();
return Send_relative_addr::Response::Rca::get(rsp0);
}
void Driver::read_dma(Block::sector_t blk_nr,
size_t blk_cnt,
addr_t buf_phys,
Block::Packet_descriptor &packet)
{
if (_prepare_dma_mb(packet, true, blk_cnt, buf_phys)) {
throw Io_error(); }
if (!issue_command(Read_multiple_block((unsigned long)blk_nr))) {
throw Io_error(); }
}
void Driver::write_dma(Block::sector_t blk_nr,
size_t blk_cnt,
addr_t buf_phys,
Block::Packet_descriptor &packet)
{
if (_prepare_dma_mb(packet, false, blk_cnt, buf_phys)) {
throw Io_error(); }
if (!issue_command(Write_multiple_block((unsigned long)blk_nr))) {
throw Io_error(); }
}
int Driver::_prepare_dma_mb(Block::Packet_descriptor packet,
bool reading,
size_t blk_cnt,
addr_t buf_phys)
{
if (_block_transfer.pending) {
throw Request_congestion(); }
/* 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 */
Mmio::write<Adsaddr>(_adma2_table.base_dma());
Mmio::write<Blkattr::Blksize>(_block_size());
Mmio::write<Blkattr::Blkcnt>(blk_cnt);
_block_transfer.read = reading;
_block_transfer.packet = packet;
_block_transfer.pending = true;
return 0;
}
int Driver::_wait_for_cmd_allowed()
{
/*
* 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.
*/
try { wait_for(_delayer, Prsstat::Dla::Equal(0),
Prsstat::Sdstb::Equal(1),
Prsstat::Cihb::Equal(0),
Prsstat::Cdihb::Equal(0)); }
catch (Polling_timeout) {
error("wait till issuing a new command is allowed timed out");
return -1;
}
return 0;
}
Card_info Driver::_init()
{
/* install IRQ signal */
_irq.sigh(_irq_handler);
/* configure host for initialization stage */
if (_reset()) {
_detect_err("Host reset failed"); }
_disable_irqs();
if (!_supported_host_version(Mmio::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 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_INITIAL);
/*
* 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 (Mmio::read<Cmdrsp0>() != 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 (Mmio::read<Cmdrsp0>() != 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(Mmio::read<Cmdrsp0>())) { 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_OPERATIONAL);
/*
* 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 = Mmio::read<Wml>();
_watermark_level(wml);
Mmio::write<Wml>(wml);
/* configure ADMA */
Mmio::write<Proctl::Dmas>(Proctl::Dmas::ADMA2);
/* configure interrupts for operational mode */
_disable_irqs();
Mmio::write<Irqstat>(~0);
_enable_irqs();
return card_info;
}
void Driver::_detect_err(char const * const err)
{
error(err);
throw Detection_failed();
}
int Driver::_reset()
{
/* start reset */
Mmio::write<Sysctl::Rsta>(1);
_reset_amendments();
/* wait for reset completion */
try { wait_for(_delayer, Sysctl::Rsta::Equal(0)); }
catch (Polling_timeout) {
error("Reset timed out");
return -1;
}
return 0;
}
void Driver::_disable_irqs()
{
Mmio::write<Irqstaten>(0);
Mmio::write<Irqsigen>(0);
}
void Driver::_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);
Mmio::write<Irqstaten>(irq);
Mmio::write<Irqsigen>(irq);
}
void Driver::_bus_width(Bus_width bus_width)
{
switch (bus_width) {
case BUS_WIDTH_1: Mmio::write<Proctl::Dtw>(Proctl::Dtw::_1BIT); break;
case BUS_WIDTH_4: Mmio::write<Proctl::Dtw>(Proctl::Dtw::_4BIT); break;
}
}
void Driver::_disable_clock()
{
_disable_clock_preparation();
Sysctl::access_t sysctl = Mmio::read<Sysctl>();
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);
Mmio::write<Sysctl>(sysctl);
}
void Driver::_enable_clock(Clock_divider divider)
{
Sysctl::access_t sysctl = Mmio::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);
break;
case CLOCK_DIV_512:
Sysctl::Dvs::set(sysctl, Sysctl::Dvs::DIV16);
Sysctl::Sdclkfs::set(sysctl, Sysctl::Sdclkfs::DIV32);
break;
}
Mmio::write<Sysctl>(sysctl);
_enable_clock_finish();
_delayer.usleep(1000);
}
void Driver::_clock(Clock clock)
{
wait_for(_delayer, Prsstat::Sdstb::Equal(1));
_disable_clock();
_clock_finish(clock);
}
Driver::Driver(Env & env, Platform::Connection & platform)
:
Driver_base(env.ram()),
Platform::Device(platform),
Platform::Device::Mmio<SIZE>(*static_cast<Platform::Device *>(this)),
_env(env),
_platform(platform)
{
log("SD card detected");
log("capacity: ", card_info().capacity_mb(), " MiB");
}
Driver::~Driver() { }

View File

@ -1,310 +0,0 @@
/*
* \brief Secured Digital Host Controller
* \author Martin Stein
* \date 2015-02-05
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SRC__DRIVERS__SD_CARD__SPEC__IMX__DRIVER_H_
#define _SRC__DRIVERS__SD_CARD__SPEC__IMX__DRIVER_H_
/* Genode includes */
#include <platform_session/device.h>
#include <timer_session/connection.h>
/* local includes */
#include <driver_base.h>
#include <adma2.h>
namespace Sd_card { class Driver; }
class Sd_card::Driver : public Driver_base,
private Platform::Device,
private Platform::Device::Mmio<0x100>
{
using Mmio = Genode::Mmio<SIZE>;
private:
enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 };
enum Clock { CLOCK_INITIAL, CLOCK_OPERATIONAL };
enum Clock_divider { CLOCK_DIV_4, CLOCK_DIV_8, CLOCK_DIV_512 };
/********************
** MMIO structure **
********************/
struct Blkattr : Register<0x4, 32>
{
struct Blksize : Bitfield<0, 13> { };
struct Blkcnt : Bitfield<16, 16> { };
};
template <off_t OFFSET>
struct Cmdrsp_tpl : Register<OFFSET, 32>
{
struct Rsp136_8_24 : Register<OFFSET, 32>::template Bitfield<0, 24> { };
struct Rsp136_0_8 : Register<OFFSET, 32>::template Bitfield<24, 8> { };
};
struct Cmdarg : Register<0x8, 32> { };
struct Cmdrsp0 : Cmdrsp_tpl<0x10> { };
struct Cmdrsp1 : Cmdrsp_tpl<0x14> { };
struct Cmdrsp2 : Cmdrsp_tpl<0x18> { };
struct Cmdrsp3 : Cmdrsp_tpl<0x1c> { };
struct Rsp136_0 : Bitset_2<Cmdrsp3::Rsp136_0_8, Cmdrsp0::Rsp136_8_24> { };
struct Rsp136_1 : Bitset_2<Cmdrsp0::Rsp136_0_8, Cmdrsp1::Rsp136_8_24> { };
struct Rsp136_2 : Bitset_2<Cmdrsp1::Rsp136_0_8, Cmdrsp2::Rsp136_8_24> { };
struct Rsp136_3 : Bitset_2<Cmdrsp2::Rsp136_0_8, Cmdrsp3::Rsp136_8_24> { };
template <off_t OFFSET>
struct Xfertyp_base : Register<OFFSET, 32>
{
struct Dmaen : Register<OFFSET, 32>::template Bitfield<0, 1> { };
struct Bcen : Register<OFFSET, 32>::template Bitfield<1, 1> { };
struct Ac12en : Register<OFFSET, 32>::template Bitfield<2, 1> { };
struct Dtdsel : Register<OFFSET, 32>::template Bitfield<4, 1>
{
enum { WRITE = 0, READ = 1, };
};
struct Msbsel : Register<OFFSET, 32>::template Bitfield<5, 1> { };
};
struct Mixctrl : Xfertyp_base<0x48>
{
struct Ddren : Bitfield<3, 1> { };
struct Nibblepos : Bitfield<6, 1> { };
struct Ac23en : Bitfield<7, 1> { };
struct Always_ones : Bitfield<31, 1> { };
};
struct Xfertyp : Xfertyp_base<0xc>
{
struct Rsptyp : Bitfield<16, 2>
{
enum {
_0BIT = 0,
_136BIT = 1,
_48BIT = 2,
_48BIT_BUSY = 3,
};
};
struct Cccen : Bitfield<19, 1> { };
struct Cicen : Bitfield<20, 1> { };
struct Dpsel : Bitfield<21, 1> { };
struct Cmdtyp : Bitfield<22, 2>
{
enum { ABORT_CMD12 = 3 };
};
struct Cmdinx : Bitfield<24, 6> { };
};
struct Prsstat : Register<0x24, 32>
{
struct Cihb : Bitfield<0, 1> { };
struct Cdihb : Bitfield<1, 1> { };
struct Dla : Bitfield<2, 1> { };
struct Sdstb : Bitfield<3, 1> { };
};
struct Proctl : Register<0x28, 32>
{
struct Dtw : Bitfield<1, 2>
{
enum { _1BIT = 0, _4BIT = 1 };
};
struct Dmas : Bitfield<8, 2> { enum { ADMA2 = 2 }; };
};
struct Sysctl : Register<0x2c, 32>
{
struct Ipgen : Bitfield<0, 1> { };
struct Hcken : Bitfield<1, 1> { };
struct Peren : Bitfield<2, 1> { };
struct Dvs : Bitfield<4, 4>
{
enum { DIV1 = 0x0, DIV4 = 0x3, DIV16 = 0xf, };
};
struct Sdclkfs : Bitfield<8, 8>
{
enum { DIV1 = 0x00, DIV2 = 0x01, DIV32 = 0x10, };
};
struct Dtocv : Bitfield<16, 4>
{
enum {
SDCLK_TIMES_2_POW_28 = 0xf,
SDCLK_TIMES_2_POW_27 = 0xe,
SDCLK_TIMES_2_POW_13 = 0x0,
};
};
struct Ipp_rst_n : Bitfield<23, 1> { };
struct Rsta : Bitfield<24, 1> { };
struct Rstc : Bitfield<25, 1> { };
struct Rstd : Bitfield<26, 1> { };
};
template <off_t OFFSET>
struct Irq_tpl : Register<OFFSET, 32>
{
struct Cc : Register<OFFSET, 32>::template Bitfield<0, 1> { };
struct Tc : Register<OFFSET, 32>::template Bitfield<1, 1> { };
struct Dint : Register<OFFSET, 32>::template Bitfield<3, 1> { };
struct Ctoe : Register<OFFSET, 32>::template Bitfield<16, 1> { };
struct Cce : Register<OFFSET, 32>::template Bitfield<17, 1> { };
struct Cebe : Register<OFFSET, 32>::template Bitfield<18, 1> { };
struct Cie : Register<OFFSET, 32>::template Bitfield<19, 1> { };
struct Dtoe : Register<OFFSET, 32>::template Bitfield<20, 1> { };
struct Dce : Register<OFFSET, 32>::template Bitfield<21, 1> { };
struct Debe : Register<OFFSET, 32>::template Bitfield<22, 1> { };
struct Ac12e : Register<OFFSET, 32>::template Bitfield<24, 1> { };
struct Dmae : Register<OFFSET, 32>::template Bitfield<28, 1> { };
};
struct Irq : Irq_tpl<0> { };
struct Irqstat : Irq_tpl<0x30> { };
struct Irqstaten : Irq_tpl<0x34> { };
struct Irqsigen : Irq_tpl<0x38> { };
struct Maxcurrent : Register<0x48, 32> { };
struct Adsaddr : Register<0x58, 32> { };
struct Hostver : Register<0xfc, 32>
{
struct Svn : Bitfield<0, 8> { };
struct Vvn : Bitfield<8, 8> { };
};
struct Wml : Register<0x44, 32>
{
struct Rd_wml : Bitfield<0, 8> { };
struct Rd_brst_len : Bitfield<8, 5> { };
struct Wr_wml : Bitfield<16, 8> { };
struct Wr_brst_len : Bitfield<24, 5> { };
};
struct Vendspec : Register<0xc0, 32>
{
struct Frc_sdclk_on : Bitfield<8, 1> { };
};
/************************
** Utility structures **
************************/
struct Timer_delayer : Timer::Connection, Mmio::Delayer
{
Timer_delayer(Genode::Env &env) : Timer::Connection(env) { }
void usleep(uint64_t us) override { Timer::Connection::usleep(us); }
};
struct Block_transfer
{
Block::Packet_descriptor packet { };
bool pending = false;
bool read = false;
};
Env & _env;
Platform::Connection & _platform;
Block_transfer _block_transfer { };
Timer_delayer _delayer { _env };
Signal_handler<Driver> _irq_handler { _env.ep(), *this,
&Driver::_handle_irq };
Platform::Device::Irq _irq { *this };
Card_info _card_info { _init() };
Adma2::Table _adma2_table { _platform };
static bool _supported_host_version(Hostver::access_t hostver);
static void _watermark_level(Wml::access_t &wml);
void _handle_irq();
void _detect_err(char const * const err);
void _disable_irqs();
void _enable_irqs();
void _bus_width(Bus_width bus_width);
void _disable_clock();
void _disable_clock_preparation();
void _enable_clock(Clock_divider divider);
void _enable_clock_finish();
void _clock(Clock clock);
void _clock_finish(Clock clock);
int _reset();
void _reset_amendments();
int _wait_for_cmd_allowed();
int _wait_for_cmd_complete();
int _wait_for_card_ready_mbw();
int _stop_transmission();
void _stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp);
int _wait_for_cmd_complete_mb_finish(bool const reading);
int _prepare_dma_mb(Block::Packet_descriptor packet,
bool reading,
size_t blk_cnt,
addr_t buf_phys);
bool _issue_cmd_finish_xfertyp(Xfertyp::access_t &xfertyp,
bool const transfer,
bool const multiblock,
bool const reading);
Card_info _init();
/*********************
** Host_controller **
*********************/
Cid _read_cid() override;
Csd _read_csd() override;
unsigned _read_rca() override;
bool _issue_command(Command_base const & cmd) override;
Card_info card_info() const override { return _card_info; }
public:
using Block::Driver::read;
using Block::Driver::write;
Driver(Env &env, Platform::Connection & platform);
~Driver();
/*******************
** Block::Driver **
*******************/
void read_dma(Block::sector_t block_number,
size_t block_count,
addr_t phys,
Block::Packet_descriptor &packet) override;
void write_dma(Block::sector_t block_number,
size_t block_count,
addr_t phys,
Block::Packet_descriptor &packet) override;
bool dma_enabled() override { return true; }
Dma_buffer alloc_dma_buffer(size_t size, Cache cache) override
{
Ram_dataspace_capability ds =
_platform.retry_with_upgrade(Ram_quota{4096}, Cap_quota{2},
[&] () { return _platform.alloc_dma_buffer(size, cache); });
return { .ds = ds,
.dma_addr = _platform.dma_addr(ds) };
}
};
#endif /* _SRC__DRIVERS__SD_CARD__SPEC__IMX__DRIVER_H_ */

View File

@ -1,119 +0,0 @@
/*
* \brief Secured Digital Host Controller
* \author Martin Stein
* \date 2016-12-13
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* local includes */
#include <driver.h>
using namespace Sd_card;
using namespace Genode;
void Driver::_stop_transmission_finish_xfertyp(Xfertyp::access_t &xfertyp)
{
Xfertyp::Msbsel::set(xfertyp, 1);
Xfertyp::Bcen::set(xfertyp, 1);
Xfertyp::Dmaen::set(xfertyp, 1);
}
int Driver::_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 Driver::_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); }
Xfertyp::Dmaen::set(xfertyp, 1);
}
Xfertyp::Dtdsel::set(xfertyp,
reading ? Xfertyp::Dtdsel::READ : Xfertyp::Dtdsel::WRITE);
}
return _wait_for_cmd_allowed() ? false : true;
}
bool Driver::_supported_host_version(Hostver::access_t hostver)
{
return Hostver::Vvn::get(hostver) == 18 &&
Hostver::Svn::get(hostver) == 1;
}
void Driver::_watermark_level(Wml::access_t &wml)
{
Wml::Wr_wml::set(wml, 16);
Wml::Wr_brst_len::set(wml, 8);
}
void Driver::_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 = Mmio::read<Sysctl>();
Sysctl::Ipgen::set(sysctl, 1);
Sysctl::Hcken::set(sysctl, 1);
Sysctl::Peren::set(sysctl, 1);
Mmio::write<Sysctl>(sysctl);
}
void Driver::_clock_finish(Clock clock)
{
Mmio::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; }
Mmio::write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_27);
}
void Driver::_disable_clock_preparation() { }
void Driver::_enable_clock_finish() { }

View File

@ -1,6 +0,0 @@
TARGET = imx53_sd_card
REQUIRES = arm_v7
SRC_CC = adma2.cc imx/driver.cc
INC_DIR = $(REP_DIR)/src/driver/sd_card/imx
include $(REP_DIR)/src/driver/sd_card/target.inc

View File

@ -1,120 +0,0 @@
/*
* \brief Secured Digital Host Controller
* \author Martin Stein
* \date 2016-12-13
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* local includes */
#include <driver.h>
using namespace Sd_card;
using namespace Genode;
void Driver::_stop_transmission_finish_xfertyp(Xfertyp::access_t &)
{
Mixctrl::access_t mixctrl = Mmio::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);
Mmio::write<Mixctrl>(mixctrl);
}
int Driver::_wait_for_cmd_complete_mb_finish(bool)
{
/* we can't use the "Auto Command 12" feature as it does not work */
return _stop_transmission() ? -1 : 0;
}
bool Driver::_issue_cmd_finish_xfertyp(Xfertyp::access_t &,
bool const transfer,
bool const multiblock,
bool const reading)
{
Mixctrl::access_t mixctrl = Mmio::read<Mixctrl>();
Mixctrl::Dmaen ::set(mixctrl, transfer && multiblock);
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; }
Mmio::write<Mixctrl>(mixctrl);
return true;
}
bool Driver::_supported_host_version(Hostver::access_t)
{
/*
* on i.MX6 there exist board-specific (tested) drivers only,
* therefore we do not need to differentiate in between controller versions
*/
return true;
}
void Driver::_watermark_level(Wml::access_t &wml)
{
Wml::Wr_wml::set(wml, 64);
Wml::Wr_brst_len::set(wml, 16);
}
void Driver::_reset_amendments()
{
/* the USDHC doesn't reset the Mixer Control register automatically */
Mixctrl::access_t mixctrl = Mmio::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);
Mmio::write<Mixctrl>(mixctrl);
}
void Driver::_clock_finish(Clock clock)
{
switch (clock) {
case CLOCK_INITIAL:
Mmio::write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_13);
_enable_clock(CLOCK_DIV_512);
break;
case CLOCK_OPERATIONAL:
Mmio::write<Sysctl::Dtocv>(Sysctl::Dtocv::SDCLK_TIMES_2_POW_28);
Mmio::write<Sysctl::Ipp_rst_n>(0);
_enable_clock(CLOCK_DIV_4);
break;
}
}
void Driver::_disable_clock_preparation() {
Mmio::write<Vendspec::Frc_sdclk_on>(0); }
void Driver::_enable_clock_finish() { Mmio::write<Vendspec::Frc_sdclk_on>(0); }

View File

@ -1,6 +0,0 @@
TARGET = imx6_sd_card
SRC_CC = adma2.cc imx/driver.cc
INC_DIR = $(REP_DIR)/src/driver/sd_card/imx
REQUIRES = arm_v7a
include $(REP_DIR)/src/driver/sd_card/target.inc