multi-mmc support
[qemu] / hw / omap3_mmc.c
1 /*\r
2  * OMAP3 Multimedia Card/Secure Digital/Secure Digital I/O (MMC/SD/SDIO) Card Interface emulation\r
3  *\r
4  * Copyright (C) 2008 yajin  <yajin@vm-kernel.org>\r
5  *\r
6  * This program is free software; you can redistribute it and/or\r
7  * modify it under the terms of the GNU General Public License as\r
8  * published by the Free Software Foundation; either version 2 or\r
9  * (at your option) version 3 of the License.\r
10  *\r
11  * This program is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with this program; if not, write to the Free Software\r
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,\r
19  * MA 02111-1307 USA\r
20  */\r
21 \r
22 \r
23 \r
24 /*The MMCHS of OMAP3530/3430 is different from OMAP1 and OAMP2420.*/\r
25 \r
26 \r
27 #include "hw.h"\r
28 #include "omap.h"\r
29 #include "sd.h"\r
30 \r
31 struct omap3_mmc_s\r
32 {\r
33     qemu_irq irq;\r
34     qemu_irq *dma;\r
35     qemu_irq coverswitch;\r
36     omap_clk clk;\r
37     SDState *card;\r
38 \r
39 \r
40     uint32_t sysconfig;         /*0x10 */\r
41     uint32_t sysstatus;         /*0x14 */\r
42     uint32_t csre;              /*0x24 */\r
43     uint32_t systest;           /*0x28 */\r
44     uint32_t con;               /*0x2c */\r
45     uint32_t pwcnt;             /*0x30 */\r
46     uint32_t blk;               /*0x104 */\r
47     uint32_t arg;               /*0x108 */\r
48     uint32_t cmd;               /*0x10c */\r
49     uint32_t rsp10;             /*0x110 */\r
50     uint32_t rsp32;             /*0x114 */\r
51     uint32_t rsp54;             /*0x118 */\r
52     uint32_t rsp76;             /*0x11c */\r
53     uint32_t data;              /*0x120 */\r
54     uint32_t pstate;            /*0x124 */\r
55     uint32_t hctl;              /*0x128 */\r
56     uint32_t sysctl;            /*0x12c */\r
57     uint32_t stat;              /*0x130 */\r
58     uint32_t ie;                /*0x134 */\r
59     uint32_t ise;               /*0x138 */\r
60     uint32_t ac12;              /*0x13c */\r
61     uint32_t capa;              /*0x140 */\r
62     uint32_t cur_capa;          /*0x148 */\r
63     uint32_t rev;               /*0x1fc */\r
64 \r
65     /*for quick reference */\r
66     uint16_t blen_counter;\r
67     uint16_t nblk_counter;\r
68 \r
69     uint32_t fifo[256];\r
70     int fifo_start;\r
71     int fifo_len;\r
72 \r
73     int ddir;\r
74     int transfer;\r
75     \r
76     uint32_t stat_pending;\r
77 };\r
78 \r
79 \r
80 typedef enum\r
81 {\r
82     sd_nore = 0,                /* no response */\r
83     sd_136_bits = 1,            /*Response Length 136 bits */\r
84     sd_48_bits = 2,             /*Response Length 48 bits */\r
85     sd_48b_bits = 3,            /*Response Length 48 bits with busy after response */\r
86 } omap3_sd_rsp_type_t;\r
87 \r
88 int test=1;\r
89 static void omap3_mmc_interrupts_update(struct omap3_mmc_s *s)\r
90 {\r
91     qemu_set_irq(s->irq, !!((s->stat | s->stat_pending) & s->ise & s->ie));\r
92 }\r
93 \r
94 static void omap3_mmc_fifolevel_update(struct omap3_mmc_s *host,int de)\r
95 {\r
96         if (!de)\r
97                 return;\r
98 \r
99         if (host->ddir)\r
100         {\r
101                 /*card -> host*/\r
102                 if (host->fifo_len<(host->blk&0x7f))\r
103                 {\r
104                         /*DMA has read one byte from fifo*/\r
105                          qemu_irq_lower(host->dma[1]);\r
106                 }\r
107         }\r
108         else\r
109         {\r
110                 if (host->fifo_len>0)\r
111                 {\r
112                         /*DMA has written one byte to fifo*/\r
113                         qemu_irq_lower(host->dma[0]);\r
114                 }\r
115         }\r
116 \r
117 }\r
118 \r
119 static void omap3_mmc_transfer(struct omap3_mmc_s *host, int msbs, int ace,\r
120                                int bce, int de)\r
121 {\r
122     uint8_t value;\r
123     int i;\r
124 \r
125     if (!host->transfer)\r
126         return;\r
127     while (1)\r
128     {\r
129         if (host->ddir)\r
130         {\r
131             /*data read. card->host */\r
132             for (i = 0; i < 4; i++)\r
133             {\r
134                 if (host->blen_counter)\r
135                 {\r
136                    value = sd_read_data(host->card);\r
137                    //if (host->arg==0x13c00)\r
138                    //   printf("value %x ",value);\r
139                     host->fifo[(host->fifo_start + host->fifo_len) & 255] |=\r
140                         value << (i * 8);\r
141                     host->blen_counter--;\r
142                 }\r
143                 else\r
144                     break;\r
145             }\r
146             host->fifo_len++;\r
147         }\r
148         else\r
149         {\r
150             /*data write. host->card */\r
151             if (!host->fifo_len)\r
152                 break;\r
153             for (i = 0; i < 4; i++)\r
154             {\r
155                 if (host->blen_counter)\r
156                 {\r
157                     value = (host->fifo[host->fifo_start] >> (i * 8)) & 0xff;\r
158                     sd_write_data(host->card, value);\r
159                     host->blen_counter--;\r
160                 }\r
161                 else\r
162                     break;\r
163             }\r
164             host->fifo_start++;\r
165             host->fifo_len--;\r
166             host->fifo_start &= 255;\r
167         }\r
168 \r
169         if (host->blen_counter == 0)\r
170         {\r
171             host->nblk_counter--;\r
172             host->blen_counter = host->blk & 0x7ff;\r
173             if (msbs)\r
174             {\r
175                 /*multi block transfer */\r
176                 if (host->nblk_counter == 0)\r
177                 {\r
178                     host->nblk_counter = (host->blk >> 16) & 0xffff;\r
179                     host->transfer = 0;\r
180                     host->stat_pending |= 0x2;        /*tc */\r
181                     break;\r
182                 }\r
183             }\r
184             else\r
185             {\r
186                   /*single block transfer*/\r
187                 host->transfer = 0;\r
188                 host->stat_pending |= 0x2;    /*tc */\r
189                 break;\r
190             }\r
191         }\r
192 \r
193     }\r
194 \r
195     /*transfer complete*/\r
196     if (de)\r
197     {\r
198         /*DMA*/\r
199         if (host->ddir)\r
200         {\r
201                 /*card->host*/\r
202           qemu_irq_raise(host->dma[1]);\r
203         }\r
204         else\r
205         {\r
206                 qemu_irq_raise(host->dma[0]);\r
207         }\r
208         /*clear BRR BWR*/\r
209         host->stat &= ~0x30;\r
210         host->stat_pending &= ~0x30;\r
211     }\r
212    else\r
213     {\r
214         /*not DMA*/\r
215         if (host->ddir)\r
216         {\r
217                 host->pstate |= 0x800;  /*BRE*/\r
218                 host->pstate &= ~0x400;  /*BWE*/  /*can not write*/\r
219                 host->stat_pending |= 0x20;  /*BRR*/\r
220                 host->stat &= ~0x10; /*BWR*/\r
221             host->stat_pending &= ~0x10;\r
222         }\r
223         else\r
224         {\r
225                 host->pstate &= ~0x800;  /*BRE*/\r
226                 host->pstate |= 0x400;  /*BWE*/\r
227                 host->stat_pending |= 0x10;  /*BWR*/\r
228                 host->stat &= ~0x20; /*BRR*/\r
229             host->stat_pending &= ~0x20;\r
230         }\r
231                 \r
232     }\r
233 \r
234         //printf("after MMC TRANS host->stat %x \n",host->stat);\r
235 \r
236    \r
237 }\r
238 \r
239 static void omap3_mmc_command(struct omap3_mmc_s *host, int indx, int dp,\r
240                               omap3_sd_rsp_type_t rsp_type, int ddir)\r
241 {\r
242     uint32_t rspstatus, mask;\r
243     int rsplen, timeout;\r
244     struct sd_request_s request;\r
245     uint8_t response[16];\r
246 \r
247     //printf("CMD %d host->arg %x \n",indx,host->arg);\r
248 \r
249     if ((host->con & 2) && !indx) /* INIT and CMD0 */\r
250     {\r
251         host->stat_pending |= 0x1;\r
252         host->pstate &= 0xfffffffe;\r
253         return;\r
254     }\r
255 \r
256     if (dp)\r
257     {\r
258         host->fifo_start = 0;\r
259         host->fifo_len = 0;\r
260         host->transfer = 1;\r
261         host->ddir = ddir;\r
262     }\r
263     else\r
264         host->transfer = 0;\r
265 \r
266     timeout = 0;\r
267     mask = 0;\r
268     rspstatus = 0;\r
269 \r
270     request.cmd = indx;\r
271     request.arg = host->arg;\r
272     request.crc = 0;            /* FIXME */\r
273 \r
274     rsplen = sd_do_command(host->card, &request, response);\r
275 \r
276     switch (rsp_type)\r
277     {\r
278     case sd_nore:\r
279         rsplen = 0;\r
280         break;\r
281     case sd_136_bits:\r
282         if (rsplen < 16)\r
283         {\r
284             timeout = 1;\r
285             break;\r
286         }\r
287         rsplen = 16;\r
288         host->rsp76 = (response[0] << 24) | (response[1] << 16) |\r
289             (response[2] << 8) | (response[3] << 0);\r
290         host->rsp54 = (response[4] << 24) | (response[5] << 16) |\r
291             (response[6] << 8) | (response[7] << 0);\r
292         host->rsp32 = (response[8] << 24) | (response[9] << 16) |\r
293             (response[10] << 8) | (response[11] << 0);\r
294         host->rsp10 = (response[12] << 24) | (response[13] << 16) |\r
295             (response[14] << 8) | (response[15] << 0);\r
296         break;\r
297     case sd_48_bits:\r
298     case sd_48b_bits:\r
299         if (rsplen < 4)\r
300         {\r
301             timeout = 1;\r
302             break;\r
303         }\r
304         rsplen = 4;\r
305         host->rsp10 = (response[0] << 24) | (response[1] << 16) |\r
306             (response[2] << 8) | (response[3] << 0);\r
307         switch (indx)\r
308         {\r
309         case 41:               /*r3 */\r
310         case 8:                /*r7 */\r
311         case 6:                /*r6 */\r
312             break;\r
313         default:\r
314             mask = OUT_OF_RANGE | ADDRESS_ERROR | BLOCK_LEN_ERROR |\r
315                 ERASE_SEQ_ERROR | ERASE_PARAM | WP_VIOLATION |\r
316                 LOCK_UNLOCK_FAILED | COM_CRC_ERROR | ILLEGAL_COMMAND |\r
317                 CARD_ECC_FAILED | CC_ERROR | SD_ERROR |\r
318                 CID_CSD_OVERWRITE | WP_ERASE_SKIP;\r
319             rspstatus = (response[0] << 24) | (response[1] << 16) |\r
320                 (response[2] << 8) | (response[3] << 0);\r
321 \r
322             break;\r
323 \r
324         }\r
325 \r
326     }\r
327 \r
328     if (rspstatus & mask & host->csre)\r
329         host->stat_pending |= 0x10000000;\r
330     else {\r
331         host->stat &= ~0x10000000;\r
332         host->stat_pending &= ~0x10000000;\r
333     }\r
334 \r
335     if (timeout)\r
336         host->stat_pending |= 0x10000;\r
337     else\r
338         host->stat_pending |= 0x1;\r
339 \r
340     /*do we allow to set the stat bit? */\r
341     host->stat_pending &= host->ie;\r
342 \r
343     if (host->stat_pending & 0xffff0000)\r
344         host->stat_pending |= 0x8000;\r
345 }\r
346 \r
347 static void omap3_mmc_reset(struct omap3_mmc_s *s)\r
348 {\r
349     s->sysconfig = 0x00000015;\r
350     s->con       = 0x00000500;\r
351     s->pstate    = 0x00040000;\r
352     s->capa      = 0x00e10080;\r
353     s->rev       = 0x26000000;\r
354 \r
355     s->fifo_start =0;\r
356     s->fifo_len =0;\r
357 }\r
358 \r
359 static uint32_t omap3_mmc_read(void *opaque, target_phys_addr_t addr)\r
360 {\r
361     struct omap3_mmc_s *s = (struct omap3_mmc_s *) opaque;\r
362     uint32_t i ;\r
363    //if ((offset!=0x12c)&&(offset!=0x120))\r
364    //fprintf(stderr, "%s: addr %03x pc %08x \n", __FUNCTION__, addr, cpu_single_env->regs[15]);\r
365     switch (addr)\r
366     {\r
367     case 0x10:\r
368         return s->sysconfig;\r
369     case 0x14:\r
370         return s->sysstatus | 0x1;      /*reset completed */\r
371     case 0x24:\r
372         return s->csre;\r
373     case 0x28:\r
374         return s->systest;\r
375     case 0x2c: /* MMCHS_CON */\r
376         return s->con;\r
377     case 0x30:\r
378         return s->pwcnt;\r
379     case 0x104: /* MMCHS_BLK */\r
380         return s->blk;\r
381     case 0x108: /* MMCHS_ARG */\r
382         return s->arg;\r
383     case 0x10c:\r
384         return s->cmd;\r
385     case 0x110:\r
386         return s->rsp10;\r
387     case 0x114:\r
388         return s->rsp32;\r
389     case 0x118:\r
390         return s->rsp54;\r
391     case 0x11c:\r
392         return s->rsp76;\r
393     case 0x120:\r
394         /*Read Data */\r
395         i = s->fifo[s->fifo_start];\r
396         /*set the buffer to default value*/\r
397         s->fifo[s->fifo_start] = 0x0;\r
398         if (s->fifo_len == 0) {\r
399             printf("MMC: FIFO underrun\n");\r
400             return i;\r
401         }\r
402         s->fifo_start ++;\r
403         s->fifo_len --;\r
404         s->fifo_start &= 255;\r
405         omap3_mmc_transfer(s,(s->cmd>>5)&1,(s->cmd>>2)&1,(s->cmd>>1)&1,(s->cmd)&1);\r
406         omap3_mmc_fifolevel_update(s,s->cmd&1);\r
407         omap3_mmc_interrupts_update(s);\r
408         return i;\r
409 \r
410     case 0x124: /* MMCHS_PSTATE */\r
411         return s->pstate;\r
412     case 0x128:\r
413         return s->hctl;\r
414     case 0x12c: /* MMCHS_SYSCTL */\r
415         return s->sysctl;\r
416     case 0x130: /* MMCHS_STAT */\r
417         s->stat |= s->stat_pending;\r
418         s->stat_pending = 0;\r
419         //fprintf(stderr, "%s: MMCHS_STAT = %08x\n", __FUNCTION__, s->stat);\r
420         return s->stat;\r
421     case 0x134:\r
422         return s->ie;\r
423     case 0x138:\r
424         return s->ise;\r
425     case 0x13c:\r
426         return s->ac12;\r
427     case 0x140: /* MMCHS_CAPA */\r
428         return s->capa;\r
429     case 0x148:\r
430         return s->cur_capa;\r
431     case 0x1fc:\r
432         return s->rev;\r
433     default:\r
434         OMAP_BAD_REG(addr);\r
435         exit(-1);\r
436         return 0;\r
437     }\r
438 \r
439 }\r
440 \r
441 static void omap3_mmc_write(void *opaque, target_phys_addr_t addr,\r
442                             uint32_t value)\r
443 {\r
444     struct omap3_mmc_s *s = (struct omap3_mmc_s *) opaque;\r
445         //fprintf(stderr, "%s: addr %x value %08x \n", __FUNCTION__, addr, value);\r
446     switch (addr)\r
447     {\r
448     case 0x14:\r
449     case 0x110:\r
450     case 0x114:\r
451     case 0x118:\r
452     case 0x11c:\r
453     case 0x124:\r
454     case 0x13c:\r
455     case 0x1fc:\r
456         OMAP_RO_REG(addr);\r
457         exit(-1);\r
458     case 0x10:\r
459         if (value & 2) omap3_mmc_reset(s);\r
460         s->sysconfig = value & 0x31d;\r
461         break;\r
462     case 0x24:\r
463         s->csre = value;\r
464         break;\r
465     case 0x28:\r
466         s->systest = value;\r
467         break;\r
468     case 0x2c: /* MMCHS_CON */\r
469         if (value & 0x10) {\r
470             fprintf(stderr, "%s: SYSTEST mode is not supported\n", __FUNCTION__);\r
471             exit(-1);\r
472         }\r
473         if (value & 0x20) {\r
474             fprintf(stderr, "%s: 8-bit data width is not supported\n", __FUNCTION__);\r
475             exit(-1);\r
476         }\r
477         s->con = value & 0x1ffff;\r
478         break;\r
479     case 0x30:\r
480         s->pwcnt = value;\r
481         break;\r
482     case 0x104: /* MMCHS_BLK */\r
483         s->blk = value & 0xffff07ff;\r
484         s->blen_counter = value & 0x7ff;\r
485         s->nblk_counter = (value & 0xffff) >> 16;\r
486         break;\r
487     case 0x108: /* MMCHS_ARG */\r
488         s->arg = value;\r
489         break;\r
490     case 0x10c: /* MMCHS_CMD */\r
491         s->cmd = value & 0x3ffb0037;\r
492         omap3_mmc_command(s, (value >> 24) & 0x3f, (value >> 21) & 1,\r
493                           (value >> 16) & 3, (value >> 4) & 1);\r
494         omap3_mmc_transfer(s,(s->cmd>>5)&1,(s->cmd>>2)&1,(s->cmd>>1)&1,(s->cmd)&1);\r
495         omap3_mmc_fifolevel_update(s,s->cmd&0x1);\r
496         omap3_mmc_interrupts_update(s);\r
497         break;\r
498     case 0x120:\r
499         /*data */\r
500         if (s->fifo_len == 256)\r
501             break;\r
502         s->fifo[(s->fifo_start + s->fifo_len) & 255] = value;\r
503         s->fifo_len ++;\r
504         omap3_mmc_transfer(s,(s->cmd>>5)&1,(s->cmd>>2)&1,(s->cmd>>1)&1,(s->cmd)&1);\r
505         omap3_mmc_fifolevel_update(s,s->cmd&0x1);\r
506         omap3_mmc_interrupts_update(s);\r
507         break;\r
508 \r
509     case 0x128: /* MMCHS_HCTL */\r
510         s->hctl = value & 0xf0f0f02;\r
511         break;\r
512     case 0x12c: /* MMCHS_SYSCTL */\r
513         if (value & 0x04000000) { /* SRD */\r
514             s->data    = 0;\r
515                 s->pstate &= ~0x00000f06; /* BRE, BWE, RTA, WTA, DLA, DATI */\r
516                 s->hctl   &= ~0x00030000; /* SGBR, CR */\r
517                 s->stat   &= ~0x00000034; /* BRR, BWR, BGE */\r
518             s->stat_pending &= ~0x00000034;\r
519                 s->fifo_start = 0;\r
520                 s->fifo_len = 0;\r
521         }\r
522         if (value & 0x02000000) { /* SRC */\r
523             s->pstate &= ~0x00000001; /* CMDI */\r
524         }\r
525         if (value & 0x01000000) { /* SRA */\r
526             uint32_t capa = s->capa;\r
527             uint32_t cur_capa = s->cur_capa;\r
528             omap3_mmc_reset(s);\r
529             s->capa = capa;\r
530             s->cur_capa = cur_capa;\r
531         }\r
532         value = (value & ~2) | ((value & 1) << 1); /* copy ICE directly to ICS */\r
533         s->sysctl = value & 0x000fffc7;\r
534         break;\r
535     case 0x130:\r
536         value = value & 0X317f0237;\r
537         s->stat &= ~value;\r
538         /* stat_pending is NOT cleared */\r
539         break;\r
540     case 0x134: /* MMCHS_IE */\r
541         if (!(s->con & 0x4000)) /* if CON:OBIE is clear, ignore write to OBI_ENABLE */\r
542             value = (value & ~0x200) | (s->ie & 0x200);\r
543         s->ie = value & 0x317f0337;\r
544         if (!(s->ie & 0x100)) {\r
545             s->stat &= ~0x100;\r
546             s->stat_pending &= ~0x100;\r
547         }\r
548         omap3_mmc_interrupts_update(s);\r
549         break;\r
550     case 0x138:\r
551         s->ise = value & 0x317f0337;\r
552         omap3_mmc_interrupts_update(s);\r
553         break;\r
554     case 0x140: /* MMCHS_CAPA */\r
555         s->capa = value & 0x07000000;\r
556         break;\r
557     case 0x148:\r
558         s->cur_capa = value & 0xffffff;\r
559         break;\r
560     default:\r
561         OMAP_BAD_REG(addr);\r
562         exit(-1);\r
563     }\r
564 \r
565 }\r
566 static CPUReadMemoryFunc *omap3_mmc_readfn[] = {\r
567     omap_badwidth_read32,\r
568     omap_badwidth_read32,\r
569     omap3_mmc_read,\r
570 };\r
571 \r
572 static CPUWriteMemoryFunc *omap3_mmc_writefn[] = {\r
573     omap_badwidth_write32,\r
574     omap_badwidth_write32,\r
575     omap3_mmc_write,\r
576 };\r
577 \r
578 static void omap3_mmc_enable(struct omap3_mmc_s *s, int enable)\r
579 {\r
580     sd_enable(s->card, enable);\r
581 }\r
582 \r
583 struct omap3_mmc_s *omap3_mmc_init(struct omap_target_agent_s *ta,\r
584                                    BlockDriverState * bd, qemu_irq irq,\r
585                                    qemu_irq dma[], omap_clk fclk, omap_clk iclk)\r
586 {\r
587     int iomemtype;\r
588     struct omap3_mmc_s *s = (struct omap3_mmc_s *)\r
589         qemu_mallocz(sizeof(struct omap3_mmc_s));\r
590 \r
591     s->irq = irq;\r
592     s->dma = dma;\r
593     s->clk = fclk;\r
594 \r
595     omap3_mmc_reset(s);\r
596 \r
597     iomemtype = l4_register_io_memory(0, omap3_mmc_readfn,\r
598                                       omap3_mmc_writefn, s);\r
599     omap_l4_attach(ta, 0, iomemtype);\r
600 \r
601     /* Instantiate the storage */\r
602     if (bd!=NULL) {\r
603         s->card = sd_init(bd, 0);\r
604             omap3_mmc_enable(s,1);\r
605     }\r
606 \r
607     return s;\r
608 }\r
609 \r
610 \r
611 \r
612 \r