00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
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
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
00152 outr(PIOA_PDR, MMC_PINS_A | MMC_PINS_B);
00153
00154 outr(PIOA_ASR, MMC_PINS_A);
00155 outr(PIOA_BSR, MMC_PINS_B);
00156
00157
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
00166
00167
00168
00169 outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (11 << MCI_CLKDIV_LSB));
00170
00171
00172
00173 outr(MCI_SDCR, MCI_SDCSEL_SLOTB);
00174
00175
00176 outr(PMC_PCER, _BV(MCI_ID));
00177
00178 return 0;
00179 }
00180
00181
00182
00183
00184
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
00253 #ifdef NUTDEBUG
00254 printf("\n[MciIdle]");
00255 #endif
00256 outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (47 << MCI_CLKDIV_LSB));
00257 At91MciTxCmd(ifc, MMCMD_GO_IDLE_STATE, 0);
00258
00259
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
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
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));
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
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
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
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
00480
00481
00482
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
00509 NutEventPost(&mutex);
00510
00511
00512 if (At91MciReadSingle(ifc, 0, fcb->fcb_blkbuf)) {
00513 NutHeapFree(fcb);
00514 return NUTFILE_EOF;
00515 }
00516
00517
00518 part = (DOSPART *) & fcb->fcb_blkbuf[DOSPART_SECTORPOS];
00519 for (i = 1; i <= 4; i++) {
00520 if (partno) {
00521 if (i == partno) {
00522
00523 fcb->fcb_part = *part;
00524 break;
00525 }
00526 } else if (part->part_state & 0x80) {
00527
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
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