2010-02-15 10:03:33 -08:00
/*
* gpmc - nand . c
*
* Copyright ( C ) 2009 Texas Instruments
* Vimal Singh < vimalsingh @ ti . 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/kernel.h>
# include <linux/platform_device.h>
# include <linux/io.h>
2011-01-28 15:42:03 +05:30
# include <linux/mtd/nand.h>
2012-08-24 15:21:06 +02:00
# include <linux/platform_data/mtd-nand-omap2.h>
2010-02-15 10:03:33 -08:00
# include <asm/mach/flash.h>
2012-10-05 10:37:27 +05:30
# include "gpmc.h"
2012-08-31 10:59:07 -07:00
# include "soc.h"
2012-09-29 12:26:13 +05:30
# include "gpmc-nand.h"
/* minimum size for IO mapping */
# define NAND_IO_SIZE 4
2012-08-31 10:59:07 -07:00
2012-12-14 11:36:42 +01:00
static bool gpmc_hwecc_bch_capable ( enum omap_ecc ecc_opt )
2012-10-01 02:47:28 +05:30
{
2014-02-05 18:58:30 +05:30
/* platforms which support all ECC schemes */
2014-05-19 16:52:36 +05:30
if ( soc_is_am33xx ( ) | | soc_is_am43xx ( ) | | cpu_is_omap44xx ( ) | |
2014-02-05 18:58:30 +05:30
soc_is_omap54xx ( ) | | soc_is_dra7xx ( ) )
return 1 ;
2014-07-14 03:36:18 +02:00
if ( ecc_opt = = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW | |
ecc_opt = = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW ) {
if ( cpu_is_omap24xx ( ) )
return 0 ;
else if ( cpu_is_omap3630 ( ) & & ( GET_OMAP_REVISION ( ) = = 0 ) )
return 0 ;
else
return 1 ;
}
2014-02-05 18:58:30 +05:30
/* OMAP3xxx do not have ELM engine, so cannot support ECC schemes
* which require H / W based ECC error detection */
if ( ( cpu_is_omap34xx ( ) | | cpu_is_omap3630 ( ) ) & &
( ( ecc_opt = = OMAP_ECC_BCH4_CODE_HW ) | |
( ecc_opt = = OMAP_ECC_BCH8_CODE_HW ) ) )
2012-10-01 02:47:28 +05:30
return 0 ;
2014-02-05 18:58:30 +05:30
/* legacy platforms support only HAM1 (1-bit Hamming) ECC scheme */
2014-08-25 16:15:32 -07:00
if ( ecc_opt = = OMAP_ECC_HAM1_CODE_HW | |
ecc_opt = = OMAP_ECC_HAM1_CODE_SW )
2014-02-05 18:58:30 +05:30
return 1 ;
else
return 0 ;
2012-10-01 02:47:28 +05:30
}
2013-10-27 21:51:47 -03:00
/* This function will go away once the device-tree convertion is complete */
static void gpmc_set_legacy ( struct omap_nand_platform_data * gpmc_nand_data ,
struct gpmc_settings * s )
{
/* Enable RD PIN Monitoring Reg */
if ( gpmc_nand_data - > dev_ready ) {
s - > wait_on_read = true ;
s - > wait_on_write = true ;
}
2013-10-27 21:51:48 -03:00
if ( gpmc_nand_data - > devsize = = NAND_BUSWIDTH_16 )
s - > device_width = GPMC_DEVWIDTH_16BIT ;
else
s - > device_width = GPMC_DEVWIDTH_8BIT ;
2013-10-27 21:51:47 -03:00
}
2012-12-14 11:36:42 +01:00
int gpmc_nand_init ( struct omap_nand_platform_data * gpmc_nand_data ,
struct gpmc_timings * gpmc_t )
2010-02-15 10:03:33 -08:00
{
int err = 0 ;
2013-02-21 15:43:08 -06:00
struct gpmc_settings s ;
2014-06-04 16:24:35 +02:00
struct platform_device * pdev ;
struct resource gpmc_nand_res [ ] = {
{ . flags = IORESOURCE_MEM , } ,
{ . flags = IORESOURCE_IRQ , } ,
{ . flags = IORESOURCE_IRQ , } ,
} ;
2013-02-21 15:43:08 -06:00
2014-06-04 16:24:35 +02:00
BUG_ON ( gpmc_nand_data - > cs > = GPMC_CS_NUM ) ;
2010-02-15 10:03:33 -08:00
err = gpmc_cs_request ( gpmc_nand_data - > cs , NAND_IO_SIZE ,
2014-06-04 16:24:35 +02:00
( unsigned long * ) & gpmc_nand_res [ 0 ] . start ) ;
2010-02-15 10:03:33 -08:00
if ( err < 0 ) {
2014-06-04 16:24:35 +02:00
pr_err ( " omap2-gpmc: Cannot request GPMC CS %d, error %d \n " ,
gpmc_nand_data - > cs , err ) ;
2010-02-15 10:03:33 -08:00
return err ;
}
2014-06-04 16:24:35 +02:00
gpmc_nand_res [ 0 ] . end = gpmc_nand_res [ 0 ] . start + NAND_IO_SIZE - 1 ;
gpmc_nand_res [ 1 ] . start = gpmc_get_client_irq ( GPMC_IRQ_FIFOEVENTENABLE ) ;
gpmc_nand_res [ 2 ] . start = gpmc_get_client_irq ( GPMC_IRQ_COUNT_EVENT ) ;
2012-09-29 12:26:13 +05:30
if ( gpmc_t ) {
2013-04-19 18:29:41 +02:00
err = gpmc_cs_set_timings ( gpmc_nand_data - > cs , gpmc_t ) ;
2012-09-29 12:26:13 +05:30
if ( err < 0 ) {
2014-06-04 16:24:35 +02:00
pr_err ( " omap2-gpmc: Unable to set gpmc timings: %d \n " , err ) ;
2012-09-29 12:26:13 +05:30
return err ;
}
2013-10-27 21:51:46 -03:00
}
2010-02-15 10:03:33 -08:00
2014-06-04 16:24:35 +02:00
memset ( & s , 0 , sizeof ( struct gpmc_settings ) ) ;
2013-10-27 21:51:47 -03:00
if ( gpmc_nand_data - > of_node )
2013-10-27 21:51:46 -03:00
gpmc_read_settings_dt ( gpmc_nand_data - > of_node , & s ) ;
2013-10-27 21:51:47 -03:00
else
gpmc_set_legacy ( gpmc_nand_data , & s ) ;
2013-02-21 15:43:08 -06:00
2013-10-27 21:51:46 -03:00
s . device_nand = true ;
err = gpmc_cs_program_settings ( gpmc_nand_data - > cs , & s ) ;
if ( err < 0 )
goto out_free_cs ;
2013-02-21 15:43:08 -06:00
2013-10-27 21:51:46 -03:00
err = gpmc_configure ( GPMC_CONFIG_WP , 0 ) ;
if ( err < 0 )
goto out_free_cs ;
2010-02-15 10:03:33 -08:00
2012-08-30 12:53:22 -07:00
gpmc_update_nand_reg ( & gpmc_nand_data - > reg , gpmc_nand_data - > cs ) ;
2014-02-05 18:58:30 +05:30
if ( ! gpmc_hwecc_bch_capable ( gpmc_nand_data - > ecc_opt ) ) {
2014-06-04 16:24:35 +02:00
pr_err ( " omap2-nand: Unsupported NAND ECC scheme selected \n " ) ;
err = - EINVAL ;
goto out_free_cs ;
2014-02-05 18:58:30 +05:30
}
2012-10-01 02:47:28 +05:30
2014-06-04 16:24:35 +02:00
pdev = platform_device_alloc ( " omap2-nand " , gpmc_nand_data - > cs ) ;
if ( pdev ) {
err = platform_device_add_resources ( pdev , gpmc_nand_res ,
ARRAY_SIZE ( gpmc_nand_res ) ) ;
if ( ! err )
pdev - > dev . platform_data = gpmc_nand_data ;
} else {
err = - ENOMEM ;
}
if ( err )
goto out_free_pdev ;
err = platform_device_add ( pdev ) ;
if ( err ) {
dev_err ( & pdev - > dev , " Unable to register NAND device \n " ) ;
goto out_free_pdev ;
2010-02-15 10:03:33 -08:00
}
return 0 ;
2014-06-04 16:24:35 +02:00
out_free_pdev :
platform_device_put ( pdev ) ;
2010-02-15 10:03:33 -08:00
out_free_cs :
gpmc_cs_free ( gpmc_nand_data - > cs ) ;
return err ;
}