/*
 * \brief  OMAP4 MMCHS controller registers
 * \author Norman Feske
 * \date   2012-07-03
 */

/*
 * Copyright (C) 2012-2013 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>

/* 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;

		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 (;;) {
				_irq.wait_for_irq();

				/* 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 = 83 + 32 };

		/**
		 * 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),
			              false),
			_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)
		{ }


		/****************************************
		 ** 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_ */
