/* * (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 #include #include #include #ifdef CONFIG_MMC 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 uchar * /****************************************************/ mmc_cmd(ushort cmd, ushort argh, ushort argl, ushort cmdat) /****************************************************/ { static uchar resp[20]; ulong status; int words, i; debug("mmc_cmd %x %x %x %x\n", cmd, argh, argl, cmdat); MMC_STRPCL = MMC_STRPCL_STOP_CLK; MMC_I_MASK = ~MMC_I_MASK_CLK_IS_OFF; while (!(MMC_I_REG & MMC_I_REG_CLK_IS_OFF)); MMC_CMD = cmd; MMC_ARGH = argh; MMC_ARGL = argl; MMC_CMDAT = cmdat; MMC_I_MASK = ~MMC_I_MASK_END_CMD_RES; MMC_STRPCL = MMC_STRPCL_START_CLK; while (!(MMC_I_REG & MMC_I_REG_END_CMD_RES)); status = MMC_STAT; debug("MMC status %x\n", status); if (status & MMC_STAT_TIME_OUT_RESPONSE) { return 0; } switch (cmdat & 0x3) { case MMC_CMDAT_R1: case MMC_CMDAT_R3: words = 3; break; case MMC_CMDAT_R2: words = 8; break; default: return 0; } for (i = words-1; i >= 0; i--) { ulong res_fifo = MMC_RES; int offset = i << 1; resp[offset] = ((uchar *)&res_fifo)[0]; resp[offset+1] = ((uchar *)&res_fifo)[1]; } #ifdef MMC_DEBUG for (i=0; i> 16; argl = len & 0xffff; /* set block len */ resp = mmc_cmd(MMC_CMD_SET_BLOCKLEN, argh, argl, MMC_CMDAT_R1); /* send read command */ argh = src >> 16; argl = src & 0xffff; MMC_STRPCL = MMC_STRPCL_STOP_CLK; MMC_RDTO = 0xffff; MMC_NOB = 1; MMC_BLKLEN = len; resp = mmc_cmd(MMC_CMD_READ_BLOCK, argh, argl, MMC_CMDAT_R1|MMC_CMDAT_READ|MMC_CMDAT_BLOCK|MMC_CMDAT_DATA_EN); MMC_I_MASK = ~MMC_I_MASK_RXFIFO_RD_REQ; while (len) { if (MMC_I_REG & MMC_I_REG_RXFIFO_RD_REQ) { *dst++ = MMC_RXFIFO; len--; } status = MMC_STAT; if (status & MMC_STAT_ERRORS) { printf("MMC_STAT error %lx\n", status); return -1; } } MMC_I_MASK = ~MMC_I_MASK_DATA_TRAN_DONE; while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE)); status = MMC_STAT; if (status & MMC_STAT_ERRORS) { printf("MMC_STAT error %lx\n", status); return -1; } return 0; } int /****************************************************/ mmc_block_write(ulong dst, uchar *src, int len) /****************************************************/ { uchar *resp; ushort argh, argl; ulong status; 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.dev = 0; mmc_dev.lun = 0; mmc_dev.type = 0; /* FIXME fill in the correct size (is set to 32MByte) */ mmc_dev.blksz = 512; mmc_dev.lba = 0x10000; sprintf(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(mmc_dev.product,"%s",cid->name); sprintf(mmc_dev.revision,"%x %x",cid->hwrev, cid->fwrev); mmc_dev.removable = 0; mmc_dev.block_read = mmc_bread; /* MMC exists, get CSD too */ resp = mmc_cmd(MMC_CMD_SET_RCA, MMC_DEFAULT_RCA, 0, MMC_CMDAT_R1); resp = mmc_cmd(MMC_CMD_SEND_CSD, MMC_DEFAULT_RCA, 0, MMC_CMDAT_R2); if (resp) { mmc_csd_t *csd = (mmc_csd_t *)resp; memcpy(&mmc_csd, csd, sizeof(csd)); rc = 0; mmc_ready = 1; /* FIXME add verbose printout for csd */ } } MMC_CLKRT = 0; /* 20 MHz */ resp = mmc_cmd(7, MMC_DEFAULT_RCA, 0, MMC_CMDAT_R1); 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 32 MB device */ if (addr >= CFG_MMC_BASE && addr < CFG_MMC_BASE + 0x02000000) { return 1; } return 0; } #endif