300 lines
8.7 KiB
C++
300 lines
8.7 KiB
C++
/*
|
|
* \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 _DRIVER_H_
|
|
#define _DRIVER_H_
|
|
|
|
/* Genode includes */
|
|
#include <timer_session/connection.h>
|
|
#include <irq_session/connection.h>
|
|
#include <os/attached_mmio.h>
|
|
|
|
/* local includes */
|
|
#include <driver_base.h>
|
|
#include <adma2.h>
|
|
|
|
namespace Sd_card { class Driver; }
|
|
|
|
|
|
class Sd_card::Driver : public Driver_base,
|
|
private Attached_mmio
|
|
{
|
|
private:
|
|
|
|
enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 };
|
|
|
|
enum Clock { CLOCK_INITIAL, CLOCK_OPERATIONAL };
|
|
|
|
enum Clock_divider { CLOCK_DIV_4, CLOCK_DIV_8, CLOCK_DIV_512 };
|
|
|
|
|
|
/********************
|
|
** MMIO structure **
|
|
********************/
|
|
|
|
struct Blkattr : Register<0x4, 32>
|
|
{
|
|
struct Blksize : Bitfield<0, 13> { };
|
|
struct Blkcnt : Bitfield<16, 16> { };
|
|
};
|
|
template <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;
|
|
Block_transfer _block_transfer { };
|
|
Timer_delayer _delayer { _env };
|
|
Signal_handler<Driver> _irq_handler { _env.ep(), *this,
|
|
&Driver::_handle_irq };
|
|
Irq_connection _irq;
|
|
Card_info _card_info { _init() };
|
|
Adma2::Table _adma2_table { _env.ram(), _env.rm() };
|
|
|
|
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);
|
|
|
|
|
|
/*******************
|
|
** Block::Driver **
|
|
*******************/
|
|
|
|
void read_dma(Block::sector_t block_number,
|
|
size_t block_count,
|
|
addr_t phys,
|
|
Block::Packet_descriptor &packet) override;
|
|
|
|
void write_dma(Block::sector_t block_number,
|
|
size_t block_count,
|
|
addr_t phys,
|
|
Block::Packet_descriptor &packet) override;
|
|
|
|
bool dma_enabled() override { return true; }
|
|
|
|
Ram_dataspace_capability alloc_dma_buffer(size_t size) override {
|
|
return _env.ram().alloc(size, UNCACHED); }
|
|
};
|
|
|
|
#endif /* _DRIVER_H_ */
|