18ac5c651SPaolo Bonzini /*
28ac5c651SPaolo Bonzini * OMAP on-chip MMC/SD host emulation.
38ac5c651SPaolo Bonzini *
47abf56eeSPhilippe Mathieu-Daudé * Datasheet: TI Multimedia Card (MMC/SD/SDIO) Interface (SPRU765A)
57abf56eeSPhilippe Mathieu-Daudé *
68ac5c651SPaolo Bonzini * Copyright (C) 2006-2007 Andrzej Zaborowski <balrog@zabor.org>
78ac5c651SPaolo Bonzini *
88ac5c651SPaolo Bonzini * This program is free software; you can redistribute it and/or
98ac5c651SPaolo Bonzini * modify it under the terms of the GNU General Public License as
108ac5c651SPaolo Bonzini * published by the Free Software Foundation; either version 2 or
118ac5c651SPaolo Bonzini * (at your option) version 3 of the License.
128ac5c651SPaolo Bonzini *
138ac5c651SPaolo Bonzini * This program is distributed in the hope that it will be useful,
148ac5c651SPaolo Bonzini * but WITHOUT ANY WARRANTY; without even the implied warranty of
158ac5c651SPaolo Bonzini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
168ac5c651SPaolo Bonzini * GNU General Public License for more details.
178ac5c651SPaolo Bonzini *
188ac5c651SPaolo Bonzini * You should have received a copy of the GNU General Public License along
198ac5c651SPaolo Bonzini * with this program; if not, see <http://www.gnu.org/licenses/>.
208ac5c651SPaolo Bonzini */
2164552b6bSMarkus Armbruster
2217b7f2dbSPeter Maydell #include "qemu/osdep.h"
2325b98b96SPhilippe Mathieu-Daudé #include "qemu/log.h"
2464552b6bSMarkus Armbruster #include "hw/irq.h"
258ac5c651SPaolo Bonzini #include "hw/arm/omap.h"
269006f1e7SPhilippe Mathieu-Daudé #include "hw/sd/sdcard_legacy.h"
278ac5c651SPaolo Bonzini
288ac5c651SPaolo Bonzini struct omap_mmc_s {
298ac5c651SPaolo Bonzini qemu_irq irq;
308ac5c651SPaolo Bonzini qemu_irq *dma;
318ac5c651SPaolo Bonzini qemu_irq coverswitch;
328ac5c651SPaolo Bonzini MemoryRegion iomem;
338ac5c651SPaolo Bonzini omap_clk clk;
348ac5c651SPaolo Bonzini SDState *card;
358ac5c651SPaolo Bonzini uint16_t last_cmd;
368ac5c651SPaolo Bonzini uint16_t sdio;
378ac5c651SPaolo Bonzini uint16_t rsp[8];
388ac5c651SPaolo Bonzini uint32_t arg;
398ac5c651SPaolo Bonzini int lines;
408ac5c651SPaolo Bonzini int dw;
418ac5c651SPaolo Bonzini int mode;
428ac5c651SPaolo Bonzini int enable;
438ac5c651SPaolo Bonzini int be;
448ac5c651SPaolo Bonzini int rev;
458ac5c651SPaolo Bonzini uint16_t status;
468ac5c651SPaolo Bonzini uint16_t mask;
478ac5c651SPaolo Bonzini uint8_t cto;
488ac5c651SPaolo Bonzini uint16_t dto;
498ac5c651SPaolo Bonzini int clkdiv;
508ac5c651SPaolo Bonzini uint16_t fifo[32];
518ac5c651SPaolo Bonzini int fifo_start;
528ac5c651SPaolo Bonzini int fifo_len;
538ac5c651SPaolo Bonzini uint16_t blen;
548ac5c651SPaolo Bonzini uint16_t blen_counter;
558ac5c651SPaolo Bonzini uint16_t nblk;
568ac5c651SPaolo Bonzini uint16_t nblk_counter;
578ac5c651SPaolo Bonzini int tx_dma;
588ac5c651SPaolo Bonzini int rx_dma;
598ac5c651SPaolo Bonzini int af_level;
608ac5c651SPaolo Bonzini int ae_level;
618ac5c651SPaolo Bonzini
628ac5c651SPaolo Bonzini int ddir;
638ac5c651SPaolo Bonzini int transfer;
648ac5c651SPaolo Bonzini
658ac5c651SPaolo Bonzini int cdet_wakeup;
668ac5c651SPaolo Bonzini int cdet_enable;
678ac5c651SPaolo Bonzini int cdet_state;
688ac5c651SPaolo Bonzini qemu_irq cdet;
698ac5c651SPaolo Bonzini };
708ac5c651SPaolo Bonzini
omap_mmc_interrupts_update(struct omap_mmc_s * s)718ac5c651SPaolo Bonzini static void omap_mmc_interrupts_update(struct omap_mmc_s *s)
728ac5c651SPaolo Bonzini {
738ac5c651SPaolo Bonzini qemu_set_irq(s->irq, !!(s->status & s->mask));
748ac5c651SPaolo Bonzini }
758ac5c651SPaolo Bonzini
omap_mmc_fifolevel_update(struct omap_mmc_s * host)768ac5c651SPaolo Bonzini static void omap_mmc_fifolevel_update(struct omap_mmc_s *host)
778ac5c651SPaolo Bonzini {
788ac5c651SPaolo Bonzini if (!host->transfer && !host->fifo_len) {
798ac5c651SPaolo Bonzini host->status &= 0xf3ff;
808ac5c651SPaolo Bonzini return;
818ac5c651SPaolo Bonzini }
828ac5c651SPaolo Bonzini
838ac5c651SPaolo Bonzini if (host->fifo_len > host->af_level && host->ddir) {
848ac5c651SPaolo Bonzini if (host->rx_dma) {
858ac5c651SPaolo Bonzini host->status &= 0xfbff;
868ac5c651SPaolo Bonzini qemu_irq_raise(host->dma[1]);
878ac5c651SPaolo Bonzini } else
888ac5c651SPaolo Bonzini host->status |= 0x0400;
898ac5c651SPaolo Bonzini } else {
908ac5c651SPaolo Bonzini host->status &= 0xfbff;
918ac5c651SPaolo Bonzini qemu_irq_lower(host->dma[1]);
928ac5c651SPaolo Bonzini }
938ac5c651SPaolo Bonzini
948ac5c651SPaolo Bonzini if (host->fifo_len < host->ae_level && !host->ddir) {
958ac5c651SPaolo Bonzini if (host->tx_dma) {
968ac5c651SPaolo Bonzini host->status &= 0xf7ff;
978ac5c651SPaolo Bonzini qemu_irq_raise(host->dma[0]);
988ac5c651SPaolo Bonzini } else
998ac5c651SPaolo Bonzini host->status |= 0x0800;
1008ac5c651SPaolo Bonzini } else {
1018ac5c651SPaolo Bonzini qemu_irq_lower(host->dma[0]);
1028ac5c651SPaolo Bonzini host->status &= 0xf7ff;
1038ac5c651SPaolo Bonzini }
1048ac5c651SPaolo Bonzini }
1058ac5c651SPaolo Bonzini
106*fde3af19SPeter Maydell /* These must match the encoding of the MMC_CMD Response field */
1078ac5c651SPaolo Bonzini typedef enum {
1088ac5c651SPaolo Bonzini sd_nore = 0, /* no response */
1098ac5c651SPaolo Bonzini sd_r1, /* normal response command */
1108ac5c651SPaolo Bonzini sd_r2, /* CID, CSD registers */
1118ac5c651SPaolo Bonzini sd_r3, /* OCR register */
1128ac5c651SPaolo Bonzini sd_r6 = 6, /* Published RCA response */
1138ac5c651SPaolo Bonzini sd_r1b = -1,
1148ac5c651SPaolo Bonzini } sd_rsp_type_t;
1158ac5c651SPaolo Bonzini
116*fde3af19SPeter Maydell /* These must match the encoding of the MMC_CMD Type field */
117*fde3af19SPeter Maydell typedef enum {
118*fde3af19SPeter Maydell SD_TYPE_BC = 0, /* broadcast -- no response */
119*fde3af19SPeter Maydell SD_TYPE_BCR = 1, /* broadcast with response */
120*fde3af19SPeter Maydell SD_TYPE_AC = 2, /* addressed -- no data transfer */
121*fde3af19SPeter Maydell SD_TYPE_ADTC = 3, /* addressed with data transfer */
122*fde3af19SPeter Maydell } MMCCmdType;
123*fde3af19SPeter Maydell
omap_mmc_command(struct omap_mmc_s * host,int cmd,int dir,MMCCmdType type,int busy,sd_rsp_type_t resptype,int init)1248ac5c651SPaolo Bonzini static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir,
125*fde3af19SPeter Maydell MMCCmdType type, int busy,
126*fde3af19SPeter Maydell sd_rsp_type_t resptype, int init)
1278ac5c651SPaolo Bonzini {
1288ac5c651SPaolo Bonzini uint32_t rspstatus, mask;
1298ac5c651SPaolo Bonzini int rsplen, timeout;
1308ac5c651SPaolo Bonzini SDRequest request;
1318ac5c651SPaolo Bonzini uint8_t response[16];
1328ac5c651SPaolo Bonzini
1338ac5c651SPaolo Bonzini if (init && cmd == 0) {
1348ac5c651SPaolo Bonzini host->status |= 0x0001;
1358ac5c651SPaolo Bonzini return;
1368ac5c651SPaolo Bonzini }
1378ac5c651SPaolo Bonzini
1388ac5c651SPaolo Bonzini if (resptype == sd_r1 && busy)
1398ac5c651SPaolo Bonzini resptype = sd_r1b;
1408ac5c651SPaolo Bonzini
141*fde3af19SPeter Maydell if (type == SD_TYPE_ADTC) {
1428ac5c651SPaolo Bonzini host->fifo_start = 0;
1438ac5c651SPaolo Bonzini host->fifo_len = 0;
1448ac5c651SPaolo Bonzini host->transfer = 1;
1458ac5c651SPaolo Bonzini host->ddir = dir;
1468ac5c651SPaolo Bonzini } else
1478ac5c651SPaolo Bonzini host->transfer = 0;
1488ac5c651SPaolo Bonzini timeout = 0;
1498ac5c651SPaolo Bonzini mask = 0;
1508ac5c651SPaolo Bonzini rspstatus = 0;
1518ac5c651SPaolo Bonzini
1528ac5c651SPaolo Bonzini request.cmd = cmd;
1538ac5c651SPaolo Bonzini request.arg = host->arg;
1548ac5c651SPaolo Bonzini request.crc = 0; /* FIXME */
1558ac5c651SPaolo Bonzini
1568ac5c651SPaolo Bonzini rsplen = sd_do_command(host->card, &request, response);
1578ac5c651SPaolo Bonzini
1588ac5c651SPaolo Bonzini /* TODO: validate CRCs */
1598ac5c651SPaolo Bonzini switch (resptype) {
1608ac5c651SPaolo Bonzini case sd_nore:
1618ac5c651SPaolo Bonzini rsplen = 0;
1628ac5c651SPaolo Bonzini break;
1638ac5c651SPaolo Bonzini
1648ac5c651SPaolo Bonzini case sd_r1:
1658ac5c651SPaolo Bonzini case sd_r1b:
1668ac5c651SPaolo Bonzini if (rsplen < 4) {
1678ac5c651SPaolo Bonzini timeout = 1;
1688ac5c651SPaolo Bonzini break;
1698ac5c651SPaolo Bonzini }
1708ac5c651SPaolo Bonzini rsplen = 4;
1718ac5c651SPaolo Bonzini
1728ac5c651SPaolo Bonzini mask = OUT_OF_RANGE | ADDRESS_ERROR | BLOCK_LEN_ERROR |
1738ac5c651SPaolo Bonzini ERASE_SEQ_ERROR | ERASE_PARAM | WP_VIOLATION |
1748ac5c651SPaolo Bonzini LOCK_UNLOCK_FAILED | COM_CRC_ERROR | ILLEGAL_COMMAND |
1758ac5c651SPaolo Bonzini CARD_ECC_FAILED | CC_ERROR | SD_ERROR |
1768ac5c651SPaolo Bonzini CID_CSD_OVERWRITE;
1778ac5c651SPaolo Bonzini if (host->sdio & (1 << 13))
1788ac5c651SPaolo Bonzini mask |= AKE_SEQ_ERROR;
179b3141c06SPhilippe Mathieu-Daudé rspstatus = ldl_be_p(response);
1808ac5c651SPaolo Bonzini break;
1818ac5c651SPaolo Bonzini
1828ac5c651SPaolo Bonzini case sd_r2:
1838ac5c651SPaolo Bonzini if (rsplen < 16) {
1848ac5c651SPaolo Bonzini timeout = 1;
1858ac5c651SPaolo Bonzini break;
1868ac5c651SPaolo Bonzini }
1878ac5c651SPaolo Bonzini rsplen = 16;
1888ac5c651SPaolo Bonzini break;
1898ac5c651SPaolo Bonzini
1908ac5c651SPaolo Bonzini case sd_r3:
1918ac5c651SPaolo Bonzini if (rsplen < 4) {
1928ac5c651SPaolo Bonzini timeout = 1;
1938ac5c651SPaolo Bonzini break;
1948ac5c651SPaolo Bonzini }
1958ac5c651SPaolo Bonzini rsplen = 4;
1968ac5c651SPaolo Bonzini
197b3141c06SPhilippe Mathieu-Daudé rspstatus = ldl_be_p(response);
1988ac5c651SPaolo Bonzini if (rspstatus & 0x80000000)
1998ac5c651SPaolo Bonzini host->status &= 0xe000;
2008ac5c651SPaolo Bonzini else
2018ac5c651SPaolo Bonzini host->status |= 0x1000;
2028ac5c651SPaolo Bonzini break;
2038ac5c651SPaolo Bonzini
2048ac5c651SPaolo Bonzini case sd_r6:
2058ac5c651SPaolo Bonzini if (rsplen < 4) {
2068ac5c651SPaolo Bonzini timeout = 1;
2078ac5c651SPaolo Bonzini break;
2088ac5c651SPaolo Bonzini }
2098ac5c651SPaolo Bonzini rsplen = 4;
2108ac5c651SPaolo Bonzini
2118ac5c651SPaolo Bonzini mask = 0xe000 | AKE_SEQ_ERROR;
2128ac5c651SPaolo Bonzini rspstatus = (response[2] << 8) | (response[3] << 0);
2138ac5c651SPaolo Bonzini }
2148ac5c651SPaolo Bonzini
2158ac5c651SPaolo Bonzini if (rspstatus & mask)
2168ac5c651SPaolo Bonzini host->status |= 0x4000;
2178ac5c651SPaolo Bonzini else
2188ac5c651SPaolo Bonzini host->status &= 0xb000;
2198ac5c651SPaolo Bonzini
2208ac5c651SPaolo Bonzini if (rsplen)
2218ac5c651SPaolo Bonzini for (rsplen = 0; rsplen < 8; rsplen ++)
2228ac5c651SPaolo Bonzini host->rsp[~rsplen & 7] = response[(rsplen << 1) | 1] |
2238ac5c651SPaolo Bonzini (response[(rsplen << 1) | 0] << 8);
2248ac5c651SPaolo Bonzini
2258ac5c651SPaolo Bonzini if (timeout)
2268ac5c651SPaolo Bonzini host->status |= 0x0080;
2278ac5c651SPaolo Bonzini else if (cmd == 12)
2288ac5c651SPaolo Bonzini host->status |= 0x0005; /* Makes it more real */
2298ac5c651SPaolo Bonzini else
2308ac5c651SPaolo Bonzini host->status |= 0x0001;
2318ac5c651SPaolo Bonzini }
2328ac5c651SPaolo Bonzini
omap_mmc_transfer(struct omap_mmc_s * host)2338ac5c651SPaolo Bonzini static void omap_mmc_transfer(struct omap_mmc_s *host)
2348ac5c651SPaolo Bonzini {
2358ac5c651SPaolo Bonzini uint8_t value;
2368ac5c651SPaolo Bonzini
2378ac5c651SPaolo Bonzini if (!host->transfer)
2388ac5c651SPaolo Bonzini return;
2398ac5c651SPaolo Bonzini
2408ac5c651SPaolo Bonzini while (1) {
2418ac5c651SPaolo Bonzini if (host->ddir) {
2428ac5c651SPaolo Bonzini if (host->fifo_len > host->af_level)
2438ac5c651SPaolo Bonzini break;
2448ac5c651SPaolo Bonzini
245c769a88dSPhilippe Mathieu-Daudé value = sd_read_byte(host->card);
2468ac5c651SPaolo Bonzini host->fifo[(host->fifo_start + host->fifo_len) & 31] = value;
2478ac5c651SPaolo Bonzini if (-- host->blen_counter) {
248c769a88dSPhilippe Mathieu-Daudé value = sd_read_byte(host->card);
2498ac5c651SPaolo Bonzini host->fifo[(host->fifo_start + host->fifo_len) & 31] |=
2508ac5c651SPaolo Bonzini value << 8;
2518ac5c651SPaolo Bonzini host->blen_counter --;
2528ac5c651SPaolo Bonzini }
2538ac5c651SPaolo Bonzini
2548ac5c651SPaolo Bonzini host->fifo_len ++;
2558ac5c651SPaolo Bonzini } else {
2568ac5c651SPaolo Bonzini if (!host->fifo_len)
2578ac5c651SPaolo Bonzini break;
2588ac5c651SPaolo Bonzini
2598ac5c651SPaolo Bonzini value = host->fifo[host->fifo_start] & 0xff;
260c769a88dSPhilippe Mathieu-Daudé sd_write_byte(host->card, value);
2618ac5c651SPaolo Bonzini if (-- host->blen_counter) {
2628ac5c651SPaolo Bonzini value = host->fifo[host->fifo_start] >> 8;
263c769a88dSPhilippe Mathieu-Daudé sd_write_byte(host->card, value);
2648ac5c651SPaolo Bonzini host->blen_counter --;
2658ac5c651SPaolo Bonzini }
2668ac5c651SPaolo Bonzini
2678ac5c651SPaolo Bonzini host->fifo_start ++;
2688ac5c651SPaolo Bonzini host->fifo_len --;
2698ac5c651SPaolo Bonzini host->fifo_start &= 31;
2708ac5c651SPaolo Bonzini }
2718ac5c651SPaolo Bonzini
2728ac5c651SPaolo Bonzini if (host->blen_counter == 0) {
2738ac5c651SPaolo Bonzini host->nblk_counter --;
2748ac5c651SPaolo Bonzini host->blen_counter = host->blen;
2758ac5c651SPaolo Bonzini
2768ac5c651SPaolo Bonzini if (host->nblk_counter == 0) {
2778ac5c651SPaolo Bonzini host->nblk_counter = host->nblk;
2788ac5c651SPaolo Bonzini host->transfer = 0;
2798ac5c651SPaolo Bonzini host->status |= 0x0008;
2808ac5c651SPaolo Bonzini break;
2818ac5c651SPaolo Bonzini }
2828ac5c651SPaolo Bonzini }
2838ac5c651SPaolo Bonzini }
2848ac5c651SPaolo Bonzini }
2858ac5c651SPaolo Bonzini
omap_mmc_update(void * opaque)2868ac5c651SPaolo Bonzini static void omap_mmc_update(void *opaque)
2878ac5c651SPaolo Bonzini {
2888ac5c651SPaolo Bonzini struct omap_mmc_s *s = opaque;
2898ac5c651SPaolo Bonzini omap_mmc_transfer(s);
2908ac5c651SPaolo Bonzini omap_mmc_fifolevel_update(s);
2918ac5c651SPaolo Bonzini omap_mmc_interrupts_update(s);
2928ac5c651SPaolo Bonzini }
2938ac5c651SPaolo Bonzini
omap_mmc_pseudo_reset(struct omap_mmc_s * host)2947abf56eeSPhilippe Mathieu-Daudé static void omap_mmc_pseudo_reset(struct omap_mmc_s *host)
2957abf56eeSPhilippe Mathieu-Daudé {
2967abf56eeSPhilippe Mathieu-Daudé host->status = 0;
2977abf56eeSPhilippe Mathieu-Daudé host->fifo_len = 0;
2987abf56eeSPhilippe Mathieu-Daudé }
2997abf56eeSPhilippe Mathieu-Daudé
omap_mmc_reset(struct omap_mmc_s * host)3008ac5c651SPaolo Bonzini void omap_mmc_reset(struct omap_mmc_s *host)
3018ac5c651SPaolo Bonzini {
3028ac5c651SPaolo Bonzini host->last_cmd = 0;
3038ac5c651SPaolo Bonzini memset(host->rsp, 0, sizeof(host->rsp));
3048ac5c651SPaolo Bonzini host->arg = 0;
3058ac5c651SPaolo Bonzini host->dw = 0;
3068ac5c651SPaolo Bonzini host->mode = 0;
3078ac5c651SPaolo Bonzini host->enable = 0;
3088ac5c651SPaolo Bonzini host->mask = 0;
3098ac5c651SPaolo Bonzini host->cto = 0;
3108ac5c651SPaolo Bonzini host->dto = 0;
3118ac5c651SPaolo Bonzini host->blen = 0;
3128ac5c651SPaolo Bonzini host->blen_counter = 0;
3138ac5c651SPaolo Bonzini host->nblk = 0;
3148ac5c651SPaolo Bonzini host->nblk_counter = 0;
3158ac5c651SPaolo Bonzini host->tx_dma = 0;
3168ac5c651SPaolo Bonzini host->rx_dma = 0;
3178ac5c651SPaolo Bonzini host->ae_level = 0x00;
3188ac5c651SPaolo Bonzini host->af_level = 0x1f;
3198ac5c651SPaolo Bonzini host->transfer = 0;
3208ac5c651SPaolo Bonzini host->cdet_wakeup = 0;
3218ac5c651SPaolo Bonzini host->cdet_enable = 0;
3228ac5c651SPaolo Bonzini qemu_set_irq(host->coverswitch, host->cdet_state);
3238ac5c651SPaolo Bonzini host->clkdiv = 0;
324ecd219f7SPeter Maydell
3257abf56eeSPhilippe Mathieu-Daudé omap_mmc_pseudo_reset(host);
3267abf56eeSPhilippe Mathieu-Daudé
327ecd219f7SPeter Maydell /* Since we're still using the legacy SD API the card is not plugged
328ecd219f7SPeter Maydell * into any bus, and we must reset it manually. When omap_mmc is
329ecd219f7SPeter Maydell * QOMified this must move into the QOM reset function.
330ecd219f7SPeter Maydell */
331f16a3bf8SPeter Maydell device_cold_reset(DEVICE(host->card));
3328ac5c651SPaolo Bonzini }
3338ac5c651SPaolo Bonzini
omap_mmc_read(void * opaque,hwaddr offset,unsigned size)334a75ed3c4SPhilippe Mathieu-Daudé static uint64_t omap_mmc_read(void *opaque, hwaddr offset, unsigned size)
3358ac5c651SPaolo Bonzini {
3368ac5c651SPaolo Bonzini uint16_t i;
337a75ed3c4SPhilippe Mathieu-Daudé struct omap_mmc_s *s = opaque;
3388ac5c651SPaolo Bonzini
3398ac5c651SPaolo Bonzini if (size != 2) {
3408ac5c651SPaolo Bonzini return omap_badwidth_read16(opaque, offset);
3418ac5c651SPaolo Bonzini }
3428ac5c651SPaolo Bonzini
3438ac5c651SPaolo Bonzini switch (offset) {
3448ac5c651SPaolo Bonzini case 0x00: /* MMC_CMD */
3458ac5c651SPaolo Bonzini return s->last_cmd;
3468ac5c651SPaolo Bonzini
3478ac5c651SPaolo Bonzini case 0x04: /* MMC_ARGL */
3488ac5c651SPaolo Bonzini return s->arg & 0x0000ffff;
3498ac5c651SPaolo Bonzini
3508ac5c651SPaolo Bonzini case 0x08: /* MMC_ARGH */
3518ac5c651SPaolo Bonzini return s->arg >> 16;
3528ac5c651SPaolo Bonzini
3538ac5c651SPaolo Bonzini case 0x0c: /* MMC_CON */
3548ac5c651SPaolo Bonzini return (s->dw << 15) | (s->mode << 12) | (s->enable << 11) |
3558ac5c651SPaolo Bonzini (s->be << 10) | s->clkdiv;
3568ac5c651SPaolo Bonzini
3578ac5c651SPaolo Bonzini case 0x10: /* MMC_STAT */
3588ac5c651SPaolo Bonzini return s->status;
3598ac5c651SPaolo Bonzini
3608ac5c651SPaolo Bonzini case 0x14: /* MMC_IE */
3618ac5c651SPaolo Bonzini return s->mask;
3628ac5c651SPaolo Bonzini
3638ac5c651SPaolo Bonzini case 0x18: /* MMC_CTO */
3648ac5c651SPaolo Bonzini return s->cto;
3658ac5c651SPaolo Bonzini
3668ac5c651SPaolo Bonzini case 0x1c: /* MMC_DTO */
3678ac5c651SPaolo Bonzini return s->dto;
3688ac5c651SPaolo Bonzini
3698ac5c651SPaolo Bonzini case 0x20: /* MMC_DATA */
3708ac5c651SPaolo Bonzini /* TODO: support 8-bit access */
3718ac5c651SPaolo Bonzini i = s->fifo[s->fifo_start];
3728ac5c651SPaolo Bonzini if (s->fifo_len == 0) {
3738ac5c651SPaolo Bonzini printf("MMC: FIFO underrun\n");
3748ac5c651SPaolo Bonzini return i;
3758ac5c651SPaolo Bonzini }
3768ac5c651SPaolo Bonzini s->fifo_start ++;
3778ac5c651SPaolo Bonzini s->fifo_len --;
3788ac5c651SPaolo Bonzini s->fifo_start &= 31;
3798ac5c651SPaolo Bonzini omap_mmc_transfer(s);
3808ac5c651SPaolo Bonzini omap_mmc_fifolevel_update(s);
3818ac5c651SPaolo Bonzini omap_mmc_interrupts_update(s);
3828ac5c651SPaolo Bonzini return i;
3838ac5c651SPaolo Bonzini
3848ac5c651SPaolo Bonzini case 0x24: /* MMC_BLEN */
3858ac5c651SPaolo Bonzini return s->blen_counter;
3868ac5c651SPaolo Bonzini
3878ac5c651SPaolo Bonzini case 0x28: /* MMC_NBLK */
3888ac5c651SPaolo Bonzini return s->nblk_counter;
3898ac5c651SPaolo Bonzini
3908ac5c651SPaolo Bonzini case 0x2c: /* MMC_BUF */
3918ac5c651SPaolo Bonzini return (s->rx_dma << 15) | (s->af_level << 8) |
3928ac5c651SPaolo Bonzini (s->tx_dma << 7) | s->ae_level;
3938ac5c651SPaolo Bonzini
3948ac5c651SPaolo Bonzini case 0x30: /* MMC_SPI */
3958ac5c651SPaolo Bonzini return 0x0000;
3968ac5c651SPaolo Bonzini case 0x34: /* MMC_SDIO */
3978ac5c651SPaolo Bonzini return (s->cdet_wakeup << 2) | (s->cdet_enable) | s->sdio;
3988ac5c651SPaolo Bonzini case 0x38: /* MMC_SYST */
3998ac5c651SPaolo Bonzini return 0x0000;
4008ac5c651SPaolo Bonzini
4018ac5c651SPaolo Bonzini case 0x3c: /* MMC_REV */
4028ac5c651SPaolo Bonzini return s->rev;
4038ac5c651SPaolo Bonzini
4048ac5c651SPaolo Bonzini case 0x40: /* MMC_RSP0 */
4058ac5c651SPaolo Bonzini case 0x44: /* MMC_RSP1 */
4068ac5c651SPaolo Bonzini case 0x48: /* MMC_RSP2 */
4078ac5c651SPaolo Bonzini case 0x4c: /* MMC_RSP3 */
4088ac5c651SPaolo Bonzini case 0x50: /* MMC_RSP4 */
4098ac5c651SPaolo Bonzini case 0x54: /* MMC_RSP5 */
4108ac5c651SPaolo Bonzini case 0x58: /* MMC_RSP6 */
4118ac5c651SPaolo Bonzini case 0x5c: /* MMC_RSP7 */
4128ac5c651SPaolo Bonzini return s->rsp[(offset - 0x40) >> 2];
4138ac5c651SPaolo Bonzini
4148ac5c651SPaolo Bonzini /* OMAP2-specific */
4158ac5c651SPaolo Bonzini case 0x60: /* MMC_IOSR */
4168ac5c651SPaolo Bonzini case 0x64: /* MMC_SYSC */
4178ac5c651SPaolo Bonzini return 0;
4188ac5c651SPaolo Bonzini case 0x68: /* MMC_SYSS */
4198ac5c651SPaolo Bonzini return 1; /* RSTD */
4208ac5c651SPaolo Bonzini }
4218ac5c651SPaolo Bonzini
4228ac5c651SPaolo Bonzini OMAP_BAD_REG(offset);
4238ac5c651SPaolo Bonzini return 0;
4248ac5c651SPaolo Bonzini }
4258ac5c651SPaolo Bonzini
omap_mmc_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)4268ac5c651SPaolo Bonzini static void omap_mmc_write(void *opaque, hwaddr offset,
4278ac5c651SPaolo Bonzini uint64_t value, unsigned size)
4288ac5c651SPaolo Bonzini {
4298ac5c651SPaolo Bonzini int i;
430a75ed3c4SPhilippe Mathieu-Daudé struct omap_mmc_s *s = opaque;
4318ac5c651SPaolo Bonzini
4328ac5c651SPaolo Bonzini if (size != 2) {
43377a8257eSStefan Weil omap_badwidth_write16(opaque, offset, value);
43477a8257eSStefan Weil return;
4358ac5c651SPaolo Bonzini }
4368ac5c651SPaolo Bonzini
4378ac5c651SPaolo Bonzini switch (offset) {
4388ac5c651SPaolo Bonzini case 0x00: /* MMC_CMD */
4398ac5c651SPaolo Bonzini if (!s->enable)
4408ac5c651SPaolo Bonzini break;
4418ac5c651SPaolo Bonzini
4428ac5c651SPaolo Bonzini s->last_cmd = value;
4438ac5c651SPaolo Bonzini for (i = 0; i < 8; i ++)
4448ac5c651SPaolo Bonzini s->rsp[i] = 0x0000;
4458ac5c651SPaolo Bonzini omap_mmc_command(s, value & 63, (value >> 15) & 1,
446*fde3af19SPeter Maydell (MMCCmdType)((value >> 12) & 3),
4478ac5c651SPaolo Bonzini (value >> 11) & 1,
4488ac5c651SPaolo Bonzini (sd_rsp_type_t) ((value >> 8) & 7),
4498ac5c651SPaolo Bonzini (value >> 7) & 1);
4508ac5c651SPaolo Bonzini omap_mmc_update(s);
4518ac5c651SPaolo Bonzini break;
4528ac5c651SPaolo Bonzini
4538ac5c651SPaolo Bonzini case 0x04: /* MMC_ARGL */
4548ac5c651SPaolo Bonzini s->arg &= 0xffff0000;
4558ac5c651SPaolo Bonzini s->arg |= 0x0000ffff & value;
4568ac5c651SPaolo Bonzini break;
4578ac5c651SPaolo Bonzini
4588ac5c651SPaolo Bonzini case 0x08: /* MMC_ARGH */
4598ac5c651SPaolo Bonzini s->arg &= 0x0000ffff;
4608ac5c651SPaolo Bonzini s->arg |= value << 16;
4618ac5c651SPaolo Bonzini break;
4628ac5c651SPaolo Bonzini
4638ac5c651SPaolo Bonzini case 0x0c: /* MMC_CON */
4648ac5c651SPaolo Bonzini s->dw = (value >> 15) & 1;
4658ac5c651SPaolo Bonzini s->mode = (value >> 12) & 3;
4668ac5c651SPaolo Bonzini s->enable = (value >> 11) & 1;
4678ac5c651SPaolo Bonzini s->be = (value >> 10) & 1;
4688ac5c651SPaolo Bonzini s->clkdiv = (value >> 0) & (s->rev >= 2 ? 0x3ff : 0xff);
46925b98b96SPhilippe Mathieu-Daudé if (s->mode != 0) {
47025b98b96SPhilippe Mathieu-Daudé qemu_log_mask(LOG_UNIMP,
47125b98b96SPhilippe Mathieu-Daudé "omap_mmc_wr: mode #%i unimplemented\n", s->mode);
47225b98b96SPhilippe Mathieu-Daudé }
47325b98b96SPhilippe Mathieu-Daudé if (s->be != 0) {
47425b98b96SPhilippe Mathieu-Daudé qemu_log_mask(LOG_UNIMP,
47525b98b96SPhilippe Mathieu-Daudé "omap_mmc_wr: Big Endian not implemented\n");
47625b98b96SPhilippe Mathieu-Daudé }
4778ac5c651SPaolo Bonzini if (s->dw != 0 && s->lines < 4)
4788ac5c651SPaolo Bonzini printf("4-bit SD bus enabled\n");
4798ac5c651SPaolo Bonzini if (!s->enable)
4807abf56eeSPhilippe Mathieu-Daudé omap_mmc_pseudo_reset(s);
4818ac5c651SPaolo Bonzini break;
4828ac5c651SPaolo Bonzini
4838ac5c651SPaolo Bonzini case 0x10: /* MMC_STAT */
4848ac5c651SPaolo Bonzini s->status &= ~value;
4858ac5c651SPaolo Bonzini omap_mmc_interrupts_update(s);
4868ac5c651SPaolo Bonzini break;
4878ac5c651SPaolo Bonzini
4888ac5c651SPaolo Bonzini case 0x14: /* MMC_IE */
4898ac5c651SPaolo Bonzini s->mask = value & 0x7fff;
4908ac5c651SPaolo Bonzini omap_mmc_interrupts_update(s);
4918ac5c651SPaolo Bonzini break;
4928ac5c651SPaolo Bonzini
4938ac5c651SPaolo Bonzini case 0x18: /* MMC_CTO */
4948ac5c651SPaolo Bonzini s->cto = value & 0xff;
4958ac5c651SPaolo Bonzini if (s->cto > 0xfd && s->rev <= 1)
4968ac5c651SPaolo Bonzini printf("MMC: CTO of 0xff and 0xfe cannot be used!\n");
4978ac5c651SPaolo Bonzini break;
4988ac5c651SPaolo Bonzini
4998ac5c651SPaolo Bonzini case 0x1c: /* MMC_DTO */
5008ac5c651SPaolo Bonzini s->dto = value & 0xffff;
5018ac5c651SPaolo Bonzini break;
5028ac5c651SPaolo Bonzini
5038ac5c651SPaolo Bonzini case 0x20: /* MMC_DATA */
5048ac5c651SPaolo Bonzini /* TODO: support 8-bit access */
5058ac5c651SPaolo Bonzini if (s->fifo_len == 32)
5068ac5c651SPaolo Bonzini break;
5078ac5c651SPaolo Bonzini s->fifo[(s->fifo_start + s->fifo_len) & 31] = value;
5088ac5c651SPaolo Bonzini s->fifo_len ++;
5098ac5c651SPaolo Bonzini omap_mmc_transfer(s);
5108ac5c651SPaolo Bonzini omap_mmc_fifolevel_update(s);
5118ac5c651SPaolo Bonzini omap_mmc_interrupts_update(s);
5128ac5c651SPaolo Bonzini break;
5138ac5c651SPaolo Bonzini
5148ac5c651SPaolo Bonzini case 0x24: /* MMC_BLEN */
5158ac5c651SPaolo Bonzini s->blen = (value & 0x07ff) + 1;
5168ac5c651SPaolo Bonzini s->blen_counter = s->blen;
5178ac5c651SPaolo Bonzini break;
5188ac5c651SPaolo Bonzini
5198ac5c651SPaolo Bonzini case 0x28: /* MMC_NBLK */
5208ac5c651SPaolo Bonzini s->nblk = (value & 0x07ff) + 1;
5218ac5c651SPaolo Bonzini s->nblk_counter = s->nblk;
5228ac5c651SPaolo Bonzini s->blen_counter = s->blen;
5238ac5c651SPaolo Bonzini break;
5248ac5c651SPaolo Bonzini
5258ac5c651SPaolo Bonzini case 0x2c: /* MMC_BUF */
5268ac5c651SPaolo Bonzini s->rx_dma = (value >> 15) & 1;
5278ac5c651SPaolo Bonzini s->af_level = (value >> 8) & 0x1f;
5288ac5c651SPaolo Bonzini s->tx_dma = (value >> 7) & 1;
5298ac5c651SPaolo Bonzini s->ae_level = value & 0x1f;
5308ac5c651SPaolo Bonzini
5318ac5c651SPaolo Bonzini if (s->rx_dma)
5328ac5c651SPaolo Bonzini s->status &= 0xfbff;
5338ac5c651SPaolo Bonzini if (s->tx_dma)
5348ac5c651SPaolo Bonzini s->status &= 0xf7ff;
5358ac5c651SPaolo Bonzini omap_mmc_fifolevel_update(s);
5368ac5c651SPaolo Bonzini omap_mmc_interrupts_update(s);
5378ac5c651SPaolo Bonzini break;
5388ac5c651SPaolo Bonzini
5398ac5c651SPaolo Bonzini /* SPI, SDIO and TEST modes unimplemented */
5408ac5c651SPaolo Bonzini case 0x30: /* MMC_SPI (OMAP1 only) */
5418ac5c651SPaolo Bonzini break;
5428ac5c651SPaolo Bonzini case 0x34: /* MMC_SDIO */
5438ac5c651SPaolo Bonzini s->sdio = value & (s->rev >= 2 ? 0xfbf3 : 0x2020);
5448ac5c651SPaolo Bonzini s->cdet_wakeup = (value >> 9) & 1;
5458ac5c651SPaolo Bonzini s->cdet_enable = (value >> 2) & 1;
5468ac5c651SPaolo Bonzini break;
5478ac5c651SPaolo Bonzini case 0x38: /* MMC_SYST */
5488ac5c651SPaolo Bonzini break;
5498ac5c651SPaolo Bonzini
5508ac5c651SPaolo Bonzini case 0x3c: /* MMC_REV */
5518ac5c651SPaolo Bonzini case 0x40: /* MMC_RSP0 */
5528ac5c651SPaolo Bonzini case 0x44: /* MMC_RSP1 */
5538ac5c651SPaolo Bonzini case 0x48: /* MMC_RSP2 */
5548ac5c651SPaolo Bonzini case 0x4c: /* MMC_RSP3 */
5558ac5c651SPaolo Bonzini case 0x50: /* MMC_RSP4 */
5568ac5c651SPaolo Bonzini case 0x54: /* MMC_RSP5 */
5578ac5c651SPaolo Bonzini case 0x58: /* MMC_RSP6 */
5588ac5c651SPaolo Bonzini case 0x5c: /* MMC_RSP7 */
5598ac5c651SPaolo Bonzini OMAP_RO_REG(offset);
5608ac5c651SPaolo Bonzini break;
5618ac5c651SPaolo Bonzini
5628ac5c651SPaolo Bonzini /* OMAP2-specific */
5638ac5c651SPaolo Bonzini case 0x60: /* MMC_IOSR */
5648ac5c651SPaolo Bonzini if (value & 0xf)
5658ac5c651SPaolo Bonzini printf("MMC: SDIO bits used!\n");
5668ac5c651SPaolo Bonzini break;
5678ac5c651SPaolo Bonzini case 0x64: /* MMC_SYSC */
5688ac5c651SPaolo Bonzini if (value & (1 << 2)) /* SRTS */
5698ac5c651SPaolo Bonzini omap_mmc_reset(s);
5708ac5c651SPaolo Bonzini break;
5718ac5c651SPaolo Bonzini case 0x68: /* MMC_SYSS */
5728ac5c651SPaolo Bonzini OMAP_RO_REG(offset);
5738ac5c651SPaolo Bonzini break;
5748ac5c651SPaolo Bonzini
5758ac5c651SPaolo Bonzini default:
5768ac5c651SPaolo Bonzini OMAP_BAD_REG(offset);
5778ac5c651SPaolo Bonzini }
5788ac5c651SPaolo Bonzini }
5798ac5c651SPaolo Bonzini
5808ac5c651SPaolo Bonzini static const MemoryRegionOps omap_mmc_ops = {
5818ac5c651SPaolo Bonzini .read = omap_mmc_read,
5828ac5c651SPaolo Bonzini .write = omap_mmc_write,
5838ac5c651SPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN,
5848ac5c651SPaolo Bonzini };
5858ac5c651SPaolo Bonzini
omap_mmc_init(hwaddr base,MemoryRegion * sysmem,BlockBackend * blk,qemu_irq irq,qemu_irq dma[],omap_clk clk)5868ac5c651SPaolo Bonzini struct omap_mmc_s *omap_mmc_init(hwaddr base,
5878ac5c651SPaolo Bonzini MemoryRegion *sysmem,
588a75ed3c4SPhilippe Mathieu-Daudé BlockBackend *blk,
5898ac5c651SPaolo Bonzini qemu_irq irq, qemu_irq dma[], omap_clk clk)
5908ac5c651SPaolo Bonzini {
5918ac5c651SPaolo Bonzini struct omap_mmc_s *s = g_new0(struct omap_mmc_s, 1);
5928ac5c651SPaolo Bonzini
5938ac5c651SPaolo Bonzini s->irq = irq;
5948ac5c651SPaolo Bonzini s->dma = dma;
5958ac5c651SPaolo Bonzini s->clk = clk;
5968ac5c651SPaolo Bonzini s->lines = 1; /* TODO: needs to be settable per-board */
5978ac5c651SPaolo Bonzini s->rev = 1;
5988ac5c651SPaolo Bonzini
5998ac5c651SPaolo Bonzini memory_region_init_io(&s->iomem, NULL, &omap_mmc_ops, s, "omap.mmc", 0x800);
6008ac5c651SPaolo Bonzini memory_region_add_subregion(sysmem, base, &s->iomem);
6018ac5c651SPaolo Bonzini
6028ac5c651SPaolo Bonzini /* Instantiate the storage */
6038ac5c651SPaolo Bonzini s->card = sd_init(blk, false);
6048ac5c651SPaolo Bonzini if (s->card == NULL) {
6058ac5c651SPaolo Bonzini exit(1);
6064be74634SMarkus Armbruster }
6078ac5c651SPaolo Bonzini
6088ac5c651SPaolo Bonzini omap_mmc_reset(s);
609b45c03f5SMarkus Armbruster
6108ac5c651SPaolo Bonzini return s;
6118ac5c651SPaolo Bonzini }
6128ac5c651SPaolo Bonzini