qemu-arm
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-arm] [PATCH 4/8] bcm2835_emmc: add bcm2835 MMC/SD controller


From: Andrew Baumann
Subject: [Qemu-arm] [PATCH 4/8] bcm2835_emmc: add bcm2835 MMC/SD controller
Date: Thu, 3 Dec 2015 22:01:23 -0800

Signed-off-by: Andrew Baumann <address@hidden>
---
 hw/sd/Makefile.objs          |   1 +
 hw/sd/bcm2835_emmc.c         | 800 +++++++++++++++++++++++++++++++++++++++++++
 include/hw/sd/bcm2835_emmc.h |  56 +++
 3 files changed, 857 insertions(+)
 create mode 100644 hw/sd/bcm2835_emmc.c
 create mode 100644 include/hw/sd/bcm2835_emmc.h

diff --git a/hw/sd/Makefile.objs b/hw/sd/Makefile.objs
index f1aed83..b9cb9b8 100644
--- a/hw/sd/Makefile.objs
+++ b/hw/sd/Makefile.objs
@@ -6,3 +6,4 @@ common-obj-$(CONFIG_SDHCI) += sdhci.o
 obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o
 obj-$(CONFIG_OMAP) += omap_mmc.o
 obj-$(CONFIG_PXA2XX) += pxa2xx_mmci.o
+obj-$(CONFIG_RASPI) += bcm2835_emmc.o
diff --git a/hw/sd/bcm2835_emmc.c b/hw/sd/bcm2835_emmc.c
new file mode 100644
index 0000000..46628b5
--- /dev/null
+++ b/hw/sd/bcm2835_emmc.c
@@ -0,0 +1,800 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * This code is licensed under the GNU GPLv2 and later.
+ */
+
+#include "sysemu/blockdev.h"
+#include "hw/sd/bcm2835_emmc.h"
+
+/*
+ * Controller registers
+ */
+
+#define SDHCI_DMA_ADDRESS   0x00
+#define SDHCI_ARGUMENT2     SDHCI_DMA_ADDRESS
+
+#define SDHCI_BLOCK_SIZE    0x04
+#define  SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
+
+#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_AUTO_CMD12  0x04
+#define  SDHCI_TRNS_AUTO_CMD23  0x08
+#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_ABORTCMD 0xC0
+
+#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_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
+#define SDHCI_GET_CMD(c) ((c>>8) & 0x3f)
+
+#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_DATA_LVL_MASK    0x00F00000
+#define   SDHCI_DATA_LVL_SHIFT  20
+
+#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_PROG_CLOCK_MODE  0x0020
+#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_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \
+        SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX)
+#define  SDHCI_INT_DATA_MASK    (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
+        SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
+        SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
+        SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR)
+#define SDHCI_INT_ALL_MASK  ((unsigned int)-1)
+
+/* Per the BCM2835 ARM peripherals document (p76), some interrupts are
+ * not implemented (reserved) */
+#define BCM2835_DISABLED_INTS (SDHCI_INT_DMA_END | SDHCI_INT_CARD_INSERT \
+                               | SDHCI_INT_CARD_REMOVE)
+
+#define SDHCI_ACMD12_ERR    0x3C
+
+#define SDHCI_HOST_CONTROL2     0x3E
+#define  SDHCI_CTRL_UHS_MASK        0x0007
+#define   SDHCI_CTRL_UHS_SDR12      0x0000
+#define   SDHCI_CTRL_UHS_SDR25      0x0001
+#define   SDHCI_CTRL_UHS_SDR50      0x0002
+#define   SDHCI_CTRL_UHS_SDR104     0x0003
+#define   SDHCI_CTRL_UHS_DDR50      0x0004
+#define  SDHCI_CTRL_VDD_180     0x0008
+#define  SDHCI_CTRL_DRV_TYPE_MASK   0x0030
+#define   SDHCI_CTRL_DRV_TYPE_B     0x0000
+#define   SDHCI_CTRL_DRV_TYPE_A     0x0010
+#define   SDHCI_CTRL_DRV_TYPE_C     0x0020
+#define   SDHCI_CTRL_DRV_TYPE_D     0x0030
+#define  SDHCI_CTRL_EXEC_TUNING     0x0040
+#define  SDHCI_CTRL_TUNED_CLK       0x0080
+#define  SDHCI_CTRL_PRESET_VAL_ENABLE   0x8000
+
+#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_SUPPORT_SDR50    0x00000001
+#define  SDHCI_SUPPORT_SDR104   0x00000002
+#define  SDHCI_SUPPORT_DDR50    0x00000004
+#define  SDHCI_DRIVER_TYPE_A    0x00000010
+#define  SDHCI_DRIVER_TYPE_C    0x00000020
+#define  SDHCI_DRIVER_TYPE_D    0x00000040
+#define  SDHCI_RETUNING_TIMER_COUNT_MASK    0x00000F00
+#define  SDHCI_RETUNING_TIMER_COUNT_SHIFT   8
+#define  SDHCI_USE_SDR50_TUNING         0x00002000
+#define  SDHCI_RETUNING_MODE_MASK       0x0000C000
+#define  SDHCI_RETUNING_MODE_SHIFT      14
+#define  SDHCI_CLOCK_MUL_MASK   0x00FF0000
+#define  SDHCI_CLOCK_MUL_SHIFT  16
+
+#define SDHCI_CAPABILITIES_1    0x44
+
+#define SDHCI_MAX_CURRENT       0x48
+#define  SDHCI_MAX_CURRENT_330_MASK 0x0000FF
+#define  SDHCI_MAX_CURRENT_330_SHIFT    0
+#define  SDHCI_MAX_CURRENT_300_MASK 0x00FF00
+#define  SDHCI_MAX_CURRENT_300_SHIFT    8
+#define  SDHCI_MAX_CURRENT_180_MASK 0xFF0000
+#define  SDHCI_MAX_CURRENT_180_SHIFT    16
+#define   SDHCI_MAX_CURRENT_MULTIPLIER  4
+
+/* 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
+
+/*
+ * End of controller registers.
+ */
+#define MMC_VDD_165_195     0x00000080  /* VDD voltage 1.65 - 1.95 */
+#define MMC_VDD_20_21       0x00000100  /* VDD voltage 2.0 ~ 2.1 */
+#define MMC_VDD_21_22       0x00000200  /* VDD voltage 2.1 ~ 2.2 */
+#define MMC_VDD_22_23       0x00000400  /* VDD voltage 2.2 ~ 2.3 */
+#define MMC_VDD_23_24       0x00000800  /* VDD voltage 2.3 ~ 2.4 */
+#define MMC_VDD_24_25       0x00001000  /* VDD voltage 2.4 ~ 2.5 */
+#define MMC_VDD_25_26       0x00002000  /* VDD voltage 2.5 ~ 2.6 */
+#define MMC_VDD_26_27       0x00004000  /* VDD voltage 2.6 ~ 2.7 */
+#define MMC_VDD_27_28       0x00008000  /* VDD voltage 2.7 ~ 2.8 */
+#define MMC_VDD_28_29       0x00010000  /* VDD voltage 2.8 ~ 2.9 */
+#define MMC_VDD_29_30       0x00020000  /* VDD voltage 2.9 ~ 3.0 */
+#define MMC_VDD_30_31       0x00040000  /* VDD voltage 3.0 ~ 3.1 */
+#define MMC_VDD_31_32       0x00080000  /* VDD voltage 3.1 ~ 3.2 */
+#define MMC_VDD_32_33       0x00100000  /* VDD voltage 3.2 ~ 3.3 */
+#define MMC_VDD_33_34       0x00200000  /* VDD voltage 3.3 ~ 3.4 */
+#define MMC_VDD_34_35       0x00400000  /* VDD voltage 3.4 ~ 3.5 */
+#define MMC_VDD_35_36       0x00800000  /* VDD voltage 3.5 ~ 3.6 */
+
+#define MMC_CAP_4_BIT_DATA  (1 << 0)    /* Can the host do 4 bit transfers */
+#define MMC_CAP_MMC_HIGHSPEED   (1 << 1)    /* Can do MMC high-speed timing */
+#define MMC_CAP_SD_HIGHSPEED    (1 << 2)    /* Can do SD high-speed timing */
+#define MMC_CAP_SDIO_IRQ    (1 << 3)    /* Can signal pending SDIO IRQs */
+#define MMC_CAP_SPI     (1 << 4)    /* Talks only SPI protocols */
+#define MMC_CAP_NEEDS_POLL  (1 << 5)    /* Needs polling for card-detection */
+#define MMC_CAP_8_BIT_DATA  (1 << 6)    /* Can the host do 8 bit transfers */
+#define MMC_CAP_DISABLE     (1 << 7)    /* Can the host be disabled */
+#define MMC_CAP_NONREMOVABLE    (1 << 8)    /* Nonremovable e.g. eMMC */
+#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9)    /* Waits while card is busy */
+#define MMC_CAP_ERASE       (1 << 10)   /* Allow erase/trim commands */
+#define MMC_CAP_1_8V_DDR    (1 << 11)   /* can support */
+                        /* DDR mode at 1.8V */
+#define MMC_CAP_1_2V_DDR    (1 << 12)   /* can support */
+                        /* DDR mode at 1.2V */
+#define MMC_CAP_POWER_OFF_CARD  (1 << 13)   /* Can power off after boot */
+#define MMC_CAP_BUS_WIDTH_TEST  (1 << 14)   /* CMD14/CMD19 bus width ok */
+#define MMC_CAP_UHS_SDR12   (1 << 15) /* Host supports UHS SDR12 mode */
+#define MMC_CAP_UHS_SDR25   (1 << 16) /* Host supports UHS SDR25 mode */
+#define MMC_CAP_UHS_SDR50   (1 << 17) /* Host supports UHS SDR50 mode */
+#define MMC_CAP_UHS_SDR104  (1 << 18) /* Host supports UHS SDR104 mode */
+#define MMC_CAP_UHS_DDR50   (1 << 19) /* Host supports UHS DDR50 mode */
+#define MMC_CAP_SET_XPC_330 (1 << 20) /* Host supports >150mA current at 3.3V 
*/
+#define MMC_CAP_SET_XPC_300 (1 << 21) /* Host supports >150mA current at 3.0V 
*/
+#define MMC_CAP_SET_XPC_180 (1 << 22) /* Host supports >150mA current at 1.8V 
*/
+#define MMC_CAP_DRIVER_TYPE_A   (1 << 23) /* Host supports Driver Type A */
+#define MMC_CAP_DRIVER_TYPE_C   (1 << 24) /* Host supports Driver Type C */
+#define MMC_CAP_DRIVER_TYPE_D   (1 << 25) /* Host supports Driver Type D */
+#define MMC_CAP_MAX_CURRENT_200 (1 << 26) /* Host max current limit is 200mA */
+#define MMC_CAP_MAX_CURRENT_400 (1 << 27) /* Host max current limit is 400mA */
+#define MMC_CAP_MAX_CURRENT_600 (1 << 28) /* Host max current limit is 600mA */
+#define MMC_CAP_MAX_CURRENT_800 (1 << 29) /* Host max current limit is 800mA */
+#define MMC_CAP_CMD23       (1 << 30)   /* CMD23 supported. */
+#define MMC_CAP_HW_RESET    (1 << 31)   /* Hardware reset */
+
+
+#define MMC_CAP2_BOOTPART_NOACC (1 << 0)   /* Boot partition no access */
+#define MMC_CAP2_CACHE_CTRL (1 << 1)       /* Allow cache control */
+#define MMC_CAP2_POWEROFF_NOTIFY (1 << 2)  /* Notify poweroff supported */
+#define MMC_CAP2_NO_MULTI_READ  (1 << 3)   /* Multiblock reads don't work */
+#define MMC_CAP2_FORCE_MULTIBLOCK (1 << 4) /* Always use multiblock transfers 
*/
+
+#define COMPLETION_DELAY (100000)
+
+static void bcm2835_emmc_set_irq(BCM2835EmmcState *s)
+{
+    /* the error bit must be set iff there are any other errors */
+    assert(((s->interrupt & SDHCI_INT_ERROR) == 0)
+           == ((s->interrupt & SDHCI_INT_ERROR_MASK & ~SDHCI_INT_ERROR) == 0));
+
+    if (s->irpt_en & s->irpt_mask & s->interrupt & ~BCM2835_DISABLED_INTS) {
+        qemu_set_irq(s->irq, 1);
+    } else {
+        qemu_set_irq(s->irq, 0);
+    }
+}
+
+static void autocmd12(BCM2835EmmcState *s)
+{
+    SDRequest request;
+    uint8_t response[16];
+
+    if (!(s->cmdtm & SDHCI_TRNS_AUTO_CMD12)) {
+        return;
+    }
+
+    request.cmd = 12;
+    request.arg = 0;
+    request.crc = 0;
+    sd_do_command(s->card, &request, response);
+}
+
+static void autocmd23(BCM2835EmmcState *s)
+{
+    SDRequest request;
+    uint8_t response[16];
+
+    if (!(s->cmdtm & SDHCI_TRNS_AUTO_CMD23)) {
+        return;
+    }
+
+    request.cmd = 23;
+    request.arg = (s->blksizecnt >> 16) & 0xffff;
+    request.crc = 0;
+    sd_do_command(s->card, &request, response);
+}
+
+static void delayed_completion(void *opaque)
+{
+    BCM2835EmmcState *s = (BCM2835EmmcState *)opaque;
+
+    s->interrupt |= SDHCI_INT_DATA_END;
+    autocmd12(s);
+
+    bcm2835_emmc_set_irq(s);
+}
+
+
+static uint64_t bcm2835_emmc_read(void *opaque, hwaddr offset,
+    unsigned size)
+{
+    BCM2835EmmcState *s = (BCM2835EmmcState *)opaque;
+    uint32_t res = 0;
+    uint8_t tmp = 0;
+    int set_irq = 0;
+    uint32_t blkcnt;
+    uint8_t cmd;
+    int64_t now;
+
+    assert(size == 4);
+
+    switch (offset) {
+    case SDHCI_ARGUMENT2:      /* ARG2 */
+        res = s->arg2;
+        break;
+    case SDHCI_BLOCK_SIZE:     /* BLKSIZECNT */
+        res = s->blksizecnt;
+        break;
+    case SDHCI_ARGUMENT:       /* ARG1 */
+        res = s->arg1;
+        break;
+    case SDHCI_TRANSFER_MODE:   /* CMDTM */
+        res = s->cmdtm;
+        break;
+    case SDHCI_RESPONSE+0:      /* RESP0 */
+        res = s->resp0;
+        break;
+    case SDHCI_RESPONSE+4:      /* RESP1 */
+        res = s->resp1;
+        break;
+    case SDHCI_RESPONSE+8:      /* RESP2 */
+        res = s->resp2;
+        break;
+    case SDHCI_RESPONSE+12:      /* RESP3 */
+        res = s->resp3;
+        break;
+    case SDHCI_BUFFER:          /* DATA */
+        cmd = ((s->cmdtm >> (16 + 8)) & 0x3f);
+
+        s->data = 0;
+        tmp = sd_read_data(s->card);
+        s->data |= (tmp << 0);
+        tmp = sd_read_data(s->card);
+        s->data |= (tmp << 8);
+        tmp = sd_read_data(s->card);
+        s->data |= (tmp << 16);
+        tmp = sd_read_data(s->card);
+        s->data |= (tmp << 24);
+
+        s->status |= SDHCI_DATA_AVAILABLE;
+
+        s->bytecnt += 4;
+
+        if (s->bytecnt == 512) {
+            s->bytecnt = 0;
+            if (s->cmdtm & SDHCI_TRNS_BLK_CNT_EN) {
+                blkcnt = (s->blksizecnt >> 16) & 0xffff;
+                blkcnt--;
+                s->blksizecnt = (blkcnt << 16) | (s->blksizecnt & 0xffff);
+                if (blkcnt == 0) {
+                    s->status &= ~SDHCI_DATA_AVAILABLE;
+
+                    if (COMPLETION_DELAY > 0) {
+                        now = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
+                        timer_mod(s->delay_timer,
+                            now + COMPLETION_DELAY);
+                    } else {
+                        s->interrupt |= SDHCI_INT_DATA_END;
+                        autocmd12(s);
+                    }
+                }
+            }
+            if (!s->acmd && (cmd == 17)) {
+                /* Single read */
+                s->status &= ~SDHCI_DATA_AVAILABLE;
+
+                s->interrupt |= SDHCI_INT_DATA_END;
+            }
+        }
+        if (!sd_data_ready(s->card)) {
+            s->status &= ~SDHCI_DATA_AVAILABLE;
+
+            s->interrupt |= SDHCI_INT_DATA_END;
+        }
+
+        if (s->status & SDHCI_DATA_AVAILABLE) {
+            s->interrupt |= SDHCI_INT_DATA_AVAIL;
+        }
+
+        set_irq = 1;
+        res = s->data;
+        break;
+    case SDHCI_PRESENT_STATE:   /* STATUS */
+        res = s->status;
+        break;
+    case SDHCI_HOST_CONTROL:    /* CONTROL0 */
+        res = s->control0;
+        break;
+    case SDHCI_CLOCK_CONTROL:   /* CONTROL1 */
+        res = s->control1;
+        break;
+    case SDHCI_INT_STATUS:      /* INTERRUPT */
+        res = s->interrupt & s->irpt_mask;
+        break;
+    case SDHCI_INT_ENABLE:      /* IRPT_MASK */
+        res = s->irpt_mask;
+        break;
+    case SDHCI_SIGNAL_ENABLE:   /* IRPT_EN */
+        res = s->irpt_en;
+        break;
+    case SDHCI_CAPABILITIES:
+        res = s->caps;
+        break;
+    case SDHCI_CAPABILITIES_1:
+        res = s->caps;
+        break;
+    case SDHCI_ACMD12_ERR:      /* CONTROL2 */
+        res = s->control2;
+        break;
+    case SDHCI_SET_ACMD12_ERROR:    /* FORCE_IRPT */
+        res = s->force_irpt;
+        break;
+    case SDHCI_SLOT_INT_STATUS: /* SLOTISR_VERSION */
+        res = s->slotisr_ver;
+        break;
+    case SDHCI_MAX_CURRENT:
+        res = s->maxcurr;
+        break;
+    case SDHCI_MAX_CURRENT+4:
+        res = s->maxcurr2;
+        break;
+    default:
+        break;
+    }
+
+    if (set_irq) {
+        bcm2835_emmc_set_irq(s);
+    }
+
+    return res;
+}
+
+static void bcm2835_emmc_write(void *opaque, hwaddr offset,
+                        uint64_t value, unsigned size)
+{
+    BCM2835EmmcState *s = (BCM2835EmmcState *)opaque;
+    uint8_t cmd;
+    SDRequest request;
+    uint8_t response[16];
+    int resplen;
+    uint32_t blkcnt;
+    int64_t now;
+
+    assert(size == 4);
+
+    switch (offset) {
+    case SDHCI_ARGUMENT2:      /* ARG2 */
+        s->arg2 = value;
+        break;
+    case SDHCI_BLOCK_SIZE:     /* BLKSIZECNT */
+        s->blksizecnt = value;
+        break;
+    case SDHCI_ARGUMENT:       /* ARG1 */
+        s->arg1 = value;
+        break;
+    case SDHCI_TRANSFER_MODE:   /* CMDTM */
+        s->cmdtm = value;
+        cmd = ((value >> (16 + 8)) & 0x3f);
+
+        if (!s->acmd && (cmd == 18 || cmd == 25)) {
+            autocmd23(s);
+        }
+
+        request.cmd = cmd;
+        request.arg = s->arg1;
+        request.crc = 0;
+
+        s->bytecnt = 0;
+
+        s->status &= ~SDHCI_DATA_AVAILABLE;
+        s->status &= ~SDHCI_SPACE_AVAILABLE;
+
+        resplen = sd_do_command(s->card, &request, response);
+
+        if (resplen > 0) {
+            if (resplen == 4) {
+                s->resp0 = (response[0] << 24)
+                    | (response[1] << 16)
+                    | (response[2] << 8)
+                    | (response[3] << 0);
+                if (!s->acmd && ((cmd == 24) || (cmd == 25))) {
+                    s->status |= SDHCI_SPACE_AVAILABLE;
+                    s->interrupt |= SDHCI_INT_SPACE_AVAIL;
+                }
+            } else if (resplen == 16) {
+                s->resp3 = 0
+                    | (response[1-1] << 16)
+                    | (response[2-1] << 8)
+                    | (response[3-1] << 0);
+                s->resp2 = 0
+                    | (response[0+4-1] << 24)
+                    | (response[1+4-1] << 16)
+                    | (response[2+4-1] << 8)
+                    | (response[3+4-1] << 0);
+                s->resp1 = 0
+                    | (response[0+8-1] << 24)
+                    | (response[1+8-1] << 16)
+                    | (response[2+8-1] << 8)
+                    | (response[3+8-1] << 0);
+                s->resp0 = 0
+                    | (response[0+12-1] << 24)
+                    | (response[1+12-1] << 16)
+                    | (response[2+12-1] << 8)
+                    | (response[3+12-1] << 0);
+            }
+
+            s->interrupt |= SDHCI_INT_RESPONSE;
+
+            if (!s->acmd && (cmd == 12)) {
+                /* Stop transmission */
+                s->status &= ~SDHCI_SPACE_AVAILABLE;
+                s->interrupt |= SDHCI_INT_DATA_END;
+            } else {
+                if (sd_data_ready(s->card)) {
+                    s->status |= SDHCI_DATA_AVAILABLE;
+                    s->interrupt |= SDHCI_INT_DATA_AVAIL;
+                } else {
+                    s->interrupt |= SDHCI_INT_DATA_END;
+                }
+            }
+            bcm2835_emmc_set_irq(s);
+        } else {
+            /* Unrecognized commands */
+            if ((!s->acmd && (cmd == 52))
+                || (!s->acmd && (cmd == 5))
+       ) {
+                s->interrupt |= SDHCI_INT_TIMEOUT;
+                s->interrupt |= SDHCI_INT_ERROR;
+            }
+            if (!s->acmd && (cmd == 0)) {
+                s->interrupt |= SDHCI_INT_RESPONSE;
+            }
+            if (!s->acmd && (cmd == 7)) {
+                s->interrupt |= SDHCI_INT_RESPONSE;
+            }
+            bcm2835_emmc_set_irq(s);
+        }
+        if (cmd == 55) {
+            s->acmd = 1;
+        } else {
+            s->acmd = 0;
+        }
+        break;
+    case SDHCI_BUFFER:          /* DATA */
+        cmd = ((s->cmdtm >> (16 + 8)) & 0x3f);
+
+        s->data = value;
+
+        sd_write_data(s->card, (value >> 0) & 0xff);
+        sd_write_data(s->card, (value >> 8) & 0xff);
+        sd_write_data(s->card, (value >> 16) & 0xff);
+        sd_write_data(s->card, (value >> 24) & 0xff);
+
+        s->status |= SDHCI_SPACE_AVAILABLE;
+
+        s->bytecnt += 4;
+
+        if (s->bytecnt == 512) {
+            s->bytecnt = 0;
+            if (s->cmdtm & SDHCI_TRNS_BLK_CNT_EN) {
+                blkcnt = (s->blksizecnt >> 16) & 0xffff;
+                blkcnt--;
+                s->blksizecnt = (blkcnt << 16) | (s->blksizecnt & 0xffff);
+                if (blkcnt == 0) {
+                    if (COMPLETION_DELAY > 0) {
+                        now = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
+                        timer_mod(s->delay_timer,
+                            now + COMPLETION_DELAY);
+                    } else {
+                        s->interrupt |= SDHCI_INT_DATA_END;
+                        autocmd12(s);
+                    }
+                }
+            }
+            if (!s->acmd && (cmd == 24)) {
+                /* Single write */
+                s->status &= ~SDHCI_SPACE_AVAILABLE;
+
+                s->interrupt |= SDHCI_INT_DATA_END;
+            }
+        }
+
+        if (s->status & SDHCI_SPACE_AVAILABLE) {
+            s->interrupt |= SDHCI_INT_SPACE_AVAIL;
+        }
+
+        bcm2835_emmc_set_irq(s);
+        break;
+    case SDHCI_HOST_CONTROL:    /* CONTROL0 */
+        s->control0 &= ~0x007f0026;
+        value &= 0x007f0026;
+        s->control0 |= value;
+        break;
+    case SDHCI_CLOCK_CONTROL:  /* CONTROL1 */
+        s->control0 &= ~0x070fffe7;
+        value &= 0x070fffe7;
+        if (value & ((SDHCI_RESET_ALL
+            | SDHCI_RESET_CMD
+            | SDHCI_RESET_DATA) << 24)) {
+            /* Reset */
+            /* TODO: implement proper reset logic. In the meantime,
+             * one observed side-effect of reset is clearing the card
+             * inserted bit. */
+            if (value & (SDHCI_RESET_ALL << 24)) {
+                s->interrupt &= ~SDHCI_INT_CARD_INSERT;
+            }
+            value &= ~((SDHCI_RESET_ALL
+                | SDHCI_RESET_CMD
+                | SDHCI_RESET_DATA) << 24);
+        }
+        s->control1 |= value;
+        break;
+    case SDHCI_INT_STATUS:      /* INTERRUPT */
+        s->interrupt &= ~value;
+        /* clearing all error sources also clears the overall error status */
+        if ((s->interrupt & SDHCI_INT_ERROR_MASK) == SDHCI_INT_ERROR) {
+            s->interrupt &= ~SDHCI_INT_ERROR_MASK;
+        }
+        bcm2835_emmc_set_irq(s);
+        break;
+
+    case SDHCI_INT_ENABLE:      /* IRPT_MASK */
+        s->irpt_mask = value;
+        bcm2835_emmc_set_irq(s);
+        break;
+    case SDHCI_SIGNAL_ENABLE:   /* IRPT_EN */
+        s->irpt_en = value;
+        bcm2835_emmc_set_irq(s);
+        break;
+    case SDHCI_ACMD12_ERR:      /* CONTROL2 */
+        s->control2 &= ~0x00e7009f;
+        value &= 0x00e7009f;
+        s->control2 |= value;
+        break;
+    case SDHCI_SET_ACMD12_ERROR:    /* FORCE_IRPT */
+        s->force_irpt = value;
+        break;
+
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps bcm2835_emmc_ops = {
+    .read = bcm2835_emmc_read,
+    .write = bcm2835_emmc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_bcm2835_emmc = {
+    .name = TYPE_BCM2835_EMMC,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void bcm2835_emmc_init(Object *obj)
+{
+    BCM2835EmmcState *s = BCM2835_EMMC(obj);
+
+    memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_emmc_ops, s,
+                          TYPE_BCM2835_EMMC, 0x100000);
+    sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
+    sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq);
+}
+
+static void bcm2835_emmc_realize(DeviceState *dev, Error **errp)
+{
+    BCM2835EmmcState *s = BCM2835_EMMC(dev);
+    DriveInfo *dinfo;
+
+    dinfo = drive_get(IF_SD, 0, 0);
+    if (!dinfo) {
+        error_setg(errp, "bcm2835_emmc: missing SD card");
+        return;
+    }
+    s->card = sd_init(blk_by_legacy_dinfo(dinfo), 0);
+
+    s->arg2 = 0;
+    s->blksizecnt = 0;
+    s->arg1 = 0;
+    s->cmdtm = 0;
+    s->resp0 = 0;
+    s->resp1 = 0;
+    s->resp2 = 0;
+    s->resp3 = 0;
+    s->data = 0;
+    s->status = (0x1ff << 16);
+    s->control0 = 0;
+    s->control1 = SDHCI_CLOCK_INT_STABLE;
+
+    /* Although the Broadcom doc says it is unimplemented/reserved, on
+     * real hardware the card inserted interrupt is actually set at
+     * boot, and is later cleared by a reset command (as observed on a
+     * Raspberry Pi 2). Moreover, EDK2/UEFI depend on seeing this bit
+     * set, so we set it here and later clear it in the reset. */
+    s->interrupt = SDHCI_INT_CARD_INSERT;
+
+    s->irpt_mask = 0;
+    s->irpt_en = 0;
+    s->control2 = 0;
+    s->force_irpt = 0;
+    s->spi_int_spt = 0;
+    s->slotisr_ver = (0x9900 | SDHCI_SPEC_300) << 16;
+    s->caps = 0;
+    s->caps2 = 0;
+    s->maxcurr = 1;
+    s->maxcurr2 = 0;
+
+    s->acmd = 0;
+    s->write_op = 0;
+
+    s->delay_timer = timer_new_us(QEMU_CLOCK_VIRTUAL, delayed_completion, s);
+}
+
+static void bcm2835_emmc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = bcm2835_emmc_realize;
+    dc->vmsd = &vmstate_bcm2835_emmc;
+}
+
+static TypeInfo bcm2835_emmc_info = {
+    .name          = TYPE_BCM2835_EMMC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(BCM2835EmmcState),
+    .class_init    = bcm2835_emmc_class_init,
+    .instance_init = bcm2835_emmc_init,
+};
+
+static void bcm2835_emmc_register_types(void)
+{
+    type_register_static(&bcm2835_emmc_info);
+}
+
+type_init(bcm2835_emmc_register_types)
diff --git a/include/hw/sd/bcm2835_emmc.h b/include/hw/sd/bcm2835_emmc.h
new file mode 100644
index 0000000..b756c67
--- /dev/null
+++ b/include/hw/sd/bcm2835_emmc.h
@@ -0,0 +1,56 @@
+/*
+ * Raspberry Pi emulation (c) 2012 Gregory Estrade
+ * This code is licensed under the GNU GPLv2 and later.
+ */
+
+#ifndef BCM2835_EMMC_H
+#define BCM2835_EMMC_H
+
+#include "hw/sysbus.h"
+#include "hw/sd/sd.h"
+#include "qemu/timer.h"
+
+#define TYPE_BCM2835_EMMC "bcm2835_emmc"
+#define BCM2835_EMMC(obj) \
+        OBJECT_CHECK(BCM2835EmmcState, (obj), TYPE_BCM2835_EMMC)
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+
+    SDState *card;
+
+    uint32_t arg2;
+    uint32_t blksizecnt;
+    uint32_t arg1;
+    uint32_t cmdtm;
+    uint32_t resp0;
+    uint32_t resp1;
+    uint32_t resp2;
+    uint32_t resp3;
+    uint32_t data;
+    uint32_t status;
+    uint32_t control0;
+    uint32_t control1;
+    uint32_t interrupt;
+    uint32_t irpt_mask;
+    uint32_t irpt_en;
+    uint32_t control2;
+    uint32_t force_irpt;
+    uint32_t spi_int_spt;
+    uint32_t slotisr_ver;
+    uint32_t caps;
+    uint32_t caps2;
+    uint32_t maxcurr;
+    uint32_t maxcurr2;
+
+    int acmd;
+    int write_op;
+
+    uint32_t bytecnt;
+
+    QEMUTimer *delay_timer;
+    qemu_irq irq;
+} BCM2835EmmcState;
+
+#endif
-- 
2.5.3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]