genode/repos/os/src/drivers/sd_card/omap4/mmchs.h

1008 lines
21 KiB
C++

/*
* \brief OMAP4 MMCHS controller registers
* \author Norman Feske
* \date 2012-07-03
*/
/*
* 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 _MMCHS_H_
#define _MMCHS_H_
/* Genode includes */
#include <util/mmio.h>
#include <os/attached_ram_dataspace.h>
#include <irq_session/connection.h>
#include <drivers/board_base.h>
/* local includes */
#include <sd_card.h>
struct Mmchs : Genode::Mmio
{
enum { verbose = false };
typedef Genode::size_t size_t;
Mmchs(Genode::addr_t const mmio_base) : Genode::Mmio(mmio_base) { }
struct Sysconfig : Register<0x110, 32>
{
struct Autoidle : Bitfield<0, 1> { };
struct Softreset : Bitfield<1, 1> { };
struct Sidlemode : Bitfield<3, 2>
{
enum { NO_IDLE = 1 };
};
struct Clockactivity : Bitfield<8, 2>
{
enum { BOTH_ACTIVE = 3 };
};
};
struct Sysstatus : Register<0x114, 32>
{
struct Reset_done : Bitfield<0, 1> { };
};
struct Con : Register<0x12c, 32>
{
/**
* Open drain mode
*/
struct Od : Bitfield<0, 1> { };
/**
* Start initialization stream
*/
struct Init : Bitfield<1, 1> { };
struct Dw8 : Bitfield<5, 1> { };
/**
* Master master slave selection (set if master DMA)
*/
struct Dma_mns : Bitfield<20, 1> { };
};
/**
* Command register
*/
struct Cmd : Register<0x20c, 32>
{
struct Index : Bitfield<24, 6> { };
/**
* Data present
*/
struct Dp : Bitfield<21, 1> { };
struct Rsp_type : Bitfield<16, 2>
{
enum Response { RESPONSE_NONE = 0,
RESPONSE_136_BIT = 1,
RESPONSE_48_BIT = 2,
RESPONSE_48_BIT_WITH_BUSY = 3 };
};
/**
* Data direction
*/
struct Ddir : Bitfield<4, 1>
{
enum { WRITE = 0, READ = 1 };
};
/**
* Block count enable
*/
struct Bce : Bitfield<1, 1> { };
/**
* Multiple block select
*/
struct Msbs : Bitfield<5, 1> { };
/**
* Auto-CMD12 enable
*/
struct Acen : Bitfield<2, 1> { };
/**
* DMA enable
*/
struct De : Bitfield<0, 1> { };
};
/**
* Transfer length configuration
*/
struct Blk : Register<0x204, 32>
{
struct Blen : Bitfield<0, 12> { };
struct Nblk : Bitfield<16, 16> { };
};
/**
* Command argument
*/
struct Arg : Register<0x208, 32> { };
/**
* Response bits 0..31
*/
struct Rsp10 : Register<0x210, 32> { };
/**
* Response bits 32..63
*/
struct Rsp32 : Register<0x214, 32> { };
/**
* Response bits 64..95
*/
struct Rsp54 : Register<0x218, 32> { };
/**
* Response bits 96..127
*/
struct Rsp76 : Register<0x21c, 32> { };
struct Data : Register<0x220, 32> { };
struct Pstate : Register<0x224, 32>
{
/**
* Command inhibit
*/
struct Cmdi : Bitfield<0, 1> { };
/**
* Buffer write enable status
*/
struct Bwe : Bitfield<10, 1> { };
/**
* Buffer read enable status
*/
struct Bre : Bitfield<11, 1> { };
};
struct Hctl : Register<0x228, 32>
{
/**
* Wakeup event enable
*/
struct Iwe : Bitfield<24, 1> { };
/**
* SD bus power
*/
struct Sdbp : Bitfield<8, 1>
{
enum { POWER_OFF = 0, POWER_ON = 1 };
};
/**
* SD voltage select
*/
struct Sdvs : Bitfield<9, 3>
{
enum Voltage { VOLTAGE_1_8 = 5,
VOLTAGE_3_0 = 6,
VOLTAGE_3_3 = 7 };
};
/**
* Data transfer width
*/
struct Dtw : Bitfield<1, 1>
{
enum { ONE_BIT = 0, FOUR_BITS = 1 };
};
};
struct Sysctl : Register<0x22c, 32>
{
/**
* Internal clock enable
*/
struct Ice : Bitfield<0, 1> { };
/**
* Internal clock stable
*/
struct Ics : Bitfield<1, 1> { };
/**
* Clock enable
*/
struct Ce : Bitfield<2, 1> { };
/**
* Clock divider
*/
struct Clkd : Bitfield<6, 10> { };
/**
* Software reset all
*/
struct Sra : Bitfield<24, 1> { };
/**
* Software reset for command line
*/
struct Src : Bitfield<25, 1> { };
/**
* Data timeout counter
*/
struct Dto : Bitfield<16, 4>
{
enum { TCF_2_POW_27 = 0xe };
};
};
struct Stat : Register<0x230, 32>
{
/**
* Transfer completed
*/
struct Tc : Bitfield<1, 1> { };
/**
* Command completed
*/
struct Cc : Bitfield<0, 1> { };
/**
* Error
*/
struct Erri : Bitfield<15, 1> { };
/**
* Command timed out
*/
struct Cto : Bitfield<16, 1> { };
};
/**
* Interrupt enable
*/
struct Ie : Register<0x234, 32>
{
/**
* Command completed
*/
struct Cc_enable : Bitfield<0, 1> { };
/**
* Transfer completed
*/
struct Tc_enable : Bitfield<1, 1> { };
/**
* Card interrupt
*/
struct Cirq_enable : Bitfield<8, 1> { };
/**
* Command timeout error
*/
struct Cto_enable : Bitfield<16, 1> { };
};
struct Ise : Register<0x238, 32>
{
/*
* The naming follows the lines of the 'Ie' register
*/
struct Tc_sigen : Bitfield<1, 1> { };
struct Cto_sigen : Bitfield<16, 1> { };
};
/**
* Capabilities
*/
struct Capa : Register<0x240, 32>
{
struct Vs30 : Bitfield<25, 1> { };
struct Vs18 : Bitfield<26, 1> { };
};
/**
* ADMA system address
*
* Base address of the ADMA descriptor table
*/
struct Admasal : Register<0x258, 32> { };
/**
* ADMA descriptor layout
*/
struct Adma_desc : Genode::Register<64>
{
struct Valid : Bitfield<0, 1> { };
struct Ent : 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> { };
};
bool reset_cmd_line(Delayer &delayer)
{
write<Sysctl::Src>(1);
/*
* We must poll quickly. If we waited too long until checking the bit,
* the polling would be infinite. Apparently the hardware depends on
* the timing here.
*/
if (!wait_for<Sysctl::Src>(1, delayer, 1000, 0)) {
PERR("reset of cmd line timed out (src != 1)");
return false;
}
if (!wait_for<Sysctl::Src>(0, delayer, 1000, 0)) {
PERR("reset of cmd line timed out (src != 0)");
return false;
}
return true;
}
/* XXX unused */
bool soft_reset_all(Delayer &delayer)
{
write<Sysctl::Sra>(1);
if (!wait_for<Sysctl::Sra>(1, delayer, 1000, 0)) {
PERR("soft reset all timed out (src != 1)");
return false;
}
return true;
}
void disable_irq()
{
write<Ise>(0);
write<Ie>(0);
write<Stat>(~0);
}
enum Bus_width { BUS_WIDTH_1, BUS_WIDTH_4 };
void bus_width(Bus_width bus_width)
{
switch (bus_width) {
case BUS_WIDTH_1:
write<Con::Dw8>(0);
write<Hctl::Dtw>(Hctl::Dtw::ONE_BIT);
break;
case BUS_WIDTH_4:
write<Con::Dw8>(0);
write<Hctl::Dtw>(Hctl::Dtw::FOUR_BITS);
break;
}
}
bool sd_bus_power_on(Delayer &delayer)
{
write<Hctl::Sdbp>(Hctl::Sdbp::POWER_ON);
if (!wait_for<Hctl::Sdbp>(1, delayer)) {
PERR("setting Hctl::Sdbp timed out");
return false;
}
return true;
}
void stop_clock()
{
write<Sysctl::Ce>(0);
}
enum Clock_divider { CLOCK_DIV_0, CLOCK_DIV_240 };
bool set_and_enable_clock(enum Clock_divider divider, Delayer &delayer)
{
write<Sysctl::Dto>(Sysctl::Dto::TCF_2_POW_27);
switch (divider) {
case CLOCK_DIV_0: write<Sysctl::Clkd>(0); break;
case CLOCK_DIV_240: write<Sysctl::Clkd>(240); break;
}
write<Sysctl::Ice>(1);
/* wait for clock to become stable */
if (!wait_for<Sysctl::Ics>(1, delayer)) {
PERR("clock enable timed out");
return false;
}
/* enable clock */
write<Sysctl::Ce>(1);
return true;
}
enum Voltage { VOLTAGE_3_0, VOLTAGE_1_8 };
void set_bus_power(Voltage voltage)
{
switch (voltage) {
case VOLTAGE_3_0:
write<Hctl::Sdvs>(Hctl::Sdvs::VOLTAGE_3_0);
break;
case VOLTAGE_1_8:
write<Hctl::Sdvs>(Hctl::Sdvs::VOLTAGE_1_8);
break;
}
write<Capa::Vs18>(1);
if (voltage == VOLTAGE_3_0)
write<Capa::Vs30>(1);
}
bool init_stream(Delayer &delayer)
{
write<Ie>(0x307f0033);
/* start initialization sequence */
write<Con::Init>(1);
write<Cmd>(0);
if (!wait_for<Stat::Cc>(1, delayer, 1000*1000, 0)) {
PERR("init stream timed out");
return false;
}
/* stop initialization sequence */
write<Con::Init>(0);
write<Stat>(~0);
read<Stat>();
return true;
}
struct Hl_sysconfig : Register<0x10, 32> { };
};
struct Omap4_hsmmc_controller : private Mmchs, public Sd_card::Host_controller
{
private:
Delayer &_delayer;
Sd_card::Card_info _card_info;
bool const _use_dma;
/*
* DMA memory for holding the ADMA descriptor table
*/
enum { ADMA_DESC_MAX_ENTRIES = 1024 };
Genode::Attached_ram_dataspace _adma_desc_ds;
Adma_desc::access_t * const _adma_desc;
Genode::addr_t const _adma_desc_phys;
Genode::Irq_connection _irq;
Genode::Signal_receiver _irq_rec;
Genode::Signal_context _irq_ctx;
Sd_card::Card_info _init()
{
using namespace Sd_card;
write<Sysconfig>(0x2015);
write<Hctl>(0x0);
set_bus_power(VOLTAGE_3_0);
if (!sd_bus_power_on(_delayer)) {
PERR("sd_bus_power failed");
}
disable_irq();
bus_width(BUS_WIDTH_1);
_delayer.usleep(10*1000);
stop_clock();
if (!set_and_enable_clock(CLOCK_DIV_240, _delayer)) {
PERR("set_clock failed");
throw Detection_failed();
}
if (!init_stream(_delayer)) {
PERR("sending the initialization stream failed");
throw Detection_failed();
}
write<Blk>(0);
_delayer.usleep(1000);
if (!issue_command(Go_idle_state())) {
PWRN("Go_idle_state command failed");
throw Detection_failed();
}
_delayer.usleep(2000);
if (!issue_command(Send_if_cond())) {
PWRN("Send_if_cond command failed");
throw Detection_failed();
}
if (read<Rsp10>() != 0x1aa) {
PERR("unexpected response of Send_if_cond command");
throw Detection_failed();
}
/*
* 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(0x18000, true))) {
PWRN("Sd_send_op_cond command failed");
throw Detection_failed();
}
if (Ocr::Busy::get(read<Rsp10>()))
break;
_delayer.usleep(1000);
}
if (i == 0) {
PERR("Sd_send_op_cond timed out, could no power-on SD card");
throw Detection_failed();
}
Card_info card_info = _detect();
/*
* Switch card to use 4 data signals
*/
if (!issue_command(Set_bus_width(Set_bus_width::Arg::Bus_width::FOUR_BITS),
card_info.rca())) {
PWRN("Set_bus_width(FOUR_BITS) command failed");
throw Detection_failed();
}
bus_width(BUS_WIDTH_4);
_delayer.usleep(10*1000);
stop_clock();
if (!set_and_enable_clock(CLOCK_DIV_0, _delayer)) {
PERR("set_clock failed");
throw Detection_failed();
}
/* enable master DMA */
write<Con::Dma_mns>(1);
/* enable IRQs */
write<Ie::Tc_enable>(1);
write<Ie::Cto_enable>(1);
write<Ise::Tc_sigen>(1);
write<Ise::Cto_sigen>(1);
return card_info;
}
/**
* Marshal ADMA descriptors according to block request
*
* \return false if block request is too large
*/
bool _setup_adma_descriptor_table(size_t block_count,
Genode::addr_t out_buffer_phys)
{
using namespace Sd_card;
/* reset ADMA offset to first descriptor */
write<Admasal>(_adma_desc_phys);
enum { BLOCK_SIZE = 512 /* bytes */ };
size_t const max_adma_request_size = 64*1024 - 4; /* bytes */
/*
* sanity check
*
* XXX An alternative to this sanity check would be to expose
* the maximum DMA transfer size to the driver and let the
* driver partition large requests into ones that are
* supported by the controller.
*/
if (block_count*BLOCK_SIZE > max_adma_request_size*ADMA_DESC_MAX_ENTRIES) {
PERR("Block request too large");
return false;
}
/*
* Each ADMA descriptor can transfer up to MAX_ADMA_REQUEST_SIZE
* bytes. If the request is larger, we generate a list of ADMA
* descriptors.
*/
size_t const total_bytes = block_count*BLOCK_SIZE;
/* number of bytes for which descriptors have been created */
addr_t consumed_bytes = 0;
for (int index = 0; consumed_bytes < total_bytes; index++) {
size_t const remaining_bytes = total_bytes - consumed_bytes;
/* clamp current request to maximum ADMA request size */
size_t const curr_bytes = Genode::min(max_adma_request_size,
remaining_bytes);
/*
* Assemble new ADMA descriptor
*/
Adma_desc::access_t desc = 0;
Adma_desc::Address::set(desc, out_buffer_phys + consumed_bytes);
Adma_desc::Length::set(desc, curr_bytes);
/* set action to transfer */
Adma_desc::Act1::set(desc, 0);
Adma_desc::Act2::set(desc, 1);
Adma_desc::Valid::set(desc, 1);
/*
* Let the last descriptor generate transfer-complete interrupt
*/
if (consumed_bytes + curr_bytes == total_bytes)
Adma_desc::Ent::set(desc, 1);
/* install descriptor into ADMA descriptor table */
_adma_desc[index] = desc;
consumed_bytes += curr_bytes;
}
return true;
}
bool _wait_for_transfer_complete()
{
if (!wait_for<Stat::Tc>(1, _delayer, 1000*1000, 0)
&& !wait_for<Stat::Tc>(1, _delayer)) {
PERR("Stat::Tc timed out");
return false;
}
/* clear transfer-completed bit */
write<Stat::Tc>(1);
return true;
}
bool _wait_for_bre()
{
if (!wait_for<Pstate::Bre>(1, _delayer, 1000*1000, 0)
&& !wait_for<Pstate::Bre>(1, _delayer)) {
PERR("Pstate::Bre timed out");
return false;
}
return true;
}
bool _wait_for_bwe()
{
if (!wait_for<Pstate::Bwe>(1, _delayer, 1000*1000, 0)
&& !wait_for<Pstate::Bwe>(1, _delayer)) {
PERR("Pstate::Bwe timed out");
return false;
}
return true;
}
bool _wait_for_transfer_complete_irq()
{
/*
* XXX For now, the driver works fully synchronous. We merely use
* the interrupt mechanism to yield CPU time to concurrently
* running processes.
*/
for (;;) {
/*
* We ack the IRQ first to implicitly active receiving
* IRQ signals when entering this loop for the first time.
*/
_irq.ack_irq();
_irq_rec.wait_for_signal();
/* check for transfer completion */
if (read<Stat::Tc>() == 1) {
/* clear transfer-completed bit */
write<Stat::Tc>(1);
if (read<Stat>() != 0)
PWRN("unexpected state (Stat: 0x%x Blen: 0x%x Nblk: %d)",
read<Stat>(), read<Blk::Blen>(), read<Blk::Nblk>());
return true;
}
PWRN("unexpected interrupt, Stat: 0x%08x", read<Stat>());
}
}
public:
enum { IRQ_NUMBER = Genode::Board_base::HSMMC_IRQ };
/**
* Constructor
*
* \param mmio_base local base address of MMIO registers
*/
Omap4_hsmmc_controller(Genode::addr_t const mmio_base, Delayer &delayer,
bool use_dma)
:
Mmchs(mmio_base), _delayer(delayer), _card_info(_init()),
_use_dma(use_dma),
_adma_desc_ds(Genode::env()->ram_session(),
ADMA_DESC_MAX_ENTRIES*sizeof(Adma_desc::access_t),
Genode::UNCACHED),
_adma_desc(_adma_desc_ds.local_addr<Adma_desc::access_t>()),
_adma_desc_phys(Genode::Dataspace_client(_adma_desc_ds.cap()).phys_addr()),
_irq(IRQ_NUMBER)
{
_irq.sigh(_irq_rec.manage(&_irq_ctx));
}
~Omap4_hsmmc_controller() { _irq_rec.dissolve(&_irq_ctx); }
/****************************************
** Sd_card::Host_controller interface **
****************************************/
bool _issue_command(Sd_card::Command_base const &command)
{
if (verbose)
PLOG("-> index=0x%08x, arg=0x%08x, rsp_type=%d",
command.index, command.arg, command.rsp_type);
if (!wait_for<Pstate::Cmdi>(0, _delayer)) {
PERR("wait for Pstate::Cmdi timed out");
return false;
}
/* write command argument */
write<Arg>(command.arg);
/* assemble command register */
Cmd::access_t cmd = 0;
Cmd::Index::set(cmd, command.index);
if (command.transfer != Sd_card::TRANSFER_NONE) {
Cmd::Dp::set(cmd);
Cmd::Bce::set(cmd);
Cmd::Msbs::set(cmd);
if (command.index == Sd_card::Read_multiple_block::INDEX
|| command.index == Sd_card::Write_multiple_block::INDEX) {
Cmd::Acen::set(cmd);
if (_use_dma)
Cmd::De::set(cmd);
}
/* set data-direction bit depending on the command */
bool const read = command.transfer == Sd_card::TRANSFER_READ;
Cmd::Ddir::set(cmd, read ? Cmd::Ddir::READ : Cmd::Ddir::WRITE);
}
Cmd::access_t rsp_type = 0;
switch (command.rsp_type) {
case Sd_card::RESPONSE_NONE: rsp_type = Cmd::Rsp_type::RESPONSE_NONE; break;
case Sd_card::RESPONSE_136_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_136_BIT; break;
case Sd_card::RESPONSE_48_BIT: rsp_type = Cmd::Rsp_type::RESPONSE_48_BIT; break;
case Sd_card::RESPONSE_48_BIT_WITH_BUSY: rsp_type = Cmd::Rsp_type::RESPONSE_48_BIT_WITH_BUSY; break;
}
Cmd::Rsp_type::set(cmd, rsp_type);
/* write command */
write<Cmd>(cmd);
bool result = false;
/* wait until command is completed, return false on timeout */
for (unsigned long i = 0; i < 1000*1000; i++) {
Stat::access_t const stat = read<Stat>();
if (Stat::Erri::get(stat)) {
PWRN("SD command error");
if (Stat::Cto::get(stat))
PWRN("timeout");
reset_cmd_line(_delayer);
write<Stat::Cc>(~0);
read<Stat>();
result = false;
break;
}
if (Stat::Cc::get(stat) == 1) {
result = true;
break;
}
}
if (verbose)
PLOG("<- %s", result ? "succeeded" : "timed out");
/* clear status of command-completed bit */
write<Stat::Cc>(1);
read<Stat>();
return result;
}
Sd_card::Card_info card_info() const
{
return _card_info;
}
Sd_card::Cid _read_cid()
{
Sd_card::Cid cid;
cid.raw_0 = read<Rsp10>();
cid.raw_1 = read<Rsp32>();
cid.raw_2 = read<Rsp54>();
cid.raw_3 = read<Rsp76>();
return cid;
}
Sd_card::Csd _read_csd()
{
Sd_card::Csd csd;
csd.csd0 = read<Rsp10>();
csd.csd1 = read<Rsp32>();
csd.csd2 = read<Rsp54>();
csd.csd3 = read<Rsp76>();
return csd;
}
unsigned _read_rca()
{
return Sd_card::Send_relative_addr::Response::Rca::get(read<Rsp10>());
}
/**
* Read data blocks from SD card
*
* \return true on success
*/
bool read_blocks(size_t block_number, size_t block_count, char *out_buffer)
{
using namespace Sd_card;
write<Blk::Blen>(0x200);
write<Blk::Nblk>(block_count);
if (!issue_command(Read_multiple_block(block_number))) {
PERR("Read_multiple_block failed, Stat: 0x%08x", read<Stat>());
return false;
}
size_t const num_accesses = block_count*512/sizeof(Data::access_t);
Data::access_t *dst = (Data::access_t *)(out_buffer);
for (size_t i = 0; i < num_accesses; i++) {
if (!_wait_for_bre())
return false;
*dst++ = read<Data>();
}
return _wait_for_transfer_complete();
}
/**
* Write data blocks to SD card
*
* \return true on success
*/
bool write_blocks(size_t block_number, size_t block_count, char const *buffer)
{
using namespace Sd_card;
write<Blk::Blen>(0x200);
write<Blk::Nblk>(block_count);
if (!issue_command(Write_multiple_block(block_number))) {
PERR("Write_multiple_block failed");
return false;
}
size_t const num_accesses = block_count*512/sizeof(Data::access_t);
Data::access_t const *src = (Data::access_t const *)(buffer);
for (size_t i = 0; i < num_accesses; i++) {
if (!_wait_for_bwe())
return false;
write<Data>(*src++);
}
return _wait_for_transfer_complete();
}
/**
* Read data blocks from SD card via master DMA
*
* \return true on success
*/
bool read_blocks_dma(size_t block_number, size_t block_count,
Genode::addr_t out_buffer_phys)
{
using namespace Sd_card;
write<Blk::Blen>(0x200);
write<Blk::Nblk>(block_count);
_setup_adma_descriptor_table(block_count, out_buffer_phys);
if (!issue_command(Read_multiple_block(block_number))) {
PERR("Read_multiple_block failed, Stat: 0x%08x", read<Stat>());
return false;
}
return _wait_for_transfer_complete_irq();
}
/**
* Write data blocks to SD card via master DMA
*
* \return true on success
*/
bool write_blocks_dma(size_t block_number, size_t block_count,
Genode::addr_t buffer_phys)
{
using namespace Sd_card;
write<Blk::Blen>(0x200);
write<Blk::Nblk>(block_count);
_setup_adma_descriptor_table(block_count, buffer_phys);
if (!issue_command(Write_multiple_block(block_number))) {
PERR("Write_multiple_block failed");
return false;
}
return _wait_for_transfer_complete_irq();
}
};
#endif /* _MMCHS_H_ */