2005-04-17 02:20:36 +04:00
/*
2005-11-07 14:15:40 +03:00
* $ Id : ixp2000 . c , v 1.9 2005 / 11 / 07 11 : 14 : 27 gleixner Exp $
2005-04-17 02:20:36 +04:00
*
* drivers / mtd / maps / ixp2000 . c
*
* Mapping for the Intel XScale IXP2000 based systems
*
* Copyright ( C ) 2002 Intel Corp .
* Copyright ( C ) 2003 - 2004 MontaVista Software , Inc .
*
* Original Author : Naeem M Afzal < naeem . m . afzal @ intel . com >
* Maintainer : Deepak Saxena < dsaxena @ plexity . net >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
2005-11-07 14:15:40 +03:00
*
2005-04-17 02:20:36 +04:00
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/string.h>
2005-10-31 02:03:48 +03:00
# include <linux/slab.h>
# include <linux/ioport.h>
# include <linux/device.h>
2005-10-31 18:32:56 +03:00
# include <linux/platform_device.h>
2005-10-31 02:03:48 +03:00
2005-04-17 02:20:36 +04:00
# include <linux/mtd/mtd.h>
# include <linux/mtd/map.h>
# include <linux/mtd/partitions.h>
# include <asm/io.h>
# include <asm/hardware.h>
# include <asm/mach/flash.h>
# include <linux/reboot.h>
struct ixp2000_flash_info {
struct mtd_info * mtd ;
struct map_info map ;
struct mtd_partition * partitions ;
struct resource * res ;
int nr_banks ;
} ;
static inline unsigned long flash_bank_setup ( struct map_info * map , unsigned long ofs )
2005-11-07 14:15:40 +03:00
{
unsigned long ( * set_bank ) ( unsigned long ) =
2005-04-17 02:20:36 +04:00
( unsigned long ( * ) ( unsigned long ) ) map - > map_priv_2 ;
return ( set_bank ? set_bank ( ofs ) : ofs ) ;
}
# ifdef __ARMEB__
/*
2005-11-07 14:15:40 +03:00
* Rev A0 and A1 of IXP2400 silicon have a broken addressing unit which
* causes the lower address bits to be XORed with 0x11 on 8 bit accesses
2005-04-17 02:20:36 +04:00
* and XORed with 0x10 on 16 bit accesses . See the spec update , erratum 44.
*/
static int erratum44_workaround = 0 ;
static inline unsigned long address_fix8_write ( unsigned long addr )
{
if ( erratum44_workaround ) {
return ( addr ^ 3 ) ;
}
return addr ;
}
# else
# define address_fix8_write(x) (x)
# endif
static map_word ixp2000_flash_read8 ( struct map_info * map , unsigned long ofs )
{
map_word val ;
val . x [ 0 ] = * ( ( u8 * ) ( map - > map_priv_1 + flash_bank_setup ( map , ofs ) ) ) ;
return val ;
}
/*
* We can ' t use the standard memcpy due to the broken SlowPort
* address translation on rev A0 and A1 silicon and the fact that
* we have banked flash .
*/
static void ixp2000_flash_copy_from ( struct map_info * map , void * to ,
unsigned long from , ssize_t len )
{
from = flash_bank_setup ( map , from ) ;
2005-11-07 14:15:40 +03:00
while ( len - - )
2005-04-17 02:20:36 +04:00
* ( __u8 * ) to + + = * ( __u8 * ) ( map - > map_priv_1 + from + + ) ;
}
static void ixp2000_flash_write8 ( struct map_info * map , map_word d , unsigned long ofs )
{
* ( __u8 * ) ( address_fix8_write ( map - > map_priv_1 +
flash_bank_setup ( map , ofs ) ) ) = d . x [ 0 ] ;
}
static void ixp2000_flash_copy_to ( struct map_info * map , unsigned long to ,
const void * from , ssize_t len )
{
to = flash_bank_setup ( map , to ) ;
while ( len - - ) {
unsigned long tmp = address_fix8_write ( map - > map_priv_1 + to + + ) ;
* ( __u8 * ) ( tmp ) = * ( __u8 * ) ( from + + ) ;
}
}
2005-11-10 01:32:44 +03:00
static int ixp2000_flash_remove ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
struct flash_platform_data * plat = dev - > dev . platform_data ;
2005-11-10 01:32:44 +03:00
struct ixp2000_flash_info * info = platform_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( dev , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ! info )
return 0 ;
if ( info - > mtd ) {
del_mtd_partitions ( info - > mtd ) ;
map_destroy ( info - > mtd ) ;
}
if ( info - > map . map_priv_1 )
iounmap ( ( void * ) info - > map . map_priv_1 ) ;
2005-11-07 12:01:27 +03:00
kfree ( info - > partitions ) ;
2005-04-17 02:20:36 +04:00
if ( info - > res ) {
release_resource ( info - > res ) ;
kfree ( info - > res ) ;
}
if ( plat - > exit )
plat - > exit ( ) ;
return 0 ;
}
2005-11-10 01:32:44 +03:00
static int ixp2000_flash_probe ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
static const char * probes [ ] = { " RedBoot " , " cmdlinepart " , NULL } ;
struct ixp2000_flash_data * ixp_data = dev - > dev . platform_data ;
2005-11-07 14:15:40 +03:00
struct flash_platform_data * plat ;
2005-04-17 02:20:36 +04:00
struct ixp2000_flash_info * info ;
unsigned long window_size ;
int err = - 1 ;
2005-11-07 14:15:40 +03:00
2005-04-17 02:20:36 +04:00
if ( ! ixp_data )
return - ENODEV ;
plat = ixp_data - > platform_data ;
if ( ! plat )
return - ENODEV ;
window_size = dev - > resource - > end - dev - > resource - > start + 1 ;
2005-11-17 18:47:30 +03:00
dev_info ( & dev - > dev , " Probe of IXP2000 flash(%d banks x %dMiB) \n " ,
ixp_data - > nr_banks , ( ( u32 ) window_size > > 20 ) ) ;
2005-04-17 02:20:36 +04:00
if ( plat - > width ! = 1 ) {
2005-11-17 18:47:30 +03:00
dev_err ( & dev - > dev , " IXP2000 MTD map only supports 8-bit mode, asking for %d \n " ,
plat - > width * 8 ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
info = kmalloc ( sizeof ( struct ixp2000_flash_info ) , GFP_KERNEL ) ;
if ( ! info ) {
err = - ENOMEM ;
goto Error ;
2005-11-07 14:15:40 +03:00
}
2005-04-17 02:20:36 +04:00
memzero ( info , sizeof ( struct ixp2000_flash_info ) ) ;
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( dev , info ) ;
2005-04-17 02:20:36 +04:00
/*
* Tell the MTD layer we ' re not 1 : 1 mapped so that it does
* not attempt to do a direct access on us .
*/
info - > map . phys = NO_XIP ;
2005-11-07 14:15:40 +03:00
2005-04-17 02:20:36 +04:00
info - > nr_banks = ixp_data - > nr_banks ;
info - > map . size = ixp_data - > nr_banks * window_size ;
info - > map . bankwidth = 1 ;
/*
* map_priv_2 is used to store a ptr to to the bank_setup routine
*/
2005-11-07 11:09:05 +03:00
info - > map . map_priv_2 = ( unsigned long ) ixp_data - > bank_setup ;
2005-04-17 02:20:36 +04:00
info - > map . name = dev - > dev . bus_id ;
info - > map . read = ixp2000_flash_read8 ;
info - > map . write = ixp2000_flash_write8 ;
info - > map . copy_from = ixp2000_flash_copy_from ;
info - > map . copy_to = ixp2000_flash_copy_to ;
2005-11-07 14:15:40 +03:00
info - > res = request_mem_region ( dev - > resource - > start ,
dev - > resource - > end - dev - > resource - > start + 1 ,
2005-04-17 02:20:36 +04:00
dev - > dev . bus_id ) ;
if ( ! info - > res ) {
2005-11-17 18:47:30 +03:00
dev_err ( & dev - > dev , " Could not reserve memory region \n " ) ;
2005-04-17 02:20:36 +04:00
err = - ENOMEM ;
goto Error ;
}
2005-11-07 14:15:40 +03:00
info - > map . map_priv_1 = ( unsigned long ) ioremap ( dev - > resource - > start ,
2005-04-17 02:20:36 +04:00
dev - > resource - > end - dev - > resource - > start + 1 ) ;
if ( ! info - > map . map_priv_1 ) {
2005-11-17 18:47:30 +03:00
dev_err ( & dev - > dev , " Failed to ioremap flash region \n " ) ;
2005-04-17 02:20:36 +04:00
err = - EIO ;
goto Error ;
}
# if defined(__ARMEB__)
/*
* Enable erratum 44 workaround for NPUs with broken slowport
*/
erratum44_workaround = ixp2000_has_broken_slowport ( ) ;
2005-11-17 18:47:30 +03:00
dev_info ( & dev - > dev , " Erratum 44 workaround %s \n " ,
2005-04-17 02:20:36 +04:00
erratum44_workaround ? " enabled " : " disabled " ) ;
# endif
info - > mtd = do_map_probe ( plat - > map_name , & info - > map ) ;
if ( ! info - > mtd ) {
2005-11-17 18:47:30 +03:00
dev_err ( & dev - > dev , " map_probe failed \n " ) ;
2005-04-17 02:20:36 +04:00
err = - ENXIO ;
goto Error ;
}
info - > mtd - > owner = THIS_MODULE ;
err = parse_mtd_partitions ( info - > mtd , probes , & info - > partitions , 0 ) ;
if ( err > 0 ) {
err = add_mtd_partitions ( info - > mtd , info - > partitions , err ) ;
if ( err )
2005-11-17 18:47:30 +03:00
dev_err ( & dev - > dev , " Could not parse partitions \n " ) ;
2005-04-17 02:20:36 +04:00
}
if ( err )
goto Error ;
return 0 ;
Error :
2005-11-10 01:32:44 +03:00
ixp2000_flash_remove ( dev ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2005-11-10 01:32:44 +03:00
static struct platform_driver ixp2000_flash_driver = {
2005-11-17 18:47:30 +03:00
. probe = ixp2000_flash_probe ,
. remove = ixp2000_flash_remove ,
2005-11-10 01:32:44 +03:00
. driver = {
. name = " IXP2000-Flash " ,
} ,
2005-04-17 02:20:36 +04:00
} ;
static int __init ixp2000_flash_init ( void )
{
2005-11-10 01:32:44 +03:00
return platform_driver_register ( & ixp2000_flash_driver ) ;
2005-04-17 02:20:36 +04:00
}
static void __exit ixp2000_flash_exit ( void )
{
2005-11-10 01:32:44 +03:00
platform_driver_unregister ( & ixp2000_flash_driver ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( ixp2000_flash_init ) ;
module_exit ( ixp2000_flash_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Deepak Saxena <dsaxena@plexity.net> " ) ;