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 
00050 #include <cfg/arch.h>
00051 #include <cfg/arch/gpio.h>
00052 
00053 #include <errno.h>
00054 #include <string.h>
00055 #include <stdlib.h>
00056 
00057 #include <sys/heap.h>
00058 #include <sys/timer.h>
00059 #include <sys/event.h>
00060 #include <fs/dospart.h>
00061 #include <fs/fs.h>
00062 
00063 #include <dev/blockdev.h>
00064 #include <dev/mmcard.h>
00065 #include <dev/at91_mci.h>
00066 
00067 
00068 #if 0
00069 /* Use for local debugging. */
00070 #define NUTDEBUG
00071 #include <stdio.h>
00072 #endif
00073 
00078 
00079 #ifndef MMC_BLOCK_SIZE
00080 #define MMC_BLOCK_SIZE  512
00081 #endif
00082 
00083 #ifndef MMC_PINS_A
00084 #define MMC_PINS_A  _BV(PA8_MCCK_A)
00085 #endif
00086 
00087 #ifndef MMC_PINS_B
00088 #define MMC_PINS_B  _BV(PA1_MCCDB_B) | _BV(PA0_MCDB0_B) | _BV(PA5_MCDB1_B) | _BV(PA4_MCDB2_B) | _BV(PA3_MCDB3_B)
00089 #endif
00090 
00094 typedef struct _MCIFC {
00096     u_long ifc_opcond;
00098     u_int ifc_reladdr;
00100     u_char *ifc_buff;
00102     u_int ifc_resp[4];
00103 } MCIFC;
00104 
00108 typedef struct _MCIFCB {
00111     NUTDEVICE *fcb_fsdev;
00112 
00115     DOSPART fcb_part;
00116 
00124     u_long fcb_blknum;
00125 
00135     u_char fcb_blkbuf[MMC_BLOCK_SIZE];
00136 } MCIFCB;
00137 
00146 static int At91MciInit(NUTDEVICE * dev)
00147 {
00148 #ifdef NUTDEBUG
00149     printf("[DevInit '%s']", dev->dev_name);
00150 #endif
00151     /* Disable PIO lines used for MCI. */
00152     outr(PIOA_PDR, MMC_PINS_A | MMC_PINS_B);
00153     /* Enable peripherals. */
00154     outr(PIOA_ASR, MMC_PINS_A);
00155     outr(PIOA_BSR, MMC_PINS_B);
00156 
00157     /* Disable and software reset. */
00158     outr(MCI_CR, MCI_MCIDIS | MCI_SWRST);
00159     NutSleep(10);
00160 
00161     outr(MCI_CR, MCI_MCIEN);
00162     outr(MCI_IDR, 0xFFFFFFFF);
00163     outr(MCI_DTOR, MCI_DTOMUL_1M | MCI_DTOCYC);
00164     /*
00165      * MMC clock is MCK / (2 * (CLKDIV + 1))
00166      */
00167     //outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (47 << MCI_CLKDIV_LSB));
00168     //outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (15 << MCI_CLKDIV_LSB)); // 180MHz
00169     outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (11 << MCI_CLKDIV_LSB)); // Test
00170     //outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (11 << MCI_CLKDIV_LSB));
00171 
00172     /* Select 1-bit mode and slot B. */
00173     outr(MCI_SDCR, MCI_SDCSEL_SLOTB);
00174 
00175     /* Enable MCI clock. */
00176     outr(PMC_PCER, _BV(MCI_ID));
00177 
00178     return 0;
00179 }
00180 
00181 /*
00182  * Several routines call NutSleep, which results in a context switch.
00183  * This mutual exclusion semaphore takes care, that multiple threads
00184  * do not interfere with each other.
00185  */
00186 static HANDLE mutex;
00187 
00195 static u_int At91MciTxCmd(MCIFC * ifc, u_int cmd, u_int param)
00196 {
00197     u_int sr;
00198     u_int rl;
00199     u_int i;
00200     u_int wfs = MCI_CMDRDY;
00201 
00202 #ifdef NUTDEBUG
00203     printf("[MmcCmd %X", cmd);
00204 #endif
00205     outr(MCI_PTCR, PDC_TXTDIS | PDC_RXTDIS);
00206     if ((cmd & MCI_TRCMD_START) != 0 && ifc->ifc_buff) {
00207         outr(MCI_MR, (inr(MCI_MR) & ~MCI_BLKLEN) | (MMC_BLOCK_SIZE << MCI_BLKLEN_LSB) | MCI_PDCMODE);
00208         outr(MCI_RPR, (u_int) ifc->ifc_buff);
00209         outr(MCI_RCR, MMC_BLOCK_SIZE / 4);
00210         outr(MCI_PTCR, PDC_RXTEN);
00211         wfs = MCI_ENDRX;
00212         ifc->ifc_buff = NULL;
00213     } else {
00214         outr(MCI_RCR, 0);
00215         outr(MCI_RNCR, 0);
00216         outr(MCI_TCR, 0);
00217         outr(MCI_TNCR, 0);
00218     }
00219     outr(MCI_ARGR, param);
00220     outr(MCI_CMDR, cmd);
00221     while (((sr = inr(MCI_SR)) & wfs) == 0);
00222 
00223     if ((cmd & MCI_RSPTYP) == MCI_RSPTYP_48) {
00224         rl = 2;
00225     } else if ((cmd & MCI_RSPTYP) == MCI_RSPTYP_136) {
00226         rl = 4;
00227     } else {
00228         rl = 0;
00229     }
00230     for (i = 0; i < rl; i++) {
00231         ifc->ifc_resp[i] = inr(MCI_RSPR);
00232     }
00233 #ifdef NUTDEBUG
00234     printf("=%X]", sr);
00235 #endif
00236     return sr;
00237 }
00238 
00239 
00247 static int At91MciDiscover(MCIFC * ifc)
00248 {
00249     u_int sr;
00250     int tmo;
00251 
00252     /* Put all cards in idle state. */
00253 #ifdef NUTDEBUG
00254     printf("\n[MciIdle]");
00255 #endif
00256     outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (47 << MCI_CLKDIV_LSB)); // 400kHz during identification
00257     At91MciTxCmd(ifc, MMCMD_GO_IDLE_STATE, 0);
00258 
00259     /* Poll operating conditions. */
00260 #ifdef NUTDEBUG
00261     printf("\n[MciOpCond]");
00262 #endif
00263     for (tmo = 1000; --tmo;) {
00264         sr = At91MciTxCmd(ifc, MCI_OPCMD | MCI_MAXLAT | MCI_RSPTYP_48 | MMCMD_SEND_OP_COND, ifc->ifc_opcond);
00265         if (sr & 0xC06B0000) {
00266             return -1;
00267         }
00268         if ((ifc->ifc_resp[0] & MMCOP_NBUSY) != 0) {
00269             break;
00270         }
00271         ifc->ifc_opcond = ifc->ifc_resp[0];
00272         NutSleep(1);
00273     }
00274     if (tmo == 0) {
00275 #ifdef NUTDEBUG
00276         printf("[Failed]");
00277 #endif
00278         return -1;
00279     }
00280 
00281     /* Discover cards. */
00282 #ifdef NUTDEBUG
00283     printf("\n[MciDiscover]");
00284 #endif
00285     ifc->ifc_reladdr = 0;
00286     for (tmo = 500; --tmo;) {
00287         sr = At91MciTxCmd(ifc, MCI_OPCMD | MCI_MAXLAT | MCI_RSPTYP_136 | MMCMD_ALL_SEND_CID, 0);
00288         if (sr & MCI_RTOE) {
00289             /* No more cards. */
00290             break;
00291         }
00292         ifc->ifc_reladdr++;
00293         At91MciTxCmd(ifc, MCI_MAXLAT | MCI_RSPTYP_48 | MMCMD_SEND_RELATIVE_ADDR, ifc->ifc_reladdr << 16);
00294     }
00295 #ifdef NUTDEBUG
00296     printf("[%u Cards]", ifc->ifc_reladdr);
00297 #endif
00298     outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (1 << MCI_CLKDIV_LSB)); // 10MHz @ 180/4 MHz
00299     return ifc->ifc_reladdr ? 0 : -1;
00300 }
00301 
00312 static int At91MciReadSingle(MCIFC * ifc, u_long blk, u_char * buf)
00313 {
00314     int rc = -1;
00315     u_int sr;
00316 
00317     /* Gain mutex access. */
00318     NutEventWait(&mutex, 0);
00319 
00320 #ifdef NUTDEBUG
00321     printf("[RB%lu]", blk);
00322 #endif
00323 
00324     sr = At91MciTxCmd(ifc, MCI_MAXLAT | MCI_RSPTYP_48 | MMCMD_SELECT_CARD, ifc->ifc_reladdr << 16);
00325     if ((sr & 0xC07F0000) == 0) {
00326         sr = At91MciTxCmd(ifc, MCI_MAXLAT | MCI_RSPTYP_48 | MMCMD_SET_BLOCKLEN, MMC_BLOCK_SIZE);
00327         if ((sr & 0xC07F0000) == 0) {
00328             ifc->ifc_buff = buf;
00329             sr = At91MciTxCmd(ifc, MCI_TRDIR | MCI_TRCMD_START | MCI_MAXLAT | MCI_RSPTYP_48 |
00330                               MMCMD_READ_SINGLE_BLOCK, blk * MMC_BLOCK_SIZE);
00331             if ((sr & 0xC07F0000) == 0) {
00332                 rc = 0;
00333             }
00334         }
00335         At91MciTxCmd(ifc, MMCMD_SELECT_CARD, 0);
00336     }
00337 
00338     /* Release mutex access. */
00339     NutEventPost(&mutex);
00340 
00341     return rc;
00342 }
00343 
00359 static int At91MciBlockRead(NUTFILE * nfp, void *buffer, int num)
00360 {
00361     MCIFCB *fcb = (MCIFCB *) nfp->nf_fcb;
00362     u_long blk = fcb->fcb_blknum;
00363     NUTDEVICE *dev = (NUTDEVICE *) nfp->nf_dev;
00364     MCIFC *ifc = (MCIFC *) dev->dev_icb;
00365 
00366     if (buffer == 0) {
00367         buffer = fcb->fcb_blkbuf;
00368     }
00369     blk += fcb->fcb_part.part_sect_offs;
00370 
00371     if (At91MciReadSingle(ifc, blk, buffer) == 0) {
00372         return 1;
00373     }
00374     return -1;
00375 }
00376 
00394 static int At91MciBlockWrite(NUTFILE * nfp, CONST void *buffer, int num)
00395 {
00396     return -1;
00397 }
00398 
00407 static int At91MciUnmount(NUTFILE * nfp)
00408 {
00409     int rc = -1;
00410 
00411     if (nfp) {
00412         MCIFCB *fcb = (MCIFCB *) nfp->nf_fcb;
00413 
00414         if (fcb) {
00415             rc = fcb->fcb_fsdev->dev_ioctl(fcb->fcb_fsdev, FS_VOL_UNMOUNT, NULL);
00416             NutHeapFree(fcb);
00417         }
00418         NutHeapFree(nfp);
00419     }
00420     return rc;
00421 }
00422 
00448 static NUTFILE *At91MciMount(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00449 {
00450     int partno = 0;
00451     int i;
00452     NUTDEVICE *fsdev;
00453     NUTFILE *nfp;
00454     MCIFCB *fcb;
00455     DOSPART *part;
00456     MCIFC *ifc = (MCIFC *) dev->dev_icb;
00457     FSCP_VOL_MOUNT mparm;
00458 
00459     if (At91MciDiscover(ifc)) {
00460         errno = ENODEV;
00461         return NUTFILE_EOF;
00462     }
00463 
00464     /* Parse the name for a partition number and a file system driver. */
00465     if (*name) {
00466         partno = atoi(name);
00467         do {
00468             name++;
00469         } while (*name && *name != '/');
00470         if (*name == '/') {
00471             name++;
00472         }
00473     }
00474 #ifdef NUTDEBUG
00475     printf("['%s'-PART%d]", name, partno);
00476 #endif
00477 
00478     /*
00479      * Check the list of registered devices for the given name of the
00480      * files system driver. If none has been specified, get the first
00481      * file system driver in the list. Hopefully the application
00482      * registered one only.
00483      */
00484     for (fsdev = nutDeviceList; fsdev; fsdev = fsdev->dev_next) {
00485         if (*name == 0) {
00486             if (fsdev->dev_type == IFTYP_FS) {
00487                 break;
00488             }
00489         } else if (strcmp(fsdev->dev_name, name) == 0) {
00490             break;
00491         }
00492     }
00493 
00494     if (fsdev == 0) {
00495 #ifdef NUTDEBUG
00496         printf("[No FSDriver]");
00497 #endif
00498         errno = ENODEV;
00499         return NUTFILE_EOF;
00500     }
00501 
00502     if ((fcb = NutHeapAllocClear(sizeof(MCIFCB))) == 0) {
00503         errno = ENOMEM;
00504         return NUTFILE_EOF;
00505     }
00506     fcb->fcb_fsdev = fsdev;
00507 
00508     /* Initialize MMC access mutex semaphore. */
00509     NutEventPost(&mutex);
00510 
00511     /* Read MBR. */
00512     if (At91MciReadSingle(ifc, 0, fcb->fcb_blkbuf)) {
00513         NutHeapFree(fcb);
00514         return NUTFILE_EOF;
00515     }
00516 
00517     /* Read partition table. */
00518     part = (DOSPART *) & fcb->fcb_blkbuf[DOSPART_SECTORPOS];
00519     for (i = 1; i <= 4; i++) {
00520         if (partno) {
00521             if (i == partno) {
00522                 /* Found specified partition number. */
00523                 fcb->fcb_part = *part;
00524                 break;
00525             }
00526         } else if (part->part_state & 0x80) {
00527             /* Located first active partition. */
00528             fcb->fcb_part = *part;
00529             break;
00530         }
00531         part++;
00532     }
00533 
00534     if (fcb->fcb_part.part_type == PTYPE_EMPTY) {
00535         NutHeapFree(fcb);
00536         return NUTFILE_EOF;
00537     }
00538 
00539     if ((nfp = NutHeapAlloc(sizeof(NUTFILE))) == 0) {
00540         NutHeapFree(fcb);
00541         errno = ENOMEM;
00542         return NUTFILE_EOF;
00543     }
00544     nfp->nf_next = 0;
00545     nfp->nf_dev = dev;
00546     nfp->nf_fcb = fcb;
00547 
00548     /*
00549      * Mount the file system volume.
00550      */
00551     mparm.fscp_bmnt = nfp;
00552     mparm.fscp_part_type = fcb->fcb_part.part_type;
00553     if (fsdev->dev_ioctl(fsdev, FS_VOL_MOUNT, &mparm)) {
00554         At91MciUnmount(nfp);
00555         return NUTFILE_EOF;
00556     }
00557     return nfp;
00558 }
00559 
00583 static int At91MciIOCtrl(NUTDEVICE * dev, int req, void *conf)
00584 {
00585     int rc = 0;
00586     MCIFC *ifc = (MCIFC *) dev->dev_icb;
00587 
00588     switch (req) {
00589     case NUTBLKDEV_MEDIAAVAIL:
00590         *((int *) conf) = 1;
00591         break;
00592     case NUTBLKDEV_MEDIACHANGE:
00593         *((int *) conf) = 0;
00594         break;
00595     case NUTBLKDEV_INFO:
00596         {
00597             BLKPAR_INFO *par = (BLKPAR_INFO *) conf;
00598             MCIFCB *fcb = (MCIFCB *) par->par_nfp->nf_fcb;
00599 
00600             par->par_nblks = fcb->fcb_part.part_sects;
00601             par->par_blksz = MMC_BLOCK_SIZE;
00602             par->par_blkbp = fcb->fcb_blkbuf;
00603         }
00604         break;
00605     case NUTBLKDEV_SEEK:
00606         {
00607             BLKPAR_SEEK *par = (BLKPAR_SEEK *) conf;
00608             MCIFCB *fcb = (MCIFCB *) par->par_nfp->nf_fcb;
00609 
00610             fcb->fcb_blknum = par->par_blknum;
00611         }
00612         break;
00613     case MMCARD_GETOCR:
00614         *((u_long *) conf) = ifc->ifc_opcond;
00615         break;
00616     default:
00617         rc = -1;
00618         break;
00619     }
00620     return rc;
00621 }
00622 
00623 static MCIFC mci0_info;
00624 
00637 NUTDEVICE devAt91Mci0 = {
00638     0,                          
00639     {'M', 'C', 'I', '0', 0, 0, 0, 0, 0}
00640     ,                           
00641     0,                          
00642     0,                          
00643     0,                          
00644     &mci0_info,                 
00645     0,                          
00646     At91MciInit,                
00647     At91MciIOCtrl,              
00648     At91MciBlockRead,           
00649     At91MciBlockWrite,          
00650     At91MciMount,               
00651     At91MciUnmount,             
00652     0                           
00653 };
00654 

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