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 00:26:58 +01: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 ;
2009-10-19 19:45:29 -04:00
struct resource * res ;
int err = 0 ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res )
return - ENXIO ;
2007-05-06 19:31:18 +04:00
/* 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 ;
}
2009-10-19 19:45:29 -04:00
if ( ! request_mem_region ( res - > start , resource_size ( res ) ,
dev_name ( & pdev - > dev ) ) ) {
dev_err ( & pdev - > dev , " request_mem_region failed \n " ) ;
err = - EBUSY ;
goto out_free ;
}
data - > io_base = ioremap ( res - > start , resource_size ( res ) ) ;
2007-05-06 19:31:18 +04:00
if ( data - > io_base = = NULL ) {
dev_err ( & pdev - > dev , " ioremap failed \n " ) ;
2009-10-19 19:45:29 -04:00
err = - EIO ;
goto out_release_io ;
2007-05-06 19:31:18 +04:00
}
data - > chip . priv = & data ;
data - > mtd . priv = & data - > chip ;
data - > mtd . owner = THIS_MODULE ;
2009-01-06 10:44:38 -08: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 19:28:01 +01: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-12 13:46:58 -07:00
/* Handle any platform specific setup */
if ( pdata - > ctrl . probe ) {
2009-10-19 19:45:29 -04:00
err = pdata - > ctrl . probe ( pdev ) ;
if ( err )
2009-05-12 13:46:58 -07:00
goto out ;
}
2007-05-06 19:31:18 +04:00
/* Scan to find existance of the device */
2010-07-28 07:36:54 +02:00
if ( nand_scan ( & data - > mtd , pdata - > chip . nr_chips ) ) {
2009-10-19 19:45:29 -04:00
err = - ENXIO ;
2007-05-06 19:31:18 +04:00
goto out ;
}
# ifdef CONFIG_MTD_PARTITIONS
if ( pdata - > chip . part_probe_types ) {
2009-10-19 19:45:29 -04:00
err = parse_mtd_partitions ( & data - > mtd ,
2007-05-06 19:31:18 +04:00
pdata - > chip . part_probe_types ,
& data - > parts , 0 ) ;
2009-10-19 19:45:29 -04:00
if ( err > 0 ) {
add_mtd_partitions ( & data - > mtd , data - > parts , err ) ;
2007-05-06 19:31:18 +04:00
return 0 ;
}
}
2009-05-12 13:46:59 -07: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 ;
2009-10-19 19:45:29 -04:00
err = add_mtd_partitions ( & data - > mtd , data - > parts ,
2007-05-06 19:31:18 +04:00
pdata - > chip . nr_partitions ) ;
} else
# endif
2009-10-19 19:45:29 -04:00
err = add_mtd_device ( & data - > mtd ) ;
2007-05-06 19:31:18 +04:00
2009-10-19 19:45:29 -04:00
if ( ! err )
return err ;
2007-05-06 19:31:18 +04:00
nand_release ( & data - > mtd ) ;
out :
2009-05-12 13:46:58 -07: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 ) ;
2009-10-19 19:45:29 -04:00
out_release_io :
release_mem_region ( res - > start , resource_size ( res ) ) ;
out_free :
2007-05-06 19:31:18 +04:00
kfree ( data ) ;
2009-10-19 19:45:29 -04:00
return err ;
2007-05-06 19:31:18 +04:00
}
/*
* 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 ;
2009-10-19 19:45:29 -04:00
struct resource * res ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2007-05-06 19:31:18 +04:00
nand_release ( & data - > mtd ) ;
# ifdef CONFIG_MTD_PARTITIONS
if ( data - > parts & & data - > parts ! = pdata - > chip . partitions )
kfree ( data - > parts ) ;
# endif
2009-05-12 13:46:58 -07:00
if ( pdata - > ctrl . remove )
pdata - > ctrl . remove ( pdev ) ;
2007-05-06 19:31:18 +04:00
iounmap ( data - > io_base ) ;
2009-10-19 19:45:29 -04:00
release_mem_region ( res - > start , resource_size ( res ) ) ;
2007-05-06 19:31:18 +04:00
kfree ( data ) ;
return 0 ;
}
static struct platform_driver plat_nand_driver = {
. probe = plat_nand_probe ,
2009-04-21 12:27:34 +08: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-18 13:44:27 -07:00
MODULE_ALIAS ( " platform:gen_nand " ) ;