2009-05-28 13:23:52 -07:00
/*
* linux / arch / arm / mach - omap2 / gpmc - onenand . c
*
* Copyright ( C ) 2006 - 2009 Nokia Corporation
* Contacts : Juha Yrjola
* Tony Lindgren
*
* 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 .
*/
2011-07-31 10:52:44 -04:00
# include <linux/string.h>
2009-05-28 13:23:52 -07:00
# include <linux/kernel.h>
# include <linux/platform_device.h>
# include <linux/mtd/onenand_regs.h>
# include <linux/io.h>
2012-08-24 15:21:06 +02:00
# include <linux/platform_data/mtd-onenand-omap2.h>
2012-06-05 10:11:48 +05:30
# include <linux/err.h>
2009-05-28 13:23:52 -07: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 10:32:42 +05:30
# include "gpmc-onenand.h"
2012-08-31 10:59:07 -07:00
2012-08-30 12:53:23 -07:00
# define ONENAND_IO_SIZE SZ_128K
2012-06-05 10:11:48 +05:30
# define ONENAND_FLAG_SYNCREAD (1 << 0)
# define ONENAND_FLAG_SYNCWRITE (1 << 1)
# define ONENAND_FLAG_HF (1 << 2)
# define ONENAND_FLAG_VHF (1 << 3)
static unsigned onenand_flags ;
static unsigned latency ;
2009-05-28 13:23:52 -07:00
static struct omap_onenand_platform_data * gpmc_onenand_data ;
2012-08-30 12:53:23 -07:00
static struct resource gpmc_onenand_resource = {
. flags = IORESOURCE_MEM ,
} ;
2009-05-28 13:23:52 -07:00
static struct platform_device gpmc_onenand_device = {
. name = " omap2-onenand " ,
. id = - 1 ,
2012-08-30 12:53:23 -07:00
. num_resources = 1 ,
. resource = & gpmc_onenand_resource ,
2009-05-28 13:23:52 -07:00
} ;
2012-06-05 10:11:48 +05:30
static struct gpmc_timings omap2_onenand_calc_async_timings ( void )
2009-05-28 13:23:52 -07:00
{
2012-11-09 18:05:17 +05:30
struct gpmc_device_timings dev_t ;
2009-05-28 13:23:52 -07:00
struct gpmc_timings t ;
const int t_cer = 15 ;
const int t_avdp = 12 ;
const int t_aavdh = 7 ;
const int t_ce = 76 ;
const int t_aa = 76 ;
const int t_oe = 20 ;
const int t_cez = 20 ; /* max of t_cez, t_oez */
const int t_wpl = 40 ;
const int t_wph = 30 ;
2012-11-09 18:05:17 +05:30
memset ( & dev_t , 0 , sizeof ( dev_t ) ) ;
dev_t . mux = true ;
dev_t . t_avdp_r = max_t ( int , t_avdp , t_cer ) * 1000 ;
dev_t . t_avdp_w = dev_t . t_avdp_r ;
dev_t . t_aavdh = t_aavdh * 1000 ;
dev_t . t_aa = t_aa * 1000 ;
dev_t . t_ce = t_ce * 1000 ;
dev_t . t_oe = t_oe * 1000 ;
dev_t . t_cez_r = t_cez * 1000 ;
dev_t . t_cez_w = dev_t . t_cez_r ;
dev_t . t_wpl = t_wpl * 1000 ;
dev_t . t_wph = t_wph * 1000 ;
gpmc_calc_timings ( & t , & dev_t ) ;
2009-05-28 13:23:52 -07:00
2012-06-05 10:11:48 +05:30
return t ;
}
static int gpmc_set_async_mode ( int cs , struct gpmc_timings * t )
{
2009-05-28 13:23:52 -07:00
/* Configure GPMC for asynchronous read */
gpmc_cs_write_reg ( cs , GPMC_CS_CONFIG1 ,
GPMC_CONFIG1_DEVICESIZE_16 |
GPMC_CONFIG1_MUXADDDATA ) ;
2012-06-05 10:11:48 +05:30
return gpmc_cs_set_timings ( cs , t ) ;
}
static void omap2_onenand_set_async_mode ( void __iomem * onenand_base )
{
u32 reg ;
2009-06-23 13:30:24 +03:00
/* Ensure sync read and sync write are disabled */
reg = readw ( onenand_base + ONENAND_REG_SYS_CFG1 ) ;
reg & = ~ ONENAND_SYS_CFG1_SYNC_READ & ~ ONENAND_SYS_CFG1_SYNC_WRITE ;
writew ( reg , onenand_base + ONENAND_REG_SYS_CFG1 ) ;
2009-05-28 13:23:52 -07:00
}
2012-06-05 10:11:48 +05:30
static void set_onenand_cfg ( void __iomem * onenand_base )
2009-05-28 13:23:52 -07:00
{
u32 reg ;
reg = readw ( onenand_base + ONENAND_REG_SYS_CFG1 ) ;
reg & = ~ ( ( 0x7 < < ONENAND_SYS_CFG1_BRL_SHIFT ) | ( 0x7 < < 9 ) ) ;
reg | = ( latency < < ONENAND_SYS_CFG1_BRL_SHIFT ) |
ONENAND_SYS_CFG1_BL_16 ;
2012-06-05 10:11:48 +05:30
if ( onenand_flags & ONENAND_FLAG_SYNCREAD )
2009-05-28 13:23:52 -07:00
reg | = ONENAND_SYS_CFG1_SYNC_READ ;
else
reg & = ~ ONENAND_SYS_CFG1_SYNC_READ ;
2012-06-05 10:11:48 +05:30
if ( onenand_flags & ONENAND_FLAG_SYNCWRITE )
2009-05-28 13:23:52 -07:00
reg | = ONENAND_SYS_CFG1_SYNC_WRITE ;
else
reg & = ~ ONENAND_SYS_CFG1_SYNC_WRITE ;
2012-06-05 10:11:48 +05:30
if ( onenand_flags & ONENAND_FLAG_HF )
2009-05-28 13:23:52 -07:00
reg | = ONENAND_SYS_CFG1_HF ;
else
reg & = ~ ONENAND_SYS_CFG1_HF ;
2012-06-05 10:11:48 +05:30
if ( onenand_flags & ONENAND_FLAG_VHF )
2011-02-07 10:46:58 +02:00
reg | = ONENAND_SYS_CFG1_VHF ;
else
reg & = ~ ONENAND_SYS_CFG1_VHF ;
2009-05-28 13:23:52 -07:00
writew ( reg , onenand_base + ONENAND_REG_SYS_CFG1 ) ;
}
2011-02-07 10:47:00 +02:00
static int omap2_onenand_get_freq ( struct omap_onenand_platform_data * cfg ,
2012-06-28 13:41:29 -05:00
void __iomem * onenand_base )
2011-02-07 10:47:00 +02:00
{
u16 ver = readw ( onenand_base + ONENAND_REG_VERSION_ID ) ;
2012-06-28 13:41:29 -05:00
int freq ;
2011-02-07 10:47:00 +02:00
switch ( ( ver > > 4 ) & 0xf ) {
case 0 :
freq = 40 ;
break ;
case 1 :
freq = 54 ;
break ;
case 2 :
freq = 66 ;
break ;
case 3 :
freq = 83 ;
break ;
case 4 :
freq = 104 ;
break ;
default :
freq = 54 ;
break ;
}
return freq ;
}
2012-06-05 10:11:48 +05:30
static struct gpmc_timings
omap2_onenand_calc_sync_timings ( struct omap_onenand_platform_data * cfg ,
2012-06-28 13:41:29 -05:00
int freq )
2009-05-28 13:23:52 -07:00
{
2012-11-09 18:05:17 +05:30
struct gpmc_device_timings dev_t ;
2009-05-28 13:23:52 -07:00
struct gpmc_timings t ;
const int t_cer = 15 ;
const int t_avdp = 12 ;
const int t_cez = 20 ; /* max of t_cez, t_oez */
const int t_wpl = 40 ;
const int t_wph = 30 ;
int min_gpmc_clk_period , t_ces , t_avds , t_avdh , t_ach , t_aavdh , t_rdyo ;
2012-11-09 18:05:17 +05:30
int div , gpmc_clk_ns ;
2009-05-28 13:23:52 -07:00
2012-06-05 10:11:48 +05:30
if ( cfg - > flags & ONENAND_SYNC_READ )
onenand_flags = ONENAND_FLAG_SYNCREAD ;
else if ( cfg - > flags & ONENAND_SYNC_READWRITE )
onenand_flags = ONENAND_FLAG_SYNCREAD | ONENAND_FLAG_SYNCWRITE ;
2009-05-28 13:23:52 -07:00
switch ( freq ) {
2010-12-09 11:22:50 +02:00
case 104 :
min_gpmc_clk_period = 9600 ; /* 104 MHz */
t_ces = 3 ;
t_avds = 4 ;
t_avdh = 2 ;
t_ach = 3 ;
t_aavdh = 6 ;
2011-02-07 10:46:58 +02:00
t_rdyo = 6 ;
2010-12-09 11:22:50 +02:00
break ;
2009-05-28 13:23:52 -07:00
case 83 :
2010-12-09 10:48:27 +02:00
min_gpmc_clk_period = 12000 ; /* 83 MHz */
2009-05-28 13:23:52 -07:00
t_ces = 5 ;
t_avds = 4 ;
t_avdh = 2 ;
t_ach = 6 ;
t_aavdh = 6 ;
t_rdyo = 9 ;
break ;
case 66 :
2010-12-09 10:48:27 +02:00
min_gpmc_clk_period = 15000 ; /* 66 MHz */
2009-05-28 13:23:52 -07:00
t_ces = 6 ;
t_avds = 5 ;
t_avdh = 2 ;
t_ach = 6 ;
t_aavdh = 6 ;
t_rdyo = 11 ;
break ;
default :
2010-12-09 10:48:27 +02:00
min_gpmc_clk_period = 18500 ; /* 54 MHz */
2009-05-28 13:23:52 -07:00
t_ces = 7 ;
t_avds = 7 ;
t_avdh = 7 ;
t_ach = 9 ;
t_aavdh = 7 ;
t_rdyo = 15 ;
2012-06-05 10:11:48 +05:30
onenand_flags & = ~ ONENAND_FLAG_SYNCWRITE ;
2009-05-28 13:23:52 -07:00
break ;
}
2012-08-19 18:29:45 +05:30
div = gpmc_calc_divider ( min_gpmc_clk_period ) ;
2009-05-28 13:23:52 -07:00
gpmc_clk_ns = gpmc_ticks_to_ns ( div ) ;
if ( gpmc_clk_ns < 15 ) /* >66Mhz */
2012-06-05 10:11:48 +05:30
onenand_flags | = ONENAND_FLAG_HF ;
else
onenand_flags & = ~ ONENAND_FLAG_HF ;
2011-02-07 10:46:58 +02:00
if ( gpmc_clk_ns < 12 ) /* >83Mhz */
2012-06-05 10:11:48 +05:30
onenand_flags | = ONENAND_FLAG_VHF ;
else
onenand_flags & = ~ ONENAND_FLAG_VHF ;
if ( onenand_flags & ONENAND_FLAG_VHF )
2011-02-07 10:46:58 +02:00
latency = 8 ;
2012-06-05 10:11:48 +05:30
else if ( onenand_flags & ONENAND_FLAG_HF )
2009-05-28 13:23:52 -07:00
latency = 6 ;
else if ( gpmc_clk_ns > = 25 ) /* 40 MHz*/
latency = 3 ;
else
latency = 4 ;
2012-06-05 10:11:48 +05:30
/* Set synchronous read timings */
2012-11-09 18:05:17 +05:30
memset ( & dev_t , 0 , sizeof ( dev_t ) ) ;
2009-05-28 13:23:52 -07:00
2012-11-09 18:05:17 +05:30
dev_t . mux = true ;
dev_t . sync_read = true ;
2012-06-05 10:11:48 +05:30
if ( onenand_flags & ONENAND_FLAG_SYNCWRITE ) {
2012-11-09 18:05:17 +05:30
dev_t . sync_write = true ;
2009-05-28 13:23:52 -07:00
} else {
2012-11-09 18:05:17 +05:30
dev_t . t_avdp_w = max ( t_avdp , t_cer ) * 1000 ;
dev_t . t_wpl = t_wpl * 1000 ;
dev_t . t_wph = t_wph * 1000 ;
dev_t . t_aavdh = t_aavdh * 1000 ;
2009-05-28 13:23:52 -07:00
}
2012-11-09 18:05:17 +05:30
dev_t . ce_xdelay = true ;
dev_t . avd_xdelay = true ;
dev_t . oe_xdelay = true ;
dev_t . we_xdelay = true ;
dev_t . clk = min_gpmc_clk_period ;
dev_t . t_bacc = dev_t . clk ;
dev_t . t_ces = t_ces * 1000 ;
dev_t . t_avds = t_avds * 1000 ;
dev_t . t_avdh = t_avdh * 1000 ;
dev_t . t_ach = t_ach * 1000 ;
dev_t . cyc_iaa = ( latency + 1 ) ;
dev_t . t_cez_r = t_cez * 1000 ;
dev_t . t_cez_w = dev_t . t_cez_r ;
dev_t . cyc_aavdh_oe = 1 ;
dev_t . t_rdyo = t_rdyo * 1000 + min_gpmc_clk_period ;
gpmc_calc_timings ( & t , & dev_t ) ;
2009-05-28 13:23:52 -07:00
2012-06-05 10:11:48 +05:30
return t ;
}
static int gpmc_set_sync_mode ( int cs , struct gpmc_timings * t )
{
unsigned sync_read = onenand_flags & ONENAND_FLAG_SYNCREAD ;
unsigned sync_write = onenand_flags & ONENAND_FLAG_SYNCWRITE ;
2009-05-28 13:23:52 -07:00
/* Configure GPMC for synchronous read */
gpmc_cs_write_reg ( cs , GPMC_CS_CONFIG1 ,
GPMC_CONFIG1_WRAPBURST_SUPP |
GPMC_CONFIG1_READMULTIPLE_SUPP |
( sync_read ? GPMC_CONFIG1_READTYPE_SYNC : 0 ) |
( sync_write ? GPMC_CONFIG1_WRITEMULTIPLE_SUPP : 0 ) |
( sync_write ? GPMC_CONFIG1_WRITETYPE_SYNC : 0 ) |
GPMC_CONFIG1_PAGE_LEN ( 2 ) |
( cpu_is_omap34xx ( ) ? 0 :
( GPMC_CONFIG1_WAIT_READ_MON |
GPMC_CONFIG1_WAIT_PIN_SEL ( 0 ) ) ) |
GPMC_CONFIG1_DEVICESIZE_16 |
GPMC_CONFIG1_DEVICETYPE_NOR |
GPMC_CONFIG1_MUXADDDATA ) ;
2012-06-05 10:11:48 +05:30
return gpmc_cs_set_timings ( cs , t ) ;
}
static int omap2_onenand_setup_async ( void __iomem * onenand_base )
{
struct gpmc_timings t ;
int ret ;
omap2_onenand_set_async_mode ( onenand_base ) ;
t = omap2_onenand_calc_async_timings ( ) ;
ret = gpmc_set_async_mode ( gpmc_onenand_data - > cs , & t ) ;
ARM: OMAP: use consistent error checking
Consistently check errors using the usual method used in the kernel
for much of its history. For instance:
int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t)
{
int div;
div = gpmc_calc_divider(t->sync_clk);
if (div < 0)
return div;
static int gpmc_set_async_mode(int cs, struct gpmc_timings *t)
{
...
return gpmc_cs_set_timings(cs, t);
.....
ret = gpmc_set_async_mode(gpmc_onenand_data->cs, &t);
if (IS_ERR_VALUE(ret))
return ret;
So, gpmc_cs_set_timings() thinks any negative return value is an error,
but where we check that in higher levels, only a limited range are
errors...
There is only _one_ use of IS_ERR_VALUE() in arch/arm which is really
appropriate, and that is in arch/arm/include/asm/syscall.h:
static inline long syscall_get_error(struct task_struct *task,
struct pt_regs *regs)
{
unsigned long error = regs->ARM_r0;
return IS_ERR_VALUE(error) ? error : 0;
}
because this function really does have to differentiate between error
return values and addresses which look like negative numbers (eg, from
mmap()).
So, here's a patch to remove them from OMAP, except for the above.
Acked-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
2013-03-13 20:44:21 +00:00
if ( ret < 0 )
2012-06-05 10:11:48 +05:30
return ret ;
omap2_onenand_set_async_mode ( onenand_base ) ;
return 0 ;
}
static int omap2_onenand_setup_sync ( void __iomem * onenand_base , int * freq_ptr )
{
int ret , freq = * freq_ptr ;
struct gpmc_timings t ;
if ( ! freq ) {
/* Very first call freq is not known */
2012-06-28 13:41:29 -05:00
freq = omap2_onenand_get_freq ( gpmc_onenand_data , onenand_base ) ;
2012-06-05 10:11:48 +05:30
set_onenand_cfg ( onenand_base ) ;
}
2012-06-28 13:41:29 -05:00
t = omap2_onenand_calc_sync_timings ( gpmc_onenand_data , freq ) ;
2009-05-28 13:23:52 -07:00
2012-06-05 10:11:48 +05:30
ret = gpmc_set_sync_mode ( gpmc_onenand_data - > cs , & t ) ;
ARM: OMAP: use consistent error checking
Consistently check errors using the usual method used in the kernel
for much of its history. For instance:
int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t)
{
int div;
div = gpmc_calc_divider(t->sync_clk);
if (div < 0)
return div;
static int gpmc_set_async_mode(int cs, struct gpmc_timings *t)
{
...
return gpmc_cs_set_timings(cs, t);
.....
ret = gpmc_set_async_mode(gpmc_onenand_data->cs, &t);
if (IS_ERR_VALUE(ret))
return ret;
So, gpmc_cs_set_timings() thinks any negative return value is an error,
but where we check that in higher levels, only a limited range are
errors...
There is only _one_ use of IS_ERR_VALUE() in arch/arm which is really
appropriate, and that is in arch/arm/include/asm/syscall.h:
static inline long syscall_get_error(struct task_struct *task,
struct pt_regs *regs)
{
unsigned long error = regs->ARM_r0;
return IS_ERR_VALUE(error) ? error : 0;
}
because this function really does have to differentiate between error
return values and addresses which look like negative numbers (eg, from
mmap()).
So, here's a patch to remove them from OMAP, except for the above.
Acked-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
2013-03-13 20:44:21 +00:00
if ( ret < 0 )
2012-06-05 10:11:48 +05:30
return ret ;
set_onenand_cfg ( onenand_base ) ;
2009-05-28 13:23:52 -07:00
2011-02-07 10:46:59 +02:00
* freq_ptr = freq ;
2009-05-28 13:23:52 -07:00
return 0 ;
}
2011-02-07 10:46:59 +02:00
static int gpmc_onenand_setup ( void __iomem * onenand_base , int * freq_ptr )
2009-05-28 13:23:52 -07:00
{
struct device * dev = & gpmc_onenand_device . dev ;
2012-06-05 10:11:48 +05:30
unsigned l = ONENAND_SYNC_READ | ONENAND_SYNC_READWRITE ;
int ret ;
2009-05-28 13:23:52 -07:00
2012-06-05 10:11:48 +05:30
ret = omap2_onenand_setup_async ( onenand_base ) ;
if ( ret ) {
dev_err ( dev , " unable to set to async mode \n " ) ;
return ret ;
2009-05-28 13:23:52 -07:00
}
2012-06-05 10:11:48 +05:30
if ( ! ( gpmc_onenand_data - > flags & l ) )
return 0 ;
ret = omap2_onenand_setup_sync ( onenand_base , freq_ptr ) ;
if ( ret )
dev_err ( dev , " unable to set to sync mode \n " ) ;
return ret ;
2009-05-28 13:23:52 -07:00
}
2013-01-25 09:23:10 -03:00
void gpmc_onenand_init ( struct omap_onenand_platform_data * _onenand_data )
2009-05-28 13:23:52 -07:00
{
2012-08-30 12:53:23 -07:00
int err ;
2013-02-12 16:22:22 -03:00
struct device * dev = & gpmc_onenand_device . dev ;
2012-08-30 12:53:23 -07:00
2009-05-28 13:23:52 -07:00
gpmc_onenand_data = _onenand_data ;
gpmc_onenand_data - > onenand_setup = gpmc_onenand_setup ;
gpmc_onenand_device . dev . platform_data = gpmc_onenand_data ;
if ( cpu_is_omap24xx ( ) & &
( gpmc_onenand_data - > flags & ONENAND_SYNC_READWRITE ) ) {
2013-02-12 16:22:23 -03:00
dev_warn ( dev , " OneNAND using only SYNC_READ on 24xx \n " ) ;
2009-05-28 13:23:52 -07:00
gpmc_onenand_data - > flags & = ~ ONENAND_SYNC_READWRITE ;
gpmc_onenand_data - > flags | = ONENAND_SYNC_READ ;
}
2012-10-05 11:39:54 +05:30
if ( cpu_is_omap34xx ( ) )
gpmc_onenand_data - > flags | = ONENAND_IN_OMAP34XX ;
else
gpmc_onenand_data - > flags & = ~ ONENAND_IN_OMAP34XX ;
2012-08-30 12:53:23 -07:00
err = gpmc_cs_request ( gpmc_onenand_data - > cs , ONENAND_IO_SIZE ,
( unsigned long * ) & gpmc_onenand_resource . start ) ;
if ( err < 0 ) {
2013-02-12 16:22:22 -03:00
dev_err ( dev , " Cannot request GPMC CS %d, error %d \n " ,
gpmc_onenand_data - > cs , err ) ;
2012-08-30 12:53:23 -07:00
return ;
}
gpmc_onenand_resource . end = gpmc_onenand_resource . start +
ONENAND_IO_SIZE - 1 ;
2009-05-28 13:23:52 -07:00
if ( platform_device_register ( & gpmc_onenand_device ) < 0 ) {
2013-02-12 16:22:22 -03:00
dev_err ( dev , " Unable to register OneNAND device \n " ) ;
2012-08-30 12:53:23 -07:00
gpmc_cs_free ( gpmc_onenand_data - > cs ) ;
2009-05-28 13:23:52 -07:00
return ;
}
}