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
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
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
00155 outr(PIOA_PDR, MMC_PINS_A | MMC_PINS_B);
00156
00157 outr(PIOA_ASR, MMC_PINS_A);
00158 outr(PIOA_BSR, MMC_PINS_B);
00159
00160
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
00169
00170
00171
00172 outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (11 << MCI_CLKDIV_LSB));
00173
00174
00175
00176 outr(MCI_SDCR, MCI_SDCSEL_SLOTB);
00177
00178
00179 outr(PMC_PCER, _BV(MCI_ID));
00180
00181 return 0;
00182 }
00183
00184
00185
00186
00187
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
00256 #ifdef NUTDEBUG
00257 printf("\n[MciIdle]");
00258 #endif
00259 outr(MCI_MR, MCI_PDCMODE | (3 << MCI_PWSDIV_LSB) | (47 << MCI_CLKDIV_LSB));
00260 At91MciTxCmd(ifc, MMCMD_GO_IDLE_STATE, 0);
00261
00262
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
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
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));
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
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
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
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
00483
00484
00485
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
00512 NutEventPost(&mutex);
00513
00514
00515 if (At91MciReadSingle(ifc, 0, fcb->fcb_blkbuf)) {
00516 NutHeapFree(fcb);
00517 return NUTFILE_EOF;
00518 }
00519
00520
00521 part = (DOSPART *) & fcb->fcb_blkbuf[DOSPART_SECTORPOS];
00522 for (i = 1; i <= 4; i++) {
00523 if (partno) {
00524 if (i == partno) {
00525
00526 fcb->fcb_part = *part;
00527 break;
00528 }
00529 } else if (part->part_state & 0x80) {
00530
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
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