at91_mci.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  */
00032 
00053 #include <cfg/arch.h>
00054 #include <cfg/arch/gpio.h>
00055 
00056 #include <errno.h>
00057 #include <string.h>
00058 #include <stdlib.h>
00059 
00060 #include <sys/heap.h>
00061 #include <sys/timer.h>
00062 #include <sys/event.h>
00063 #include <fs/dospart.h>
00064 #include <fs/fs.h>
00065 
00066 #include <dev/blockdev.h>
00067 #include <dev/mmcard.h>
00068 #include <dev/at91_mci.h>
00069 
00070 
00071 #if 0
00072 /* Use for local debugging. */
00073 #define NUTDEBUG
00074 #include <stdio.h>
00075 #endif
00076 
00081 
00082 #ifndef MMC_BLOCK_SIZE
00083 #define MMC_BLOCK_SIZE  512
00084 #endif
00085 
00086 #ifndef MMC_PINS_A
00087 #define MMC_PINS_A  _BV(PA8_MCCK_A)
00088 #endif
00089 
00090 #ifndef MMC_PINS_B
00091 #define MMC_PINS_B  _BV(PA1_MCCDB_B) | _BV(PA0_MCDB0_B) | _BV(PA5_MCDB1_B) | _BV(PA4_MCDB2_B) | _BV(PA3_MCDB3_B)
00092 #endif
00093 
00097 typedef struct _MCIFC {
00099     uint32_t ifc_opcond;
00101     u_int ifc_reladdr;
00103     uint8_t *ifc_buff;
00105     u_int ifc_resp[4];
00106 } MCIFC;
00107 
00111 typedef struct _MCIFCB {
00114     NUTDEVICE *fcb_fsdev;
00115 
00118     DOSPART fcb_part;
00119 
00127     uint32_t fcb_blknum;
00128 
00138     uint8_t fcb_blkbuf[MMC_BLOCK_SIZE];
00139 } MCIFCB;
00140 
00149 static int At91MciInit(NUTDEVICE * dev)
00150 {
00151 #ifdef NUTDEBUG
00152     printf("[DevInit '%s']", dev->dev_name);
00153 #endif
00154     /* Disable PIO lines used for MCI. */
00155     outr(PIOA_PDR, MMC_PINS_A | MMC_PINS_B);
00156     /* Enable peripherals. */
00157     outr(PIOA_ASR, MMC_PINS_A);
00158     outr(PIOA_BSR, MMC_PINS_B);
00159 
00160     /* Disable and software reset. */
00161     outr(MCI_CR, MCI_MCIDIS | MCI_SWRST);
00162     NutSleep(10);
00163 
00164     outr(MCI_CR, MCI_MCIEN);
00165     outr(MCI_IDR, 0xFFFFFFFF);
00166     outr(MCI_DTOR, MCI_DTOMUL_1M | MCI_DTOCYC);
00167     /*
00168      * MMC clock is MCK / (2 * (CLKDIV + 1))
00169      */
00170     //outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (47 << MCI_CLKDIV_LSB));
00171     //outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (15 << MCI_CLKDIV_LSB)); // 180MHz
00172     outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (11 << MCI_CLKDIV_LSB)); // Test
00173     //outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (11 << MCI_CLKDIV_LSB));
00174 
00175     /* Select 1-bit mode and slot B. */
00176     outr(MCI_SDCR, MCI_SDCSEL_SLOTB);
00177 
00178     /* Enable MCI clock. */
00179     outr(PMC_PCER, _BV(MCI_ID));
00180 
00181     return 0;
00182 }
00183 
00184 /*
00185  * Several routines call NutSleep, which results in a context switch.
00186  * This mutual exclusion semaphore takes care, that multiple threads
00187  * do not interfere with each other.
00188  */
00189 static HANDLE mutex;
00190 
00198 static u_int At91MciTxCmd(MCIFC * ifc, u_int cmd, u_int param)
00199 {
00200     u_int sr;
00201     u_int rl;
00202     u_int i;
00203     u_int wfs = MCI_CMDRDY;
00204 
00205 #ifdef NUTDEBUG
00206     printf("[MmcCmd %X", cmd);
00207 #endif
00208     outr(MCI_PTCR, PDC_TXTDIS | PDC_RXTDIS);
00209     if ((cmd & MCI_TRCMD_START) != 0 && ifc->ifc_buff) {
00210         outr(MCI_MR, (inr(MCI_MR) & ~MCI_BLKLEN) | (MMC_BLOCK_SIZE << MCI_BLKLEN_LSB) | MCI_PDCMODE);
00211         outr(MCI_RPR, (u_int) ifc->ifc_buff);
00212         outr(MCI_RCR, MMC_BLOCK_SIZE / 4);
00213         outr(MCI_PTCR, PDC_RXTEN);
00214         wfs = MCI_ENDRX;
00215         ifc->ifc_buff = NULL;
00216     } else {
00217         outr(MCI_RCR, 0);
00218         outr(MCI_RNCR, 0);
00219         outr(MCI_TCR, 0);
00220         outr(MCI_TNCR, 0);
00221     }
00222     outr(MCI_ARGR, param);
00223     outr(MCI_CMDR, cmd);
00224     while (((sr = inr(MCI_SR)) & wfs) == 0);
00225 
00226     if ((cmd & MCI_RSPTYP) == MCI_RSPTYP_48) {
00227         rl = 2;
00228     } else if ((cmd & MCI_RSPTYP) == MCI_RSPTYP_136) {
00229         rl = 4;
00230     } else {
00231         rl = 0;
00232     }
00233     for (i = 0; i < rl; i++) {
00234         ifc->ifc_resp[i] = inr(MCI_RSPR);
00235     }
00236 #ifdef NUTDEBUG
00237     printf("=%X]", sr);
00238 #endif
00239     return sr;
00240 }
00241 
00242 
00250 static int At91MciDiscover(MCIFC * ifc)
00251 {
00252     u_int sr;
00253     int tmo;
00254 
00255     /* Put all cards in idle state. */
00256 #ifdef NUTDEBUG
00257     printf("\n[MciIdle]");
00258 #endif
00259     outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (47 << MCI_CLKDIV_LSB)); // 400kHz during identification
00260     At91MciTxCmd(ifc, MMCMD_GO_IDLE_STATE, 0);
00261 
00262     /* Poll operating conditions. */
00263 #ifdef NUTDEBUG
00264     printf("\n[MciOpCond]");
00265 #endif
00266     for (tmo = 1000; --tmo;) {
00267         sr = At91MciTxCmd(ifc, MCI_OPCMD | MCI_MAXLAT | MCI_RSPTYP_48 | MMCMD_SEND_OP_COND, ifc->ifc_opcond);
00268         if (sr & 0xC06B0000) {
00269             return -1;
00270         }
00271         if ((ifc->ifc_resp[0] & MMCOP_NBUSY) != 0) {
00272             break;
00273         }
00274         ifc->ifc_opcond = ifc->ifc_resp[0];
00275         NutSleep(1);
00276     }
00277     if (tmo == 0) {
00278 #ifdef NUTDEBUG
00279         printf("[Failed]");
00280 #endif
00281         return -1;
00282     }
00283 
00284     /* Discover cards. */
00285 #ifdef NUTDEBUG
00286     printf("\n[MciDiscover]");
00287 #endif
00288     ifc->ifc_reladdr = 0;
00289     for (tmo = 500; --tmo;) {
00290         sr = At91MciTxCmd(ifc, MCI_OPCMD | MCI_MAXLAT | MCI_RSPTYP_136 | MMCMD_ALL_SEND_CID, 0);
00291         if (sr & MCI_RTOE) {
00292             /* No more cards. */
00293             break;
00294         }
00295         ifc->ifc_reladdr++;
00296         At91MciTxCmd(ifc, MCI_MAXLAT | MCI_RSPTYP_48 | MMCMD_SEND_RELATIVE_ADDR, ifc->ifc_reladdr << 16);
00297     }
00298 #ifdef NUTDEBUG
00299     printf("[%u Cards]", ifc->ifc_reladdr);
00300 #endif
00301     outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (1 << MCI_CLKDIV_LSB)); // 10MHz @ 180/4 MHz
00302     return ifc->ifc_reladdr ? 0 : -1;
00303 }
00304 
00315 static int At91MciReadSingle(MCIFC * ifc, uint32_t blk, uint8_t * buf)
00316 {
00317     int rc = -1;
00318     u_int sr;
00319 
00320     /* Gain mutex access. */
00321     NutEventWait(&mutex, 0);
00322 
00323 #ifdef NUTDEBUG
00324     printf("[RB%lu]", blk);
00325 #endif
00326 
00327     sr = At91MciTxCmd(ifc, MCI_MAXLAT | MCI_RSPTYP_48 | MMCMD_SELECT_CARD, ifc->ifc_reladdr << 16);
00328     if ((sr & 0xC07F0000) == 0) {
00329         sr = At91MciTxCmd(ifc, MCI_MAXLAT | MCI_RSPTYP_48 | MMCMD_SET_BLOCKLEN, MMC_BLOCK_SIZE);
00330         if ((sr & 0xC07F0000) == 0) {
00331             ifc->ifc_buff = buf;
00332             sr = At91MciTxCmd(ifc, MCI_TRDIR | MCI_TRCMD_START | MCI_MAXLAT | MCI_RSPTYP_48 |
00333                               MMCMD_READ_SINGLE_BLOCK, blk * MMC_BLOCK_SIZE);
00334             if ((sr & 0xC07F0000) == 0) {
00335                 rc = 0;
00336             }
00337         }
00338         At91MciTxCmd(ifc, MMCMD_SELECT_CARD, 0);
00339     }
00340 
00341     /* Release mutex access. */
00342     NutEventPost(&mutex);
00343 
00344     return rc;
00345 }
00346 
00362 static int At91MciBlockRead(NUTFILE * nfp, void *buffer, int num)
00363 {
00364     MCIFCB *fcb = (MCIFCB *) nfp->nf_fcb;
00365     uint32_t blk = fcb->fcb_blknum;
00366     NUTDEVICE *dev = (NUTDEVICE *) nfp->nf_dev;
00367     MCIFC *ifc = (MCIFC *) dev->dev_icb;
00368 
00369     if (buffer == 0) {
00370         buffer = fcb->fcb_blkbuf;
00371     }
00372     blk += fcb->fcb_part.part_sect_offs;
00373 
00374     if (At91MciReadSingle(ifc, blk, buffer) == 0) {
00375         return 1;
00376     }
00377     return -1;
00378 }
00379 
00397 static int At91MciBlockWrite(NUTFILE * nfp, CONST void *buffer, int num)
00398 {
00399     return -1;
00400 }
00401 
00410 static int At91MciUnmount(NUTFILE * nfp)
00411 {
00412     int rc = -1;
00413 
00414     if (nfp) {
00415         MCIFCB *fcb = (MCIFCB *) nfp->nf_fcb;
00416 
00417         if (fcb) {
00418             rc = fcb->fcb_fsdev->dev_ioctl(fcb->fcb_fsdev, FS_VOL_UNMOUNT, NULL);
00419             NutHeapFree(fcb);
00420         }
00421         NutHeapFree(nfp);
00422     }
00423     return rc;
00424 }
00425 
00451 static NUTFILE *At91MciMount(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00452 {
00453     int partno = 0;
00454     int i;
00455     NUTDEVICE *fsdev;
00456     NUTFILE *nfp;
00457     MCIFCB *fcb;
00458     DOSPART *part;
00459     MCIFC *ifc = (MCIFC *) dev->dev_icb;
00460     FSCP_VOL_MOUNT mparm;
00461 
00462     if (At91MciDiscover(ifc)) {
00463         errno = ENODEV;
00464         return NUTFILE_EOF;
00465     }
00466 
00467     /* Parse the name for a partition number and a file system driver. */
00468     if (*name) {
00469         partno = atoi(name);
00470         do {
00471             name++;
00472         } while (*name && *name != '/');
00473         if (*name == '/') {
00474             name++;
00475         }
00476     }
00477 #ifdef NUTDEBUG
00478     printf("['%s'-PART%d]", name, partno);
00479 #endif
00480 
00481     /*
00482      * Check the list of registered devices for the given name of the
00483      * files system driver. If none has been specified, get the first
00484      * file system driver in the list. Hopefully the application
00485      * registered one only.
00486      */
00487     for (fsdev = nutDeviceList; fsdev; fsdev = fsdev->dev_next) {
00488         if (*name == 0) {
00489             if (fsdev->dev_type == IFTYP_FS) {
00490                 break;
00491             }
00492         } else if (strcmp(fsdev->dev_name, name) == 0) {
00493             break;
00494         }
00495     }
00496 
00497     if (fsdev == 0) {
00498 #ifdef NUTDEBUG
00499         printf("[No FSDriver]");
00500 #endif
00501         errno = ENODEV;
00502         return NUTFILE_EOF;
00503     }
00504 
00505     if ((fcb = NutHeapAllocClear(sizeof(MCIFCB))) == 0) {
00506         errno = ENOMEM;
00507         return NUTFILE_EOF;
00508     }
00509     fcb->fcb_fsdev = fsdev;
00510 
00511     /* Initialize MMC access mutex semaphore. */
00512     NutEventPost(&mutex);
00513 
00514     /* Read MBR. */
00515     if (At91MciReadSingle(ifc, 0, fcb->fcb_blkbuf)) {
00516         NutHeapFree(fcb);
00517         return NUTFILE_EOF;
00518     }
00519 
00520     /* Read partition table. */
00521     part = (DOSPART *) & fcb->fcb_blkbuf[DOSPART_SECTORPOS];
00522     for (i = 1; i <= 4; i++) {
00523         if (partno) {
00524             if (i == partno) {
00525                 /* Found specified partition number. */
00526                 fcb->fcb_part = *part;
00527                 break;
00528             }
00529         } else if (part->part_state & 0x80) {
00530             /* Located first active partition. */
00531             fcb->fcb_part = *part;
00532             break;
00533         }
00534         part++;
00535     }
00536 
00537     if (fcb->fcb_part.part_type == PTYPE_EMPTY) {
00538         NutHeapFree(fcb);
00539         return NUTFILE_EOF;
00540     }
00541 
00542     if ((nfp = NutHeapAlloc(sizeof(NUTFILE))) == 0) {
00543         NutHeapFree(fcb);
00544         errno = ENOMEM;
00545         return NUTFILE_EOF;
00546     }
00547     nfp->nf_next = 0;
00548     nfp->nf_dev = dev;
00549     nfp->nf_fcb = fcb;
00550 
00551     /*
00552      * Mount the file system volume.
00553      */
00554     mparm.fscp_bmnt = nfp;
00555     mparm.fscp_part_type = fcb->fcb_part.part_type;
00556     if (fsdev->dev_ioctl(fsdev, FS_VOL_MOUNT, &mparm)) {
00557         At91MciUnmount(nfp);
00558         return NUTFILE_EOF;
00559     }
00560     return nfp;
00561 }
00562 
00586 static int At91MciIOCtrl(NUTDEVICE * dev, int req, void *conf)
00587 {
00588     int rc = 0;
00589     MCIFC *ifc = (MCIFC *) dev->dev_icb;
00590 
00591     switch (req) {
00592     case NUTBLKDEV_MEDIAAVAIL:
00593         *((int *) conf) = 1;
00594         break;
00595     case NUTBLKDEV_MEDIACHANGE:
00596         *((int *) conf) = 0;
00597         break;
00598     case NUTBLKDEV_INFO:
00599         {
00600             BLKPAR_INFO *par = (BLKPAR_INFO *) conf;
00601             MCIFCB *fcb = (MCIFCB *) par->par_nfp->nf_fcb;
00602 
00603             par->par_nblks = fcb->fcb_part.part_sects;
00604             par->par_blksz = MMC_BLOCK_SIZE;
00605             par->par_blkbp = fcb->fcb_blkbuf;
00606         }
00607         break;
00608     case NUTBLKDEV_SEEK:
00609         {
00610             BLKPAR_SEEK *par = (BLKPAR_SEEK *) conf;
00611             MCIFCB *fcb = (MCIFCB *) par->par_nfp->nf_fcb;
00612 
00613             fcb->fcb_blknum = par->par_blknum;
00614         }
00615         break;
00616     case MMCARD_GETOCR:
00617         *((uint32_t *) conf) = ifc->ifc_opcond;
00618         break;
00619     default:
00620         rc = -1;
00621         break;
00622     }
00623     return rc;
00624 }
00625 
00626 static MCIFC mci0_info;
00627 
00640 NUTDEVICE devAt91Mci0 = {
00641     0,                          
00642     {'M', 'C', 'I', '0', 0, 0, 0, 0, 0}
00643     ,                           
00644     0,                          
00645     0,                          
00646     0,                          
00647     &mci0_info,                 
00648     0,                          
00649     At91MciInit,                
00650     At91MciIOCtrl,              
00651     At91MciBlockRead,           
00652     At91MciBlockWrite,          
00653     At91MciMount,               
00654     At91MciUnmount,             
00655     0                           
00656 };
00657 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/