qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] sd: add SDHCI and eMMC support
@ 2011-07-25 23:19 Vincent Palatin
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 1/7] sd: do not add one sector to the disk size Vincent Palatin
                   ` (7 more replies)
  0 siblings, 8 replies; 10+ messages in thread
From: Vincent Palatin @ 2011-07-25 23:19 UTC (permalink / raw)
  To: Qemu devel

Dear Qemu developers,

This patchset adds the support for eMMC as found soldered on many embedded board
in addition to current support for SD/SDHC cards.
It also adds a standard SDHCI controller emulation.
The first patches are a couple of fixes to the current SD code found while
implementing these features.

The SDHCI emulation has both a MMIO interface as found in several ARM SoC and
a PCI interface. The PCI interface allows to test it with the current code base.
I hope to send for review soon the patches for an ARM SoC using the MMIO
interface.

The PCI version of the SDHCI controller can be tested with such a command line :
./i386-softmmu/qemu -hda rootfs.qcow2 -device sdhci_pci -sd sd_image.raw

An eMMC connected to the SDHCI controller can be instantied like this :
./x86_64-softmmu/qemu-system-x86_64 -hda rootfs.qcow2  -device sdhci_pci,block=internal_card  -drive id=internal_card,if=emmc,file=emmc4G.raw

The patch series has also been tested with MMC_TEST linux kernel module
and a chromium image booted from eMMC.

-- 
Vincent

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 1/7] sd: do not add one sector to the disk size
  2011-07-25 23:19 [Qemu-devel] sd: add SDHCI and eMMC support Vincent Palatin
@ 2011-07-25 23:19 ` Vincent Palatin
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 2/7] sd: fix card size checking on R/W accesses Vincent Palatin
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Vincent Palatin @ 2011-07-25 23:19 UTC (permalink / raw)
  To: Qemu devel; +Cc: Vincent Palatin

This leads to random off-by-one error.
When the size of the SD is exactly 1GB, the emulation was returning a
wrong SDHC CSD descriptor.

Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
---
 hw/sd.c |    4 +---
 1 files changed, 1 insertions(+), 3 deletions(-)

diff --git a/hw/sd.c b/hw/sd.c
index cedfb20..f48d589 100644
--- a/hw/sd.c
+++ b/hw/sd.c
@@ -393,9 +393,7 @@ static void sd_reset(SDState *sd, BlockDriverState *bdrv)
     } else {
         sect = 0;
     }
-    sect <<= 9;
-
-    size = sect + 1;
+    size = sect << 9;
 
     sect = (size >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + 1;
 
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 2/7] sd: fix card size checking on R/W accesses
  2011-07-25 23:19 [Qemu-devel] sd: add SDHCI and eMMC support Vincent Palatin
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 1/7] sd: do not add one sector to the disk size Vincent Palatin
@ 2011-07-25 23:19 ` Vincent Palatin
  2011-07-30  5:50   ` andrzej zaborowski
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 3/7] block: add eMMC block device type Vincent Palatin
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 10+ messages in thread
From: Vincent Palatin @ 2011-07-25 23:19 UTC (permalink / raw)
  To: Qemu devel; +Cc: Vincent Palatin

We need to check that we are not crossing the boundaries of the card for
the current access not for the next one which might not happen.

Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
---
 hw/sd.c |   22 ++++++++++++----------
 1 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/hw/sd.c b/hw/sd.c
index f48d589..de477fe 100644
--- a/hw/sd.c
+++ b/hw/sd.c
@@ -1451,11 +1451,6 @@ void sd_write_data(SDState *sd, uint8_t value)
         sd->data[sd->data_offset ++] = value;
         if (sd->data_offset >= sd->blk_len) {
             /* TODO: Check CRC before committing */
-            sd->state = sd_programming_state;
-            BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
-            sd->blk_written ++;
-            sd->data_start += sd->blk_len;
-            sd->data_offset = 0;
             if (sd->data_start + sd->blk_len > sd->size) {
                 sd->card_status |= ADDRESS_ERROR;
                 break;
@@ -1464,6 +1459,11 @@ void sd_write_data(SDState *sd, uint8_t value)
                 sd->card_status |= WP_VIOLATION;
                 break;
             }
+            sd->state = sd_programming_state;
+            BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
+            sd->blk_written ++;
+            sd->data_start += sd->blk_len;
+            sd->data_offset = 0;
             sd->csd[14] |= 0x40;
 
             /* Bzzzzzzztt .... Operation complete.  */
@@ -1606,17 +1606,19 @@ uint8_t sd_read_data(SDState *sd)
         break;
 
     case 18:	/* CMD18:  READ_MULTIPLE_BLOCK */
-        if (sd->data_offset == 0)
+        if (sd->data_offset == 0) {
+            if (sd->data_start + io_len > sd->size) {
+                sd->card_status |= ADDRESS_ERROR;
+                ret = 0;
+                break;
+            }
             BLK_READ_BLOCK(sd->data_start, io_len);
+        }
         ret = sd->data[sd->data_offset ++];
 
         if (sd->data_offset >= io_len) {
             sd->data_start += io_len;
             sd->data_offset = 0;
-            if (sd->data_start + io_len > sd->size) {
-                sd->card_status |= ADDRESS_ERROR;
-                break;
-            }
         }
         break;
 
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 3/7] block: add eMMC block device type
  2011-07-25 23:19 [Qemu-devel] sd: add SDHCI and eMMC support Vincent Palatin
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 1/7] sd: do not add one sector to the disk size Vincent Palatin
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 2/7] sd: fix card size checking on R/W accesses Vincent Palatin
@ 2011-07-25 23:19 ` Vincent Palatin
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 4/7] sd: add eMMC support Vincent Palatin
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Vincent Palatin @ 2011-07-25 23:19 UTC (permalink / raw)
  To: Qemu devel; +Cc: Vincent Palatin

Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
---
 blockdev.c |    2 ++
 blockdev.h |    1 +
 2 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/blockdev.c b/blockdev.c
index 7d579d6..c836311 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -29,6 +29,7 @@ static const char *const if_name[IF_COUNT] = {
     [IF_SD] = "sd",
     [IF_VIRTIO] = "virtio",
     [IF_XEN] = "xen",
+    [IF_EMMC] = "emmc",
 };
 
 static const int if_max_devs[IF_COUNT] = {
@@ -500,6 +501,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
         break;
     case IF_PFLASH:
     case IF_MTD:
+    case IF_EMMC:
         break;
     case IF_VIRTIO:
         /* add virtio block device */
diff --git a/blockdev.h b/blockdev.h
index 3587786..ef06335 100644
--- a/blockdev.h
+++ b/blockdev.h
@@ -22,6 +22,7 @@ typedef enum {
     IF_DEFAULT = -1,            /* for use with drive_add() only */
     IF_NONE,
     IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN,
+    IF_EMMC,
     IF_COUNT
 } BlockInterfaceType;
 
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 4/7] sd: add eMMC support
  2011-07-25 23:19 [Qemu-devel] sd: add SDHCI and eMMC support Vincent Palatin
                   ` (2 preceding siblings ...)
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 3/7] block: add eMMC block device type Vincent Palatin
@ 2011-07-25 23:19 ` Vincent Palatin
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 5/7] sd: add PCI ids for SDHCI controller Vincent Palatin
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Vincent Palatin @ 2011-07-25 23:19 UTC (permalink / raw)
  To: Qemu devel; +Cc: Vincent Palatin

The parameters mimick a real 4GB eMMC, but it can be set to various
sizes.

Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
---
 hw/sd.c |  155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 136 insertions(+), 19 deletions(-)

diff --git a/hw/sd.c b/hw/sd.c
index de477fe..0db8d78 100644
--- a/hw/sd.c
+++ b/hw/sd.c
@@ -91,11 +91,13 @@ struct SDState {
     int function_group[6];
 
     int spi;
+    int emmc;
     int current_cmd;
     int blk_written;
     uint64_t data_start;
     uint32_t data_offset;
     uint8_t data[512];
+    uint8_t ext_csd[512];
     qemu_irq readonly_cb;
     qemu_irq inserted_cb;
     BlockDriverState *bdrv;
@@ -196,7 +198,7 @@ static uint16_t sd_crc16(void *message, size_t width)
 static void sd_set_ocr(SDState *sd)
 {
     /* All voltages OK, card power-up OK, Standard Capacity SD Memory Card */
-    sd->ocr = 0x80ffff00;
+    sd->ocr = 0x80ffff80;
 }
 
 static void sd_set_scr(SDState *sd)
@@ -250,13 +252,85 @@ static const uint8_t sd_csd_rw_mask[16] = {
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfe,
 };
 
+static void mmc_set_ext_csd(SDState *sd, uint64_t size)
+{
+    uint32_t sectcount = size >> HWBLOCK_SHIFT;
+
+    memset(sd->ext_csd, 0, 512);
+    sd->ext_csd[504] = 0x1; /* supported command sets */
+    sd->ext_csd[503] = 0x1; /* HPI features  */
+    sd->ext_csd[502] = 0x1; /* Background operations support */
+    sd->ext_csd[241] = 0xA; /* 1st initialization time after partitioning */
+    sd->ext_csd[232] = 0x1; /* Trim multiplier */
+    sd->ext_csd[231] = 0x15; /* Secure feature support */
+    sd->ext_csd[230] = 0x96; /* Secure erase support */
+    sd->ext_csd[229] = 0x96; /* Secure TRIM multiplier */
+    sd->ext_csd[228] = 0x7; /* Boot information */
+    sd->ext_csd[226] = 0x8; /* Boot partition size */
+    sd->ext_csd[225] = 0x6; /* Access size */
+    sd->ext_csd[224] = 0x4; /* HC Erase unit size */
+    sd->ext_csd[223] = 0x1; /* HC erase timeout */
+    sd->ext_csd[222] = 0x1; /* Reliable write sector count */
+    sd->ext_csd[221] = 0x4; /* HC write protect group size */
+    sd->ext_csd[220] = 0x8; /* Sleep current VCC  */
+    sd->ext_csd[219] = 0x7; /* Sleep current VCCQ */
+    sd->ext_csd[217] = 0x11; /* Sleep/Awake timeout */
+    sd->ext_csd[215] = (sectcount >> 24) & 0xff; /* Sector count */
+    sd->ext_csd[214] = (sectcount >> 16) & 0xff; /* ... */
+    sd->ext_csd[213] = (sectcount >> 8) & 0xff;  /* ... */
+    sd->ext_csd[212] = (sectcount & 0xff);       /* ... */
+    sd->ext_csd[210] = 0xa; /* Min write perf for 8bit@52Mhz */
+    sd->ext_csd[209] = 0xa; /* Min read perf for 8bit@52Mhz  */
+    sd->ext_csd[208] = 0xa; /* Min write perf for 4bit@52Mhz */
+    sd->ext_csd[207] = 0xa; /* Min read perf for 4bit@52Mhz */
+    sd->ext_csd[206] = 0xa; /* Min write perf for 4bit@26Mhz */
+    sd->ext_csd[205] = 0xa; /* Min read perf for 4bit@26Mhz */
+    sd->ext_csd[199] = 0x1; /* Partition switching timing */
+    sd->ext_csd[198] = 0x1; /* Out-of-interrupt busy timing */
+    sd->ext_csd[196] = 0x7; /* Card type */
+    sd->ext_csd[194] = 0x2; /* CSD Structure version */
+    sd->ext_csd[192] = 0x5; /* Extended CSD revision */
+    sd->ext_csd[168] = 0x1; /* RPMB size */
+    sd->ext_csd[160] = 0x3; /* Partinioning support */
+    sd->ext_csd[159] = 0x00; /* Max enhanced area size */
+    sd->ext_csd[158] = 0x00; /* ... */
+    sd->ext_csd[157] = 0xEC; /* ... */
+}
+
 static void sd_set_csd(SDState *sd, uint64_t size)
 {
     uint32_t csize = (size >> (CMULT_SHIFT + HWBLOCK_SHIFT)) - 1;
     uint32_t sectsize = (1 << (SECTOR_SHIFT + 1)) - 1;
     uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1;
 
-    if (size <= 0x40000000) {	/* Standard Capacity SD */
+    if (sd->emmc) { /* eMMC */
+        sd->csd[0] = 0xd0;
+        sd->csd[1] = 0x0f;
+        sd->csd[2] = 0x00;
+        sd->csd[3] = 0x32;
+        sd->csd[4] = 0x0f;
+        if (size <= 0x80000000ULL) {
+            /* use 1k blocks */
+            uint32_t csize1k = (size >> (CMULT_SHIFT + 10)) - 1;
+            sd->csd[5] = 0x5a;
+            sd->csd[6] = 0x80 | ((csize1k >> 10) & 0xf);
+            sd->csd[7] = (csize1k >> 2) & 0xff;
+        } else { /* >= 2GB : size stored in ext CSD, block addressing */
+            sd->csd[5] = 0x59;
+            sd->csd[6] = 0x8f;
+            sd->csd[7] = 0xff;
+            sd->ocr |= 1 << 30;
+        }
+        sd->csd[8] = 0xff;
+        sd->csd[9] = 0xff;
+        sd->csd[10] = 0xf7;
+        sd->csd[11] = 0xfe;
+        sd->csd[12] = 0x49;
+        sd->csd[13] = 0x10;
+        sd->csd[14] = 0x00;
+        sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1;
+        mmc_set_ext_csd(sd, size);
+    } else if (size <= 0x40000000) { /* Standard Capacity SD */
         sd->csd[0] = 0x00;	/* CSD structure */
         sd->csd[1] = 0x26;	/* Data read access-time-1 */
         sd->csd[2] = 0x00;	/* Data read access-time-2 */
@@ -305,9 +379,13 @@ static void sd_set_csd(SDState *sd, uint64_t size)
     }
 }
 
-static void sd_set_rca(SDState *sd)
+static void sd_set_rca(SDState *sd, uint16_t value)
 {
-    sd->rca += 0x4567;
+    if (sd->emmc) {
+        sd->rca = value;
+    } else {
+        sd->rca += 0x4567;
+    }
 }
 
 #define CARD_STATUS_A	0x02004100
@@ -343,6 +421,8 @@ static void sd_response_r1_make(SDState *sd,
     uint32_t status;
 
     status = (sd->card_status & ~mask) | (last_status & mask);
+    status &= ~CURRENT_STATE;
+    status |= sd->state << 9;
     sd->card_status &= ~CARD_STATUS_C | APP_CMD;
 
     response[0] = (status >> 24) & 0xff;
@@ -446,6 +526,7 @@ SDState *sd_init(BlockDriverState *bs, int is_spi)
     sd = (SDState *) qemu_mallocz(sizeof(SDState));
     sd->buf = qemu_blockalign(bs, 512);
     sd->spi = is_spi;
+    sd->emmc = !bdrv_is_removable(bs);
     sd->enable = 1;
     sd_reset(sd, bs);
     if (sd->bdrv) {
@@ -627,6 +708,11 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
         break;
 
     case 1:	/* CMD1:   SEND_OP_CMD */
+        if (sd->emmc) {
+            sd->state = sd_ready_state;
+            return sd_r3;
+        }
+
         if (!sd->spi)
             goto bad_cmd;
 
@@ -653,8 +739,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
         case sd_identification_state:
         case sd_standby_state:
             sd->state = sd_standby_state;
-            sd_set_rca(sd);
-            return sd_r6;
+            sd_set_rca(sd, req.arg >> 16);
+            return sd->emmc ? sd_r1 : sd_r6;
 
         default:
             break;
@@ -678,6 +764,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
         return sd_r0;
 
     case 6:	/* CMD6:   SWITCH_FUNCTION */
+        if (sd->emmc) {
+            return sd_r1b;
+        } else {
         if (sd->spi)
             goto bad_cmd;
         switch (sd->mode) {
@@ -691,6 +780,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
         default:
             break;
         }
+        }
         break;
 
     case 7:	/* CMD7:   SELECT/DESELECT_CARD */
@@ -731,22 +821,36 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
         }
         break;
 
-    case 8:	/* CMD8:   SEND_IF_COND */
-        /* Physical Layer Specification Version 2.00 command */
-        switch (sd->state) {
-        case sd_idle_state:
-            sd->vhs = 0;
+    case 8:	/* CMD8:   SEND_IF_COND / SEND_EXT_CSD */
+        if (sd->emmc) {
+            switch (sd->state) {
+            case sd_transfer_state:
+                /* MMC : Sends the EXT_CSD register as a Block of data */
+                sd->state = sd_sendingdata_state;
+                memcpy(sd->data, sd->ext_csd, 512);
+                sd->data_start = addr;
+                sd->data_offset = 0;
+                return sd_r1;
+            default:
+                break;
+            }
+        } else {
+            /* SD Physical Layer Specification Version 2.00 command */
+            switch (sd->state) {
+            case sd_idle_state:
+                sd->vhs = 0;
 
-            /* No response if not exactly one VHS bit is set.  */
-            if (!(req.arg >> 8) || (req.arg >> ffs(req.arg & ~0xff)))
-                return sd->spi ? sd_r7 : sd_r0;
+                /* No response if not exactly one VHS bit is set.  */
+                if (!(req.arg >> 8) || (req.arg >> ffs(req.arg & ~0xff)))
+                    return sd->spi ? sd_r7 : sd_r0;
 
-            /* Accept.  */
-            sd->vhs = req.arg;
-            return sd_r7;
+                /* Accept.  */
+                sd->vhs = req.arg;
+                return sd_r7;
 
-        default:
-            break;
+            default:
+                break;
+        }
         }
         break;
 
@@ -1115,6 +1219,11 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
 
     /* Application specific commands (Class 8) */
     case 55:	/* CMD55:  APP_CMD */
+        /* Not supported by MMC */
+        if (sd->emmc) {
+            return sd_r0;
+        }
+
         if (sd->rca != rca)
             return sd_r0;
 
@@ -1566,6 +1675,14 @@ uint8_t sd_read_data(SDState *sd)
             sd->state = sd_transfer_state;
         break;
 
+    case 8:     /* CMD8: SEND_EXT_CSD on MMC */
+        ret = sd->data[sd->data_offset++];
+
+        if (sd->data_offset >= 512) {
+            sd->state = sd_transfer_state;
+        }
+        break;
+
     case 9:	/* CMD9:   SEND_CSD */
     case 10:	/* CMD10:  SEND_CID */
         ret = sd->data[sd->data_offset ++];
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 5/7] sd: add PCI ids for SDHCI controller
  2011-07-25 23:19 [Qemu-devel] sd: add SDHCI and eMMC support Vincent Palatin
                   ` (3 preceding siblings ...)
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 4/7] sd: add eMMC support Vincent Palatin
@ 2011-07-25 23:19 ` Vincent Palatin
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 6/7] sd: add SD Host Controller (SDHCI) emulation Vincent Palatin
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Vincent Palatin @ 2011-07-25 23:19 UTC (permalink / raw)
  To: Qemu devel; +Cc: Vincent Palatin

Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
---
 hw/pci.h     |    1 +
 hw/pci_ids.h |    1 +
 2 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/hw/pci.h b/hw/pci.h
index c220745..e0bfbfb 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -51,6 +51,7 @@
 /* QEMU/Bochs VGA (0x1234) */
 #define PCI_VENDOR_ID_QEMU               0x1234
 #define PCI_DEVICE_ID_QEMU_VGA           0x1111
+#define PCI_DEVICE_ID_SDHCI              0x2222
 
 /* VMWare (0x15ad) */
 #define PCI_VENDOR_ID_VMWARE             0x15ad
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index d94578c..6294658 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -27,6 +27,7 @@
 
 #define PCI_CLASS_MEMORY_RAM             0x0500
 
+#define PCI_CLASS_SYSTEM_SDHCI           0x0805
 #define PCI_CLASS_SYSTEM_OTHER           0x0880
 
 #define PCI_CLASS_SERIAL_USB             0x0c03
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 6/7] sd: add SD Host Controller (SDHCI) emulation
  2011-07-25 23:19 [Qemu-devel] sd: add SDHCI and eMMC support Vincent Palatin
                   ` (4 preceding siblings ...)
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 5/7] sd: add PCI ids for SDHCI controller Vincent Palatin
@ 2011-07-25 23:19 ` Vincent Palatin
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 7/7] sd: compile SDHCI on PCI platforms Vincent Palatin
  2011-12-13 16:38 ` [Qemu-devel] sd: add SDHCI and eMMC support Stefan Hajnoczi
  7 siblings, 0 replies; 10+ messages in thread
From: Vincent Palatin @ 2011-07-25 23:19 UTC (permalink / raw)
  To: Qemu devel; +Cc: Vincent Palatin

Try to be compliant with "SD Specifications Part A2 SD Host Controller
Simplified Specification Version 3.00", but not every feature is
implemented.

Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
---
 hw/sdhci.c |  670 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 670 insertions(+), 0 deletions(-)
 create mode 100644 hw/sdhci.c

diff --git a/hw/sdhci.c b/hw/sdhci.c
new file mode 100644
index 0000000..d097f0f
--- /dev/null
+++ b/hw/sdhci.c
@@ -0,0 +1,670 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * SDHCI (SD Host Controler Interface) emulation
+ */
+
+#include "blockdev.h"
+#include "sysbus.h"
+#include "pci.h"
+#include "sd.h"
+
+/* from Linux sources : drivers/mmc/host/sdhci.h */
+/*
+ * Controller registers
+ */
+
+#define SDHCI_DMA_ADDRESS       0x00
+
+#define SDHCI_BLOCK_SIZE        0x04
+
+#define SDHCI_BLOCK_COUNT       0x06
+
+#define SDHCI_ARGUMENT          0x08
+
+#define SDHCI_TRANSFER_MODE     0x0C
+#define  SDHCI_TRNS_DMA         0x01
+#define  SDHCI_TRNS_BLK_CNT_EN  0x02
+#define  SDHCI_TRNS_ACMD12      0x04
+#define  SDHCI_TRNS_READ        0x10
+#define  SDHCI_TRNS_MULTI       0x20
+
+#define SDHCI_COMMAND           0x0E
+#define  SDHCI_CMD_RESP_MASK    0x03
+#define  SDHCI_CMD_CRC          0x08
+#define  SDHCI_CMD_INDEX        0x10
+#define  SDHCI_CMD_DATA         0x20
+
+#define  SDHCI_CMD_RESP_NONE    0x00
+#define  SDHCI_CMD_RESP_LONG    0x01
+#define  SDHCI_CMD_RESP_SHORT   0x02
+#define  SDHCI_CMD_RESP_SHORT_BUSY 0x03
+
+#define SDHCI_RESPONSE          0x10
+
+#define SDHCI_BUFFER            0x20
+
+#define SDHCI_PRESENT_STATE     0x24
+#define  SDHCI_CMD_INHIBIT      0x00000001
+#define  SDHCI_DATA_INHIBIT     0x00000002
+#define  SDHCI_DOING_WRITE      0x00000100
+#define  SDHCI_DOING_READ       0x00000200
+#define  SDHCI_SPACE_AVAILABLE  0x00000400
+#define  SDHCI_DATA_AVAILABLE   0x00000800
+#define  SDHCI_CARD_PRESENT     0x00010000
+#define  SDHCI_WRITE_PROTECT    0x00080000
+
+#define SDHCI_HOST_CONTROL      0x28
+#define  SDHCI_CTRL_LED         0x01
+#define  SDHCI_CTRL_4BITBUS     0x02
+#define  SDHCI_CTRL_HISPD       0x04
+#define  SDHCI_CTRL_DMA_MASK    0x18
+#define   SDHCI_CTRL_SDMA       0x00
+#define   SDHCI_CTRL_ADMA1      0x08
+#define   SDHCI_CTRL_ADMA32     0x10
+#define   SDHCI_CTRL_ADMA64     0x18
+#define   SDHCI_CTRL_8BITBUS    0x20
+
+#define SDHCI_POWER_CONTROL     0x29
+#define  SDHCI_POWER_ON         0x01
+#define  SDHCI_POWER_180        0x0A
+#define  SDHCI_POWER_300        0x0C
+#define  SDHCI_POWER_330        0x0E
+
+#define SDHCI_BLOCK_GAP_CONTROL 0x2A
+
+#define SDHCI_WAKE_UP_CONTROL   0x2B
+#define  SDHCI_WAKE_ON_INT      0x01
+#define  SDHCI_WAKE_ON_INSERT   0x02
+#define  SDHCI_WAKE_ON_REMOVE   0x04
+
+#define SDHCI_CLOCK_CONTROL     0x2C
+#define  SDHCI_DIVIDER_SHIFT    8
+#define  SDHCI_DIVIDER_HI_SHIFT 6
+#define  SDHCI_DIV_MASK 0xFF
+#define  SDHCI_DIV_MASK_LEN     8
+#define  SDHCI_DIV_HI_MASK      0x300
+#define  SDHCI_CLOCK_CARD_EN    0x0004
+#define  SDHCI_CLOCK_INT_STABLE 0x0002
+#define  SDHCI_CLOCK_INT_EN     0x0001
+
+#define SDHCI_TIMEOUT_CONTROL   0x2E
+
+#define SDHCI_SOFTWARE_RESET    0x2F
+#define  SDHCI_RESET_ALL        0x01
+#define  SDHCI_RESET_CMD        0x02
+#define  SDHCI_RESET_DATA       0x04
+
+#define SDHCI_INT_STATUS        0x30
+#define SDHCI_INT_ENABLE        0x34
+#define SDHCI_SIGNAL_ENABLE     0x38
+#define  SDHCI_INT_RESPONSE     0x00000001
+#define  SDHCI_INT_DATA_END     0x00000002
+#define  SDHCI_INT_DMA_END      0x00000008
+#define  SDHCI_INT_SPACE_AVAIL  0x00000010
+#define  SDHCI_INT_DATA_AVAIL   0x00000020
+#define  SDHCI_INT_CARD_INSERT  0x00000040
+#define  SDHCI_INT_CARD_REMOVE  0x00000080
+#define  SDHCI_INT_CARD_INT     0x00000100
+#define  SDHCI_INT_ERROR        0x00008000
+#define  SDHCI_INT_TIMEOUT      0x00010000
+#define  SDHCI_INT_CRC          0x00020000
+#define  SDHCI_INT_END_BIT      0x00040000
+#define  SDHCI_INT_INDEX        0x00080000
+#define  SDHCI_INT_DATA_TIMEOUT 0x00100000
+#define  SDHCI_INT_DATA_CRC     0x00200000
+#define  SDHCI_INT_DATA_END_BIT 0x00400000
+#define  SDHCI_INT_BUS_POWER    0x00800000
+#define  SDHCI_INT_ACMD12ERR    0x01000000
+#define  SDHCI_INT_ADMA_ERROR   0x02000000
+
+#define  SDHCI_INT_NORMAL_MASK  0x00007FFF
+#define  SDHCI_INT_ERROR_MASK   0xFFFF8000
+
+#define SDHCI_ACMD12_ERR        0x3C
+
+/* 3E-3F reserved */
+
+#define SDHCI_CAPABILITIES      0x40
+#define  SDHCI_TIMEOUT_CLK_MASK 0x0000003F
+#define  SDHCI_TIMEOUT_CLK_SHIFT 0
+#define  SDHCI_TIMEOUT_CLK_UNIT 0x00000080
+#define  SDHCI_CLOCK_BASE_MASK  0x00003F00
+#define  SDHCI_CLOCK_V3_BASE_MASK       0x0000FF00
+#define  SDHCI_CLOCK_BASE_SHIFT 8
+#define  SDHCI_MAX_BLOCK_MASK   0x00030000
+#define  SDHCI_MAX_BLOCK_SHIFT  16
+#define  SDHCI_CAN_DO_8BIT      0x00040000
+#define  SDHCI_CAN_DO_ADMA2     0x00080000
+#define  SDHCI_CAN_DO_ADMA1     0x00100000
+#define  SDHCI_CAN_DO_HISPD     0x00200000
+#define  SDHCI_CAN_DO_SDMA      0x00400000
+#define  SDHCI_CAN_VDD_330      0x01000000
+#define  SDHCI_CAN_VDD_300      0x02000000
+#define  SDHCI_CAN_VDD_180      0x04000000
+#define  SDHCI_CAN_64BIT        0x10000000
+#define  SDHCI_SLOT_TYPE_EMBED  0x40000000
+
+#define SDHCI_CAPABILITIES_1    0x44
+
+/* 44-47 reserved for more caps */
+
+#define SDHCI_MAX_CURRENT       0x48
+
+/* 4C-4F reserved for more max current */
+
+#define SDHCI_SET_ACMD12_ERROR  0x50
+#define SDHCI_SET_INT_ERROR     0x52
+
+#define SDHCI_ADMA_ERROR        0x54
+
+/* 55-57 reserved */
+
+#define SDHCI_ADMA_ADDRESS      0x58
+
+/* 60-FB reserved */
+
+#define SDHCI_SLOT_INT_STATUS   0xFC
+
+#define SDHCI_HOST_VERSION      0xFE
+#define  SDHCI_VENDOR_VER_MASK  0xFF00
+#define  SDHCI_VENDOR_VER_SHIFT 8
+#define  SDHCI_SPEC_VER_MASK    0x00FF
+#define  SDHCI_SPEC_VER_SHIFT   0
+#define   SDHCI_SPEC_100        0
+#define   SDHCI_SPEC_200        1
+#define   SDHCI_SPEC_300        2
+
+/* ADMA descriptor flags */
+#define ADMA_VALID   (1<<0)
+#define ADMA_END     (1<<1)
+#define ADMA_INT     (1<<2)
+
+#define ADMA_OP_MASK (3<<4)
+#define ADMA_OP_NOP  (0<<4)
+#define ADMA_OP_RSV  (1<<4)
+#define ADMA_OP_TRAN (2<<4)
+#define ADMA_OP_LINK (3<<4)
+
+/* re-write a part of a variable using a mask
+ * to emulate the "byte-enable" behaviour in hardware
+ */
+#define MASKED_WRITE(var, val, mask) do {\
+        var = (var & ~(mask)) | ((val) & (mask));\
+    } while (0)
+
+typedef struct {
+    union {
+        SysBusDevice busdev;
+        PCIDevice pcidev;
+    };
+    BlockDriverState *bs;
+    uint32_t sdma_address;
+    uint16_t block_size;
+    uint16_t block_count;
+    uint32_t arg;
+    uint16_t transfer_mode;
+    uint32_t response[4];
+    uint32_t clock;
+    uint32_t host_control;
+    uint32_t int_enable;
+    uint32_t int_status;
+    uint32_t adma_address;
+    uint8_t adma_error;
+    SDState *sd;
+    qemu_irq irq;
+} sdhci_state;
+
+static void sdhci_reset(DeviceState *d);
+
+static void sdhci_set_irq(sdhci_state *s)
+{
+    qemu_set_irq(s->irq, !!(s->int_status & s->int_enable));
+}
+
+static void sdhci_dma_transfer(sdhci_state *s)
+{
+    int b;
+    struct adma_desc {
+        uint16_t flags;
+        uint16_t size;
+        uint32_t addr;
+    } desc;
+    uint16_t xfer_size;
+    uint32_t total_size, remaining_size;
+
+    s->adma_error = 0;
+
+    if (s->host_control & SDHCI_CTRL_ADMA32) {
+        cpu_physical_memory_read(s->adma_address, (void *)&desc, sizeof(desc));
+    } else { /* use SDMA */
+        /* Generate hardcoded descriptor to emulate ADMA */
+        desc.addr = s->sdma_address;
+        desc.size = s->block_count * (s->block_size & 0xfff);
+        desc.flags = ADMA_VALID | ADMA_END | ADMA_OP_TRAN;
+        /* TODO: manage SDMA buffer boundary */
+    }
+    /* 0 in the size field means 65536 bytes */
+    remaining_size = desc.size ? desc.size : 65536;
+
+    s->int_status |= SDHCI_INT_DATA_END;
+
+    total_size = s->block_count*(s->block_size & 0xfff);
+    while (total_size) {
+        while (!(desc.flags & ADMA_VALID) || !remaining_size ||
+                ((desc.flags & ADMA_OP_MASK) != ADMA_OP_TRAN)) {
+            if ((desc.flags & ADMA_END) || !(desc.flags & ADMA_VALID)) {
+                /* Abort ADMA transfer */
+                s->int_status |= SDHCI_INT_ADMA_ERROR;
+                s->adma_error = 1 /* ST_FDS */;
+                s->block_count = total_size / s->block_size;
+                return;
+            }
+            if ((desc.flags & ADMA_OP_MASK) == ADMA_OP_LINK) {
+                s->adma_address = desc.addr;
+            } else {
+                s->adma_address += sizeof(struct adma_desc);
+            }
+            cpu_physical_memory_read(s->adma_address, (void *)&desc,
+                                     sizeof(desc));
+            /* 0 in the size field means 65536 bytes */
+            remaining_size = desc.size ? desc.size : 65536;
+            /* ADMA_INT flag not implemented : it seems to be debug only */
+        }
+
+        xfer_size = MIN((s->block_size & 0xfff), remaining_size);
+        for (b = 0; b < xfer_size; b++, desc.addr++) {
+            if (s->transfer_mode & SDHCI_TRNS_READ) {
+                uint8_t data = sd_read_data(s->sd);
+                cpu_physical_memory_write(desc.addr, &data, 1);
+            } else {
+                uint8_t data;
+                cpu_physical_memory_read(desc.addr, &data, 1);
+                sd_write_data(s->sd, data);
+            }
+        }
+        remaining_size -= xfer_size;
+        total_size -= xfer_size;
+    }
+    s->block_count = 0;
+}
+
+static void sdhci_command(sdhci_state *s, uint32_t cmd)
+{
+    SDRequest request;
+    int len;
+    uint8_t r[16];
+
+    if (!s->sd) { /* nothing beyond the controller */
+        s->int_status |= SDHCI_INT_TIMEOUT;
+        sdhci_set_irq(s);
+        return;
+    }
+
+    request.cmd = (cmd>>8) & 0xff;
+    request.arg = s->arg;
+    len = sd_do_command(s->sd, &request, r);
+    if (len == 0) {
+        if (cmd & SDHCI_CMD_RESP_MASK) {
+            /* no response expected */
+            s->int_status |= SDHCI_INT_INDEX;
+        } else {
+            /* error */
+            s->int_status |= SDHCI_INT_RESPONSE;
+        }
+    } else {
+        if (len == 4) {
+            s->response[0] = (r[0]<<24) | (r[1]<<16) | (r[2]<<8) | r[3];
+        } else if (len == 16) {
+            s->response[0] = (r[11]<<24) | (r[12]<<16) | (r[13]<<8) | r[14];
+            s->response[1] =  (r[7]<<24) |  (r[8]<<16) |  (r[9]<<8) | r[10];
+            s->response[2] =  (r[3]<<24) |  (r[4]<<16) |  (r[5]<<8) |  r[6];
+            s->response[3] =  (0xcc<<24) |  (r[0]<<16) |  (r[1]<<8) |  r[2];
+        }
+        s->int_status |= SDHCI_INT_RESPONSE;
+        if ((cmd & 3) == SDHCI_CMD_RESP_SHORT_BUSY) {
+            /* the command will trigger the busy (DAT[0]) line ON then OFF
+             * this will raise the Data END interrupt when done.
+             */
+            s->int_status |= SDHCI_INT_DATA_END;
+        }
+        if ((s->transfer_mode & SDHCI_TRNS_DMA) && (cmd & SDHCI_CMD_DATA)) {
+            sdhci_dma_transfer(s);
+        }
+    }
+    sdhci_set_irq(s);
+}
+
+static uint32_t sdhci_read32(void *opaque, target_phys_addr_t offset)
+{
+    sdhci_state *s = opaque;
+
+    if ((offset >= 0x100) && (offset < 0x200)) {
+        fprintf(stderr, "sdhci: unsupported vendor read @" TARGET_FMT_plx "\n",
+                offset);
+        return 0;
+    }
+
+    switch (offset) {
+    case SDHCI_DMA_ADDRESS:
+        return s->sdma_address;
+    case SDHCI_BLOCK_SIZE /* +SDHCI_BLOCK_COUNT */:
+        return s->block_size | (s->block_count << 16);
+    case SDHCI_ARGUMENT:
+        return s->arg;
+    case SDHCI_TRANSFER_MODE /* +SDHCI_COMMAND */:
+        return s->transfer_mode;
+    case SDHCI_RESPONSE:
+    case SDHCI_RESPONSE+4:
+    case SDHCI_RESPONSE+8:
+    case SDHCI_RESPONSE+12:
+        return s->response[(offset-SDHCI_RESPONSE)/sizeof(uint32_t)];
+    case SDHCI_BUFFER:
+        /* TODO: implement PIO mode */
+        return 0;
+    case SDHCI_PRESENT_STATE:
+        return (s->sd ? 0x00170000 : 0) |
+               (s->bs && bdrv_is_read_only(s->bs) ? 0 : 0x00080000);
+    case SDHCI_HOST_CONTROL:
+        /*+SDHCI_POWER_CONTROL +SDHCI_BLOCK_GAP_CONTROL +SDHCI_WAKE_UP_CONTROL*/
+        return s->host_control;
+    case SDHCI_CLOCK_CONTROL /* +SDHCI_TIMEOUT_CONTROL +SDHCI_SOFTWARE_RESET */:
+        return s->clock | (0<<24 /* reset done */);
+    case SDHCI_INT_STATUS:
+        return s->int_status;
+    case SDHCI_INT_ENABLE:
+        return s->int_enable;
+    case SDHCI_SIGNAL_ENABLE:
+        return 0;
+    case SDHCI_ACMD12_ERR:
+        return 0;
+    case SDHCI_CAPABILITIES:
+        return SDHCI_SLOT_TYPE_EMBED | SDHCI_CAN_VDD_330 |
+               SDHCI_CAN_DO_ADMA1 | SDHCI_CAN_DO_HISPD | SDHCI_CAN_DO_SDMA |
+               SDHCI_CAN_DO_8BIT | SDHCI_CAN_DO_ADMA2 |
+               (52 << SDHCI_CLOCK_BASE_SHIFT) |
+               SDHCI_TIMEOUT_CLK_UNIT /* MHz */ | (52<<SDHCI_TIMEOUT_CLK_SHIFT);
+    case SDHCI_CAPABILITIES_1:
+        return 0;
+    case SDHCI_MAX_CURRENT:
+        return 0;
+    case SDHCI_SET_ACMD12_ERROR /* +SDHCI_SET_INT_ERROR */:
+         hw_error("sdhci_read: read only register at " TARGET_FMT_plx "\n",
+                  offset);
+    case SDHCI_ADMA_ERROR:
+        return s->adma_error;
+    case SDHCI_ADMA_ADDRESS:
+        return s->adma_address;
+    case SDHCI_SLOT_INT_STATUS /* +SDHCI_HOST_VERSION */:
+        return 0 | (SDHCI_SPEC_200 << 16);
+    default:
+        hw_error("sdhci_read: Bad offset " TARGET_FMT_plx "\n", offset);
+    }
+
+    return 0;
+}
+
+static uint32_t sdhci_read16(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t v = sdhci_read32(opaque, offset & ~0x3);
+
+    return (v >> (8 * (offset & 0x3))) & 0xffff;
+}
+
+static uint32_t sdhci_read8(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t v = sdhci_read32(opaque, offset & ~0x3);
+
+    return (v >> (8 * (offset & 0x3))) & 0xff;
+}
+
+static void sdhci_write_masked(void *opaque, target_phys_addr_t offset,
+                               uint32_t value, uint32_t mask)
+{
+    sdhci_state *s = opaque;
+
+    if ((offset >= 0x100) && (offset < 0x200)) {
+        fprintf(stderr, "sdhci: unsupported vendor write at "TARGET_FMT_plx"\n",
+                offset);
+        return;
+    }
+
+    switch (offset) {
+    case SDHCI_DMA_ADDRESS:
+        MASKED_WRITE(s->sdma_address, value, mask);
+        break;
+    case SDHCI_BLOCK_SIZE /* +SDHCI_BLOCK_COUNT */:
+        MASKED_WRITE(s->block_size, value & 0x7fff, mask);
+        MASKED_WRITE(s->block_count, value >> 16, mask >> 16);
+        break;
+    case SDHCI_ARGUMENT:
+        MASKED_WRITE(s->arg, value, mask);
+        break;
+    case SDHCI_TRANSFER_MODE /* +SDHCI_COMMAND */:
+        MASKED_WRITE(s->transfer_mode, value & 0x3f, mask);
+        if (mask & 0xffff0000) {
+            sdhci_command(s, value >> 16);
+        }
+        break;
+    case SDHCI_BUFFER:
+        /* TODO: implement PIO mode */
+        break;
+    case SDHCI_HOST_CONTROL:
+        /*+SDHCI_POWER_CONTROL +SDHCI_BLOCK_GAP_CONTROL +SDHCI_WAKE_UP_CONTROL*/
+        MASKED_WRITE(s->host_control, value, mask);
+        if ((mask >> 8) & SDHCI_POWER_ON) {
+            if (s->sd) {
+                sd_enable(s->sd, ((value & mask) >> 8) & SDHCI_POWER_ON);
+            }
+        }
+        break;
+    case SDHCI_CLOCK_CONTROL /* +SDHCI_TIMEOUT_CONTROL +SDHCI_SOFTWARE_RESET */:
+        if (((value & mask) >> 24) & SDHCI_RESET_ALL) {
+            sdhci_reset(opaque);
+        }
+        if (((value & mask) >> 24) & SDHCI_RESET_CMD) {
+            s->int_status &= ~SDHCI_INT_RESPONSE;
+            sdhci_set_irq(s);
+        }
+        if (((value & mask) >> 24) & SDHCI_RESET_DATA) {
+            s->adma_error = 0;
+            s->int_status &= ~(SDHCI_INT_DATA_END | SDHCI_INT_DMA_END);
+            sdhci_set_irq(s);
+        }
+        MASKED_WRITE(s->clock, (value & 0xfffff) | SDHCI_CLOCK_INT_STABLE,
+                     mask);
+        break;
+    case SDHCI_INT_STATUS:
+        s->int_status &= ~(value & mask);
+        sdhci_set_irq(s);
+        break;
+    case SDHCI_INT_ENABLE:
+        MASKED_WRITE(s->int_enable, value, mask);
+        sdhci_set_irq(s);
+        break;
+    case SDHCI_SIGNAL_ENABLE:
+        break;
+    case SDHCI_SET_ACMD12_ERROR /* +SDHCI_SET_INT_ERROR */:
+        break;
+    case SDHCI_ADMA_ADDRESS:
+        MASKED_WRITE(s->adma_address, value, mask);
+        break;
+    case SDHCI_SLOT_INT_STATUS /* +SDHCI_HOST_VERSION */:
+        break;
+    /* Read only registers */
+    case SDHCI_RESPONSE:
+    case SDHCI_RESPONSE+4:
+    case SDHCI_RESPONSE+8:
+    case SDHCI_RESPONSE+12:
+    case SDHCI_PRESENT_STATE:
+    case SDHCI_ACMD12_ERR:
+    case SDHCI_CAPABILITIES:
+    case SDHCI_CAPABILITIES_1:
+    case SDHCI_MAX_CURRENT:
+    case SDHCI_ADMA_ERROR:
+        hw_error("sdhci_write: Read only register at " TARGET_FMT_plx "\n",
+                 offset);
+    default:
+        hw_error("sdhci_write: Bad offset " TARGET_FMT_plx "\n", offset);
+    }
+}
+
+static void sdhci_write32(void *opaque, target_phys_addr_t offset,
+                          uint32_t value)
+{
+    sdhci_write_masked(opaque, offset, value, 0xffffffff);
+}
+
+static void sdhci_write16(void *opaque, target_phys_addr_t offset,
+                          uint32_t value)
+{
+    int shift = 8 * (offset & 0x3);
+
+    sdhci_write_masked(opaque, offset & ~3, value << shift, 0xffff << shift);
+}
+
+static void sdhci_write8(void *opaque, target_phys_addr_t offset,
+                          uint32_t value)
+{
+    int shift = 8 * (offset & 0x3);
+
+    sdhci_write_masked(opaque, offset & ~3, value << shift, 0xff << shift);
+}
+
+static CPUReadMemoryFunc * const sdhci_readfn[] = {
+   sdhci_read8,
+   sdhci_read16,
+   sdhci_read32
+};
+
+static CPUWriteMemoryFunc * const sdhci_writefn[] = {
+   sdhci_write8,
+   sdhci_write16,
+   sdhci_write32
+};
+
+static void sdhci_reset(DeviceState *d)
+{
+    sdhci_state *s = container_of(d, sdhci_state, busdev.qdev);
+
+    if (!s->bs) {
+        DriveInfo *inf = drive_get_next(IF_SD);
+        s->bs = inf ? inf->bdrv : NULL;
+    }
+    if (!s->sd && s->bs) {
+        s->sd = sd_init(s->bs, 0);
+    }
+
+    s->sdma_address = 0;
+    s->block_size = 0;
+    s->block_count = 0;
+    s->arg = 0;
+    s->transfer_mode = 0;
+    s->clock = SDHCI_CLOCK_INT_STABLE;
+    s->int_enable = 0;
+    s->int_status = 0;
+    s->adma_address = 0;
+    s->adma_error = 0;
+}
+
+static const VMStateDescription sdhci_vmstate = {
+    .name = "sdhci",
+    .version_id = 0,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(sdma_address, sdhci_state),
+        VMSTATE_UINT16(block_size, sdhci_state),
+        VMSTATE_UINT16(block_count, sdhci_state),
+        VMSTATE_UINT32(arg, sdhci_state),
+        VMSTATE_UINT16(transfer_mode, sdhci_state),
+        VMSTATE_UINT32_ARRAY(response, sdhci_state, 4),
+        VMSTATE_UINT32(clock, sdhci_state),
+        VMSTATE_UINT32(int_enable, sdhci_state),
+        VMSTATE_UINT32(int_status, sdhci_state),
+        VMSTATE_UINT32(adma_address, sdhci_state),
+        VMSTATE_UINT8(adma_error, sdhci_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int sdhci_mm_init(SysBusDevice *dev)
+{
+    int iomemtype;
+    sdhci_state *s = FROM_SYSBUS(sdhci_state, dev);
+
+    iomemtype = cpu_register_io_memory(sdhci_readfn,
+                                       sdhci_writefn, s,
+                                       DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, 0x200, iomemtype);
+    sysbus_init_irq(dev, &s->irq);
+
+    return 0;
+}
+
+static SysBusDeviceInfo sdhci_mm_info = {
+    .init = sdhci_mm_init,
+    .qdev.name  = "sdhci",
+    .qdev.size  = sizeof(sdhci_state),
+    .qdev.vmsd  = &sdhci_vmstate,
+    .qdev.reset = sdhci_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_DRIVE("block", sdhci_state, bs),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static int sdhci_pci_init(PCIDevice *dev)
+{
+    sdhci_state *s = DO_UPCAST(sdhci_state, pcidev, dev);
+    uint8_t *pci_conf = s->pcidev.config;
+    int iomemtype;
+
+    pci_conf[PCI_CLASS_PROG] = 0x01; /* Standard Host supported DMA */
+    pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin 0 */
+    pci_conf[0x40] = 0x00; /* 1 slot at BAR0 */
+
+    iomemtype = cpu_register_io_memory(sdhci_readfn,
+                                       sdhci_writefn, s,
+                                       DEVICE_NATIVE_ENDIAN);
+    pci_register_bar_simple(&s->pcidev, 0, 0x100, 0, iomemtype);
+    s->irq = s->pcidev.irq[0];
+    return 0;
+}
+
+static PCIDeviceInfo sdhci_pci_info = {
+    .qdev.name    = "sdhci_pci",
+    .qdev.size    = sizeof(sdhci_state),
+    .qdev.vmsd    = &sdhci_vmstate,
+    .qdev.reset   = sdhci_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_DRIVE("block", sdhci_state, bs),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+    .init         = sdhci_pci_init,
+    .vendor_id    = PCI_VENDOR_ID_QEMU,
+    .device_id    = PCI_DEVICE_ID_SDHCI,
+    .class_id     = PCI_CLASS_SYSTEM_SDHCI,
+};
+
+static void sdhci_register(void)
+{
+    sysbus_register_withprop(&sdhci_mm_info);
+    pci_qdev_register(&sdhci_pci_info);
+}
+
+device_init(sdhci_register)
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH 7/7] sd: compile SDHCI on PCI platforms
  2011-07-25 23:19 [Qemu-devel] sd: add SDHCI and eMMC support Vincent Palatin
                   ` (5 preceding siblings ...)
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 6/7] sd: add SD Host Controller (SDHCI) emulation Vincent Palatin
@ 2011-07-25 23:19 ` Vincent Palatin
  2011-12-13 16:38 ` [Qemu-devel] sd: add SDHCI and eMMC support Stefan Hajnoczi
  7 siblings, 0 replies; 10+ messages in thread
From: Vincent Palatin @ 2011-07-25 23:19 UTC (permalink / raw)
  To: Qemu devel; +Cc: Vincent Palatin

Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
---
 Makefile.objs           |    4 +++-
 default-configs/pci.mak |    1 +
 2 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/Makefile.objs b/Makefile.objs
index cea15e4..5676de7 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -244,6 +244,9 @@ hw-obj-$(CONFIG_SMC91C111) += smc91c111.o
 hw-obj-$(CONFIG_LAN9118) += lan9118.o
 hw-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o
 
+# PCI card reader
+hw-obj-$(CONFIG_PCI) += sdhci.o
+
 # IDE
 hw-obj-$(CONFIG_IDE_CORE) += ide/core.o ide/atapi.o
 hw-obj-$(CONFIG_IDE_QDEV) += ide/qdev.o
@@ -375,4 +378,3 @@ libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o
 vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
 
 vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
-
diff --git a/default-configs/pci.mak b/default-configs/pci.mak
index 22bd350..113458e 100644
--- a/default-configs/pci.mak
+++ b/default-configs/pci.mak
@@ -15,3 +15,4 @@ CONFIG_IDE_CORE=y
 CONFIG_IDE_QDEV=y
 CONFIG_IDE_PCI=y
 CONFIG_AHCI=y
+CONFIG_SD=y
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH 2/7] sd: fix card size checking on R/W accesses
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 2/7] sd: fix card size checking on R/W accesses Vincent Palatin
@ 2011-07-30  5:50   ` andrzej zaborowski
  0 siblings, 0 replies; 10+ messages in thread
From: andrzej zaborowski @ 2011-07-30  5:50 UTC (permalink / raw)
  To: Vincent Palatin; +Cc: Qemu devel

Hi Vincent,

On 26 July 2011 01:19, Vincent Palatin <vpalatin@chromium.org> wrote:
> We need to check that we are not crossing the boundaries of the card for
> the current access not for the next one which might not happen.
>
> Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
> ---
>  hw/sd.c |   22 ++++++++++++----------
>  1 files changed, 12 insertions(+), 10 deletions(-)
>
> diff --git a/hw/sd.c b/hw/sd.c
> index f48d589..de477fe 100644
> --- a/hw/sd.c
> +++ b/hw/sd.c
> @@ -1451,11 +1451,6 @@ void sd_write_data(SDState *sd, uint8_t value)
>         sd->data[sd->data_offset ++] = value;
>         if (sd->data_offset >= sd->blk_len) {
>             /* TODO: Check CRC before committing */
> -            sd->state = sd_programming_state;
> -            BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
> -            sd->blk_written ++;
> -            sd->data_start += sd->blk_len;
> -            sd->data_offset = 0;
>             if (sd->data_start + sd->blk_len > sd->size) {
>                 sd->card_status |= ADDRESS_ERROR;
>                 break;
> @@ -1464,6 +1459,11 @@ void sd_write_data(SDState *sd, uint8_t value)
>                 sd->card_status |= WP_VIOLATION;
>                 break;
>             }
> +            sd->state = sd_programming_state;
> +            BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
> +            sd->blk_written ++;
> +            sd->data_start += sd->blk_len;
> +            sd->data_offset = 0;
>             sd->csd[14] |= 0x40;
>
>             /* Bzzzzzzztt .... Operation complete.  */
> @@ -1606,17 +1606,19 @@ uint8_t sd_read_data(SDState *sd)
>         break;
>
>     case 18:   /* CMD18:  READ_MULTIPLE_BLOCK */
> -        if (sd->data_offset == 0)
> +        if (sd->data_offset == 0) {
> +            if (sd->data_start + io_len > sd->size) {
> +                sd->card_status |= ADDRESS_ERROR;
> +                ret = 0;
> +                break;
> +            }
>             BLK_READ_BLOCK(sd->data_start, io_len);
> +        }
>         ret = sd->data[sd->data_offset ++];
>
>         if (sd->data_offset >= io_len) {
>             sd->data_start += io_len;
>             sd->data_offset = 0;
> -            if (sd->data_start + io_len > sd->size) {
> -                sd->card_status |= ADDRESS_ERROR;
> -                break;
> -            }
>         }
>         break;

I have to look more closely at the spec but it looks like
ADDRESS_ERROR might not even be the right error code for when we
read/write outside of the memory, because the spec says it's for
incorrect alignment.  Also the first chunk of this patch is
unnecessary now because I committed a patch from Peter Maydell that
did the same thing.

I pushed your patch 01 in the series, thanks.

Cheers

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] sd: add SDHCI and eMMC support
  2011-07-25 23:19 [Qemu-devel] sd: add SDHCI and eMMC support Vincent Palatin
                   ` (6 preceding siblings ...)
  2011-07-25 23:19 ` [Qemu-devel] [PATCH 7/7] sd: compile SDHCI on PCI platforms Vincent Palatin
@ 2011-12-13 16:38 ` Stefan Hajnoczi
  7 siblings, 0 replies; 10+ messages in thread
From: Stefan Hajnoczi @ 2011-12-13 16:38 UTC (permalink / raw)
  To: Vincent Palatin; +Cc: Qemu devel

On Tue, Jul 26, 2011 at 12:19 AM, Vincent Palatin <vpalatin@chromium.org> wrote:
> This patchset adds the support for eMMC as found soldered on many embedded board
> in addition to current support for SD/SDHC cards.
> It also adds a standard SDHCI controller emulation.
> The first patches are a couple of fixes to the current SD code found while
> implementing these features.

Hi Vincent,
I'm curious what the status of your QEMU SD card work is?  It seems a
number of your patches have not been merged and I wonder if you are
still hoping to get them upstream?

Stefan

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2011-12-13 16:39 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-07-25 23:19 [Qemu-devel] sd: add SDHCI and eMMC support Vincent Palatin
2011-07-25 23:19 ` [Qemu-devel] [PATCH 1/7] sd: do not add one sector to the disk size Vincent Palatin
2011-07-25 23:19 ` [Qemu-devel] [PATCH 2/7] sd: fix card size checking on R/W accesses Vincent Palatin
2011-07-30  5:50   ` andrzej zaborowski
2011-07-25 23:19 ` [Qemu-devel] [PATCH 3/7] block: add eMMC block device type Vincent Palatin
2011-07-25 23:19 ` [Qemu-devel] [PATCH 4/7] sd: add eMMC support Vincent Palatin
2011-07-25 23:19 ` [Qemu-devel] [PATCH 5/7] sd: add PCI ids for SDHCI controller Vincent Palatin
2011-07-25 23:19 ` [Qemu-devel] [PATCH 6/7] sd: add SD Host Controller (SDHCI) emulation Vincent Palatin
2011-07-25 23:19 ` [Qemu-devel] [PATCH 7/7] sd: compile SDHCI on PCI platforms Vincent Palatin
2011-12-13 16:38 ` [Qemu-devel] sd: add SDHCI and eMMC support Stefan Hajnoczi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).