mmc.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. /*
  2. * (C) Copyright 2003
  3. * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
  4. *
  5. * See file CREDITS for list of people who contributed to this
  6. * project.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License as
  10. * published by the Free Software Foundation; either version 2 of
  11. * the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  21. * MA 02111-1307 USA
  22. */
  23. #include <config.h>
  24. #include <common.h>
  25. #include <mmc.h>
  26. #include <asm/errno.h>
  27. #include <asm/arch/hardware.h>
  28. #include <part.h>
  29. #ifdef CONFIG_MMC
  30. extern int
  31. fat_register_device(block_dev_desc_t *dev_desc, int part_no);
  32. static block_dev_desc_t mmc_dev;
  33. block_dev_desc_t * mmc_get_dev(int dev)
  34. {
  35. return ((block_dev_desc_t *)&mmc_dev);
  36. }
  37. /*
  38. * FIXME needs to read cid and csd info to determine block size
  39. * and other parameters
  40. */
  41. static uchar mmc_buf[MMC_BLOCK_SIZE];
  42. static mmc_csd_t mmc_csd;
  43. static int mmc_ready = 0;
  44. static uchar *
  45. /****************************************************/
  46. mmc_cmd(ushort cmd, ushort argh, ushort argl, ushort cmdat)
  47. /****************************************************/
  48. {
  49. static uchar resp[20];
  50. ulong status;
  51. int words, i;
  52. debug("mmc_cmd %x %x %x %x\n", cmd, argh, argl, cmdat);
  53. MMC_STRPCL = MMC_STRPCL_STOP_CLK;
  54. MMC_I_MASK = ~MMC_I_MASK_CLK_IS_OFF;
  55. while (!(MMC_I_REG & MMC_I_REG_CLK_IS_OFF));
  56. MMC_CMD = cmd;
  57. MMC_ARGH = argh;
  58. MMC_ARGL = argl;
  59. MMC_CMDAT = cmdat;
  60. MMC_I_MASK = ~MMC_I_MASK_END_CMD_RES;
  61. MMC_STRPCL = MMC_STRPCL_START_CLK;
  62. while (!(MMC_I_REG & MMC_I_REG_END_CMD_RES));
  63. status = MMC_STAT;
  64. debug("MMC status %x\n", status);
  65. if (status & MMC_STAT_TIME_OUT_RESPONSE)
  66. {
  67. return 0;
  68. }
  69. switch (cmdat & 0x3)
  70. {
  71. case MMC_CMDAT_R1:
  72. case MMC_CMDAT_R3:
  73. words = 3;
  74. break;
  75. case MMC_CMDAT_R2:
  76. words = 8;
  77. break;
  78. default:
  79. return 0;
  80. }
  81. for (i = words-1; i >= 0; i--)
  82. {
  83. ulong res_fifo = MMC_RES;
  84. int offset = i << 1;
  85. resp[offset] = ((uchar *)&res_fifo)[0];
  86. resp[offset+1] = ((uchar *)&res_fifo)[1];
  87. }
  88. #ifdef MMC_DEBUG
  89. for (i=0; i<words*2; i += 2)
  90. {
  91. printf("MMC resp[%d] = %02x\n", i, resp[i]);
  92. printf("MMC resp[%d] = %02x\n", i+1, resp[i+1]);
  93. }
  94. #endif
  95. return resp;
  96. }
  97. int
  98. /****************************************************/
  99. mmc_block_read(uchar *dst, ulong src, ulong len)
  100. /****************************************************/
  101. {
  102. uchar *resp;
  103. ushort argh, argl;
  104. ulong status;
  105. if (len == 0)
  106. {
  107. return 0;
  108. }
  109. debug("mmc_block_rd dst %lx src %lx len %d\n", (ulong)dst, src, len);
  110. argh = len >> 16;
  111. argl = len & 0xffff;
  112. /* set block len */
  113. resp = mmc_cmd(MMC_CMD_SET_BLOCKLEN, argh, argl, MMC_CMDAT_R1);
  114. /* send read command */
  115. argh = src >> 16;
  116. argl = src & 0xffff;
  117. MMC_STRPCL = MMC_STRPCL_STOP_CLK;
  118. MMC_RDTO = 0xffff;
  119. MMC_NOB = 1;
  120. MMC_BLKLEN = len;
  121. resp = mmc_cmd(MMC_CMD_READ_BLOCK, argh, argl,
  122. MMC_CMDAT_R1|MMC_CMDAT_READ|MMC_CMDAT_BLOCK|MMC_CMDAT_DATA_EN);
  123. MMC_I_MASK = ~MMC_I_MASK_RXFIFO_RD_REQ;
  124. while (len)
  125. {
  126. if (MMC_I_REG & MMC_I_REG_RXFIFO_RD_REQ)
  127. {
  128. *dst++ = MMC_RXFIFO;
  129. len--;
  130. }
  131. status = MMC_STAT;
  132. if (status & MMC_STAT_ERRORS)
  133. {
  134. printf("MMC_STAT error %lx\n", status);
  135. return -1;
  136. }
  137. }
  138. MMC_I_MASK = ~MMC_I_MASK_DATA_TRAN_DONE;
  139. while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE));
  140. status = MMC_STAT;
  141. if (status & MMC_STAT_ERRORS)
  142. {
  143. printf("MMC_STAT error %lx\n", status);
  144. return -1;
  145. }
  146. return 0;
  147. }
  148. int
  149. /****************************************************/
  150. mmc_block_write(ulong dst, uchar *src, int len)
  151. /****************************************************/
  152. {
  153. uchar *resp;
  154. ushort argh, argl;
  155. ulong status;
  156. if (len == 0)
  157. {
  158. return 0;
  159. }
  160. debug("mmc_block_wr dst %lx src %lx len %d\n", dst, (ulong)src, len);
  161. argh = len >> 16;
  162. argl = len & 0xffff;
  163. /* set block len */
  164. resp = mmc_cmd(MMC_CMD_SET_BLOCKLEN, argh, argl, MMC_CMDAT_R1);
  165. /* send write command */
  166. argh = dst >> 16;
  167. argl = dst & 0xffff;
  168. MMC_STRPCL = MMC_STRPCL_STOP_CLK;
  169. MMC_NOB = 1;
  170. MMC_BLKLEN = len;
  171. resp = mmc_cmd(MMC_CMD_WRITE_BLOCK, argh, argl,
  172. MMC_CMDAT_R1|MMC_CMDAT_WRITE|MMC_CMDAT_BLOCK|MMC_CMDAT_DATA_EN);
  173. MMC_I_MASK = ~MMC_I_MASK_TXFIFO_WR_REQ;
  174. while (len)
  175. {
  176. if (MMC_I_REG & MMC_I_REG_TXFIFO_WR_REQ)
  177. {
  178. int i, bytes = min(32,len);
  179. for (i=0; i<bytes; i++)
  180. {
  181. MMC_TXFIFO = *src++;
  182. }
  183. if (bytes < 32)
  184. {
  185. MMC_PRTBUF = MMC_PRTBUF_BUF_PART_FULL;
  186. }
  187. len -= bytes;
  188. }
  189. status = MMC_STAT;
  190. if (status & MMC_STAT_ERRORS)
  191. {
  192. printf("MMC_STAT error %lx\n", status);
  193. return -1;
  194. }
  195. }
  196. MMC_I_MASK = ~MMC_I_MASK_DATA_TRAN_DONE;
  197. while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE));
  198. MMC_I_MASK = ~MMC_I_MASK_PRG_DONE;
  199. while (!(MMC_I_REG & MMC_I_REG_PRG_DONE));
  200. status = MMC_STAT;
  201. if (status & MMC_STAT_ERRORS)
  202. {
  203. printf("MMC_STAT error %lx\n", status);
  204. return -1;
  205. }
  206. return 0;
  207. }
  208. int
  209. /****************************************************/
  210. mmc_read(ulong src, uchar *dst, int size)
  211. /****************************************************/
  212. {
  213. ulong end, part_start, part_end, part_len, aligned_start, aligned_end;
  214. ulong mmc_block_size, mmc_block_address;
  215. if (size == 0)
  216. {
  217. return 0;
  218. }
  219. if (!mmc_ready)
  220. {
  221. printf("Please initial the MMC first\n");
  222. return -1;
  223. }
  224. mmc_block_size = MMC_BLOCK_SIZE;
  225. mmc_block_address = ~(mmc_block_size - 1);
  226. src -= CFG_MMC_BASE;
  227. end = src + size;
  228. part_start = ~mmc_block_address & src;
  229. part_end = ~mmc_block_address & end;
  230. aligned_start = mmc_block_address & src;
  231. aligned_end = mmc_block_address & end;
  232. /* all block aligned accesses */
  233. debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
  234. src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
  235. if (part_start)
  236. {
  237. part_len = mmc_block_size - part_start;
  238. debug("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
  239. src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
  240. if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < 0)
  241. {
  242. return -1;
  243. }
  244. memcpy(dst, mmc_buf+part_start, part_len);
  245. dst += part_len;
  246. src += part_len;
  247. }
  248. debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
  249. src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
  250. for (; src < aligned_end; src += mmc_block_size, dst += mmc_block_size)
  251. {
  252. debug("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
  253. src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
  254. if ((mmc_block_read((uchar *)(dst), src, mmc_block_size)) < 0)
  255. {
  256. return -1;
  257. }
  258. }
  259. debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
  260. src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
  261. if (part_end && src < end)
  262. {
  263. debug("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
  264. src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
  265. if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0)
  266. {
  267. return -1;
  268. }
  269. memcpy(dst, mmc_buf, part_end);
  270. }
  271. return 0;
  272. }
  273. int
  274. /****************************************************/
  275. mmc_write(uchar *src, ulong dst, int size)
  276. /****************************************************/
  277. {
  278. ulong end, part_start, part_end, part_len, aligned_start, aligned_end;
  279. ulong mmc_block_size, mmc_block_address;
  280. if (size == 0)
  281. {
  282. return 0;
  283. }
  284. if (!mmc_ready)
  285. {
  286. printf("Please initial the MMC first\n");
  287. return -1;
  288. }
  289. mmc_block_size = MMC_BLOCK_SIZE;
  290. mmc_block_address = ~(mmc_block_size - 1);
  291. dst -= CFG_MMC_BASE;
  292. end = dst + size;
  293. part_start = ~mmc_block_address & dst;
  294. part_end = ~mmc_block_address & end;
  295. aligned_start = mmc_block_address & dst;
  296. aligned_end = mmc_block_address & end;
  297. /* all block aligned accesses */
  298. debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
  299. src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
  300. if (part_start)
  301. {
  302. part_len = mmc_block_size - part_start;
  303. debug("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
  304. (ulong)src, dst, end, part_start, part_end, aligned_start, aligned_end);
  305. if ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < 0)
  306. {
  307. return -1;
  308. }
  309. memcpy(mmc_buf+part_start, src, part_len);
  310. if ((mmc_block_write(aligned_start, mmc_buf, mmc_block_size)) < 0)
  311. {
  312. return -1;
  313. }
  314. dst += part_len;
  315. src += part_len;
  316. }
  317. debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
  318. src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
  319. for (; dst < aligned_end; src += mmc_block_size, dst += mmc_block_size)
  320. {
  321. debug("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
  322. src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
  323. if ((mmc_block_write(dst, (uchar *)src, mmc_block_size)) < 0)
  324. {
  325. return -1;
  326. }
  327. }
  328. debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
  329. src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
  330. if (part_end && dst < end)
  331. {
  332. debug("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n",
  333. src, (ulong)dst, end, part_start, part_end, aligned_start, aligned_end);
  334. if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0)
  335. {
  336. return -1;
  337. }
  338. memcpy(mmc_buf, src, part_end);
  339. if ((mmc_block_write(aligned_end, mmc_buf, mmc_block_size)) < 0)
  340. {
  341. return -1;
  342. }
  343. }
  344. return 0;
  345. }
  346. ulong
  347. /****************************************************/
  348. mmc_bread(int dev_num, ulong blknr, ulong blkcnt, ulong *dst)
  349. /****************************************************/
  350. {
  351. int mmc_block_size = MMC_BLOCK_SIZE;
  352. ulong src = blknr * mmc_block_size + CFG_MMC_BASE;
  353. mmc_read(src, (uchar *)dst, blkcnt*mmc_block_size);
  354. return blkcnt;
  355. }
  356. int
  357. /****************************************************/
  358. mmc_init(int verbose)
  359. /****************************************************/
  360. {
  361. int retries, rc = -ENODEV;
  362. uchar *resp;
  363. #ifdef CONFIG_LUBBOCK
  364. set_GPIO_mode( GPIO6_MMCCLK_MD );
  365. set_GPIO_mode( GPIO8_MMCCS0_MD );
  366. #endif
  367. CKEN |= CKEN12_MMC; /* enable MMC unit clock */
  368. mmc_csd.c_size = 0;
  369. MMC_CLKRT = MMC_CLKRT_0_3125MHZ;
  370. MMC_RESTO = MMC_RES_TO_MAX;
  371. MMC_SPI = MMC_SPI_DISABLE;
  372. /* reset */
  373. retries = 10;
  374. resp = mmc_cmd(0, 0, 0, 0);
  375. resp = mmc_cmd(1, 0x00ff, 0xc000, MMC_CMDAT_INIT|MMC_CMDAT_BUSY|MMC_CMDAT_R3);
  376. while (retries-- && resp && !(resp[4] & 0x80))
  377. {
  378. debug("resp %x %x\n", resp[0], resp[1]);
  379. udelay(50);
  380. resp = mmc_cmd(1, 0x00ff, 0xff00, MMC_CMDAT_BUSY|MMC_CMDAT_R3);
  381. }
  382. /* try to get card id */
  383. resp = mmc_cmd(2, 0, 0, MMC_CMDAT_R2);
  384. if (resp)
  385. {
  386. /* TODO configure mmc driver depending on card attributes */
  387. mmc_cid_t *cid = (mmc_cid_t *)resp;
  388. if (verbose)
  389. {
  390. printf("MMC found. Card desciption is:\n");
  391. printf("Manufacturer ID = %02x%02x%02x\n",
  392. cid->id[0], cid->id[1], cid->id[2]);
  393. printf("HW/FW Revision = %x %x\n",cid->hwrev, cid->fwrev);
  394. cid->hwrev = cid->fwrev = 0; /* null terminate string */
  395. printf("Product Name = %s\n",cid->name);
  396. printf("Serial Number = %02x%02x%02x\n",
  397. cid->sn[0], cid->sn[1], cid->sn[2]);
  398. printf("Month = %d\n",cid->month);
  399. printf("Year = %d\n",1997 + cid->year);
  400. }
  401. /* fill in device description */
  402. mmc_dev.if_type = IF_TYPE_MMC;
  403. mmc_dev.dev = 0;
  404. mmc_dev.lun = 0;
  405. mmc_dev.type = 0;
  406. /* FIXME fill in the correct size (is set to 32MByte) */
  407. mmc_dev.blksz = 512;
  408. mmc_dev.lba = 0x10000;
  409. sprintf(mmc_dev.vendor,"Man %02x%02x%02x Snr %02x%02x%02x",
  410. cid->id[0], cid->id[1], cid->id[2],
  411. cid->sn[0], cid->sn[1], cid->sn[2]);
  412. sprintf(mmc_dev.product,"%s",cid->name);
  413. sprintf(mmc_dev.revision,"%x %x",cid->hwrev, cid->fwrev);
  414. mmc_dev.removable = 0;
  415. mmc_dev.block_read = mmc_bread;
  416. /* MMC exists, get CSD too */
  417. resp = mmc_cmd(MMC_CMD_SET_RCA, MMC_DEFAULT_RCA, 0, MMC_CMDAT_R1);
  418. resp = mmc_cmd(MMC_CMD_SEND_CSD, MMC_DEFAULT_RCA, 0, MMC_CMDAT_R2);
  419. if (resp)
  420. {
  421. mmc_csd_t *csd = (mmc_csd_t *)resp;
  422. memcpy(&mmc_csd, csd, sizeof(csd));
  423. rc = 0;
  424. mmc_ready = 1;
  425. /* FIXME add verbose printout for csd */
  426. }
  427. }
  428. MMC_CLKRT = 0; /* 20 MHz */
  429. resp = mmc_cmd(7, MMC_DEFAULT_RCA, 0, MMC_CMDAT_R1);
  430. fat_register_device(&mmc_dev,1); /* partitions start counting with 1 */
  431. return rc;
  432. }
  433. int
  434. mmc_ident(block_dev_desc_t *dev)
  435. {
  436. return 0;
  437. }
  438. int
  439. mmc2info(ulong addr)
  440. {
  441. /* FIXME hard codes to 32 MB device */
  442. if (addr >= CFG_MMC_BASE && addr < CFG_MMC_BASE + 0x02000000)
  443. {
  444. return 1;
  445. }
  446. return 0;
  447. }
  448. #endif