2007-10-17 03:10:40 +04:00
/*
* drivers / mtd / nand / orion_nand . c
*
* NAND support for Marvell Orion SoC platforms
*
* Tzachi Perelstein < tzachi @ marvell . com >
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/nand.h>
# include <linux/mtd/partitions.h>
# include <asm/io.h>
# include <asm/sizes.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2008-08-09 15:44:58 +04:00
# include <plat/orion_nand.h>
2007-10-17 03:10:40 +04:00
static void orion_nand_cmd_ctrl ( struct mtd_info * mtd , int cmd , unsigned int ctrl )
{
struct nand_chip * nc = mtd - > priv ;
struct orion_nand_data * board = nc - > priv ;
u32 offs ;
if ( cmd = = NAND_CMD_NONE )
return ;
if ( ctrl & NAND_CLE )
offs = ( 1 < < board - > cle ) ;
else if ( ctrl & NAND_ALE )
offs = ( 1 < < board - > ale ) ;
else
return ;
if ( nc - > options & NAND_BUSWIDTH_16 )
offs < < = 1 ;
writeb ( cmd , nc - > IO_ADDR_W + offs ) ;
}
2009-06-01 06:25:40 +04:00
static void orion_nand_read_buf ( struct mtd_info * mtd , uint8_t * buf , int len )
{
struct nand_chip * chip = mtd - > priv ;
void __iomem * io_base = chip - > IO_ADDR_R ;
uint64_t * buf64 ;
int i = 0 ;
while ( len & & ( unsigned long ) buf & 7 ) {
* buf + + = readb ( io_base ) ;
len - - ;
}
buf64 = ( uint64_t * ) buf ;
while ( i < len / 8 ) {
2010-04-23 21:17:47 +04:00
/*
* Since GCC has no proper constraint ( PR 43518 )
* force x variable to r2 / r3 registers as ldrd instruction
* requires first register to be even .
*/
register uint64_t x asm ( " r2 " ) ;
2009-08-20 11:19:53 +04:00
asm volatile ( " ldrd \t %0, [%1] " : " =&r " ( x ) : " r " ( io_base ) ) ;
2009-06-01 06:25:40 +04:00
buf64 [ i + + ] = x ;
}
i * = 8 ;
while ( i < len )
buf [ i + + ] = readb ( io_base ) ;
}
2007-10-17 03:10:40 +04:00
static int __init orion_nand_probe ( struct platform_device * pdev )
{
struct mtd_info * mtd ;
struct nand_chip * nc ;
struct orion_nand_data * board ;
2009-12-15 00:48:34 +03:00
struct resource * res ;
2007-10-17 03:10:40 +04:00
void __iomem * io_base ;
int ret = 0 ;
nc = kzalloc ( sizeof ( struct nand_chip ) + sizeof ( struct mtd_info ) , GFP_KERNEL ) ;
if ( ! nc ) {
printk ( KERN_ERR " orion_nand: failed to allocate device structure. \n " ) ;
ret = - ENOMEM ;
goto no_res ;
}
mtd = ( struct mtd_info * ) ( nc + 1 ) ;
2009-12-15 00:48:34 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
2010-01-07 04:51:13 +03:00
ret = - ENODEV ;
2009-12-15 00:48:34 +03:00
goto no_res ;
}
io_base = ioremap ( res - > start , resource_size ( res ) ) ;
2007-10-17 03:10:40 +04:00
if ( ! io_base ) {
printk ( KERN_ERR " orion_nand: ioremap failed \n " ) ;
ret = - EIO ;
goto no_res ;
}
board = pdev - > dev . platform_data ;
mtd - > priv = nc ;
mtd - > owner = THIS_MODULE ;
nc - > priv = board ;
nc - > IO_ADDR_R = nc - > IO_ADDR_W = io_base ;
nc - > cmd_ctrl = orion_nand_cmd_ctrl ;
2009-06-01 06:25:40 +04:00
nc - > read_buf = orion_nand_read_buf ;
2007-10-17 03:10:40 +04:00
nc - > ecc . mode = NAND_ECC_SOFT ;
2008-05-05 10:25:52 +04:00
if ( board - > chip_delay )
nc - > chip_delay = board - > chip_delay ;
2007-10-17 03:10:40 +04:00
if ( board - > width = = 16 )
nc - > options | = NAND_BUSWIDTH_16 ;
2010-04-20 13:26:18 +04:00
if ( board - > dev_ready )
nc - > dev_ready = board - > dev_ready ;
2007-10-17 03:10:40 +04:00
platform_set_drvdata ( pdev , mtd ) ;
if ( nand_scan ( mtd , 1 ) ) {
ret = - ENXIO ;
goto no_dev ;
}
mtd - > name = " orion_nand " ;
2011-06-02 18:00:55 +04:00
ret = mtd_device_parse_register ( mtd , NULL , 0 ,
board - > parts , board - > nr_parts ) ;
2007-10-17 03:10:40 +04:00
if ( ret ) {
nand_release ( mtd ) ;
goto no_dev ;
}
return 0 ;
no_dev :
platform_set_drvdata ( pdev , NULL ) ;
iounmap ( io_base ) ;
no_res :
kfree ( nc ) ;
return ret ;
}
static int __devexit orion_nand_remove ( struct platform_device * pdev )
{
struct mtd_info * mtd = platform_get_drvdata ( pdev ) ;
struct nand_chip * nc = mtd - > priv ;
nand_release ( mtd ) ;
iounmap ( nc - > IO_ADDR_W ) ;
kfree ( nc ) ;
return 0 ;
}
static struct platform_driver orion_nand_driver = {
2009-03-03 16:43:47 +03:00
. remove = __devexit_p ( orion_nand_remove ) ,
2007-10-17 03:10:40 +04:00
. driver = {
. name = " orion_nand " ,
. owner = THIS_MODULE ,
} ,
} ;
static int __init orion_nand_init ( void )
{
2009-09-18 23:51:43 +04:00
return platform_driver_probe ( & orion_nand_driver , orion_nand_probe ) ;
2007-10-17 03:10:40 +04:00
}
static void __exit orion_nand_exit ( void )
{
platform_driver_unregister ( & orion_nand_driver ) ;
}
module_init ( orion_nand_init ) ;
module_exit ( orion_nand_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Tzachi Perelstein " ) ;
MODULE_DESCRIPTION ( " NAND glue for Orion platforms " ) ;
2008-04-19 00:44:27 +04:00
MODULE_ALIAS ( " platform:orion_nand " ) ;