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-08-30 12:53:24 -07:00
static struct resource gpmc_nand_resource [ ] = {
{
. flags = IORESOURCE_MEM ,
} ,
{
. flags = IORESOURCE_IRQ ,
} ,
{
. flags = IORESOURCE_IRQ ,
} ,
2010-02-15 10:03:33 -08:00
} ;
static struct platform_device gpmc_nand_device = {
. name = " omap2-nand " ,
. id = 0 ,
2012-08-30 12:53:24 -07:00
. num_resources = ARRAY_SIZE ( gpmc_nand_resource ) ,
. resource = gpmc_nand_resource ,
2010-02-15 10:03:33 -08:00
} ;
2012-09-29 12:26:13 +05:30
static int omap2_nand_gpmc_retime (
struct omap_nand_platform_data * gpmc_nand_data ,
struct gpmc_timings * gpmc_t )
2010-02-15 10:03:33 -08:00
{
struct gpmc_timings t ;
int err ;
memset ( & t , 0 , sizeof ( t ) ) ;
2012-09-29 12:26:13 +05:30
t . sync_clk = gpmc_t - > sync_clk ;
2012-09-18 14:43:37 +05:30
t . cs_on = gpmc_t - > cs_on ;
t . adv_on = gpmc_t - > adv_on ;
2010-02-15 10:03:33 -08:00
/* Read */
2012-09-18 14:43:37 +05:30
t . adv_rd_off = gpmc_t - > adv_rd_off ;
2010-02-15 10:03:33 -08:00
t . oe_on = t . adv_on ;
2012-09-18 14:43:37 +05:30
t . access = gpmc_t - > access ;
t . oe_off = gpmc_t - > oe_off ;
t . cs_rd_off = gpmc_t - > cs_rd_off ;
t . rd_cycle = gpmc_t - > rd_cycle ;
2010-02-15 10:03:33 -08:00
/* Write */
2012-09-18 14:43:37 +05:30
t . adv_wr_off = gpmc_t - > adv_wr_off ;
2010-02-15 10:03:33 -08:00
t . we_on = t . oe_on ;
if ( cpu_is_omap34xx ( ) ) {
2012-09-18 14:43:37 +05:30
t . wr_data_mux_bus = gpmc_t - > wr_data_mux_bus ;
t . wr_access = gpmc_t - > wr_access ;
2010-02-15 10:03:33 -08:00
}
2012-09-18 14:43:37 +05:30
t . we_off = gpmc_t - > we_off ;
t . cs_wr_off = gpmc_t - > cs_wr_off ;
t . wr_cycle = gpmc_t - > wr_cycle ;
2010-02-15 10:03:33 -08:00
/* Configure GPMC */
2011-01-28 15:42:03 +05:30
if ( gpmc_nand_data - > devsize = = NAND_BUSWIDTH_16 )
gpmc_cs_configure ( gpmc_nand_data - > cs , GPMC_CONFIG_DEV_SIZE , 1 ) ;
else
gpmc_cs_configure ( gpmc_nand_data - > cs , GPMC_CONFIG_DEV_SIZE , 0 ) ;
2010-07-09 09:14:45 +00:00
gpmc_cs_configure ( gpmc_nand_data - > cs ,
GPMC_CONFIG_DEV_TYPE , GPMC_DEVICETYPE_NAND ) ;
2012-08-30 12:53:24 -07:00
gpmc_cs_configure ( gpmc_nand_data - > cs , GPMC_CONFIG_WP , 0 ) ;
2010-02-15 10:03:33 -08:00
err = gpmc_cs_set_timings ( gpmc_nand_data - > cs , & t ) ;
if ( err )
return err ;
return 0 ;
}
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
{
/* support only OMAP3 class */
2012-12-14 11:36:43 +01:00
if ( ! cpu_is_omap34xx ( ) & & ! soc_is_am33xx ( ) ) {
2012-10-01 02:47:28 +05:30
pr_err ( " BCH ecc is not supported on this CPU \n " ) ;
return 0 ;
}
/*
2012-12-14 11:36:43 +01:00
* For now , assume 4 - bit mode is only supported on OMAP3630 ES1 . x , x > = 1
* and AM33xx derivates . Other chips may be added if confirmed to work .
2012-10-01 02:47:28 +05:30
*/
if ( ( ecc_opt = = OMAP_ECC_BCH4_CODE_HW ) & &
2012-12-14 11:36:43 +01:00
( ! cpu_is_omap3630 ( ) | | ( GET_OMAP_REVISION ( ) = = 0 ) ) & &
( ! soc_is_am33xx ( ) ) ) {
2012-10-01 02:47:28 +05:30
pr_err ( " BCH 4-bit mode is not supported on this CPU \n " ) ;
return 0 ;
}
return 1 ;
}
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 ;
struct device * dev = & gpmc_nand_device . dev ;
gpmc_nand_device . dev . platform_data = gpmc_nand_data ;
err = gpmc_cs_request ( gpmc_nand_data - > cs , NAND_IO_SIZE ,
2012-08-30 12:53:24 -07:00
( unsigned long * ) & gpmc_nand_resource [ 0 ] . start ) ;
2010-02-15 10:03:33 -08:00
if ( err < 0 ) {
2013-02-12 16:22:20 -03:00
dev_err ( dev , " Cannot request GPMC CS %d, error %d \n " ,
gpmc_nand_data - > cs , err ) ;
2010-02-15 10:03:33 -08:00
return err ;
}
2012-08-30 12:53:24 -07:00
gpmc_nand_resource [ 0 ] . end = gpmc_nand_resource [ 0 ] . start +
NAND_IO_SIZE - 1 ;
2012-08-30 12:53:23 -07:00
2012-08-30 12:53:24 -07:00
gpmc_nand_resource [ 1 ] . start =
gpmc_get_client_irq ( GPMC_IRQ_FIFOEVENTENABLE ) ;
gpmc_nand_resource [ 2 ] . start =
gpmc_get_client_irq ( GPMC_IRQ_COUNT_EVENT ) ;
2012-09-29 12:26:13 +05:30
if ( gpmc_t ) {
err = omap2_nand_gpmc_retime ( gpmc_nand_data , gpmc_t ) ;
if ( err < 0 ) {
dev_err ( dev , " Unable to set gpmc timings: %d \n " , err ) ;
return err ;
}
2010-02-15 10:03:33 -08:00
}
/* Enable RD PIN Monitoring Reg */
if ( gpmc_nand_data - > dev_ready ) {
2010-07-09 09:14:45 +00:00
gpmc_cs_configure ( gpmc_nand_data - > cs , GPMC_CONFIG_RDY_BSY , 1 ) ;
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 ) ;
2012-10-01 02:47:28 +05:30
if ( ! gpmc_hwecc_bch_capable ( gpmc_nand_data - > ecc_opt ) )
return - EINVAL ;
2010-02-15 10:03:33 -08:00
err = platform_device_register ( & gpmc_nand_device ) ;
if ( err < 0 ) {
dev_err ( dev , " Unable to register NAND device \n " ) ;
goto out_free_cs ;
}
return 0 ;
out_free_cs :
gpmc_cs_free ( gpmc_nand_data - > cs ) ;
return err ;
}