bcm47xx-flash.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. /*
  2. * Copyright (C) 2006 Felix Fietkau <[email protected]>
  3. * Copyright (C) 2005 Waldemar Brodkorb <[email protected]>
  4. * Copyright (C) 2004 Florian Schirmer ([email protected])
  5. *
  6. * original functions for finding root filesystem from Mike Baker
  7. *
  8. * This program is free software; you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation; either version 2 of the License, or (at your
  11. * option) any later version.
  12. *
  13. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  14. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  15. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
  16. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  17. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  18. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  19. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  20. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  21. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  22. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23. *
  24. * You should have received a copy of the GNU General Public License along
  25. * with this program; if not, write to the Free Software Foundation, Inc.,
  26. * 675 Mass Ave, Cambridge, MA 02139, USA.
  27. *
  28. * Copyright 2001-2003, Broadcom Corporation
  29. * All Rights Reserved.
  30. *
  31. * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
  32. * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
  33. * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
  34. * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
  35. *
  36. * Flash mapping for BCM947XX boards
  37. */
  38. #include <linux/init.h>
  39. #include <linux/module.h>
  40. #include <linux/types.h>
  41. #include <linux/kernel.h>
  42. #include <linux/sched.h>
  43. #include <linux/wait.h>
  44. #include <linux/mtd/mtd.h>
  45. #include <linux/mtd/map.h>
  46. #ifdef CONFIG_MTD_PARTITIONS
  47. #include <linux/mtd/partitions.h>
  48. #endif
  49. #include <linux/crc32.h>
  50. #ifdef CONFIG_SSB
  51. #include <linux/ssb/ssb.h>
  52. #endif
  53. #include <asm/io.h>
  54. #define TRX_MAGIC 0x30524448 /* "HDR0" */
  55. #define TRX_VERSION 1
  56. #define TRX_MAX_LEN 0x3A0000
  57. #define TRX_NO_HEADER 1 /* Do not write TRX header */
  58. #define TRX_GZ_FILES 0x2 /* Contains up to TRX_MAX_OFFSET individual gzip files */
  59. #define TRX_MAX_OFFSET 3
  60. struct trx_header {
  61. u32 magic; /* "HDR0" */
  62. u32 len; /* Length of file including header */
  63. u32 crc32; /* 32-bit CRC from flag_version to end of file */
  64. u32 flag_version; /* 0:15 flags, 16:31 version */
  65. u32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of header */
  66. };
  67. #define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))
  68. #define NVRAM_SPACE 0x8000
  69. #define WINDOW_ADDR 0x1fc00000
  70. #define WINDOW_SIZE 0x400000
  71. #define BUSWIDTH 2
  72. #ifdef CONFIG_SSB
  73. extern struct ssb_bus ssb;
  74. #endif
  75. static struct mtd_info *bcm947xx_mtd;
  76. static void bcm947xx_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
  77. {
  78. if (len==1) {
  79. memcpy_fromio(to, map->virt + from, len);
  80. } else {
  81. int i;
  82. u16 *dest = (u16 *) to;
  83. u16 *src = (u16 *) (map->virt + from);
  84. for (i = 0; i < (len / 2); i++) {
  85. dest[i] = src[i];
  86. }
  87. if (len & 1)
  88. *((u8 *)dest+len-1) = src[i] & 0xff;
  89. }
  90. }
  91. static struct map_info bcm947xx_map = {
  92. name: "Physically mapped flash",
  93. size: WINDOW_SIZE,
  94. bankwidth: BUSWIDTH,
  95. phys: WINDOW_ADDR,
  96. };
  97. #ifdef CONFIG_MTD_PARTITIONS
  98. static struct mtd_partition bcm947xx_parts[] = {
  99. { name: "cfe", offset: 0, size: 0, mask_flags: MTD_WRITEABLE, },
  100. { name: "linux", offset: 0, size: 0, },
  101. { name: "rootfs", offset: 0, size: 0, },
  102. { name: "nvram", offset: 0, size: 0, },
  103. { name: NULL, },
  104. };
  105. static int __init
  106. find_cfe_size(struct mtd_info *mtd, size_t size)
  107. {
  108. struct trx_header *trx;
  109. unsigned char buf[512];
  110. int off;
  111. size_t len;
  112. int blocksize;
  113. trx = (struct trx_header *) buf;
  114. blocksize = mtd->erasesize;
  115. if (blocksize < 0x10000)
  116. blocksize = 0x10000;
  117. for (off = (128*1024); off < size; off += blocksize) {
  118. memset(buf, 0xe5, sizeof(buf));
  119. /*
  120. * Read into buffer
  121. */
  122. if (mtd->read(mtd, off, sizeof(buf), &len, buf) ||
  123. len != sizeof(buf))
  124. continue;
  125. /* found a TRX header */
  126. if (le32_to_cpu(trx->magic) == TRX_MAGIC) {
  127. goto found;
  128. }
  129. }
  130. printk(KERN_NOTICE
  131. "%s: Couldn't find bootloader size\n",
  132. mtd->name);
  133. return -1;
  134. found:
  135. printk(KERN_NOTICE "bootloader size: %d\n", off);
  136. return off;
  137. }
  138. /*
  139. * Copied from mtdblock.c
  140. *
  141. * Cache stuff...
  142. *
  143. * Since typical flash erasable sectors are much larger than what Linux's
  144. * buffer cache can handle, we must implement read-modify-write on flash
  145. * sectors for each block write requests. To avoid over-erasing flash sectors
  146. * and to speed things up, we locally cache a whole flash sector while it is
  147. * being written to until a different sector is required.
  148. */
  149. static void erase_callback(struct erase_info *done)
  150. {
  151. wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
  152. wake_up(wait_q);
  153. }
  154. static int erase_write (struct mtd_info *mtd, unsigned long pos,
  155. int len, const char *buf)
  156. {
  157. struct erase_info erase;
  158. DECLARE_WAITQUEUE(wait, current);
  159. wait_queue_head_t wait_q;
  160. size_t retlen;
  161. int ret;
  162. /*
  163. * First, let's erase the flash block.
  164. */
  165. init_waitqueue_head(&wait_q);
  166. erase.mtd = mtd;
  167. erase.callback = erase_callback;
  168. erase.addr = pos;
  169. erase.len = len;
  170. erase.priv = (u_long)&wait_q;
  171. set_current_state(TASK_INTERRUPTIBLE);
  172. add_wait_queue(&wait_q, &wait);
  173. ret = mtd->erase(mtd, &erase);
  174. if (ret) {
  175. set_current_state(TASK_RUNNING);
  176. remove_wait_queue(&wait_q, &wait);
  177. printk (KERN_WARNING "erase of region [0x%lx, 0x%x] "
  178. "on \"%s\" failed\n",
  179. pos, len, mtd->name);
  180. return ret;
  181. }
  182. schedule(); /* Wait for erase to finish. */
  183. remove_wait_queue(&wait_q, &wait);
  184. /*
  185. * Next, writhe data to flash.
  186. */
  187. ret = mtd->write (mtd, pos, len, &retlen, buf);
  188. if (ret)
  189. return ret;
  190. if (retlen != len)
  191. return -EIO;
  192. return 0;
  193. }
  194. static int __init
  195. find_root(struct mtd_info *mtd, size_t size, struct mtd_partition *part)
  196. {
  197. struct trx_header trx, *trx2;
  198. unsigned char buf[512], *block;
  199. int off, blocksize;
  200. u32 i, crc = ~0;
  201. size_t len;
  202. struct squashfs_super_block *sb = (struct squashfs_super_block *) buf;
  203. blocksize = mtd->erasesize;
  204. if (blocksize < 0x10000)
  205. blocksize = 0x10000;
  206. for (off = (128*1024); off < size; off += blocksize) {
  207. memset(&trx, 0xe5, sizeof(trx));
  208. /*
  209. * Read into buffer
  210. */
  211. if (mtd->read(mtd, off, sizeof(trx), &len, (char *) &trx) ||
  212. len != sizeof(trx))
  213. continue;
  214. /* found a TRX header */
  215. if (le32_to_cpu(trx.magic) == TRX_MAGIC) {
  216. part->offset = le32_to_cpu(trx.offsets[2]) ? :
  217. le32_to_cpu(trx.offsets[1]);
  218. part->size = le32_to_cpu(trx.len);
  219. part->size -= part->offset;
  220. part->offset += off;
  221. goto found;
  222. }
  223. }
  224. printk(KERN_NOTICE
  225. "%s: Couldn't find root filesystem\n",
  226. mtd->name);
  227. return -1;
  228. found:
  229. if (part->size == 0)
  230. return 0;
  231. if (mtd->read(mtd, part->offset, sizeof(buf), &len, buf) || len != sizeof(buf))
  232. return 0;
  233. /* Move the fs outside of the trx */
  234. part->size = 0;
  235. if (trx.len != part->offset + part->size - off) {
  236. /* Update the trx offsets and length */
  237. trx.len = part->offset + part->size - off;
  238. /* Update the trx crc32 */
  239. for (i = (u32) &(((struct trx_header *)NULL)->flag_version); i <= trx.len; i += sizeof(buf)) {
  240. if (mtd->read(mtd, off + i, sizeof(buf), &len, buf) || len != sizeof(buf))
  241. return 0;
  242. crc = crc32_le(crc, buf, min(sizeof(buf), trx.len - i));
  243. }
  244. trx.crc32 = crc;
  245. /* read first eraseblock from the trx */
  246. block = kmalloc(mtd->erasesize, GFP_KERNEL);
  247. trx2 = (struct trx_header *) block;
  248. if (mtd->read(mtd, off, mtd->erasesize, &len, block) || len != mtd->erasesize) {
  249. printk("Error accessing the first trx eraseblock\n");
  250. return 0;
  251. }
  252. printk("Updating TRX offsets and length:\n");
  253. printk("old trx = [0x%08x, 0x%08x, 0x%08x], len=0x%08x crc32=0x%08x\n", trx2->offsets[0], trx2->offsets[1], trx2->offsets[2], trx2->len, trx2->crc32);
  254. printk("new trx = [0x%08x, 0x%08x, 0x%08x], len=0x%08x crc32=0x%08x\n", trx.offsets[0], trx.offsets[1], trx.offsets[2], trx.len, trx.crc32);
  255. /* Write updated trx header to the flash */
  256. memcpy(block, &trx, sizeof(trx));
  257. if (mtd->unlock)
  258. mtd->unlock(mtd, off, mtd->erasesize);
  259. erase_write(mtd, off, mtd->erasesize, block);
  260. if (mtd->sync)
  261. mtd->sync(mtd);
  262. kfree(block);
  263. printk("Done\n");
  264. }
  265. return part->size;
  266. }
  267. struct mtd_partition * __init
  268. init_mtd_partitions(struct mtd_info *mtd, size_t size)
  269. {
  270. int cfe_size;
  271. if ((cfe_size = find_cfe_size(mtd,size)) < 0)
  272. return NULL;
  273. /* boot loader */
  274. bcm947xx_parts[0].offset = 0;
  275. bcm947xx_parts[0].size = cfe_size;
  276. /* nvram */
  277. if (cfe_size != 384 * 1024) {
  278. bcm947xx_parts[3].offset = size - ROUNDUP(NVRAM_SPACE, mtd->erasesize);
  279. bcm947xx_parts[3].size = ROUNDUP(NVRAM_SPACE, mtd->erasesize);
  280. } else {
  281. /* nvram (old 128kb config partition on netgear wgt634u) */
  282. bcm947xx_parts[3].offset = bcm947xx_parts[0].size;
  283. bcm947xx_parts[3].size = ROUNDUP(NVRAM_SPACE, mtd->erasesize);
  284. }
  285. /* linux (kernel and rootfs) */
  286. if (cfe_size != 384 * 1024) {
  287. bcm947xx_parts[1].offset = bcm947xx_parts[0].size;
  288. bcm947xx_parts[1].size = bcm947xx_parts[3].offset -
  289. bcm947xx_parts[1].offset;
  290. } else {
  291. /* do not count the elf loader, which is on one block */
  292. bcm947xx_parts[1].offset = bcm947xx_parts[0].size +
  293. bcm947xx_parts[3].size + mtd->erasesize;
  294. bcm947xx_parts[1].size = size -
  295. bcm947xx_parts[0].size -
  296. (2*bcm947xx_parts[3].size) -
  297. mtd->erasesize;
  298. }
  299. /* find and size rootfs */
  300. find_root(mtd,size,&bcm947xx_parts[2]);
  301. bcm947xx_parts[2].size = size - bcm947xx_parts[2].offset - bcm947xx_parts[3].size;
  302. return bcm947xx_parts;
  303. }
  304. #endif
  305. int __init init_bcm947xx_map(void)
  306. {
  307. #ifdef CONFIG_SSB
  308. struct ssb_mipscore *mcore = &ssb.mipscore;
  309. #endif
  310. size_t size;
  311. int ret = 0;
  312. #ifdef CONFIG_MTD_PARTITIONS
  313. struct mtd_partition *parts;
  314. int i;
  315. #endif
  316. #ifdef CONFIG_SSB
  317. u32 window = mcore->flash_window;
  318. u32 window_size = mcore->flash_window_size;
  319. printk("flash init: 0x%08x 0x%08x\n", window, window_size);
  320. bcm947xx_map.phys = window;
  321. bcm947xx_map.size = window_size;
  322. bcm947xx_map.virt = ioremap_nocache(window, window_size);
  323. #else
  324. printk("flash init: 0x%08x 0x%08x\n", WINDOW_ADDR, WINDOW_SIZE);
  325. bcm947xx_map.virt = ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE);
  326. #endif
  327. if (!bcm947xx_map.virt) {
  328. printk("Failed to ioremap\n");
  329. return -EIO;
  330. }
  331. simple_map_init(&bcm947xx_map);
  332. if (!(bcm947xx_mtd = do_map_probe("cfi_probe", &bcm947xx_map))) {
  333. printk("Failed to do_map_probe\n");
  334. iounmap((void *)bcm947xx_map.virt);
  335. return -ENXIO;
  336. }
  337. /* override copy_from routine */
  338. bcm947xx_map.copy_from = bcm947xx_map_copy_from;
  339. bcm947xx_mtd->owner = THIS_MODULE;
  340. size = bcm947xx_mtd->size;
  341. printk(KERN_NOTICE "Flash device: 0x%x at 0x%x\n", size, WINDOW_ADDR);
  342. #ifdef CONFIG_MTD_PARTITIONS
  343. parts = init_mtd_partitions(bcm947xx_mtd, size);
  344. for (i = 0; parts[i].name; i++);
  345. ret = add_mtd_partitions(bcm947xx_mtd, parts, i);
  346. if (ret) {
  347. printk(KERN_ERR "Flash: add_mtd_partitions failed\n");
  348. goto fail;
  349. }
  350. #endif
  351. return 0;
  352. fail:
  353. if (bcm947xx_mtd)
  354. map_destroy(bcm947xx_mtd);
  355. if (bcm947xx_map.virt)
  356. iounmap((void *)bcm947xx_map.virt);
  357. bcm947xx_map.virt = 0;
  358. return ret;
  359. }
  360. void __exit cleanup_bcm947xx_map(void)
  361. {
  362. #ifdef CONFIG_MTD_PARTITIONS
  363. del_mtd_partitions(bcm947xx_mtd);
  364. #endif
  365. map_destroy(bcm947xx_mtd);
  366. iounmap((void *)bcm947xx_map.virt);
  367. }
  368. module_init(init_bcm947xx_map);
  369. module_exit(cleanup_bcm947xx_map);