2007-05-06 19:31:18 +04:00
/*
* Generic NAND driver
*
* Author : Vitaly Wool < vitalywool @ gmail . com >
*
* 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 .
*
*/
# include <linux/io.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/nand.h>
# include <linux/mtd/partitions.h>
struct plat_nand_data {
struct nand_chip chip ;
struct mtd_info mtd ;
void __iomem * io_base ;
# ifdef CONFIG_MTD_PARTITIONS
int nr_parts ;
struct mtd_partition * parts ;
# endif
} ;
/*
* Probe for the NAND device .
*/
2009-03-28 02:26:58 +03:00
static int __devinit plat_nand_probe ( struct platform_device * pdev )
2007-05-06 19:31:18 +04:00
{
struct platform_nand_data * pdata = pdev - > dev . platform_data ;
struct plat_nand_data * data ;
int res = 0 ;
/* Allocate memory for the device structure (and zero it) */
data = kzalloc ( sizeof ( struct plat_nand_data ) , GFP_KERNEL ) ;
if ( ! data ) {
dev_err ( & pdev - > dev , " failed to allocate device structure. \n " ) ;
return - ENOMEM ;
}
data - > io_base = ioremap ( pdev - > resource [ 0 ] . start ,
pdev - > resource [ 0 ] . end - pdev - > resource [ 0 ] . start + 1 ) ;
if ( data - > io_base = = NULL ) {
dev_err ( & pdev - > dev , " ioremap failed \n " ) ;
kfree ( data ) ;
return - EIO ;
}
data - > chip . priv = & data ;
data - > mtd . priv = & data - > chip ;
data - > mtd . owner = THIS_MODULE ;
2009-01-06 21:44:38 +03:00
data - > mtd . name = dev_name ( & pdev - > dev ) ;
2007-05-06 19:31:18 +04:00
data - > chip . IO_ADDR_R = data - > io_base ;
data - > chip . IO_ADDR_W = data - > io_base ;
data - > chip . cmd_ctrl = pdata - > ctrl . cmd_ctrl ;
data - > chip . dev_ready = pdata - > ctrl . dev_ready ;
data - > chip . select_chip = pdata - > ctrl . select_chip ;
2009-05-11 22:28:01 +04:00
data - > chip . write_buf = pdata - > ctrl . write_buf ;
data - > chip . read_buf = pdata - > ctrl . read_buf ;
2007-05-06 19:31:18 +04:00
data - > chip . chip_delay = pdata - > chip . chip_delay ;
data - > chip . options | = pdata - > chip . options ;
data - > chip . ecc . hwctl = pdata - > ctrl . hwcontrol ;
data - > chip . ecc . layout = pdata - > chip . ecclayout ;
data - > chip . ecc . mode = NAND_ECC_SOFT ;
platform_set_drvdata ( pdev , data ) ;
2009-05-13 00:46:58 +04:00
/* Handle any platform specific setup */
if ( pdata - > ctrl . probe ) {
res = pdata - > ctrl . probe ( pdev ) ;
if ( res )
goto out ;
}
2007-05-06 19:31:18 +04:00
/* Scan to find existance of the device */
if ( nand_scan ( & data - > mtd , 1 ) ) {
res = - ENXIO ;
goto out ;
}
# ifdef CONFIG_MTD_PARTITIONS
if ( pdata - > chip . part_probe_types ) {
res = parse_mtd_partitions ( & data - > mtd ,
pdata - > chip . part_probe_types ,
& data - > parts , 0 ) ;
if ( res > 0 ) {
add_mtd_partitions ( & data - > mtd , data - > parts , res ) ;
return 0 ;
}
}
2009-05-13 00:46:59 +04:00
if ( pdata - > chip . set_parts )
pdata - > chip . set_parts ( data - > mtd . size , & pdata - > chip ) ;
2007-05-06 19:31:18 +04:00
if ( pdata - > chip . partitions ) {
data - > parts = pdata - > chip . partitions ;
res = add_mtd_partitions ( & data - > mtd , data - > parts ,
pdata - > chip . nr_partitions ) ;
} else
# endif
res = add_mtd_device ( & data - > mtd ) ;
if ( ! res )
return res ;
nand_release ( & data - > mtd ) ;
out :
2009-05-13 00:46:58 +04:00
if ( pdata - > ctrl . remove )
pdata - > ctrl . remove ( pdev ) ;
2007-05-06 19:31:18 +04:00
platform_set_drvdata ( pdev , NULL ) ;
iounmap ( data - > io_base ) ;
kfree ( data ) ;
return res ;
}
/*
* Remove a NAND device .
*/
static int __devexit plat_nand_remove ( struct platform_device * pdev )
{
struct plat_nand_data * data = platform_get_drvdata ( pdev ) ;
struct platform_nand_data * pdata = pdev - > dev . platform_data ;
nand_release ( & data - > mtd ) ;
# ifdef CONFIG_MTD_PARTITIONS
if ( data - > parts & & data - > parts ! = pdata - > chip . partitions )
kfree ( data - > parts ) ;
# endif
2009-05-13 00:46:58 +04:00
if ( pdata - > ctrl . remove )
pdata - > ctrl . remove ( pdev ) ;
2007-05-06 19:31:18 +04:00
iounmap ( data - > io_base ) ;
kfree ( data ) ;
return 0 ;
}
static struct platform_driver plat_nand_driver = {
. probe = plat_nand_probe ,
2009-04-21 08:27:34 +04:00
. remove = __devexit_p ( plat_nand_remove ) ,
2007-05-06 19:31:18 +04:00
. driver = {
. name = " gen_nand " ,
. owner = THIS_MODULE ,
} ,
} ;
static int __init plat_nand_init ( void )
{
return platform_driver_register ( & plat_nand_driver ) ;
}
static void __exit plat_nand_exit ( void )
{
platform_driver_unregister ( & plat_nand_driver ) ;
}
module_init ( plat_nand_init ) ;
module_exit ( plat_nand_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Vitaly Wool " ) ;
MODULE_DESCRIPTION ( " Simple generic NAND driver " ) ;
2008-04-19 00:44:27 +04:00
MODULE_ALIAS ( " platform:gen_nand " ) ;