|
|
|
@ -1,30 +1,30 @@ |
|
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us> |
|
|
|
|
* Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> |
|
|
|
|
* Copyright (C) 2005 Waldemar Brodkorb <wbx@openwrt.org> |
|
|
|
|
* Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org) |
|
|
|
|
* Infineon/ADMTek ADM8668 (WildPass) partition parser support |
|
|
|
|
* |
|
|
|
|
* original functions for finding root filesystem from Mike Baker |
|
|
|
|
* Copyright (C) 2010 Scott Nicholas <neutronscott@scottn.us> |
|
|
|
|
* Copyright (C) 2012 Florian Fainelli <florian@openwrt.org> |
|
|
|
|
* |
|
|
|
|
* 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. |
|
|
|
|
* original functions for finding root filesystem from Mike Baker |
|
|
|
|
* |
|
|
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
|
|
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
|
|
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
|
|
|
|
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
|
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
|
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
|
|
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
|
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
|
|
|
* 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. |
|
|
|
|
* |
|
|
|
|
* 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., |
|
|
|
|
* 675 Mass Ave, Cambridge, MA 02139, USA. |
|
|
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
|
|
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
|
|
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
|
|
|
|
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
|
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
|
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
|
|
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
|
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
|
|
|
* |
|
|
|
|
* 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., |
|
|
|
|
* 675 Mass Ave, Cambridge, MA 02139, USA. |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* Copyright 2004, Broadcom Corporation |
|
|
|
@ -34,9 +34,6 @@ |
|
|
|
|
* KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM |
|
|
|
|
* SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS |
|
|
|
|
* FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. |
|
|
|
|
* |
|
|
|
|
* Flash mapping for adm8668 boards |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
#include <linux/module.h> |
|
|
|
@ -45,16 +42,13 @@ |
|
|
|
|
#include <linux/sched.h> |
|
|
|
|
#include <linux/wait.h> |
|
|
|
|
#include <linux/mtd/mtd.h> |
|
|
|
|
#include <linux/mtd/map.h> |
|
|
|
|
#include <linux/slab.h> |
|
|
|
|
#include <linux/mtd/partitions.h> |
|
|
|
|
#include <linux/vmalloc.h> |
|
|
|
|
#include <linux/slab.h> |
|
|
|
|
#include <linux/crc32.h> |
|
|
|
|
#include <linux/magic.h> |
|
|
|
|
#include <asm/io.h> |
|
|
|
|
|
|
|
|
|
#define WINDOW_ADDR 0x10000000 |
|
|
|
|
#define WINDOW_SIZE 0x800000 |
|
|
|
|
#define BANKWIDTH 2 |
|
|
|
|
#define PFX "adm8668-part: " |
|
|
|
|
|
|
|
|
|
/* first a little bit about the headers i need.. */ |
|
|
|
|
|
|
|
|
@ -81,176 +75,122 @@ struct uboot_header { |
|
|
|
|
char ih_name[32]; /* image name */ |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/************************************************/ |
|
|
|
|
|
|
|
|
|
static struct mtd_info *adm8668_mtd; |
|
|
|
|
|
|
|
|
|
struct map_info adm8668_map = { |
|
|
|
|
name: "adm8668-nor", |
|
|
|
|
size: WINDOW_SIZE, |
|
|
|
|
phys: WINDOW_ADDR, |
|
|
|
|
bankwidth: BANKWIDTH, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Copied from mtdblock.c |
|
|
|
|
* |
|
|
|
|
* Cache stuff... |
|
|
|
|
* |
|
|
|
|
* Since typical flash erasable sectors are much larger than what Linux's |
|
|
|
|
* buffer cache can handle, we must implement read-modify-write on flash |
|
|
|
|
* sectors for each block write requests. To avoid over-erasing flash sectors |
|
|
|
|
* and to speed things up, we locally cache a whole flash sector while it is |
|
|
|
|
* being written to until a different sector is required. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
static void erase_callback(struct erase_info *done) |
|
|
|
|
{ |
|
|
|
|
wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; |
|
|
|
|
wake_up(wait_q); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int erase_write (struct mtd_info *mtd, unsigned long pos, |
|
|
|
|
int len, const char *buf) |
|
|
|
|
{ |
|
|
|
|
struct erase_info erase; |
|
|
|
|
DECLARE_WAITQUEUE(wait, current); |
|
|
|
|
wait_queue_head_t wait_q; |
|
|
|
|
size_t retlen; |
|
|
|
|
int ret; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* First, let's erase the flash block. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
init_waitqueue_head(&wait_q); |
|
|
|
|
erase.mtd = mtd; |
|
|
|
|
erase.callback = erase_callback; |
|
|
|
|
erase.addr = pos; |
|
|
|
|
erase.len = len; |
|
|
|
|
erase.priv = (u_long)&wait_q; |
|
|
|
|
|
|
|
|
|
set_current_state(TASK_INTERRUPTIBLE); |
|
|
|
|
add_wait_queue(&wait_q, &wait); |
|
|
|
|
|
|
|
|
|
ret = mtd->erase(mtd, &erase); |
|
|
|
|
if (ret) { |
|
|
|
|
set_current_state(TASK_RUNNING); |
|
|
|
|
remove_wait_queue(&wait_q, &wait); |
|
|
|
|
printk (KERN_WARNING "erase of region [0x%lx, 0x%x] " |
|
|
|
|
"on \"%s\" failed\n", |
|
|
|
|
pos, len, mtd->name); |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
schedule(); /* Wait for erase to finish. */ |
|
|
|
|
remove_wait_queue(&wait_q, &wait); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Next, write data to flash. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
ret = mtd->write (mtd, pos, len, &retlen, buf); |
|
|
|
|
if (ret) |
|
|
|
|
return ret; |
|
|
|
|
if (retlen != len) |
|
|
|
|
return -EIO; |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* decent defaults in case... shrug */ |
|
|
|
|
static struct mtd_partition adm8668_parts[] = { |
|
|
|
|
{ name: "linux", offset: 0x40000, size: WINDOW_SIZE-0x40000, }, |
|
|
|
|
{ name: "rootfs", offset: 0xe0000, size: 0x140000, }, |
|
|
|
|
{ name: "uboot_env", offset: 0x20000, size: 0x20000, }, |
|
|
|
|
{ name: NULL, }, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/* in case i wanna change stuff later, and to clarify the math section... */ |
|
|
|
|
#define PART_LINUX 0 |
|
|
|
|
#define PART_ROOTFS 1 |
|
|
|
|
#define PART_UBOOT_ENV 2 |
|
|
|
|
#define NR_PARTS 3 |
|
|
|
|
|
|
|
|
|
static int __init |
|
|
|
|
init_mtd_partitions(struct mtd_info *mtd, size_t size) |
|
|
|
|
static int adm8668_parse_partitions(struct mtd_info *master, |
|
|
|
|
struct mtd_partition **pparts, |
|
|
|
|
struct mtd_part_parser_data *data) |
|
|
|
|
{ |
|
|
|
|
int ret; |
|
|
|
|
struct uboot_header uhdr; |
|
|
|
|
int off, blocksize; |
|
|
|
|
size_t len, linux_len; |
|
|
|
|
struct squashfs_super_block shdr; |
|
|
|
|
struct erase_info erase_info; |
|
|
|
|
struct mtd_partition *adm8668_parts; |
|
|
|
|
|
|
|
|
|
memset(&erase_info, 0, sizeof(erase_info)); |
|
|
|
|
|
|
|
|
|
blocksize = master->erasesize; |
|
|
|
|
|
|
|
|
|
blocksize = mtd->erasesize; |
|
|
|
|
if (blocksize < 0x10000) |
|
|
|
|
blocksize = 0x10000; |
|
|
|
|
|
|
|
|
|
adm8668_parts = kzalloc(sizeof(*adm8668_parts) * NR_PARTS, GFP_KERNEL); |
|
|
|
|
if (!adm8668_parts) |
|
|
|
|
return -ENOMEM; |
|
|
|
|
|
|
|
|
|
adm8668_parts[PART_LINUX].name = kstrdup("linux", GFP_KERNEL); |
|
|
|
|
adm8668_parts[PART_LINUX].offset = 0x40000; |
|
|
|
|
adm8668_parts[PART_LINUX].size = master->size - 0x40000; |
|
|
|
|
adm8668_parts[PART_ROOTFS].name = kstrdup("rootfs", GFP_KERNEL); |
|
|
|
|
adm8668_parts[PART_ROOTFS].offset = 0xe0000; |
|
|
|
|
adm8668_parts[PART_ROOTFS].size = 0x140000; |
|
|
|
|
adm8668_parts[PART_UBOOT_ENV].name = kstrdup("uboot_env", GFP_KERNEL); |
|
|
|
|
adm8668_parts[PART_UBOOT_ENV].offset = 0x20000; |
|
|
|
|
adm8668_parts[PART_UBOOT_ENV].size = 0x20000; |
|
|
|
|
|
|
|
|
|
/* now find squashfs */ |
|
|
|
|
memset(&shdr, 0xe5, sizeof(shdr)); |
|
|
|
|
for (off = adm8668_parts[PART_LINUX].offset; off < size; off += blocksize) { |
|
|
|
|
|
|
|
|
|
for (off = 0x40000; off < master->size; off += blocksize) { |
|
|
|
|
/*
|
|
|
|
|
* Read into buffer |
|
|
|
|
*/ |
|
|
|
|
if (mtd->read(mtd, off, sizeof(shdr), &len, (char *)&shdr) || |
|
|
|
|
if (mtd_read(master, off, sizeof(shdr), &len, (char *)&shdr) || |
|
|
|
|
len != sizeof(shdr)) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
if (shdr.s_magic == SQUASHFS_MAGIC) { |
|
|
|
|
uint32_t fs_size = (uint32_t)shdr.bytes_used; |
|
|
|
|
|
|
|
|
|
printk(KERN_INFO "%s: Filesystem type: squashfs, size=%dkB\n", |
|
|
|
|
mtd->name, fs_size>>10); |
|
|
|
|
pr_info(PFX "filesystem type: squashfs, size=%dkB\n", |
|
|
|
|
fs_size >> 10); |
|
|
|
|
|
|
|
|
|
/* Update rootfs based on the superblock info, and
|
|
|
|
|
* stretch to end of MTD. rootfs_split will split it */ |
|
|
|
|
/*
|
|
|
|
|
* Update rootfs based on the superblock info, and |
|
|
|
|
* stretch to end of MTD. rootfs_split will split it |
|
|
|
|
*/ |
|
|
|
|
adm8668_parts[PART_ROOTFS].offset = off; |
|
|
|
|
adm8668_parts[PART_ROOTFS].size = mtd->size - |
|
|
|
|
adm8668_parts[PART_ROOTFS].size = master->size - |
|
|
|
|
adm8668_parts[PART_ROOTFS].offset; |
|
|
|
|
|
|
|
|
|
/* kernel ends where rootfs starts
|
|
|
|
|
* but we'll keep it full-length for upgrades */ |
|
|
|
|
linux_len = adm8668_parts[PART_LINUX+1].offset - |
|
|
|
|
/*
|
|
|
|
|
* kernel ends where rootfs starts |
|
|
|
|
* but we'll keep it full-length for upgrades |
|
|
|
|
*/ |
|
|
|
|
linux_len = adm8668_parts[PART_LINUX + 1].offset - |
|
|
|
|
adm8668_parts[PART_LINUX].offset; |
|
|
|
|
#if 1 |
|
|
|
|
adm8668_parts[PART_LINUX].size = mtd->size - |
|
|
|
|
|
|
|
|
|
adm8668_parts[PART_LINUX].size = master->size - |
|
|
|
|
adm8668_parts[PART_LINUX].offset; |
|
|
|
|
#else |
|
|
|
|
adm8668_parts[PART_LINUX].size = linux_len; |
|
|
|
|
#endif |
|
|
|
|
goto found; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
printk(KERN_NOTICE |
|
|
|
|
"%s: Couldn't find root filesystem\n", |
|
|
|
|
mtd->name); |
|
|
|
|
pr_err(PFX "could't find root filesystem\n"); |
|
|
|
|
return NR_PARTS; |
|
|
|
|
|
|
|
|
|
found: |
|
|
|
|
if (mtd->read(mtd, adm8668_parts[PART_LINUX].offset, sizeof(uhdr), &len, (char *)&uhdr) || |
|
|
|
|
len != sizeof(uhdr)) |
|
|
|
|
found: |
|
|
|
|
if (mtd_read(master, adm8668_parts[PART_LINUX].offset, sizeof(uhdr), &len, (char *)&uhdr) || |
|
|
|
|
len != sizeof(uhdr)) { |
|
|
|
|
pr_err(PFX "failed to read u-boot header\n"); |
|
|
|
|
return NR_PARTS; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* that's odd. how'd ya boot it then */ |
|
|
|
|
if (uhdr.ih_magic != IH_MAGIC) |
|
|
|
|
if (uhdr.ih_magic != IH_MAGIC) { |
|
|
|
|
pr_info(PFX "invalid u-boot magic detected?!?!\n"); |
|
|
|
|
return NR_PARTS; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (be32_to_cpu(uhdr.ih_size) != (linux_len - sizeof(uhdr))) { |
|
|
|
|
unsigned char *block, *data; |
|
|
|
|
u32 data; |
|
|
|
|
size_t data_len = 0; |
|
|
|
|
unsigned char *block; |
|
|
|
|
unsigned int offset; |
|
|
|
|
|
|
|
|
|
offset = adm8668_parts[PART_LINUX].offset + |
|
|
|
|
sizeof(struct uboot_header); |
|
|
|
|
data = (unsigned char *)(WINDOW_ADDR | 0xA0000000 | offset); |
|
|
|
|
|
|
|
|
|
printk(KERN_NOTICE "Updating U-boot image:\n"); |
|
|
|
|
printk(KERN_NOTICE " old: [size: %8d crc32: 0x%08x]\n", |
|
|
|
|
pr_info(PFX "Updating U-boot image:\n"); |
|
|
|
|
pr_info(PFX " old: [size: %8d crc32: 0x%08x]\n", |
|
|
|
|
be32_to_cpu(uhdr.ih_size), be32_to_cpu(uhdr.ih_dcrc)); |
|
|
|
|
|
|
|
|
|
if (mtd_read(master, offset, sizeof(data), &data_len, (char *)&data)) { |
|
|
|
|
pr_err(PFX "failed to read data\n"); |
|
|
|
|
goto out; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Update the data length & crc32 */ |
|
|
|
|
uhdr.ih_size = cpu_to_be32(linux_len - sizeof(uhdr)); |
|
|
|
|
uhdr.ih_dcrc = crc32_le(~0, data, linux_len - sizeof(uhdr)) ^ (~0); |
|
|
|
|
uhdr.ih_dcrc = crc32_le(~0, (char *)&data, linux_len - sizeof(uhdr)) ^ (~0); |
|
|
|
|
uhdr.ih_dcrc = cpu_to_be32(uhdr.ih_dcrc); |
|
|
|
|
|
|
|
|
|
printk(KERN_NOTICE " new: [size: %8d crc32: 0x%08x]\n", |
|
|
|
|
pr_info(PFX " new: [size: %8d crc32: 0x%08x]\n", |
|
|
|
|
be32_to_cpu(uhdr.ih_size), be32_to_cpu(uhdr.ih_dcrc)); |
|
|
|
|
|
|
|
|
|
/* update header's crc... */ |
|
|
|
@ -260,75 +200,64 @@ init_mtd_partitions(struct mtd_info *mtd, size_t size) |
|
|
|
|
uhdr.ih_hcrc = cpu_to_be32(uhdr.ih_hcrc); |
|
|
|
|
|
|
|
|
|
/* read first eraseblock from the image */ |
|
|
|
|
block = kmalloc(mtd->erasesize, GFP_KERNEL); |
|
|
|
|
if (mtd->read(mtd, adm8668_parts[PART_LINUX].offset, mtd->erasesize, &len, block) || len != mtd->erasesize) { |
|
|
|
|
printk("Error copying first eraseblock\n"); |
|
|
|
|
block = vmalloc(master->erasesize); |
|
|
|
|
if (!block) |
|
|
|
|
return -ENOMEM; |
|
|
|
|
|
|
|
|
|
if (mtd_read(master, adm8668_parts[PART_LINUX].offset, master->erasesize, &len, block) |
|
|
|
|
|| len != master->erasesize) { |
|
|
|
|
pr_err(PFX "error copying first eraseblock\n"); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Write updated header to the flash */ |
|
|
|
|
memcpy(block, &uhdr, sizeof(uhdr)); |
|
|
|
|
if (mtd->unlock) |
|
|
|
|
mtd->unlock(mtd, off, mtd->erasesize); |
|
|
|
|
erase_write(mtd, adm8668_parts[PART_LINUX].offset, mtd->erasesize, block); |
|
|
|
|
if (mtd->sync) |
|
|
|
|
mtd->sync(mtd); |
|
|
|
|
kfree(block); |
|
|
|
|
printk(KERN_NOTICE "Done\n"); |
|
|
|
|
if (master->unlock) |
|
|
|
|
master->unlock(master, off, master->erasesize); |
|
|
|
|
|
|
|
|
|
erase_info.mtd = master; |
|
|
|
|
erase_info.addr = (uint64_t)adm8668_parts[PART_LINUX].offset; |
|
|
|
|
erase_info.len = master->erasesize; |
|
|
|
|
ret = mtd_erase(master, &erase_info); |
|
|
|
|
if (!ret) { |
|
|
|
|
if (mtd_write(master, adm8668_parts[PART_LINUX].offset, master->erasesize, |
|
|
|
|
&len, block)) |
|
|
|
|
pr_err(PFX "write failed"); |
|
|
|
|
} else |
|
|
|
|
pr_err(PFX "erase failed"); |
|
|
|
|
|
|
|
|
|
mtd_sync(master); |
|
|
|
|
out: |
|
|
|
|
if (block) |
|
|
|
|
vfree(block); |
|
|
|
|
pr_info(PFX "done\n"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
*pparts = adm8668_parts; |
|
|
|
|
|
|
|
|
|
return NR_PARTS; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int __init init_adm8668_map(void) |
|
|
|
|
{ |
|
|
|
|
int nr_parts, ret; |
|
|
|
|
|
|
|
|
|
adm8668_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); |
|
|
|
|
|
|
|
|
|
if (!adm8668_map.virt) { |
|
|
|
|
printk(KERN_ERR "Failed to ioremap\n"); |
|
|
|
|
return -EIO; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
simple_map_init(&adm8668_map); |
|
|
|
|
if (!(adm8668_mtd = do_map_probe("cfi_probe", &adm8668_map))) { |
|
|
|
|
printk(KERN_ERR "cfi_probe failed\n"); |
|
|
|
|
iounmap((void *)adm8668_map.virt); |
|
|
|
|
return -ENXIO; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
adm8668_mtd->owner = THIS_MODULE; |
|
|
|
|
|
|
|
|
|
nr_parts = init_mtd_partitions(adm8668_mtd, adm8668_mtd->size); |
|
|
|
|
ret = mtd_device_register(adm8668_mtd, adm8668_parts, nr_parts); |
|
|
|
|
if (ret) { |
|
|
|
|
printk(KERN_ERR "Flash: mtd_device_register failed\n"); |
|
|
|
|
goto fail; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
static struct mtd_part_parser adm8668_parser = { |
|
|
|
|
.owner = THIS_MODULE, |
|
|
|
|
.parse_fn = adm8668_parse_partitions, |
|
|
|
|
.name = "adm8668part", |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
fail: |
|
|
|
|
if (adm8668_mtd) |
|
|
|
|
map_destroy(adm8668_mtd); |
|
|
|
|
if (adm8668_map.virt) |
|
|
|
|
iounmap((void *) adm8668_map.virt); |
|
|
|
|
adm8668_map.virt = 0; |
|
|
|
|
return ret; |
|
|
|
|
static int __init adm8668_parser_init(void) |
|
|
|
|
{ |
|
|
|
|
return register_mtd_parser(&adm8668_parser); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void __exit cleanup_adm8668_map(void) |
|
|
|
|
static void __exit adm8668_parser_exit(void) |
|
|
|
|
{ |
|
|
|
|
mtd_device_unregister(adm8668_mtd); |
|
|
|
|
map_destroy(adm8668_mtd); |
|
|
|
|
iounmap((void *) adm8668_map.virt); |
|
|
|
|
adm8668_map.virt = 0; |
|
|
|
|
deregister_mtd_parser(&adm8668_parser); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
module_init(init_adm8668_map); |
|
|
|
|
module_exit(cleanup_adm8668_map); |
|
|
|
|
module_init(adm8668_parser_init); |
|
|
|
|
module_exit(adm8668_parser_exit); |
|
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL"); |
|
|
|
|
MODULE_AUTHOR("Scott Nicholas <neutronscott@scottn.us>"); |
|
|
|
|
MODULE_DESCRIPTION("MTD map driver for ADM8668 NOR Flash"); |
|
|
|
|
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); |
|
|
|
|
MODULE_DESCRIPTION("MTD partition parser for ADM8668"); |
|
|
|
|