@ -1,30 +1,30 @@
/*
* Infineon / ADMTek ADM8668 ( WildPass ) partition parser support
* 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 )
*
* Copyright ( C ) 2010 Scott Nicholas < neutronscott @ scottn . us >
* Copyright ( C ) 2012 Florian Fainelli < florian @ openwrt . org >
* original functions for finding root filesystem from Mike Baker
*
* original functions for finding root filesystem from Mike Baker
* 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 .
*
* 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 .
* 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 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 0213 9 , USA .
* 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 0213 9 , USA .
*
*
* Copyright 2004 , Broadcom Corporation
@ -34,6 +34,9 @@
* 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>
@ -42,13 +45,16 @@
# include <linux/sched.h>
# include <linux/wait.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/partitions.h>
# include <linux/vmalloc.h>
# include <linux/mtd/map.h>
# include <linux/slab.h>
# include <linux/mtd/partitions.h>
# include <linux/crc32.h>
# include <linux/magic.h>
# include <asm/io.h>
# define PFX "adm8668-part: "
# define WINDOW_ADDR 0x10000000
# define WINDOW_SIZE 0x800000
# define BANKWIDTH 2
/* first a little bit about the headers i need.. */
@ -75,122 +81,176 @@ 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 adm8668_parse_partitions ( struct mtd_info * master ,
struct mtd_partition * * pparts ,
struct mtd_part_parser_data * data )
static int __init
init_mtd_partitions ( struct mtd_info * mtd , size_t size )
{
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 = 0x40000 ; off < master - > size ; off + = blocksize ) {
for ( off = adm8668_parts [ PART_LINUX ] . offset ; off < size ; off + = blocksize ) {
/*
* Read into buffer
*/
if ( mtd_read ( master , off , sizeof ( shdr ) , & len , ( char * ) & shdr ) | |
if ( mtd - > read ( mtd , 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 ;
pr_info ( PFX " filesystem type: squashfs, size=%dkB \n " ,
fs_size > > 10 ) ;
printk ( KERN_INFO " %s: Filesystem type: squashfs, size=%dkB \n " ,
mtd - > name , 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 = master - > size -
adm8668_parts [ PART_ROOTFS ] . size = mtd - > 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 ;
adm8668_parts [ PART_LINUX ] . size = master - > size -
# if 1
adm8668_parts [ PART_LINUX ] . size = mtd - > size -
adm8668_parts [ PART_LINUX ] . offset ;
# else
adm8668_parts [ PART_LINUX ] . size = linux_len ;
# endif
goto found ;
}
}
pr_err ( PFX " could't find root filesystem \n " ) ;
printk ( KERN_NOTICE
" %s: Couldn't find root filesystem \n " ,
mtd - > name ) ;
return NR_PARTS ;
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 " ) ;
found :
if ( mtd - > read ( mtd , adm8668_parts [ PART_LINUX ] . offset , sizeof ( uhdr ) , & len , ( char * ) & uhdr ) | |
len ! = sizeof ( uhdr ) )
return NR_PARTS ;
}
if ( uhdr . ih_magic ! = IH_MAGIC ) {
pr_info ( PFX " invalid u-boot magic detected?!?! \n " ) ;
/* that's odd. how'd ya boot it then */
if ( uhdr . ih_magic ! = IH_MAGIC )
return NR_PARTS ;
}
if ( be32_to_cpu ( uhdr . ih_size ) ! = ( linux_len - sizeof ( uhdr ) ) ) {
u32 data ;
size_t data_len = 0 ;
unsigned char * block ;
unsigned char * block , * data ;
unsigned int offset ;
offset = adm8668_parts [ PART_LINUX ] . offset +
sizeof ( struct uboot_header ) ;
data = ( unsigned char * ) ( WINDOW_ADDR | 0xA0000000 | offset ) ;
pr_info ( PFX " Updating U-boot image: \n " ) ;
pr_info ( PFX " old: [size: %8d crc32: 0x%08x] \n " ,
printk ( KERN_NOTICE " Updating U-boot image: \n " ) ;
printk ( KERN_NOTICE " 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 , ( char * ) & data , linux_len - sizeof ( uhdr ) ) ^ ( ~ 0 ) ;
uhdr . ih_dcrc = crc32_le ( ~ 0 , data , linux_len - sizeof ( uhdr ) ) ^ ( ~ 0 ) ;
uhdr . ih_dcrc = cpu_to_be32 ( uhdr . ih_dcrc ) ;
pr_info ( PFX " new: [size: %8d crc32: 0x%08x] \n " ,
printk ( KERN_NOTICE " new: [size: %8d crc32: 0x%08x] \n " ,
be32_to_cpu ( uhdr . ih_size ) , be32_to_cpu ( uhdr . ih_dcrc ) ) ;
/* update header's crc... */
@ -200,66 +260,75 @@ found:
uhdr . ih_hcrc = cpu_to_be32 ( uhdr . ih_hcrc ) ;
/* read first eraseblock from the image */
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 " ) ;
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 " ) ;
return 0 ;
}
/* Write updated header to the flash */
memcpy ( block , & uhdr , sizeof ( uhdr ) ) ;
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 " ) ;
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 " ) ;
}
* pparts = adm8668_parts ;
return NR_PARTS ;
}
static struct mtd_part_parser adm8668_parser = {
. owner = THIS_MODULE ,
. parse_fn = adm8668_parse_partitions ,
. name = " adm8668part " ,
} ;
static int __init adm8668_parser_init ( void )
int __init init_adm8668_map ( void )
{
register_mtd_parser ( & adm8668_parser ) ;
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 ;
fail :
if ( adm8668_mtd )
map_destroy ( adm8668_mtd ) ;
if ( adm8668_map . virt )
iounmap ( ( void * ) adm8668_map . virt ) ;
adm8668_map . virt = 0 ;
return ret ;
}
static void __exit adm8668_parser_exit ( void )
void __exit cleanup_adm8668_map ( void )
{
deregister_mtd_parser ( & adm8668_parser ) ;
mtd_device_unregister ( adm8668_mtd ) ;
map_destroy ( adm8668_mtd ) ;
iounmap ( ( void * ) adm8668_map . virt ) ;
adm8668_map . virt = 0 ;
}
module_init ( adm8668_parser_init ) ;
module_exit ( adm8668_parser_exit ) ;
module_init ( init_adm8668_map ) ;
module_exit ( cleanup_adm8668_map ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Scott Nicholas <neutronscott@scottn.us> " ) ;
MODULE_AUTHOR ( " Florian Fainelli <florian@openwrt.org> " ) ;
MODULE_DESCRIPTION ( " MTD partition parser for ADM8668 " ) ;
MODULE_DESCRIPTION ( " MTD map driver for ADM8668 NOR Flash " ) ;