OpenWrt – Blame information for rev 2
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us> |
||
3 | * Copyright (C) 2006 Felix Fietkau <nbd@nbd.name> |
||
4 | * Copyright (C) 2005 Waldemar Brodkorb <wbx@openwrt.org> |
||
5 | * Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org) |
||
6 | * |
||
7 | * original functions for finding root filesystem from Mike Baker |
||
8 | * |
||
9 | * This program is free software; you can redistribute it and/or modify it |
||
10 | * under the terms of the GNU General Public License as published by the |
||
11 | * Free Software Foundation; either version 2 of the License, or (at your |
||
12 | * option) any later version. |
||
13 | * |
||
14 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
||
15 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
||
16 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
||
17 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
||
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||
19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
||
20 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
||
21 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
||
23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
24 | * |
||
25 | * You should have received a copy of the GNU General Public License along |
||
26 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
27 | * 675 Mass Ave, Cambridge, MA 02139, USA. |
||
28 | * |
||
29 | * |
||
30 | * Copyright 2004, Broadcom Corporation |
||
31 | * All Rights Reserved. |
||
32 | * |
||
33 | * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY |
||
34 | * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM |
||
35 | * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS |
||
36 | * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. |
||
37 | * |
||
38 | * Flash mapping for adm8668 boards |
||
39 | * |
||
40 | */ |
||
41 | |||
42 | #include <linux/module.h> |
||
43 | #include <linux/types.h> |
||
44 | #include <linux/kernel.h> |
||
45 | #include <linux/sched.h> |
||
46 | #include <linux/wait.h> |
||
47 | #include <linux/mtd/mtd.h> |
||
48 | #include <linux/mtd/map.h> |
||
49 | #include <linux/slab.h> |
||
50 | #include <linux/mtd/partitions.h> |
||
51 | #include <linux/crc32.h> |
||
52 | #include <linux/magic.h> |
||
53 | #include <asm/io.h> |
||
54 | |||
55 | #define WINDOW_ADDR 0x10000000 |
||
56 | #define WINDOW_SIZE 0x800000 |
||
57 | #define BANKWIDTH 2 |
||
58 | |||
59 | /* first a little bit about the headers i need.. */ |
||
60 | |||
61 | /* just interested in part of the full struct */ |
||
62 | struct squashfs_super_block { |
||
63 | __le32 s_magic; |
||
64 | __le32 pad0[9]; /* it's not really padding */ |
||
65 | __le64 bytes_used; |
||
66 | }; |
||
67 | |||
68 | #define IH_MAGIC 0x56190527 /* Image Magic Number */ |
||
69 | struct uboot_header { |
||
70 | uint32_t ih_magic; /* Image Header Magic Number */ |
||
71 | uint32_t ih_hcrc; /* Image Header CRC Checksum */ |
||
72 | uint32_t ih_time; /* Image Creation Timestamp */ |
||
73 | uint32_t ih_size; /* Image Data Size */ |
||
74 | uint32_t ih_load; /* Data Load Address */ |
||
75 | uint32_t ih_ep; /* Entry Point Address */ |
||
76 | uint32_t ih_dcrc; /* Image Data CRC Checksum */ |
||
77 | uint8_t ih_os; /* Operating System */ |
||
78 | uint8_t ih_arch; /* CPU architecture */ |
||
79 | uint8_t ih_type; /* Image Type */ |
||
80 | uint8_t ih_comp; /* Compression Type */ |
||
81 | char ih_name[32]; /* image name */ |
||
82 | }; |
||
83 | |||
84 | /************************************************/ |
||
85 | |||
86 | static struct mtd_info *adm8668_mtd; |
||
87 | |||
88 | struct map_info adm8668_map = { |
||
89 | name: "adm8668-nor", |
||
90 | size: WINDOW_SIZE, |
||
91 | phys: WINDOW_ADDR, |
||
92 | bankwidth: BANKWIDTH, |
||
93 | }; |
||
94 | |||
95 | /* |
||
96 | * Copied from mtdblock.c |
||
97 | * |
||
98 | * Cache stuff... |
||
99 | * |
||
100 | * Since typical flash erasable sectors are much larger than what Linux's |
||
101 | * buffer cache can handle, we must implement read-modify-write on flash |
||
102 | * sectors for each block write requests. To avoid over-erasing flash sectors |
||
103 | * and to speed things up, we locally cache a whole flash sector while it is |
||
104 | * being written to until a different sector is required. |
||
105 | */ |
||
106 | |||
107 | static void erase_callback(struct erase_info *done) |
||
108 | { |
||
109 | wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; |
||
110 | wake_up(wait_q); |
||
111 | } |
||
112 | |||
113 | static int erase_write (struct mtd_info *mtd, unsigned long pos, |
||
114 | int len, const char *buf) |
||
115 | { |
||
116 | struct erase_info erase; |
||
117 | DECLARE_WAITQUEUE(wait, current); |
||
118 | wait_queue_head_t wait_q; |
||
119 | size_t retlen; |
||
120 | int ret; |
||
121 | |||
122 | /* |
||
123 | * First, let's erase the flash block. |
||
124 | */ |
||
125 | |||
126 | init_waitqueue_head(&wait_q); |
||
127 | erase.mtd = mtd; |
||
128 | erase.callback = erase_callback; |
||
129 | erase.addr = pos; |
||
130 | erase.len = len; |
||
131 | erase.priv = (u_long)&wait_q; |
||
132 | |||
133 | set_current_state(TASK_INTERRUPTIBLE); |
||
134 | add_wait_queue(&wait_q, &wait); |
||
135 | |||
136 | ret = mtd->_erase(mtd, &erase); |
||
137 | if (ret) { |
||
138 | set_current_state(TASK_RUNNING); |
||
139 | remove_wait_queue(&wait_q, &wait); |
||
140 | printk (KERN_WARNING "erase of region [0x%lx, 0x%x] " |
||
141 | "on \"%s\" failed\n", |
||
142 | pos, len, mtd->name); |
||
143 | return ret; |
||
144 | } |
||
145 | |||
146 | schedule(); /* Wait for erase to finish. */ |
||
147 | remove_wait_queue(&wait_q, &wait); |
||
148 | |||
149 | /* |
||
150 | * Next, write data to flash. |
||
151 | */ |
||
152 | |||
153 | ret = mtd->_write (mtd, pos, len, &retlen, buf); |
||
154 | if (ret) |
||
155 | return ret; |
||
156 | if (retlen != len) |
||
157 | return -EIO; |
||
158 | return 0; |
||
159 | } |
||
160 | |||
161 | /* decent defaults in case... shrug */ |
||
162 | static struct mtd_partition adm8668_parts[] = { |
||
163 | { name: "linux", offset: 0x40000, size: WINDOW_SIZE-0x40000, }, |
||
164 | { name: "rootfs", offset: 0xe0000, size: 0x140000, }, |
||
165 | { name: "uboot_env", offset: 0x20000, size: 0x20000, }, |
||
166 | { name: NULL, }, |
||
167 | }; |
||
168 | |||
169 | /* in case i wanna change stuff later, and to clarify the math section... */ |
||
170 | #define PART_LINUX 0 |
||
171 | #define PART_ROOTFS 1 |
||
172 | #define NR_PARTS 3 |
||
173 | |||
174 | static int __init |
||
175 | init_mtd_partitions(struct mtd_info *mtd, size_t size) |
||
176 | { |
||
177 | struct uboot_header uhdr; |
||
178 | int off, blocksize; |
||
179 | size_t len, linux_len; |
||
180 | struct squashfs_super_block shdr; |
||
181 | |||
182 | blocksize = mtd->erasesize; |
||
183 | if (blocksize < 0x10000) |
||
184 | blocksize = 0x10000; |
||
185 | |||
186 | /* now find squashfs */ |
||
187 | memset(&shdr, 0xe5, sizeof(shdr)); |
||
188 | for (off = adm8668_parts[PART_LINUX].offset; off < size; off += blocksize) { |
||
189 | /* |
||
190 | * Read into buffer |
||
191 | */ |
||
192 | if (mtd->_read(mtd, off, sizeof(shdr), &len, (char *)&shdr) || |
||
193 | len != sizeof(shdr)) |
||
194 | continue; |
||
195 | |||
196 | if (shdr.s_magic == SQUASHFS_MAGIC) { |
||
197 | uint32_t fs_size = (uint32_t)shdr.bytes_used; |
||
198 | |||
199 | printk(KERN_INFO "%s: Filesystem type: squashfs, size=%dkB\n", |
||
200 | mtd->name, fs_size>>10); |
||
201 | |||
202 | /* Update rootfs based on the superblock info, and |
||
203 | * stretch to end of MTD. rootfs_split will split it */ |
||
204 | adm8668_parts[PART_ROOTFS].offset = off; |
||
205 | adm8668_parts[PART_ROOTFS].size = mtd->size - |
||
206 | adm8668_parts[PART_ROOTFS].offset; |
||
207 | |||
208 | /* kernel ends where rootfs starts |
||
209 | * but we'll keep it full-length for upgrades */ |
||
210 | linux_len = adm8668_parts[PART_LINUX+1].offset - |
||
211 | adm8668_parts[PART_LINUX].offset; |
||
212 | #if 1 |
||
213 | adm8668_parts[PART_LINUX].size = mtd->size - |
||
214 | adm8668_parts[PART_LINUX].offset; |
||
215 | #else |
||
216 | adm8668_parts[PART_LINUX].size = linux_len; |
||
217 | #endif |
||
218 | goto found; |
||
219 | } |
||
220 | } |
||
221 | |||
222 | printk(KERN_NOTICE |
||
223 | "%s: Couldn't find root filesystem\n", |
||
224 | mtd->name); |
||
225 | return NR_PARTS; |
||
226 | |||
227 | found: |
||
228 | if (mtd->_read(mtd, adm8668_parts[PART_LINUX].offset, sizeof(uhdr), &len, (char *)&uhdr) || |
||
229 | len != sizeof(uhdr)) |
||
230 | return NR_PARTS; |
||
231 | |||
232 | /* that's odd. how'd ya boot it then */ |
||
233 | if (uhdr.ih_magic != IH_MAGIC) |
||
234 | return NR_PARTS; |
||
235 | |||
236 | if (be32_to_cpu(uhdr.ih_size) != (linux_len - sizeof(uhdr))) { |
||
237 | unsigned char *block, *data; |
||
238 | unsigned int offset; |
||
239 | |||
240 | offset = adm8668_parts[PART_LINUX].offset + |
||
241 | sizeof(struct uboot_header); |
||
242 | data = (unsigned char *)(WINDOW_ADDR | 0xA0000000 | offset); |
||
243 | |||
244 | printk(KERN_NOTICE "Updating U-boot image:\n"); |
||
245 | printk(KERN_NOTICE " old: [size: %8d crc32: 0x%08x]\n", |
||
246 | be32_to_cpu(uhdr.ih_size), be32_to_cpu(uhdr.ih_dcrc)); |
||
247 | |||
248 | /* Update the data length & crc32 */ |
||
249 | uhdr.ih_size = cpu_to_be32(linux_len - sizeof(uhdr)); |
||
250 | uhdr.ih_dcrc = crc32_le(~0, data, linux_len - sizeof(uhdr)) ^ (~0); |
||
251 | uhdr.ih_dcrc = cpu_to_be32(uhdr.ih_dcrc); |
||
252 | |||
253 | printk(KERN_NOTICE " new: [size: %8d crc32: 0x%08x]\n", |
||
254 | be32_to_cpu(uhdr.ih_size), be32_to_cpu(uhdr.ih_dcrc)); |
||
255 | |||
256 | /* update header's crc... */ |
||
257 | uhdr.ih_hcrc = 0; |
||
258 | uhdr.ih_hcrc = crc32_le(~0, (unsigned char *)&uhdr, |
||
259 | sizeof(uhdr)) ^ (~0); |
||
260 | uhdr.ih_hcrc = cpu_to_be32(uhdr.ih_hcrc); |
||
261 | |||
262 | /* read first eraseblock from the image */ |
||
263 | block = kmalloc(mtd->erasesize, GFP_KERNEL); |
||
264 | if (mtd->_read(mtd, adm8668_parts[PART_LINUX].offset, mtd->erasesize, &len, block) || len != mtd->erasesize) { |
||
265 | printk("Error copying first eraseblock\n"); |
||
266 | return 0; |
||
267 | } |
||
268 | |||
269 | /* Write updated header to the flash */ |
||
270 | memcpy(block, &uhdr, sizeof(uhdr)); |
||
271 | if (mtd->_unlock) |
||
272 | mtd->_unlock(mtd, off, mtd->erasesize); |
||
273 | erase_write(mtd, adm8668_parts[PART_LINUX].offset, mtd->erasesize, block); |
||
274 | if (mtd->_sync) |
||
275 | mtd->_sync(mtd); |
||
276 | kfree(block); |
||
277 | printk(KERN_NOTICE "Done\n"); |
||
278 | } |
||
279 | |||
280 | return NR_PARTS; |
||
281 | } |
||
282 | |||
283 | int __init init_adm8668_map(void) |
||
284 | { |
||
285 | int nr_parts, ret; |
||
286 | |||
287 | adm8668_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); |
||
288 | |||
289 | if (!adm8668_map.virt) { |
||
290 | printk(KERN_ERR "Failed to ioremap\n"); |
||
291 | return -EIO; |
||
292 | } |
||
293 | |||
294 | simple_map_init(&adm8668_map); |
||
295 | if (!(adm8668_mtd = do_map_probe("cfi_probe", &adm8668_map))) { |
||
296 | printk(KERN_ERR "cfi_probe failed\n"); |
||
297 | iounmap((void *)adm8668_map.virt); |
||
298 | return -ENXIO; |
||
299 | } |
||
300 | |||
301 | adm8668_mtd->owner = THIS_MODULE; |
||
302 | |||
303 | nr_parts = init_mtd_partitions(adm8668_mtd, adm8668_mtd->size); |
||
304 | ret = mtd_device_register(adm8668_mtd, adm8668_parts, nr_parts); |
||
305 | if (ret) { |
||
306 | printk(KERN_ERR "Flash: mtd_device_register failed\n"); |
||
307 | goto fail; |
||
308 | } |
||
309 | |||
310 | return 0; |
||
311 | |||
312 | fail: |
||
313 | if (adm8668_mtd) |
||
314 | map_destroy(adm8668_mtd); |
||
315 | if (adm8668_map.virt) |
||
316 | iounmap((void *) adm8668_map.virt); |
||
317 | adm8668_map.virt = 0; |
||
318 | return ret; |
||
319 | } |
||
320 | |||
321 | void __exit cleanup_adm8668_map(void) |
||
322 | { |
||
323 | mtd_device_unregister(adm8668_mtd); |
||
324 | map_destroy(adm8668_mtd); |
||
325 | iounmap((void *) adm8668_map.virt); |
||
326 | adm8668_map.virt = 0; |
||
327 | } |
||
328 | |||
329 | module_init(init_adm8668_map); |
||
330 | module_exit(cleanup_adm8668_map); |
||
331 | |||
332 | MODULE_LICENSE("GPL"); |
||
333 | MODULE_AUTHOR("Scott Nicholas <neutronscott@scottn.us>"); |
||
334 | MODULE_DESCRIPTION("MTD map driver for ADM8668 NOR Flash"); |