source: trunk/target/linux/brcm47xx/files/drivers/mtd/maps/bcm47xx-flash.c @ 27005

Last change on this file since 27005 was 27005, checked in by hauke, 6 years ago

brcm47xx: fix partition layout on SimpleShare devices.

The flash driver code should be cleaned up and the broad detection code should be placed into arch code and used here.

This fixes #9323

Thank you Will Holmes for the patch.

File size: 17.3 KB
Line 
1/*
2 *  Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
3 *  Copyright (C) 2005 Waldemar Brodkorb <wbx@openwrt.org>
4 *  Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org)
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
39#include <linux/init.h>
40#include <linux/module.h>
41#include <linux/types.h>
42#include <linux/kernel.h>
43#include <linux/sched.h>
44#include <linux/wait.h>
45#include <linux/mtd/mtd.h>
46#include <linux/mtd/map.h>
47#ifdef CONFIG_MTD_PARTITIONS
48#include <linux/mtd/partitions.h>
49#endif
50#include <linux/crc32.h>
51#ifdef CONFIG_SSB
52#include <linux/ssb/ssb.h>
53#endif
54#include <asm/io.h>
55#include <asm/mach-bcm47xx/nvram.h>
56#include <asm/fw/cfe/cfe_api.h>
57
58
59#define TRX_MAGIC       0x30524448      /* "HDR0" */
60#define TRX_VERSION     1
61#define TRX_MAX_LEN     0x3A0000
62#define TRX_NO_HEADER   1               /* Do not write TRX header */   
63#define TRX_GZ_FILES    0x2     /* Contains up to TRX_MAX_OFFSET individual gzip files */
64#define TRX_MAX_OFFSET  3
65
66struct trx_header {
67        u32 magic;              /* "HDR0" */
68        u32 len;                /* Length of file including header */
69        u32 crc32;              /* 32-bit CRC from flag_version to end of file */
70        u32 flag_version;       /* 0:15 flags, 16:31 version */
71        u32 offsets[TRX_MAX_OFFSET];    /* Offsets of partitions from start of header */
72};
73
74/* for Edimax Print servers which use an additional header
75 * then the firmware on flash looks like :
76 * EDIMAX HEADER | TRX HEADER
77 * As this header is 12 bytes long we have to handle it
78 * and skip it to find the TRX header
79 */
80#define EDIMAX_PS_HEADER_MAGIC  0x36315350 /*  "PS16"  */
81#define EDIMAX_PS_HEADER_LEN    0xc /* 12 bytes long for edimax header */
82
83#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))
84#define NVRAM_SPACE 0x8000
85#define WINDOW_ADDR 0x1fc00000
86#define WINDOW_SIZE 0x400000
87#define BUSWIDTH 2
88
89#define ROUTER_NETGEAR_WGR614L         1
90#define ROUTER_NETGEAR_WNR834B         2
91#define ROUTER_NETGEAR_WNDR3300        3
92#define ROUTER_NETGEAR_WNR3500L        4
93#define ROUTER_SIMPLETECH_SIMPLESHARE  5
94
95#ifdef CONFIG_SSB
96extern struct ssb_bus ssb_bcm47xx;
97#endif
98static struct mtd_info *bcm47xx_mtd;
99
100static void bcm47xx_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
101{
102        if (len==1) {
103                memcpy_fromio(to, map->virt + from, len);
104        } else {
105                int i;
106                u16 *dest = (u16 *) to;
107                u16 *src  = (u16 *) (map->virt + from);
108                for (i = 0; i < (len / 2); i++) {
109                        dest[i] = src[i];
110                }
111                if (len & 1)
112                        *((u8 *)dest+len-1) = src[i] & 0xff;
113        }
114}
115
116static struct map_info bcm47xx_map = {
117        name: "Physically mapped flash",
118        size: WINDOW_SIZE,
119        bankwidth: BUSWIDTH,
120        phys: WINDOW_ADDR,
121};
122
123#ifdef CONFIG_MTD_PARTITIONS
124
125static struct mtd_partition bcm47xx_parts[] = {
126        { name: "cfe",  offset: 0, size: 0, mask_flags: MTD_WRITEABLE, },
127        { name: "linux", offset: 0, size: 0, },
128        { name: "rootfs", offset: 0, size: 0, },
129        { name: "nvram", offset: 0, size: 0, },
130        { name: NULL, }, /* Used to create custom partitons with the function get_router() */
131        { name: NULL, },
132};
133
134static int __init
135find_cfe_size(struct mtd_info *mtd, size_t size)
136{
137        struct trx_header *trx;
138        unsigned char buf[512];
139        int off;
140        size_t len;
141        int blocksize;
142
143        trx = (struct trx_header *) buf;
144
145        blocksize = mtd->erasesize;
146        if (blocksize < 0x10000)
147                blocksize = 0x10000;
148
149        for (off = (128*1024); off < size; off += blocksize) {
150                memset(buf, 0xe5, sizeof(buf));
151
152                /*
153                 * Read into buffer
154                 */
155                if (mtd->read(mtd, off, sizeof(buf), &len, buf) ||
156                    len != sizeof(buf))
157                        continue;
158
159                if (le32_to_cpu(trx->magic) == EDIMAX_PS_HEADER_MAGIC) {
160                        if (mtd->read(mtd, off + EDIMAX_PS_HEADER_LEN,
161                            sizeof(buf), &len, buf) || len != sizeof(buf)) {
162                                continue;
163                        } else {
164                                printk(KERN_NOTICE"Found edimax header\n");
165                        }
166                }
167
168                /* found a TRX header */
169                if (le32_to_cpu(trx->magic) == TRX_MAGIC) {
170                        goto found;
171                }
172        }
173
174        printk(KERN_NOTICE
175               "%s: Couldn't find bootloader size\n",
176               mtd->name);
177        return -1;
178
179 found:
180        printk(KERN_NOTICE "bootloader size: %d\n", off);
181        return off;
182
183}
184
185/*
186 * Copied from mtdblock.c
187 *
188 * Cache stuff...
189 *
190 * Since typical flash erasable sectors are much larger than what Linux's
191 * buffer cache can handle, we must implement read-modify-write on flash
192 * sectors for each block write requests.  To avoid over-erasing flash sectors
193 * and to speed things up, we locally cache a whole flash sector while it is
194 * being written to until a different sector is required.
195 */
196
197static void erase_callback(struct erase_info *done)
198{
199        wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
200        wake_up(wait_q);
201}
202
203static int erase_write (struct mtd_info *mtd, unsigned long pos, 
204                        int len, const char *buf)
205{
206        struct erase_info erase;
207        DECLARE_WAITQUEUE(wait, current);
208        wait_queue_head_t wait_q;
209        size_t retlen;
210        int ret;
211
212        /*
213         * First, let's erase the flash block.
214         */
215
216        init_waitqueue_head(&wait_q);
217        erase.mtd = mtd;
218        erase.callback = erase_callback;
219        erase.addr = pos;
220        erase.len = len;
221        erase.priv = (u_long)&wait_q;
222
223        set_current_state(TASK_INTERRUPTIBLE);
224        add_wait_queue(&wait_q, &wait);
225
226        ret = mtd->erase(mtd, &erase);
227        if (ret) {
228                set_current_state(TASK_RUNNING);
229                remove_wait_queue(&wait_q, &wait);
230                printk (KERN_WARNING "erase of region [0x%lx, 0x%x] "
231                                     "on \"%s\" failed\n",
232                        pos, len, mtd->name);
233                return ret;
234        }
235
236        schedule();  /* Wait for erase to finish. */
237        remove_wait_queue(&wait_q, &wait);
238
239        /*
240         * Next, write data to flash.
241         */
242
243        ret = mtd->write (mtd, pos, len, &retlen, buf);
244        if (ret)
245                return ret;
246        if (retlen != len)
247                return -EIO;
248        return 0;
249}
250
251
252static int __init
253find_dual_image_off (struct mtd_info *mtd, size_t size)
254{
255        struct trx_header trx;
256        int off, blocksize;
257        size_t len;
258
259        blocksize = mtd->erasesize;
260        if (blocksize < 0x10000)
261                blocksize = 0x10000;
262
263        for (off = (128*1024); off < size; off += blocksize) {
264                memset(&trx, 0xe5, sizeof(trx));
265                /*
266                * Read into buffer
267                */
268                if (mtd->read(mtd, off, sizeof(trx), &len, (char *) &trx) ||
269                    len != sizeof(trx))
270                        continue;
271                /* found last TRX header */
272                if (le32_to_cpu(trx.magic) == TRX_MAGIC){ 
273                        if (le32_to_cpu(trx.flag_version >> 16)==2){
274                                printk("dual image TRX header found\n");
275                                return size/2;
276                        } else {
277                                return 0;
278                        }
279                }
280        }
281        return 0;
282}
283
284
285static int __init
286find_root(struct mtd_info *mtd, size_t size, struct mtd_partition *part)
287{
288        struct trx_header trx, *trx2;
289        unsigned char buf[512], *block;
290        int off, blocksize, trxoff = 0;
291        u32 i, crc = ~0;
292        size_t len;
293        bool edimax = false;
294
295        blocksize = mtd->erasesize;
296        if (blocksize < 0x10000)
297                blocksize = 0x10000;
298
299        for (off = (128*1024); off < size; off += blocksize) {
300                memset(&trx, 0xe5, sizeof(trx));
301
302                /*
303                 * Read into buffer
304                 */
305                if (mtd->read(mtd, off, sizeof(trx), &len, (char *) &trx) ||
306                    len != sizeof(trx))
307                        continue;
308
309                /* found an edimax header */
310                if (le32_to_cpu(trx.magic) == EDIMAX_PS_HEADER_MAGIC) {
311                        /* read the correct trx header */
312                        if (mtd->read(mtd, off + EDIMAX_PS_HEADER_LEN,
313                            sizeof(trx), &len, (char *) &trx) ||
314                            len != sizeof(trx)) {
315                                continue;
316                        } else {
317                                printk(KERN_NOTICE"Found an edimax ps header\n");
318                                edimax = true;
319                        }
320                }
321
322                /* found a TRX header */
323                if (le32_to_cpu(trx.magic) == TRX_MAGIC) {
324                        part->offset = le32_to_cpu(trx.offsets[2]) ? : 
325                                le32_to_cpu(trx.offsets[1]);
326                        part->size = le32_to_cpu(trx.len); 
327
328                        part->size -= part->offset;
329                        part->offset += off;
330                        if (edimax) {
331                                off += EDIMAX_PS_HEADER_LEN;
332                                trxoff = EDIMAX_PS_HEADER_LEN;
333                        }
334
335                        goto found;
336                }
337        }
338
339        printk(KERN_NOTICE
340               "%s: Couldn't find root filesystem\n",
341               mtd->name);
342        return -1;
343
344 found:
345        printk(KERN_NOTICE"TRX offset : %x\n", trxoff);
346        if (part->size == 0)
347                return 0;
348       
349        if (mtd->read(mtd, part->offset, sizeof(buf), &len, buf) || len != sizeof(buf))
350                return 0;
351
352        /* Move the fs outside of the trx */
353        part->size = 0;
354
355        if (trx.len != part->offset + part->size - off) {
356                /* Update the trx offsets and length */
357                trx.len = part->offset + part->size - off;
358       
359                /* Update the trx crc32 */
360                for (i = (u32) &(((struct trx_header *)NULL)->flag_version); i <= trx.len; i += sizeof(buf)) {
361                        if (mtd->read(mtd, off + i, sizeof(buf), &len, buf) || len != sizeof(buf))
362                                return 0;
363                        crc = crc32_le(crc, buf, min(sizeof(buf), trx.len - i));
364                }
365                trx.crc32 = crc;
366
367                /* read first eraseblock from the trx */
368                block = kmalloc(mtd->erasesize, GFP_KERNEL);
369                trx2 = (struct trx_header *) block;
370                if (mtd->read(mtd, off - trxoff, mtd->erasesize, &len, block) || len != mtd->erasesize) {
371                        printk("Error accessing the first trx eraseblock\n");
372                        return 0;
373                }
374               
375                printk("Updating TRX offsets and length:\n");
376                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);
377                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);
378
379                /* Write updated trx header to the flash */
380                memcpy(block + trxoff, &trx, sizeof(trx));
381                if (mtd->unlock)
382                        mtd->unlock(mtd, off - trxoff, mtd->erasesize);
383                erase_write(mtd, off - trxoff, mtd->erasesize, block);
384                if (mtd->sync)
385                        mtd->sync(mtd);
386                kfree(block);
387                printk("Done\n");
388        }
389       
390        return part->size;
391}
392
393static int get_router(void)
394{
395        char buf[20];
396        u32 boardnum = 0;
397        u16 boardtype = 0;
398        u16 boardrev = 0;
399        u32 boardflags = 0;
400        u16 sdram_init = 0;
401        u16 cardbus = 0;
402        u16 strev = 0;
403
404        if (nvram_getenv("boardnum", buf, sizeof(buf)) >= 0)
405                boardnum = simple_strtoul(buf, NULL, 0);
406        if (nvram_getenv("boardtype", buf, sizeof(buf)) >= 0)
407                boardtype = simple_strtoul(buf, NULL, 0);
408        if (nvram_getenv("boardrev", buf, sizeof(buf)) >= 0)
409                boardrev = simple_strtoul(buf, NULL, 0);
410        if (nvram_getenv("boardflags", buf, sizeof(buf)) >= 0)
411                boardflags = simple_strtoul(buf, NULL, 0);
412        if (nvram_getenv("sdram_init", buf, sizeof(buf)) >= 0)
413                sdram_init = simple_strtoul(buf, NULL, 0);
414        if (nvram_getenv("cardbus", buf, sizeof(buf)) >= 0)
415                cardbus = simple_strtoul(buf, NULL, 0);
416        if (nvram_getenv("st_rev", buf, sizeof(buf)) >= 0)
417                strev = simple_strtoul(buf, NULL, 0);
418
419        if ((boardnum == 8 || boardnum == 01)
420          && boardtype == 0x0472 && cardbus == 1) {
421                /* Netgear WNR834B, Netgear WNR834Bv2 */
422                return ROUTER_NETGEAR_WNR834B;
423        }
424
425        if (boardnum == 01 && boardtype == 0x0472 && boardrev == 0x23) {
426                /* Netgear WNDR-3300 */
427                return ROUTER_NETGEAR_WNDR3300;
428        }
429
430        if ((boardnum == 83258 || boardnum == 01)
431          && boardtype == 0x048e
432          && (boardrev == 0x11 || boardrev == 0x10)
433          && boardflags == 0x750
434          && sdram_init == 0x000A) {
435                /* Netgear WGR614v8/L/WW 16MB ram, cfe v1.3 or v1.5 */
436                return ROUTER_NETGEAR_WGR614L;
437        }
438
439        if ((boardnum == 1 || boardnum == 3500)
440          && boardtype == 0x04CF
441          && (boardrev == 0x1213 || boardrev == 02)) {
442                /* Netgear WNR3500v2/U/L */
443                return ROUTER_NETGEAR_WNR3500L;
444        }
445
446        if (boardtype == 0x042f
447          && boardrev == 0x10
448          && boardflags == 0 
449          && strev == 0x11) { 
450                /* Simpletech Simpleshare */
451                return ROUTER_SIMPLETECH_SIMPLESHARE;
452        }
453
454        return 0;
455}
456
457struct mtd_partition * __init
458init_mtd_partitions(struct mtd_info *mtd, size_t size)
459{
460        int cfe_size;
461        int dual_image_offset = 0;
462        /* e.g Netgear 0x003e0000-0x003f0000 : "board_data", we exclude this
463         * part from our mapping to prevent overwriting len/checksum on e.g.
464         * Netgear WGR614v8/L/WW
465         */
466        int custom_data_size = 0;
467
468        if ((cfe_size = find_cfe_size(mtd,size)) < 0)
469                return NULL;
470
471        /* boot loader */
472        bcm47xx_parts[0].offset = 0;
473        bcm47xx_parts[0].size   = cfe_size;
474
475        /* nvram */
476        if (cfe_size != 384 * 1024) {
477
478                switch (get_router()) {
479                case ROUTER_NETGEAR_WGR614L:
480                case ROUTER_NETGEAR_WNR834B:
481                case ROUTER_NETGEAR_WNDR3300:
482                case ROUTER_NETGEAR_WNR3500L:
483                        /* Netgear: checksum is @ 0x003AFFF8 for 4M flash or checksum
484                         * is @ 0x007AFFF8 for 8M flash
485                         */
486                        custom_data_size = mtd->erasesize;
487
488                        bcm47xx_parts[3].offset = size - ROUNDUP(NVRAM_SPACE, mtd->erasesize);
489                        bcm47xx_parts[3].size   = ROUNDUP(NVRAM_SPACE, mtd->erasesize);
490
491                        /* Place CFE board_data into a partition */
492                        bcm47xx_parts[4].name = "board_data";
493                        bcm47xx_parts[4].offset = bcm47xx_parts[3].offset - custom_data_size;
494                        bcm47xx_parts[4].size   =  custom_data_size;
495                        break;
496
497                case ROUTER_SIMPLETECH_SIMPLESHARE:
498                        /* Fixup Simpletech Simple share nvram  */
499
500                        printk("Setting up simpletech nvram\n");
501                        custom_data_size = mtd->erasesize;
502
503                        bcm47xx_parts[3].offset = size - ROUNDUP(NVRAM_SPACE, mtd->erasesize) * 2;
504                        bcm47xx_parts[3].size   = ROUNDUP(NVRAM_SPACE, mtd->erasesize);
505
506                        /* Place backup nvram into a partition */
507                        bcm47xx_parts[4].name = "nvram_copy";
508                        bcm47xx_parts[4].offset = size - ROUNDUP(NVRAM_SPACE, mtd->erasesize);
509                        bcm47xx_parts[4].size   = ROUNDUP(NVRAM_SPACE, mtd->erasesize);
510                        break;
511
512                default:
513                        bcm47xx_parts[3].offset = size - ROUNDUP(NVRAM_SPACE, mtd->erasesize);
514                        bcm47xx_parts[3].size   = ROUNDUP(NVRAM_SPACE, mtd->erasesize);
515                }
516
517        } else {
518                /* nvram (old 128kb config partition on netgear wgt634u) */
519                bcm47xx_parts[3].offset = bcm47xx_parts[0].size;
520                bcm47xx_parts[3].size   = ROUNDUP(NVRAM_SPACE, mtd->erasesize);
521        }
522
523        /* dual image offset*/
524        printk("Looking for dual image\n");
525        dual_image_offset=find_dual_image_off(mtd,size);
526        /* linux (kernel and rootfs) */
527        if (cfe_size != 384 * 1024) {
528                if (get_router() == ROUTER_SIMPLETECH_SIMPLESHARE) {
529                        bcm47xx_parts[1].offset = bcm47xx_parts[0].size;
530                        bcm47xx_parts[1].size   = bcm47xx_parts[4].offset - dual_image_offset -
531                                bcm47xx_parts[1].offset - custom_data_size;
532                } else {
533                        bcm47xx_parts[1].offset = bcm47xx_parts[0].size;
534                        bcm47xx_parts[1].size   = bcm47xx_parts[3].offset - dual_image_offset -
535                                bcm47xx_parts[1].offset - custom_data_size;
536                }
537        } else {
538                /* do not count the elf loader, which is on one block */
539                bcm47xx_parts[1].offset = bcm47xx_parts[0].size + 
540                        bcm47xx_parts[3].size + mtd->erasesize;
541                bcm47xx_parts[1].size   = size - 
542                        bcm47xx_parts[0].size - 
543                        (2*bcm47xx_parts[3].size) - 
544                        mtd->erasesize - custom_data_size;
545        }
546
547        /* find and size rootfs */
548        find_root(mtd,size,&bcm47xx_parts[2]);
549        bcm47xx_parts[2].size = size - dual_image_offset -
550                                bcm47xx_parts[2].offset -
551                                bcm47xx_parts[3].size - custom_data_size;
552
553        return bcm47xx_parts;
554}
555#endif
556
557int __init init_bcm47xx_map(void)
558{
559#ifdef CONFIG_SSB
560        struct ssb_mipscore *mcore = &ssb_bcm47xx.mipscore;
561#endif
562        size_t size;
563        int ret = 0;
564#ifdef CONFIG_MTD_PARTITIONS
565        struct mtd_partition *parts;
566        int i;
567#endif
568
569#ifdef CONFIG_SSB
570        u32 window = mcore->flash_window;
571        u32 window_size = mcore->flash_window_size;
572
573        printk("flash init: 0x%08x 0x%08x\n", window, window_size);
574        bcm47xx_map.phys = window;
575        bcm47xx_map.size = window_size;
576        bcm47xx_map.bankwidth = mcore->flash_buswidth;
577        bcm47xx_map.virt = ioremap_nocache(window, window_size);
578#else
579        printk("flash init: 0x%08x 0x%08x\n", WINDOW_ADDR, WINDOW_SIZE);
580        bcm47xx_map.virt = ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE);
581#endif
582
583        if (!bcm47xx_map.virt) {
584                printk("Failed to ioremap\n");
585                return -EIO;
586        }
587
588        simple_map_init(&bcm47xx_map);
589       
590        if (!(bcm47xx_mtd = do_map_probe("cfi_probe", &bcm47xx_map))) {
591                printk("Failed to do_map_probe\n");
592                iounmap((void *)bcm47xx_map.virt);
593                return -ENXIO;
594        }
595
596        /* override copy_from routine */
597        bcm47xx_map.copy_from = bcm47xx_map_copy_from;
598
599        bcm47xx_mtd->owner = THIS_MODULE;
600
601        size = bcm47xx_mtd->size;
602
603        printk(KERN_NOTICE "Flash device: 0x%x at 0x%x\n", size, WINDOW_ADDR);
604
605#ifdef CONFIG_MTD_PARTITIONS
606        parts = init_mtd_partitions(bcm47xx_mtd, size);
607        for (i = 0; parts[i].name; i++);
608        ret = add_mtd_partitions(bcm47xx_mtd, parts, i);
609        if (ret) {
610                printk(KERN_ERR "Flash: add_mtd_partitions failed\n");
611                goto fail;
612        }
613#endif
614        return 0;
615
616 fail:
617        if (bcm47xx_mtd)
618                map_destroy(bcm47xx_mtd);
619        if (bcm47xx_map.virt)
620                iounmap((void *)bcm47xx_map.virt);
621        bcm47xx_map.virt = 0;
622        return ret;
623}
624
625void __exit cleanup_bcm47xx_map(void)
626{
627#ifdef CONFIG_MTD_PARTITIONS
628        del_mtd_partitions(bcm47xx_mtd);
629#endif
630        map_destroy(bcm47xx_mtd);
631        iounmap((void *)bcm47xx_map.virt);
632}
633
634module_init(init_bcm47xx_map);
635module_exit(cleanup_bcm47xx_map);
Note: See TracBrowser for help on using the repository browser.