/* * (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 */ //#define DEBUG #include #include #include "mvsdmmc.h" #include #include #include #include "mvOs.h" #ifdef CONFIG_MMC static int is_sdhc; 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; /* MMC_DEFAULT_RCA should probably be just 1, but this may break other code that expects it to be shifted. */ static u_int16_t rca = 0; static u_int32_t mmc_size(const struct mmc_csd *csd) { u_int32_t block_len, mult, blocknr; block_len = csd->read_bl_len << 12; mult = csd->c_size_mult1 << 8; blocknr = (csd->c_size+1) * mult; return blocknr * block_len; } static int isprint (unsigned char ch) { if (ch >= 32 && ch < 127) return (1); return (0); } static int toprint(char *dst, char c) { if (isprint(c)) { *dst = c; return 1; } return sprintf(dst,"\\x%02x", c); } static void print_mmc_cid(mmc_cid_t *cid) { printf("MMC found. Card desciption is:\n"); printf("Manufacturer ID = %02x%02x%02x\n", cid->id[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); } static void print_sd_cid(sd_cid_t *cid) { int len; char tbuf[64]; printf("SD%s found. Card desciption is:\n", is_sdhc?"HC":""); len = 0; len += toprint(&tbuf[len], cid->oid_0); len += toprint(&tbuf[len], cid->oid_1); tbuf[len] = 0; printf("Manufacturer: 0x%02x, OEM \"%s\"\n", cid->mid, tbuf); len = 0; len += toprint(&tbuf[len], cid->pnm_0); len += toprint(&tbuf[len], cid->pnm_1); len += toprint(&tbuf[len], cid->pnm_2); len += toprint(&tbuf[len], cid->pnm_3); len += toprint(&tbuf[len], cid->pnm_4); tbuf[len] = 0; printf("Product name: \"%s\", revision %d.%d\n", tbuf, cid->prv >> 4, cid->prv & 15); printf("Serial number: %u\n", cid->psn_0 << 24 | cid->psn_1 << 16 | cid->psn_2 << 8 | cid->psn_3); printf("Manufacturing date: %d/%d\n", cid->mdt_1 & 15, 2000+((cid->mdt_0 & 15) << 4)+((cid->mdt_1 & 0xf0) >> 4)); printf("CRC: 0x%02x, b0 = %d\n", cid->crc >> 1, cid->crc & 1); } static void mvsdmmc_set_clock(unsigned int clock) { unsigned int m; m = MVSDMMC_BASE_FAST_CLOCK/(2*clock) - 1; debug("mvsdmmc_set_clock: dividor = 0x%x clock=%d\n", m, clock); SDIO_REG_WRITE32(SDIO_CLK_DIV, m & 0x7ff); if (isprint(1)) udelay(10*1000); } static ulong * /****************************************************/ mmc_cmd(ulong cmd, ulong arg, ushort xfermode, ushort resptype, ushort waittype) /****************************************************/ { static ulong resp[4]; ushort done ; int err = 0 ; ulong curr, start, diff, hz; ushort response[8], resp_indx = 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_NOR_INTR_STATUS, 0xffff); SDIO_REG_WRITE16(SDIO_ERR_INTR_STATUS, 0xffff); start = get_ticks(); hz = get_tbclk(); while((SDIO_REG_READ16(SDIO_PRESENT_STATE0) & CARD_BUSY)) { curr = get_ticks(); diff = (long) curr - (long) start; if (diff > (3*hz)) { // 3 seconds timeout, 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; start = get_ticks(); while( done!=waittype) { done = SDIO_REG_READ16(SDIO_NOR_INTR_STATUS) & waittype; if( SDIO_REG_READ16(SDIO_NOR_INTR_STATUS) & 0x8000 ) { printf("Error! cmd : %d, err : %04x\n", cmd, SDIO_REG_READ16(SDIO_ERR_INTR_STATUS) ) ; return 0 ; // error happen } curr = get_ticks(); diff = (long) curr - (long) start; if (diff > (3*hz)) { printf("cmd timeout, status : %04x\n", SDIO_REG_READ16(SDIO_NOR_INTR_STATUS)); printf("xfer mode : %04x\n", SDIO_REG_READ16(SDIO_XFER_MODE)); err = 1 ; break; } } for (resp_indx = 0 ; resp_indx < 8; resp_indx++) response[resp_indx] = SDIO_REG_READ16(SDIO_RSP(resp_indx)); memset(resp, 0, sizeof(resp)); switch (resptype & 0x3) { case SDIO_CMD_RSP_48: case SDIO_CMD_RSP_48BUSY: resp[0] = ((response[2] & 0x3f) << (8 - 8)) | ((response[1] & 0xffff) << (14 - 8)) | ((response[0] & 0x3ff) << (30 - 8)); resp[1] = ((response[0] & 0xfc00) >> 10); break; case SDIO_CMD_RSP_136: resp[3] = ((response[7] & 0x3fff) << 8) | ((response[6] & 0x3ff) << 22); resp[2] = ((response[6] & 0xfc00) >> 10) | ((response[5] & 0xffff) << 6) | ((response[4] & 0x3ff) << 22); resp[1] = ((response[4] & 0xfc00) >> 10) | ((response[3] & 0xffff) << 6) | ((response[2] & 0x3ff) << 22); resp[0] = ((response[2] & 0xfc00) >> 10) | ((response[1] & 0xffff) << 6) | ((response[0] & 0x3ff) << 22); 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 NULL ; else return resp; } int /****************************************************/ mmc_block_read(uchar *dst, ulong src, ulong len) /****************************************************/ { ulong *resp; //ushort argh, argl; //ulong status; if (len == 0) { return 0; } if (is_sdhc) { /* SDHC: use block address */ src >>= 9; } debug("mmc_block_rd dst %lx src %lx len %d\n", (ulong)dst, src, len); #if 0 /* set block len */ resp = mmc_cmd(MMC_CMD_SET_BLOCKLEN, len, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); if (!resp) { printf("mmc_block_read: set blk len fails\n"); return -EIO; } #endif // prepare for dma transfer SDIO_REG_WRITE16(SDIO_SYS_ADDR_LOW,((ulong)(dst))&0xffff); SDIO_REG_WRITE16(SDIO_SYS_ADDR_HI,(((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_XFER_DONE); if (!resp) { printf("mmc_block_read: mmc read block cmd fails\n"); return -EIO; } 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; imid, cid->oid_0, cid->oid_1, cid->pnm_0, cid->pnm_1, cid->pnm_2, cid->pnm_3, cid->pnm_4); sprintf((char *) mmc_dev.product, "%d", (cid->psn_0 << 24) | (cid->psn_1 <<16) | (cid->psn_2 << 8) | (cid->psn_3 << 8)); sprintf((char *) mmc_dev.revision, "%d.%d", cid->prv>>4, cid->prv & 0xff); } else { /* TODO configure mmc driver depending on card attributes */ mmc_cid_t *cid = (mmc_cid_t *) resp; memcpy(cidbuf, resp, sizeof(sd_cid_t)); sprintf((char *) 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]); sprintf((char *) mmc_dev.product, "%s", cid->name); sprintf((char *) mmc_dev.revision, "%x %x", cid->hwrev, cid->fwrev); } /* 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 = 0x10000; mmc_dev.removable = 0; mmc_dev.block_read = mmc_bread; /* MMC exists, get CSD too */ resp = mmc_cmd(MMC_CMD_SET_RCA, 0, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); if (resp == NULL) { debug ("set rca fails\n"); return -ENODEV; } debug("cmd3 resp : 0x%08x 0x%08x 0x%08x 0x%08x\n", resp[0], resp[1], resp[2], resp[3]); if (is_sd) rca = resp[0] >> 16; else rca = 0; resp = mmc_cmd(MMC_CMD_SEND_CSD, rca<<16, 0, SDIO_CMD_RSP_136,SDIO_NOR_CMD_DONE ); debug("cmd 9 resp : %08x %08x %08x %08x\n", resp[0], resp[1], resp[2], resp[3] ); if (resp == NULL) { debug ("read csd fails\n"); return -ENODEV; } memcpy(&mmc_csd, (mmc_csd_t *) resp, sizeof(mmc_csd_t)); rc = 0; mmc_ready = 1; /* FIXME add verbose printout for csd */ debug ("size = %u\n", mmc_size(&mmc_csd)); resp = mmc_cmd(7, rca<<16, 0, SDIO_CMD_RSP_48BUSY, SDIO_NOR_CMD_DONE); if (resp == NULL) { debug ("select card fails\n"); return -ENODEV; } debug("cmd 7 resp : %08x %08x %08x %08x\n", resp[0], resp[1], resp[2], resp[3] ); if (is_sd) { resp = mmc_cmd(55, rca<<16, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); if (resp == NULL) { debug ("cmd55 fails\n"); return -ENODEV; } debug("cmd55 resp : 0x%08x 0x%08x 0x%08x 0x%08x\n", resp[0], resp[1], resp[2], resp[3]); resp = mmc_cmd(6, (rca<<16) | 0x2 , 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); if (resp == NULL) { debug ("cmd55 fails\n"); return -ENODEV; } debug("cmd6 resp : 0x%08x 0x%08x 0x%08x 0x%08x\n", resp[0], resp[1], resp[2], resp[3]); } resp = (ulong *) &mmc_csd; debug("csd: 0x%08x 0x%08x 0x%08x 0x%08x\n", resp[0], resp[1], resp[2], resp[3]); /* check SDHC */ if ((resp[0]&0xf0000000)==0x40000000) is_sdhc = 1; /* set block len */ resp = mmc_cmd(MMC_CMD_SET_BLOCKLEN, 512, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); if (!resp) { printf("mmc_block_read: set blk len fails\n"); return -EIO; } if (verbose) { if (is_sd) print_sd_cid((sd_cid_t *) cidbuf); else print_mmc_cid((mmc_cid_t *) cidbuf); } mvsdmmc_set_clock(25000000); 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 */