xref: /openbmc/qemu/hw/sd/omap_mmc.c (revision fde3af1971f71a7b05dc6bcd6d1c9998b0b2c4c1)
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