[PATCH] SDHC driver for PowerPC QoriQ

Nickolay Semyonov nbkolchin at gmail.com
Mon Jul 25 01:47:42 UTC 2016


From: Nickolay Semyonov <snob at wolpike.com>

Heavily tested with iozone.

---
 c/src/lib/libbsp/powerpc/qoriq/Makefile.am   |    3 +
 c/src/lib/libbsp/powerpc/qoriq/esdhc/esdhc.c | 1215 ++++++++++++++++++++++++++
 c/src/lib/libbsp/powerpc/qoriq/include/bsp.h |    2 +
 3 files changed, 1220 insertions(+)
 create mode 100644 c/src/lib/libbsp/powerpc/qoriq/esdhc/esdhc.c

diff --git a/c/src/lib/libbsp/powerpc/qoriq/Makefile.am b/c/src/lib/libbsp/powerpc/qoriq/Makefile.am
index dea5964..44a67b4 100644
--- a/c/src/lib/libbsp/powerpc/qoriq/Makefile.am
+++ b/c/src/lib/libbsp/powerpc/qoriq/Makefile.am
@@ -109,6 +109,9 @@ libbsp_a_SOURCES += shmsupp/lock.S \
 	shmsupp/intercom.c \
 	shmsupp/intercom-mpci.c
 
+# ESDHC
+libbsp_a_SOURCES += esdhc/esdhc.c
+
 libbsp_a_LIBADD = ../../../libcpu/@RTEMS_CPU@/shared/cpuIdent.rel \
 	../../../libcpu/@RTEMS_CPU@/shared/cache.rel \
 	../../../libcpu/@RTEMS_CPU@/@exceptions@/rtems-cpu.rel \
diff --git a/c/src/lib/libbsp/powerpc/qoriq/esdhc/esdhc.c b/c/src/lib/libbsp/powerpc/qoriq/esdhc/esdhc.c
new file mode 100644
index 0000000..ec06837
--- /dev/null
+++ b/c/src/lib/libbsp/powerpc/qoriq/esdhc/esdhc.c
@@ -0,0 +1,1215 @@
+/****************************************************************************************
+ * QorIQ ESDHC memory card driver for RTEMS.
+ *
+ * Copyright (c) 2016 Wolpike LTD.
+ * Author: Evgeniy Bobkov evgen at wolpike.com
+ *
+ * At the moment this driver only supports the SD cards, MMC cards are not supported.
+ ****************************************************************************************/
+
+#define RTEMS_STATUS_CHECKS_USE_PRINTK
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/diskdevs.h>
+#include <rtems/blkdev.h>
+#include <rtems/status-checks.h>
+#include <errno.h>
+#include <rtems/irq.h>
+#include <bsp/qoriq.h>
+#include <bsp.h>
+#include <sys/endian.h>
+
+#ifndef RTEMS_BSP_ESDHC_MEMCARD_DEVICE_PATH
+#define RTEMS_BSP_ESDHC_MEMCARD_DEVICE_PATH "/dev/memcard"
+#endif
+
+/*
+ * Set to: 0 - no debugging messages, 1 - only error and some init messages,
+ * 2 - all init mesages, 3 - print each read/write request from upper layer,
+ * 4 - print each SD command, 5 - print all transferred data.
+ */
+#define DEBUG                     1
+
+/* Use DMA transfers (1 = DMA, 0 = PIO). */
+#define USE_DMA                   1
+
+/*
+ * If SD read or write command fails (for example due to data timeout),
+ * we can retry the attempt. Attention: minimal value for this macro is 1,
+ * because it includes the very first attempt.
+ */
+#define MAX_READ_WRITE_RETRIES    5
+
+/* Measure the delays of operations inside esdhc_exec_command() function */
+#define MEASURE_EXECCMD_DELAYS    0
+
+/*
+ * Check that write operation was ok by reading back what was written
+ * to SD card and comparing the write and readback buffers.
+ */
+#define CHECK_WRITE_BY_READBACK   0
+
+/* Run the card read speed test after ESDHC initialization */
+#define RUN_CARD_READ_SPEED_TEST  0
+
+/* Run the card write speed test after ESDHC initialization. SD CARD DATA WILL BE DESTROYED! */
+#define RUN_CARD_WRITE_SPEED_TEST 0
+
+/*** Standard SD commands ***************************************************************/
+
+/* These bits form the command type within the command spec */
+#define SD_R_CRC                  0x0100 /* CRC presents */
+#define SD_R_CMD_CODE             0x0200 /* Response must contain the same command code as the request */
+#define SD_R_NORESP               0x0400 /* No response is expected */
+#define SD_R_136                  0x0800 /* Response length is 136 bits */
+#define SD_R_48                   0x1000 /* Response length is 48 bits */
+#define SD_R_48_BUSY              0x2000 /* Response length is 48 bits, check busy after response */
+#define SD_T_READ                 0x4000 /* This is a read command */
+#define SD_T_WRITE                0x8000 /* This is a write command */
+
+/* Command response types */
+#define SD_R0                     (SD_R_NORESP)
+#define SD_R1                     (SD_R_CRC | SD_R_CMD_CODE | SD_R_48)
+#define SD_R1b                    (SD_R_CRC | SD_R_CMD_CODE | SD_R_48_BUSY)
+#define SD_R2                     (SD_R_CRC | SD_R_136)
+#define SD_R3                     (SD_R_48)
+#define SD_R4                     (SD_R_48)
+#define SD_R5                     (SD_R_CRC | SD_R_CMD_CODE | SD_R_48)
+#define SD_R6                     (SD_R_CRC | SD_R_CMD_CODE | SD_R_48)
+#define SD_R7                     (SD_R_CRC | SD_R_CMD_CODE | SD_R_48)
+
+/* Command specs */
+#define SD_CMD0                   (0  | SD_R0)
+#define SD_CMD2                   (2  | SD_R2)
+#define SD_CMD3                   (3  | SD_R1)
+#define SD_CMD6                   (6  | SD_R1 | SD_T_READ)
+#define SD_CMD7                   (7  | SD_R1b)
+#define SD_CMD8                   (8  | SD_R7)
+#define SD_CMD9                   (9  | SD_R2)
+#define SD_CMD16                  (16 | SD_R1)
+#define SD_CMD17                  (17 | SD_R1 | SD_T_READ)
+#define SD_CMD18                  (18 | SD_R1 | SD_T_READ)
+#define SD_CMD24                  (24 | SD_R1 | SD_T_WRITE)
+#define SD_CMD25                  (25 | SD_R1 | SD_T_WRITE)
+#define SD_CMD55                  (55 | SD_R1)
+#define SD_ACMD6                  (6  | SD_R1)
+#define SD_ACMD41                 (41 | SD_R3)
+#define SD_ACMD51                 (51 | SD_R1 | SD_T_READ)
+
+/*** Certain bits of certain hardware registers *****************************************/
+
+#define BLKATTR_SIZE_MASK         0x0000ffff
+#define BLKATTR_SIZE_SHIFT        0
+#define BLKATTR_CNT_MASK          0xffff0000
+#define BLKATTR_CNT_SHIFT         16
+
+#define XFERTYP_DMAEN             0x00000001
+#define XFERTYP_BCEN              0x00000002
+#define XFERTYP_AC12EN            0x00000004
+#define XFERTYP_DTDSEL            0x00000010
+#define XFERTYP_MSBSEL            0x00000020
+#define XFERTYP_RSPTYP_NORESP     0x00000000
+#define XFERTYP_RSPTYP_136        0x00010000
+#define XFERTYP_RSPTYP_48         0x00020000
+#define XFERTYP_RSPTYP_48_BUSY    0x00030000
+#define XFERTYP_CCCEN             0x00080000
+#define XFERTYP_CICEN             0x00100000
+#define XFERTYP_DPSEL             0x00200000
+#define XFERTYP_CMDTYP_NORMAL     0x00000000
+#define XFERTYP_CMDTYP_ABORT      0x00c00000
+#define XFERTYP_CMD_MASK          0x3f000000
+#define XFERTYP_CMD_SHIFT         24
+
+#define PRSSTAT_CIHB              0x00000001
+#define PRSSTAT_CDIHB             0x00000002
+#define PRSSTAT_DLA               0x00000004
+#define PRSSTAT_IPGOFF            0x00000010
+#define PRSSTAT_HCKOFF            0x00000020
+#define PRSSTAT_PEROFF            0x00000040
+#define PRSSTAT_SDOFF             0x00000080
+#define PRSSTAT_WTA               0x00000100
+#define PRSSTAT_RTA               0x00000200
+#define PRSSTAT_BWEN              0x00000400
+#define PRSSTAT_BREN              0x00000800
+#define PRSSTAT_CINS              0x00010000
+#define PRSSTAT_CDPL              0x00040000
+#define PRSSTAT_WPSPL             0x00080000
+#define PRSSTAT_CLSL              0x00800000
+#define PRSSTAT_DAT0_MASK         0xff000000
+#define PRSSTAT_DAT0_SHIFT        24
+
+#define PROCTL_DTW_MASK           0x00000006
+#define PROCTL_DTW_4BIT           0x00000002
+#define PROCTL_DTW_8BIT           0x00000004
+
+#define SYSCTL_IPGEN              0x00000001
+#define SYSCTL_HCKEN              0x00000002
+#define SYSCTL_PEREN              0x00000004
+#define SYSCTL_DVS_MASK           0x000000f0
+#define SYSCTL_DVS_SHIFT          4
+#define SYSCTL_SDCLKFS_MASK       0x0000ff00
+#define SYSCTL_SDCLKFS_SHIFT      8
+#define SYSCTL_DTOCV_MASK         0x000f0000
+#define SYSCTL_DTOCV_SHIFT        16
+#define SYSCTL_RSTA               0x01000000
+#define SYSCTL_RSTC               0x02000000
+#define SYSCTL_RSTD               0x04000000
+#define SYSCTL_INITA              0x08000000
+
+#define IRQSTAT_CC                0x00000001
+#define IRQSTAT_TC                0x00000002
+#define IRQSTAT_BGE               0x00000004
+#define IRQSTAT_DINT              0x00000008
+#define IRQSTAT_BWR               0x00000010
+#define IRQSTAT_BRR               0x00000020
+#define IRQSTAT_CINS              0x00000040
+#define IRQSTAT_CRM               0x00000080
+#define IRQSTAT_CTOE              0x00010000
+#define IRQSTAT_CCE               0x00020000
+#define IRQSTAT_CEBE              0x00040000
+#define IRQSTAT_CIE               0x00080000
+#define IRQSTAT_DTOE              0x00100000
+#define IRQSTAT_DCE               0x00200000
+#define IRQSTAT_DEBE              0x00400000
+#define IRQSTAT_AC12E             0x01000000
+#define IRQSTAT_DMAE              0x10000000
+
+#define HOSTCAPBLT_DMAS           0x00400000
+#define HOSTCAPBLT_HSS            0x00200000
+
+#define WML_RD_MASK               0x000000ff
+#define WML_RD_SHIFT              0
+#define WML_RD_MAX                0x10
+#define WML_WR_MASK               0x00ff0000
+#define WML_WR_SHIFT              16
+#define WML_WR_MAX                0x80
+
+/****************************************************************************************/
+
+#define BLOCK_SIZE                512
+
+#define NUM_DMA_BLOCKS            128
+
+/*
+ * These checks are required to guarantee the esdhc_memcard_disk_block_read_write() algorithm
+ * will work with any user options.
+ */
+#if (NUM_DMA_BLOCKS < CONFIGURE_BDBUF_MAX_READ_AHEAD_BLOCKS)
+#undef  NUM_DMA_BLOCKS
+#define NUM_DMA_BLOCKS            CONFIGURE_BDBUF_MAX_READ_AHEAD_BLOCKS
+#endif
+#if (NUM_DMA_BLOCKS < CONFIGURE_BDBUF_MAX_WRITE_BLOCKS)
+#undef  NUM_DMA_BLOCKS
+#define NUM_DMA_BLOCKS            CONFIGURE_BDBUF_MAX_WRITE_BLOCKS
+#endif
+
+
+/* Contiguous buffer for DMA transfers */
+static uint8_t  dma_blocks[NUM_DMA_BLOCKS][BLOCK_SIZE] __attribute__ ((aligned(BLOCK_SIZE)));
+
+/* We can store this info in global variables because there is only one ESDHC interface in the system */
+static uint32_t high_capacity;
+static uint32_t block_count;
+
+/* Semaphore to protect the execution of hardware operations */
+static rtems_id esdhc_sema_id = RTEMS_ID_NONE;
+
+
+static void check_write_by_readback(rtems_blkdev_request *wr);
+
+static inline void dbg_printk(int dbg_level, const char *format, ...)
+{
+#if (DEBUG > 0)
+  if (dbg_level <= DEBUG)
+  {
+    va_list args;
+
+    va_start(args, format);
+    vprintk(format, args);
+    va_end(args);
+  }
+#endif
+}
+
+#if (DEBUG >= 5)
+static void print_block(const uint8_t *data, uint32_t len, uint32_t block, int do_write)
+{
+  int i;
+  unsigned char c;
+  printk("%s data: block %u, buf %p:\n", do_write ? "Writing" : "Reading", block, data);
+
+#if 0
+  /* Print as ASCII or HEX (depending on symbol code) */
+  for (i = 0; i < len; i++)
+  {
+    c = data[i];
+    if ((c >= 0x20) && (c <= 0x7e))
+      printk("%c", c);
+    else
+      printk("%02x ", c);
+    if (((i + 1) % 64) == 0)
+      printk("\n");
+  }
+  printk("\n");
+#endif
+
+  /* Print again only as HEX */
+  for (i = 0; i < len; i++)
+  {
+    c = data[i];
+    printk("%02x ", c);
+    if (((i + 1) % 64) == 0)
+      printk("\n");
+  }
+  printk("\n");
+}
+#endif
+
+#if MEASURE_EXECCMD_DELAYS || RUN_CARD_READ_SPEED_TEST || RUN_CARD_WRITE_SPEED_TEST
+static long long timeval_diff_us(struct timeval *s, struct timeval *e)
+{
+  return 1000000LL * (e->tv_sec - s->tv_sec) + (e->tv_usec - s->tv_usec);
+}
+#endif
+
+
+static void esdhc_memcard_set_clock_divider(uint32_t sdclkfs, uint32_t dvs)
+{
+  qoriq.esdhc.sysctl = (qoriq.esdhc.sysctl & ~(SYSCTL_SDCLKFS_MASK | SYSCTL_DVS_MASK)) |
+                       (sdclkfs << SYSCTL_SDCLKFS_SHIFT) | (dvs << SYSCTL_DVS_SHIFT);
+  dbg_printk(2, "%s: 0x%08x\n", __func__, qoriq.esdhc.sysctl);
+}
+
+static void esdhc_memcard_set_clock(uint32_t desired_clock_hz)
+{
+  /* Base clock is bus_clock/2 */
+  uint32_t desired_div = BSP_bus_frequency / 2 / desired_clock_hz;
+  uint32_t power2_div = 1;
+
+  uint32_t sdclkfs, dvs;
+
+  while(power2_div < desired_div)
+    power2_div *= 2;
+
+  if (power2_div <= 0x100)
+  {
+    sdclkfs = power2_div / 2;
+    dvs = 0;
+  }
+  else
+  {
+    sdclkfs = 0x80;
+    dvs = power2_div / 0x100 - 1;
+  }
+
+  dbg_printk(2, "%s: BSP_bus_frequency %u, desired_div 0x%x, power2_div 0x%x, sdclkfs 0x%x dvs 0x%x\n",
+            __func__, BSP_bus_frequency, desired_div, power2_div, sdclkfs, dvs);
+
+  esdhc_memcard_set_clock_divider(sdclkfs, dvs);
+}
+
+/* Find out whether it's a data read command */
+static inline int is_read_cmd(uint32_t cmd_type)
+{
+  return (cmd_type & SD_T_READ);
+}
+
+/* Find out whether it's a data write command */
+static inline int is_write_cmd(uint32_t cmd_type)
+{
+  return (cmd_type & SD_T_WRITE);
+}
+
+/* Compute the XFERTYP value for the specified command */
+static uint32_t compute_xfertyp_for_cmd(uint8_t cmd, uint32_t cmd_type, uint32_t num_data_blocks)
+{
+  uint32_t xfertyp = (cmd << XFERTYP_CMD_SHIFT);
+
+  if (is_read_cmd(cmd_type))
+    xfertyp |= XFERTYP_DPSEL | XFERTYP_DTDSEL;
+  else if (is_write_cmd(cmd_type))
+    xfertyp |= XFERTYP_DPSEL;
+
+#if USE_DMA
+  if (is_read_cmd(cmd_type) || is_write_cmd(cmd_type))
+    xfertyp |= XFERTYP_DMAEN;
+#endif
+
+  if (num_data_blocks > 1)
+    xfertyp |= XFERTYP_MSBSEL | XFERTYP_BCEN | XFERTYP_AC12EN;
+
+  if (cmd_type & SD_R_CRC)
+    xfertyp |= XFERTYP_CCCEN;
+
+  if (cmd_type & SD_R_CMD_CODE)
+    xfertyp |= XFERTYP_CICEN;
+
+  if (cmd_type & SD_R_NORESP)
+    xfertyp |= XFERTYP_RSPTYP_NORESP;
+  else if (cmd_type & SD_R_136)
+    xfertyp |= XFERTYP_RSPTYP_136;
+  else if (cmd_type & SD_R_48)
+    xfertyp |= XFERTYP_RSPTYP_48;
+  else if (cmd_type & SD_R_48_BUSY)
+    xfertyp |= XFERTYP_RSPTYP_48_BUSY;
+
+  return xfertyp;
+}
+
+#if !USE_DMA
+static void
+esdhc_memcard_pio_read_write(uint8_t *data, uint32_t num_data_blocks, uint32_t data_block_size, int do_write)
+{
+  uint32_t size = num_data_blocks * data_block_size;
+  uint32_t prsstat = do_write ? PRSSTAT_BWEN : PRSSTAT_BREN;
+  uint32_t burst_max = do_write ? WML_WR_MAX : WML_RD_MAX;
+  uint32_t burst = 0; /* Number of 4-bytes words remaining in current burst */
+
+  while (size)
+  {
+    if (burst == 0)
+    {
+      /* TODO: check errors in irqstat in order to avoid the evernal loop? */
+      while ((qoriq.esdhc.prsstat & prsstat) == 0);
+      burst = burst_max;
+    }
+
+    if (do_write)
+    {
+      qoriq.esdhc.datport = htole32(*((uint32_t *)data));
+    }
+    else /* read */
+    {
+      *(uint32_t *)data = le32toh(qoriq.esdhc.datport);
+    }
+
+    data += 4;
+    size -= 4;
+    burst--;
+  }
+}
+#endif
+
+/* Execute an SD command */
+static rtems_status_code esdhc_exec_command(uint32_t cmd_spec, uint32_t arg,
+                                            uint8_t *data/*write in/read out*/,
+                                            uint32_t num_data_blocks, uint32_t data_block_size,
+                                            uint32_t *resp/*out*/)
+{
+  uint8_t cmd = cmd_spec & 0x3f;
+  uint32_t cmd_type = cmd_spec & ~0x3f;
+  rtems_status_code sc = RTEMS_SUCCESSFUL;
+  uint32_t xfertyp;
+  uint32_t irqstat;
+  uint32_t sysctl;
+  uint32_t cmdrsp0, cmdrsp1, cmdrsp2, cmdrsp3;
+#if MEASURE_EXECCMD_DELAYS
+  struct timeval t[5];
+#endif
+
+  dbg_printk(4, "%s: start cmd %u (arg 0x%08x, data %p nblocks %u block_size %u)\n",
+             __func__, cmd, arg, data, num_data_blocks, data_block_size);
+
+#if (DEBUG >= 5)
+  if (is_write_cmd(cmd_type))
+  {
+    uint32_t i;
+    for (i = 0; i < num_data_blocks; i++)
+      print_block(data + i * data_block_size, data_block_size, arg + i, is_write_cmd(cmd_type));
+  }
+#endif
+
+#if MEASURE_EXECCMD_DELAYS
+  memset(t, 0, sizeof(t));
+  gettimeofday(&t[0], NULL);
+#endif
+
+  xfertyp = compute_xfertyp_for_cmd(cmd, cmd_type, num_data_blocks);
+
+  /* Wait for DLA, CDIHB and CIHB bits to clear */
+  while (qoriq.esdhc.prsstat & (PRSSTAT_DLA | PRSSTAT_CDIHB | PRSSTAT_CIHB));
+
+  /* Setup the data registers */
+  if (is_read_cmd(cmd_type) || is_write_cmd(cmd_type))
+  {
+    if (!data) /* Sanity check */
+    {
+      sc = RTEMS_INVALID_ADDRESS;
+      goto finish;
+    }
+    /* Set the watermarks */
+    qoriq.esdhc.wml = (WML_WR_MAX << WML_WR_SHIFT) | (WML_RD_MAX << WML_RD_SHIFT);
+    /* Set amount of trasferred data */
+    qoriq.esdhc.blkattr = (num_data_blocks << BLKATTR_CNT_SHIFT) | (data_block_size << BLKATTR_SIZE_SHIFT);
+    /* Set the data timeout */
+    qoriq.esdhc.sysctl = (qoriq.esdhc.sysctl & ~SYSCTL_DTOCV_MASK) | (0xe << SYSCTL_DTOCV_SHIFT);
+#if USE_DMA
+    /* Set the DMA buffer address */
+    qoriq.esdhc.dsaddr = (uint32_t)data;
+    /* Invalidate or flush the cache lines depending on read or write command */
+    if (is_read_cmd(cmd_type))
+    {
+      /* We need to flush before invalidation, because there can be valid _adjacent_ data */
+      rtems_cache_flush_multiple_data_lines(data, num_data_blocks * data_block_size);
+      rtems_cache_invalidate_multiple_data_lines(data, num_data_blocks * data_block_size);
+    }
+    else
+      rtems_cache_flush_multiple_data_lines(data, num_data_blocks * data_block_size);
+#endif
+  }
+
+#if MEASURE_EXECCMD_DELAYS
+  gettimeofday(&t[1], NULL);
+#endif
+
+  /* Mask all interrupts */
+  qoriq.esdhc.irqsigen = 0;
+  /* Set the command argument */
+  qoriq.esdhc.cmdarg = arg;
+  /* Set XFERTYP register to start the command */
+  qoriq.esdhc.xfertyp = xfertyp;
+
+  /* Wait till the command is completed or timeout occurs */
+  while ((qoriq.esdhc.irqstat & (IRQSTAT_CC | IRQSTAT_CTOE)) == 0);
+
+#if MEASURE_EXECCMD_DELAYS
+  gettimeofday(&t[2], NULL);
+#endif
+
+  irqstat = qoriq.esdhc.irqstat;
+
+  if (irqstat & IRQSTAT_CTOE)
+  {
+    dbg_printk(1, "%s: cmd %u command timeout irqstat 0x%08x\n", __func__, cmd, irqstat);
+    sc = RTEMS_TIMEOUT;
+    goto done;
+  }
+
+  if (irqstat & (IRQSTAT_CIE | IRQSTAT_CEBE | IRQSTAT_CCE))
+  {
+    dbg_printk(1, "%s: cmd %u command error irqstat 0x%08x\n", __func__, cmd, irqstat);
+    sc = RTEMS_IO_ERROR;
+    goto done;
+  }
+
+  if (cmd_type & SD_R_136)
+  {
+    cmdrsp0 = qoriq.esdhc.cmdrsp0;
+    cmdrsp1 = qoriq.esdhc.cmdrsp1;
+    cmdrsp2 = qoriq.esdhc.cmdrsp2;
+    cmdrsp3 = qoriq.esdhc.cmdrsp3;
+    if (resp)
+    {
+      resp[0] = (cmdrsp3 << 8) | (cmdrsp2 >> 24);
+      resp[1] = (cmdrsp2 << 8) | (cmdrsp1 >> 24);
+      resp[2] = (cmdrsp1 << 8) | (cmdrsp0 >> 24);
+      resp[3] = (cmdrsp0 << 8);
+    }
+    dbg_printk(4, "%s: cmd %u cmdrsp %08x %08x %08x %08x\n", __func__,
+               cmd, cmdrsp0, cmdrsp1, cmdrsp2, cmdrsp3);
+  }
+  else
+  {
+    cmdrsp0 = qoriq.esdhc.cmdrsp0;
+    if (resp)
+      resp[0] = cmdrsp0;
+    dbg_printk(4, "%s: cmd %u cmdrsp %08x\n", __func__, cmd, cmdrsp0);
+  }
+
+  if (data)
+  {
+    /* Wait till all data is transferred */
+#if USE_DMA
+    do {
+      irqstat = qoriq.esdhc.irqstat;
+
+      if (irqstat & IRQSTAT_DTOE)
+      {
+        dbg_printk(1, "%s: cmd %u data timeout irqstat 0x%08x\n", __func__, cmd, irqstat);
+        sc = RTEMS_TIMEOUT;
+        goto done;
+      }
+
+      if (irqstat & (IRQSTAT_DEBE | IRQSTAT_DCE | IRQSTAT_DTOE | IRQSTAT_DMAE))
+      {
+        dbg_printk(1, "%s: cmd %u data error irqstat 0x%08x\n", __func__, cmd, irqstat);
+        sc = RTEMS_IO_ERROR;
+        goto done;
+      }
+
+    } while ((irqstat & (IRQSTAT_TC | IRQSTAT_DINT)) != (IRQSTAT_TC | IRQSTAT_DINT));
+
+    /* Invalidate once again just in case if CPU prefetched some data */
+    if (is_read_cmd(cmd_type))
+      rtems_cache_invalidate_multiple_data_lines(data, num_data_blocks * data_block_size);
+#else
+    esdhc_memcard_pio_read_write(data, num_data_blocks, data_block_size, is_write_cmd(cmd_type));
+#endif
+  }
+
+#if MEASURE_EXECCMD_DELAYS
+  gettimeofday(&t[3], NULL);
+#endif
+
+done:
+  /* Clear all bits */
+  qoriq.esdhc.irqstat = 0xffffffff;
+
+  /* Reset the SDHC_CMD and SDHC_DAT lines if error happened */
+  if (sc != RTEMS_SUCCESSFUL)
+  {
+    if (is_read_cmd(cmd_type) || is_write_cmd(cmd_type))
+      sysctl = SYSCTL_RSTC | SYSCTL_RSTD;
+    else
+      sysctl = SYSCTL_RSTC;
+
+    qoriq.esdhc.sysctl |= sysctl;
+
+    while ((qoriq.esdhc.sysctl & sysctl) != 0);
+  }
+
+finish:
+#if MEASURE_EXECCMD_DELAYS
+  gettimeofday(&t[4], NULL);
+  printk("%s: cmd %u delays %llu %llu %llu %llu %llu\n", __func__, cmd,
+    timeval_diff_us(&t[0], &t[1]),
+    timeval_diff_us(&t[1], &t[2]),
+    timeval_diff_us(&t[2], &t[3]),
+    timeval_diff_us(&t[3], &t[4]),
+    timeval_diff_us(&t[0], &t[4]));
+#endif
+
+#if (DEBUG >= 5)
+  if (is_read_cmd(cmd_type))
+  {
+    uint32_t i;
+    for (i = 0; i < num_data_blocks; i++)
+      print_block(data + i * data_block_size, data_block_size, arg + i, is_write_cmd(cmd_type));
+  }
+#endif
+
+  if (sc == RTEMS_SUCCESSFUL)
+  {
+    dbg_printk(4, "%s: finish cmd %u (arg %u, data %p nblocks %u block_size %u), ok\n",
+               __func__, cmd, arg, data, num_data_blocks, data_block_size);
+  }
+  else
+  {
+    dbg_printk(1, "%s: finish cmd %u (arg %u, data %p nblocks %u block_size %u), failed (sc %u)\n",
+               __func__, cmd, arg, data, num_data_blocks, data_block_size, sc);
+  }
+
+  return sc;
+}
+
+static int esdhc_memcard_disk_block_read_write(rtems_blkdev_request *r, int do_write)
+{
+  rtems_status_code sc = RTEMS_SUCCESSFUL;
+  uint32_t cmd_spec;
+  uint32_t response[4];
+  uint32_t start_bufidx;
+  uint32_t bufidx = 0;
+  uint32_t start_block;
+  uint32_t num_blocks;
+  uint32_t blocks_per_buf;
+  uint32_t offset;
+  uint32_t i;
+
+#if (DEBUG >= 3)
+  dbg_printk(3, "%s.1: %s bufnum %u\n", __func__, do_write ? "write" : "read", r->bufnum);
+  dbg_printk(3, "%s.2: blocks ", __func__);
+  for (i = 0; i < r->bufnum; i++)
+    dbg_printk(3, "[%u %u/%u] ", r->bufs[i].block, r->bufs[i].length, r->bufs[i].length / BLOCK_SIZE);
+  dbg_printk(3, "\n");
+#endif
+
+  while (bufidx < r->bufnum)
+  {
+    start_bufidx = bufidx;
+    start_block = r->bufs[bufidx].block;
+
+    /* The num_blocks variable counts the number of adjacent blocks */
+    for (num_blocks = 0;
+         (bufidx < r->bufnum) && (num_blocks < NUM_DMA_BLOCKS);
+         num_blocks += blocks_per_buf, bufidx++)
+    {
+      if (r->bufs[bufidx].block != start_block + num_blocks)
+        break; /* Blocks in this buffer are not adjacent with previous, break */
+
+      /* Paranoid check */
+      if ((r->bufs[bufidx].length % BLOCK_SIZE) != 0)
+      {
+        dbg_printk(1, "%s: buffer length=%u is not multiple of BLOCK_SIZE=%u\n", r->bufs[bufidx].length, BLOCK_SIZE);
+        sc = RTEMS_INVALID_SIZE;
+        goto finish;
+      }
+
+      blocks_per_buf = r->bufs[bufidx].length / BLOCK_SIZE;
+      if (blocks_per_buf > NUM_DMA_BLOCKS - num_blocks)
+        break; /* No free space left in dma_blocks[] for all blocks of this buffer, break */
+
+      if (do_write)
+      {
+        /* Copy the current buffer to the appropriate place in the contiguous DMA buffer */
+        memcpy(dma_blocks[num_blocks], r->bufs[bufidx].buffer, r->bufs[bufidx].length);
+      }
+    }
+
+    if (num_blocks == 1)
+    {
+      /*CMD17 - read one block, CMD24 - write one block */
+      cmd_spec = do_write ? SD_CMD24 : SD_CMD17;
+    }
+    else
+    {
+      /*CMD18 - read multiple blocks, CMD25 - write multiple blocks */
+      cmd_spec = do_write ? SD_CMD25 : SD_CMD18;
+    }
+
+    offset = start_block;   /* offset in blocks */
+    if (!high_capacity)
+      offset *= BLOCK_SIZE; /* offset in bytes */
+
+#if (DEBUG >= 3)
+    dbg_printk(3, "%s.3: bufnum %u num_blocks %u start_bufidx %u bufidx %u start_block %u offset %u\n",
+               __func__, r->bufnum, num_blocks, start_bufidx, bufidx, start_block, offset);
+#endif
+
+    sc = esdhc_exec_command(cmd_spec, offset, (uint8_t *)dma_blocks, num_blocks, BLOCK_SIZE, response);
+    if (sc != RTEMS_SUCCESSFUL)
+      break;
+
+    if (!do_write)
+    {
+      /* Read out the data from the contiguous DMA buffer */
+      num_blocks = 0;
+      for (i = start_bufidx; i < bufidx; i++)
+      {
+        memcpy(r->bufs[i].buffer, dma_blocks[num_blocks], r->bufs[i].length);
+        num_blocks += r->bufs[i].length / BLOCK_SIZE;
+      }
+    }
+  }
+
+finish:
+  rtems_blkdev_request_done(r, sc);
+
+  if (sc != RTEMS_SUCCESSFUL)
+  {
+    errno = EIO;
+    return -EIO;
+  }
+  else
+    return 0;
+}
+
+static int esdhc_memcard_disk_block_read_write_retriable(rtems_blkdev_request *r, int do_write)
+{
+  uint32_t i;
+  int rc;
+
+  rc = rtems_semaphore_obtain(esdhc_sema_id, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+  if (rc != RTEMS_SUCCESSFUL)
+  {
+    errno = EACCES;
+    return -EACCES;
+  }
+
+  for (i = 0; i < MAX_READ_WRITE_RETRIES; i++)
+  {
+    rc = esdhc_memcard_disk_block_read_write(r, do_write);
+    if (rc == 0)
+      break; /* Operiation succeeded */
+    else if (i < MAX_READ_WRITE_RETRIES - 1)
+      dbg_printk(1, "%s: operation failed with rc=%d, retrying\n", __func__, rc);
+  }
+
+#if CHECK_WRITE_BY_READBACK
+  if (do_write && (rc == 0))
+    check_write_by_readback(r);
+#endif
+
+  rtems_semaphore_release(esdhc_sema_id);
+
+  return rc;
+}
+
+static int esdhc_memcard_disk_block_read(rtems_blkdev_request *r)
+{
+  return esdhc_memcard_disk_block_read_write_retriable(r, 0);
+}
+
+static int esdhc_memcard_disk_block_write(rtems_blkdev_request *r)
+{
+  return esdhc_memcard_disk_block_read_write_retriable(r, 1);
+}
+
+
+#if CHECK_WRITE_BY_READBACK
+static void init_dummy_sg_buffer(rtems_blkdev_sg_buffer *sg, uint32_t block,
+                                 uint32_t length, uint8_t pattern)
+{
+  sg->block = block;
+  sg->length = length;
+  sg->buffer = calloc(length, 1);
+  memset(sg->buffer, pattern, length);
+}
+
+static void cleanup_dummy_sg_buffer(rtems_blkdev_sg_buffer *sg)
+{
+  free(sg->buffer);
+}
+
+static void dummy_request_done(rtems_blkdev_request *req, rtems_status_code sc)
+{
+}
+
+static rtems_blkdev_request *create_dummy_request(const rtems_blkdev_request *wr)
+{
+  rtems_blkdev_request *rd;
+  uint32_t i;
+
+  rd = calloc(sizeof(*rd) + sizeof(rtems_blkdev_sg_buffer) * wr->bufnum, 1);
+  rd->bufnum = wr->bufnum;
+  rd->done = dummy_request_done;
+
+  for (i = 0; i < rd->bufnum; i++)
+    init_dummy_sg_buffer(&rd->bufs[i], wr->bufs[i].block, wr->bufs[i].length, 0xfe);
+
+  return rd;
+}
+
+static void destroy_dummy_request(rtems_blkdev_request *rd)
+{
+  uint32_t i;
+
+  for (i = 0; i < rd->bufnum; i++)
+    cleanup_dummy_sg_buffer(&rd->bufs[i]);
+
+  free(rd);
+}
+
+static void check_write_by_readback(rtems_blkdev_request *wr)
+{
+  rtems_blkdev_request *rd;
+  uint32_t i;
+
+  rd = create_dummy_request(wr);
+
+  esdhc_memcard_disk_block_read_write(rd, 0);
+
+  for (i = 0; i < rd->bufnum; i++)
+  {
+    if (memcmp(rd->bufs[i].buffer, wr->bufs[i].buffer, rd->bufs[i].length) != 0)
+    {
+      printk("%s: write-read mismatch, buf %u start_block %u len %u\n",
+             __func__, i, rd->bufs[i].block, rd->bufs[i].length);
+    }
+  }
+
+  destroy_dummy_request(rd);
+}
+#endif
+
+
+#if RUN_CARD_READ_SPEED_TEST || RUN_CARD_WRITE_SPEED_TEST
+static void print_data(const uint8_t *data, uint32_t len)
+{
+  int i;
+  printk("Data at %p is:\n", data);
+  for (i = 0; i < len; i++)
+  {
+    printk("%02x ", data[i]);
+    if (((i + 1) % 64) == 0)
+      printk("\n");
+  }
+  printk("\n");
+}
+
+static void check_buf_is_zeroed(char *msg, uint8_t *buf, uint32_t total_size)
+{
+  uint32_t nonzero = 0;
+  uint32_t i;
+
+  for (i = 0; i < total_size; i++)
+  {
+    if (buf[i] != 0)
+    {
+      nonzero++;
+      if (nonzero <= 10) /* don't print too much */
+        printk("Found nonzero byte %02x at position %u (%p)\n", buf[i], i, &buf[i]);
+    }
+  }
+  printk("Amount of nonzero bytes in %p buffer %s is %u\n", buf, msg, nonzero);
+}
+
+static void esdhc_memcard_speed_test(int do_write)
+{
+  uint32_t total_size = 4 * 1024 * 1024; /* total area size to read or write */
+  uint32_t num_blocks =  total_size / BLOCK_SIZE;
+  uint32_t start_block = 0;
+  uint8_t *buf = (uint8_t *)malloc(total_size);
+  uint32_t response[4];
+  rtems_status_code sc;
+  struct timeval start, end;
+  uint64_t delay;
+  uint64_t speed = 0;
+
+  printk("%s: measuring ESDHC card %s performance (size %u, buf %p), please wait...\n",
+         __func__, do_write ? "write" : "read", total_size, buf);
+
+  memset(buf, 0, total_size);
+  check_buf_is_zeroed("before SD operation", buf, total_size);
+
+  gettimeofday(&start, NULL);
+
+  if (do_write)
+    sc = esdhc_exec_command(SD_CMD25, start_block, buf, num_blocks, BLOCK_SIZE, response);
+  else /* read */
+    sc = esdhc_exec_command(SD_CMD18, start_block, buf, num_blocks, BLOCK_SIZE, response);
+
+  gettimeofday(&end, NULL);
+
+  if (!do_write)
+  {
+    /* This check is useful if previous test has written zeros */
+    check_buf_is_zeroed("after reading SD card", buf, total_size);
+  }
+
+  free(buf);
+
+  delay = timeval_diff_us(&start, &end);
+  if (delay > 0)
+    speed = (uint64_t)total_size * 1000000ULL / delay;
+
+#if 0 /* Print the MBR */
+  if (!do_write)
+    print_data(buf, BLOCK_SIZE);
+#endif
+
+  printk("%s: sc %u, ESDHC card %s speed is %llu bytes/sec\n", __func__,
+         sc, do_write ? "write" : "read", speed);
+}
+
+static void esdhc_memcard_read_speed_test(void)
+{
+  esdhc_memcard_speed_test(0);
+}
+
+static void esdhc_memcard_write_speed_test(void)
+{
+  esdhc_memcard_speed_test(1);
+}
+#endif
+
+
+static rtems_status_code esdhc_try_set_high_speed(uint32_t rca, uint32_t *sd_speed_hz)
+{
+  uint32_t data[16] __attribute__((aligned(PPC_DEFAULT_CACHE_LINE_SIZE)));
+  uint32_t max_retries = 10;
+  rtems_status_code sc;
+  uint32_t response[4];
+  uint32_t sd_version;
+  uint32_t i;
+
+  *sd_speed_hz = 25000000; /* Default */
+
+  if (!(qoriq.esdhc.hostcapblt & HOSTCAPBLT_HSS))
+    return RTEMS_SUCCESSFUL; /* Host doesn't support high speed */
+
+  memset(data, 0, sizeof(data));
+
+
+  /* ACMD51 - get the SD configuration register */
+  for (i = 0; i < max_retries; i++)
+  {
+    sc = esdhc_exec_command(SD_CMD55, rca << 16, NULL, 0, 0, NULL);
+    if (sc != RTEMS_SUCCESSFUL) return sc;
+    sc = esdhc_exec_command(SD_ACMD51, 0, (uint8_t *)data, 1, 8, response);
+    if (sc == RTEMS_SUCCESSFUL)
+      break;
+
+    if (i < max_retries - 1)
+      dbg_printk(2, "%s: SD card is busy, retrying %u\n", __func__, i + 1);
+    else
+      return RTEMS_SUCCESSFUL; /* Give up and use 25MHz */
+  }
+
+  sd_version = (be32toh(data[0]) >> 24) & 0xf;
+  dbg_printk(2, "%s: %08x %08x, SD version 0x%x\n", __func__, be32toh(data[0]), be32toh(data[1]), sd_version);
+
+  if (sd_version == 0)
+    return RTEMS_SUCCESSFUL; /* SD version 1.0 doen't support CMD6, use 25MHz */
+
+
+  /* CMD6 - switch function (check mode) */
+  for (i = 0; i < max_retries; i++)
+  {
+    sc = esdhc_exec_command(SD_CMD6, 0x00fffff1, (uint8_t *)data, 1, 64, response);
+    if (sc != RTEMS_SUCCESSFUL) return sc;
+
+    /* Break if the high speed function is not busy, otherwise retry */
+    if (!(be32toh(data[7]) & 0x00020000))
+      break;
+
+    if (i < max_retries - 1)
+      dbg_printk(2, "%s: SD card is busy, retrying %u\n", __func__, i + 1);
+    else
+      return RTEMS_SUCCESSFUL; /* Give up and use 25MHz */
+  }
+
+  /* Check if the high speed is supported */
+  if (!(be32toh(data[3]) & 0x00020000))
+  {
+    dbg_printk(2, "%s: 0x%08x, high speed is not supported\n", __func__, be32toh(data[3]));
+    return RTEMS_SUCCESSFUL; /* High speed is not supported, use 25MHz */
+  }
+
+  /* CMD6 - switch function (switch mode) */
+  sc = esdhc_exec_command(SD_CMD6, 0x80fffff1, (uint8_t *)data, 1, 64, response);
+  if (sc != RTEMS_SUCCESSFUL) return sc;
+
+  if ((be32toh(data[4]) & 0x0f000000) == 0x01000000)
+  {
+    dbg_printk(2, "%s: 0x%08x, high speed 50MHz is established\n", __func__, be32toh(data[4]));
+    *sd_speed_hz = 50000000; /* High speed is established */
+  }
+
+  return RTEMS_SUCCESSFUL;
+}
+
+static rtems_status_code esdhc_memcard_init(void)
+{
+  static const uint32_t trans_speed_unit[] =
+    { 10000, 100000, 1000000, 10000000 };
+  static const uint32_t trans_speed_mult[] =
+    { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
+  rtems_status_code sc = RTEMS_SUCCESSFUL;
+  uint32_t response[4];
+  uint32_t rca;
+  uint32_t block_size;
+  uint32_t trans_speed;
+  uint32_t sd_speed_hz;
+  uint32_t cap_size;
+  uint32_t cap_mult;
+  uint64_t capacity;
+  uint32_t i;
+
+
+#if USE_DMA
+  if ((qoriq.esdhc.hostcapblt & HOSTCAPBLT_DMAS) == 0)
+    return RTEMS_NOT_IMPLEMENTED; /* DMA is not supported by this hardware */
+#endif
+
+  /* Reset by RSTA bit */
+  qoriq.esdhc.sysctl |= SYSCTL_RSTA;
+  /* Wait RSTA bit to clear */
+  while (qoriq.esdhc.sysctl & SYSCTL_RSTA);
+  /* Set data timeout to max */
+  qoriq.esdhc.sysctl = (qoriq.esdhc.sysctl & ~SYSCTL_DTOCV_MASK) | (0xe << SYSCTL_DTOCV_SHIFT);
+  /* Set data bus width as 4 bits */
+  qoriq.esdhc.proctl = (qoriq.esdhc.proctl & ~PROCTL_DTW_MASK) | PROCTL_DTW_4BIT;
+  /* Set max clock divider = 1/(256*16) = 1/4096 */
+  esdhc_memcard_set_clock_divider(0x80, 0xf);
+  /* Wait for CDIHB and CIHB bits to clear */
+  while (qoriq.esdhc.prsstat & (PRSSTAT_CDIHB | PRSSTAT_CIHB));
+  /* Set INITA bit (it sends 80 clock ticks for card to power-up) */
+  qoriq.esdhc.sysctl |= SYSCTL_INITA;
+  /* Wait INITA bit to clear */
+  while (qoriq.esdhc.sysctl & SYSCTL_INITA);
+
+
+  /* CMD0 */
+  sc = esdhc_exec_command(SD_CMD0, 0, NULL, 0, 0, NULL);
+  if (sc != RTEMS_SUCCESSFUL) return sc;
+
+
+  /* CMD8 */
+  sc = esdhc_exec_command(SD_CMD8, 0x1aa, NULL, 0, 0, response);
+  if (sc != RTEMS_SUCCESSFUL) return sc;
+  if ((response[0] & 0xff) != 0xaa)
+    return RTEMS_NOT_CONFIGURED;
+
+
+  /* ACMD41 - initialize (max 100 retries) */
+  for (i = 0; i < 100; i++)
+  {
+    sc = esdhc_exec_command(SD_CMD55, 0, NULL, 0, 0, NULL);
+    if (sc != RTEMS_SUCCESSFUL) return sc;
+
+    /* Say to the card that we support SDHC and 2.7-3.6V voltage */
+    sc = esdhc_exec_command(SD_ACMD41, 0x40ff8000, NULL, 0, 0, response);
+    if (sc != RTEMS_SUCCESSFUL) return sc;
+
+    if (response[0] & 0x80000000)
+      break; /* Card is ready */
+
+    if (i < 99)
+      dbg_printk(2, "%s: SD card is busy, retrying %u\n", __func__, i + 1);
+    else
+      return RTEMS_IO_ERROR;
+  }
+
+  high_capacity = !!(response[0] & 0x40000000);
+
+  dbg_printk(2, "%s: high_capacity %u\n", __func__, high_capacity);
+
+
+  /* CMD2 - get CID */
+  sc = esdhc_exec_command(SD_CMD2, 0, NULL, 0, 0, NULL);
+  if (sc != RTEMS_SUCCESSFUL) return sc;
+
+  /* CMD3 - get RCA */
+  sc = esdhc_exec_command(SD_CMD3, 0, NULL, 0, 0, response);
+  if (sc != RTEMS_SUCCESSFUL) return sc;
+  rca = response[0] >> 16;
+  dbg_printk(2, "%s: RCA %04x\n", __func__, rca);
+
+  /* CMD9 - get CSD */
+  sc = esdhc_exec_command(SD_CMD9, rca << 16, NULL, 0, 0, response);
+  if (sc != RTEMS_SUCCESSFUL) return sc;
+
+  if (high_capacity)
+  {
+    cap_size = ((response[1] & 0x3f) << 16) | (response[2] >> 16);
+    cap_mult = 8;
+  }
+  else
+  {
+    cap_size = ((response[1] & 0x3ff) << 2) | (response[2] >> 30);
+    cap_mult = (response[2] & 0x00038000) >> 15;
+  }
+
+  block_count = (cap_size + 1) << (cap_mult + 2);
+  block_size = 1 << ((response[1] >> 16) & 0xf);
+  capacity = block_count * block_size;
+
+  trans_speed = trans_speed_unit[(response[0] & 0x7)] * trans_speed_mult[((response[0] >> 3) & 0xf)];
+
+  dbg_printk(2, "%s: cap_size %u cap_mult %u block_count %u block_size %u capacity %llu trans_speed %u\n",
+             __func__, cap_size, cap_mult, block_count, block_size, capacity, trans_speed);
+
+
+  /* CMD7 - select card */
+  sc = esdhc_exec_command(SD_CMD7, rca << 16, NULL, 0, 0, NULL);
+  if (sc != RTEMS_SUCCESSFUL) return sc;
+
+
+  /* ACMD6 - set bus width as 4 bits */
+  sc = esdhc_exec_command(SD_CMD55, rca << 16, NULL, 0, 0, NULL);
+  if (sc != RTEMS_SUCCESSFUL) return sc;
+  sc = esdhc_exec_command(SD_ACMD6, 0x2, NULL, 0, 0, NULL);
+  if (sc != RTEMS_SUCCESSFUL) return sc;
+
+
+  if ((block_size % BLOCK_SIZE) != 0) /* Sanity check */
+    return RTEMS_NOT_CONFIGURED;
+  else if (block_size > BLOCK_SIZE)
+  {
+    /* CMD16 - set block length = 512 bytes */
+    sc = esdhc_exec_command(SD_CMD16, BLOCK_SIZE, NULL, 0, 0, response);
+    if (sc != RTEMS_SUCCESSFUL) return sc;
+
+    /* Adjust block_count to count the blocks of 512 bytes size */
+    block_count *= block_size / BLOCK_SIZE;
+  }
+
+
+  /* Try to set high speed */
+  sc = esdhc_try_set_high_speed(rca, &sd_speed_hz);
+  if (sc != RTEMS_SUCCESSFUL) return sc;
+
+  esdhc_memcard_set_clock(sd_speed_hz);
+
+  printk("%s: SD card initialized with %uMHz speed.\n", __func__, sd_speed_hz/1000000);
+
+
+#if RUN_CARD_WRITE_SPEED_TEST
+  esdhc_memcard_write_speed_test();
+#endif
+#if RUN_CARD_READ_SPEED_TEST
+  esdhc_memcard_read_speed_test();
+#endif
+
+  return RTEMS_SUCCESSFUL;
+}
+
+static int esdhc_memcard_disk_ioctl(rtems_disk_device *dd, uint32_t req, void *arg)
+{
+  dbg_printk(5, "%s: req 0x%08x\n", __func__, req);
+
+  if (req == RTEMS_BLKIO_REQUEST)
+  {
+    rtems_blkdev_request *r = (rtems_blkdev_request *)arg;
+    switch (r->req)
+    {
+      case RTEMS_BLKDEV_REQ_READ:
+        return esdhc_memcard_disk_block_read(r);
+      case RTEMS_BLKDEV_REQ_WRITE:
+        return esdhc_memcard_disk_block_write(r);
+      default:
+        errno = EINVAL;
+        return -1;
+    }
+  }
+  else if (req == RTEMS_BLKIO_CAPABILITIES)
+  {
+    *(uint32_t *)arg = RTEMS_BLKDEV_CAP_MULTISECTOR_CONT;
+    return 0;
+  }
+  else
+  {
+    return rtems_blkdev_ioctl(dd, req, arg);
+  }
+}
+
+static rtems_status_code esdhc_memcard_disk_init(rtems_device_major_number major,
+                                                 rtems_device_minor_number minor,
+                                                 void *arg)
+{
+  rtems_status_code sc;
+  dev_t dev;
+
+  dbg_printk(2, "%s: arg %p\n", __func__, arg);
+
+  sc = rtems_semaphore_create(rtems_build_name('s','d','h','c'), 1,
+                              RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE,
+                              0, &esdhc_sema_id);
+  RTEMS_CHECK_SC(sc, "ESDHC: Cannot create semaphore");
+
+  sc = rtems_disk_io_initialize();
+  RTEMS_CHECK_SC(sc, "ESDHC: Initialize RTEMS disk IO");
+
+  dev = rtems_filesystem_make_dev_t(major, 0);
+
+  sc = esdhc_memcard_init();
+  RTEMS_CHECK_SC(sc, "ESDHC: Initialize memory card");
+
+  sc = rtems_disk_create_phys(dev, BLOCK_SIZE, block_count, esdhc_memcard_disk_ioctl,
+                              NULL, RTEMS_BSP_ESDHC_MEMCARD_DEVICE_PATH);
+  RTEMS_CHECK_SC(sc, "ESDHC: Create disk device");
+
+  return RTEMS_SUCCESSFUL;
+}
+
+
+static const rtems_driver_address_table esdhc_memcard_disk_ops =
+{
+  .initialization_entry = esdhc_memcard_disk_init,
+  RTEMS_GENERIC_BLOCK_DEVICE_DRIVER_ENTRIES
+};
+
+
+rtems_status_code bsp_register_esdhc_memcard(void)
+{
+  rtems_status_code sc = RTEMS_SUCCESSFUL;
+  rtems_device_major_number major = 0;
+
+  sc = rtems_io_register_driver(0, &esdhc_memcard_disk_ops, &major);
+  RTEMS_CHECK_SC(sc, "ESDHC: Register disk memory card driver");
+
+  dbg_printk(2, "%s: major %d\n", __func__, major);
+
+  return RTEMS_SUCCESSFUL;
+}
diff --git a/c/src/lib/libbsp/powerpc/qoriq/include/bsp.h b/c/src/lib/libbsp/powerpc/qoriq/include/bsp.h
index a0d0092..f4d20de 100644
--- a/c/src/lib/libbsp/powerpc/qoriq/include/bsp.h
+++ b/c/src/lib/libbsp/powerpc/qoriq/include/bsp.h
@@ -82,6 +82,8 @@ void *bsp_idle_thread( uintptr_t ignored );
 #define RTEMS_BSP_NETWORK_DRIVER_NAME3 "tsec3"
 #define RTEMS_BSP_NETWORK_DRIVER_NAME4 "intercom1"
 
+#define RTEMS_BSP_ESDHC_MEMCARD_DEVICE_PATH     "/dev/memcard"
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
-- 
2.7.4 (Apple Git-66)



More information about the devel mailing list