/* * (C) Copyright 2003 * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include #include #include "mustang_sdiodef.h" #include #include #include #include "mvOs.h" #ifdef CONFIG_MMC #define DELAY 50 //#define debug printf extern int fat_register_device(block_dev_desc_t *dev_desc, int part_no); static block_dev_desc_t mmc_dev; block_dev_desc_t * mmc_get_dev(int dev) { return ((block_dev_desc_t *)&mmc_dev); } /* * FIXME needs to read cid and csd info to determine block size * and other parameters */ static uchar mmc_buf[MMC_BLOCK_SIZE]; static mmc_csd_t mmc_csd; static int mmc_ready = 0; static int rca ; static uchar * /****************************************************/ mmc_cmd(ulong cmd, ulong arg, ushort xfermode, ushort resptype, ushort waittype) /****************************************************/ { static ulong resp[4]; int count ; ushort done ; int err = 0 ; debug("mmc_cmd %x, arg: %x,xfer: %x,resp: %x, wait : %x\n", cmd, arg, xfermode, resptype, waittype); //clear status SDIO_REG_WRITE16(SDIO_ERR_INTR_EN, SDIO_REG_READ16(SDIO_ERR_INTR_STATUS)); SDIO_REG_WRITE16(SDIO_NOR_INTR_EN, SDIO_REG_READ16(SDIO_NOR_INTR_STATUS)); //disable interrupts SDIO_REG_WRITE16(SDIO_NOR_INTR_EN, 0); SDIO_REG_WRITE16(SDIO_ERR_INTR_EN, 0); udelay(2000); /* count = 10000 ; while((SDIO_REG_READ16(SDIO_PRESENT_STATE0) & CARD_BUSY)) { udelay(DELAY); if (--count <= 0 ) { // card busy, can't sent cmd printf("card too busy \n"); return 0; } } */ SDIO_REG_WRITE16(SDIO_ARG_LOW, (ushort)(arg&0xffff) ); SDIO_REG_WRITE16(SDIO_ARG_HI, (ushort)(arg>>16) ); SDIO_REG_WRITE16(SDIO_XFER_MODE, xfermode); if( (cmd == MMC_CMD_READ_BLOCK) || (cmd == 25) ) { SDIO_REG_WRITE16(SDIO_CMD, ((cmd << 8) | resptype | 0x3c ) ); debug("cmd reg : %x\n", SDIO_REG_READ16( SDIO_CMD )) ; } else { SDIO_REG_WRITE16(SDIO_CMD, ((cmd << 8) | resptype ) ); } done = SDIO_REG_READ16(SDIO_NOR_INTR_STATUS) & waittype; count = 10000 ; while( done!=waittype) { // udelay(DELAY); udelay(2000); done = SDIO_REG_READ16(SDIO_NOR_INTR_STATUS) & waittype; /* if( SDIO_REG_READ16(SDIO_NOR_INTR_STATUS) & 0x8000 ) { printf("Error! cmd : %d, err : %x\n", cmd, SDIO_REG_READ16(SDIO_ERR_INTR_STATUS) ) ; return 0 ; // error happen } if( --count <= 0 ) { debug("cmd timeout, status : %x\n", SDIO_REG_READ16(SDIO_NOR_INTR_STATUS)); debug("xfer mode : %x\n", SDIO_REG_READ16(SDIO_XFER_MODE)); err = 1 ; break; //return 0 ; } */ } // write clear the status SDIO_REG_WRITE16(SDIO_NOR_INTR_STATUS, waittype); //SDIO_REG_WRITE16(SDIO_NOR_INTR_EN, SDIO_REG_READ16(SDIO_NOR_INTR_STATUS)); switch (resptype & 0x3) { case SDIO_CMD_RSP_48: case SDIO_CMD_RSP_48BUSY: resp[0] = ( SDIO_REG_READ16(SDIO_RSP0) << (16+6)) | (SDIO_REG_READ16(SDIO_RSP1) << 6) | ((SDIO_REG_READ16(SDIO_RSP2))& 0x3f) ; break; case SDIO_CMD_RSP_136: resp[0] = ( SDIO_REG_READ16(SDIO_RSP5) << (16+14)) | (SDIO_REG_READ16(SDIO_RSP6) << 14) | ((SDIO_REG_READ16(SDIO_RSP7))& 0x3fff) ; resp[1] = ( SDIO_REG_READ16(SDIO_RSP3) << (16+14)) | (SDIO_REG_READ16(SDIO_RSP4) << 14) | ((SDIO_REG_READ16(SDIO_RSP5)>> 2 )& 0x3fff) ; resp[2] = ( SDIO_REG_READ16(SDIO_RSP1) << (16+14)) | (SDIO_REG_READ16(SDIO_RSP2) << 14) | ((SDIO_REG_READ16(SDIO_RSP3)>> 2 )& 0x3fff) ; resp[3] = (SDIO_REG_READ16(SDIO_RSP0) << 14) | ((SDIO_REG_READ16(SDIO_RSP1)>> 2 )& 0x3fff) ; break; default: return 0; } #ifdef MMC_DEBUG int i; printf("MMC resp :"); for (i=0; i<4; ++i ) { printf(" %08x", resp[i]); } printf("\n"); #endif if( err ) return 0 ; else return (uchar*)resp; } int /****************************************************/ mmc_block_read(uchar *dst, ulong src, ulong len) /****************************************************/ { uchar *resp; //ushort argh, argl; //ulong status; if (len == 0) { return 0; } debug("mmc_block_rd dst %lx src %lx len %d\n", (ulong)dst, src, len); //mmc_cmd(ulong cmd, ulong arg, ushort xfermode, ushort resptype, ushort waittype); /* set block len */ resp = mmc_cmd(MMC_CMD_SET_BLOCKLEN, len, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); // prepare for dma transfer //SDIO_REG_WRITE16(SDIO_SYS_ADDR_LOW,((ulong)(dst)>>16)&0xffff); SDIO_REG_WRITE16(SDIO_SYS_ADDR_LOW,((ulong)(dst))&0xffff); //SDIO_REG_WRITE16(SDIO_SYS_ADDR_HI,((ulong)(dst))&0xffff); SDIO_REG_WRITE16(SDIO_SYS_ADDR_HI,(0x8000 | ((ulong)(dst)>>16)&0xffff)); SDIO_REG_WRITE16(SDIO_BLK_SIZE,len); SDIO_REG_WRITE16(SDIO_BLK_COUNT,1); /* send read command */ resp = mmc_cmd(MMC_CMD_READ_BLOCK, src, 0x10 , // 0x12, SDIO_CMD_RSP_48, SDIO_NOR_DMA_INI); return 0; } int /****************************************************/ mmc_block_write(ulong dst, uchar *src, int len) /****************************************************/ { //uchar *resp; //ushort argh, argl; //ulong status; return -1 ; #if 0 if (len == 0) { return 0; } debug("mmc_block_wr dst %lx src %lx len %d\n", dst, (ulong)src, len); argh = len >> 16; argl = len & 0xffff; /* set block len */ resp = mmc_cmd(MMC_CMD_SET_BLOCKLEN, argh, argl, MMC_CMDAT_R1); /* send write command */ argh = dst >> 16; argl = dst & 0xffff; MMC_STRPCL = MMC_STRPCL_STOP_CLK; MMC_NOB = 1; MMC_BLKLEN = len; resp = mmc_cmd(MMC_CMD_WRITE_BLOCK, argh, argl, MMC_CMDAT_R1|MMC_CMDAT_WRITE|MMC_CMDAT_BLOCK|MMC_CMDAT_DATA_EN); MMC_I_MASK = ~MMC_I_MASK_TXFIFO_WR_REQ; while (len) { if (MMC_I_REG & MMC_I_REG_TXFIFO_WR_REQ) { int i, bytes = min(32,len); for (i=0; iid[0], cid->id[1], cid->id[2]); printf("HW/FW Revision = %x %x\n",cid->hwrev, cid->fwrev); cid->hwrev = cid->fwrev = 0; /* null terminate string */ printf("Product Name = %s\n",cid->name); printf("Serial Number = %02x%02x%02x\n", cid->sn[0], cid->sn[1], cid->sn[2]); printf("Month = %d\n",cid->month); printf("Year = %d\n",1997 + cid->year); } /* fill in device description */ mmc_dev.if_type = IF_TYPE_MMC; mmc_dev.part_type = PART_TYPE_DOS; mmc_dev.dev = 0; mmc_dev.lun = 0; mmc_dev.type = 0; /* FIXME fill in the correct size (is set to 128MByte) */ mmc_dev.blksz = 512; mmc_dev.lba = 0x40000; printf(mmc_dev.vendor,"Man %02x%02x%02x Snr %02x%02x%02x", cid->id[0], cid->id[1], cid->id[2], cid->sn[0], cid->sn[1], cid->sn[2]); printf(mmc_dev.product,"%s",cid->name); printf(mmc_dev.revision,"%x %x",cid->hwrev, cid->fwrev); mmc_dev.removable = 0; mmc_dev.block_read = mmc_bread; //mmc_cmd(ulong cmd, ulong arg, ushort xfermode, ushort resptype, ushort waittype); /* MMC exists, get CSD too */ resp = mmc_cmd(MMC_CMD_SET_RCA, 0, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); rca = resp[2] | (resp[3] << 8 ) ; resp = mmc_cmd(MMC_CMD_SEND_CSD, rca<<16, 0, SDIO_CMD_RSP_136,SDIO_NOR_CMD_DONE ); if (resp) { mmc_csd_t *csd = (mmc_csd_t *)resp; memcpy(&mmc_csd, csd, sizeof(csd)); debug("cmd 9 resp 0 : %x %x %x %x\n", resp[0], resp[1], resp[2], resp[3] ); debug("cmd 9 resp 1 : %x %x %x %x\n", resp[4], resp[5], resp[6], resp[7] ); debug("cmd 9 resp 2 : %x %x %x %x\n", resp[8], resp[9], resp[10], resp[11] ); debug("cmd 9 resp 3 : %x %x %x %x\n", resp[12], resp[13], resp[14], resp[15] ); rc = 0; /* Calc device size */ debug("cmd 9 csd->c_size : %x\n", csd->c_size ); debug("cmd 9 csd->c_size_mult1 : %x\n", csd->c_size_mult1 ); debug("cmd 9 csd->read_bl_len : %x\n", csd->read_bl_len ); debug("cmd 9 csd->tran_speed : %x\n", csd->tran_speed ); debug("cmd 9 csd->nsac : %x\n", csd->nsac ); debug("cmd 9 csd->taac : %x\n", csd->taac ); /* FIXME add verbose printout for csd */ mmc_ready = 1; } } resp = mmc_cmd(MMC_CMD_SELECT_CARD, rca<<16, 0, SDIO_CMD_RSP_48BUSY, SDIO_NOR_CMD_DONE); #if 0 resp = mmc_cmd(MMC_CMD_SELECT_CARD, 0, 0, SDIO_CMD_RSP_48BUSY, SDIO_NOR_CMD_DONE); resp = mmc_cmd(MMC_CMD_SELECT_CARD, rca<<16, 0, SDIO_CMD_RSP_48BUSY, SDIO_NOR_CMD_DONE); #endif resp = mmc_cmd(55, rca<<16, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); resp = mmc_cmd(6, (rca<<16) | 0x2 , 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); #if 0 // prepare for dma transfer SDIO_REG_WRITE16(SDIO_BLK_SIZE,8); SDIO_REG_WRITE16(SDIO_BLK_COUNT,1); SDIO_REG_WRITE16(SDIO_SYS_ADDR_LOW,((ulong)(scrs))&0xffff); SDIO_REG_WRITE16(SDIO_SYS_ADDR_HI,((ulong)(scrs)>>16)&0xffff); //ACMD 51 resp = mmc_cmd(51, 0, 0x12, SDIO_CMD_RSP_48, SDIO_NOR_DMA_INI ); printf("scrs : %x %x %x %x %x %x %x %x\n", scrs[0], scrs[1], scrs[2], scrs[3], scrs[4], scrs[5], scrs[6], scrs[7] ); #endif fat_register_device(&mmc_dev,1); /* partitions start counting with 1 */ return rc; } int mmc_ident(block_dev_desc_t *dev) { return 0; } int mmc2info(ulong addr) { /* FIXME hard codes to 256 MB device */ if (addr >= CFG_MMC_BASE && addr < CFG_MMC_BASE + 0xffffffff) { return 1; } return 0; } #endif /* CONFIG_MMC */