2009-05-29 00:23:52 +04: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 18:52:44 +04:00
# include <linux/string.h>
2009-05-29 00:23:52 +04:00
# include <linux/kernel.h>
# include <linux/platform_device.h>
# include <linux/mtd/onenand_regs.h>
# include <linux/io.h>
2012-08-24 17:21:06 +04:00
# include <linux/platform_data/mtd-onenand-omap2.h>
2012-06-05 08:41:48 +04:00
# include <linux/err.h>
2009-05-29 00:23:52 +04:00
# include <asm/mach/flash.h>
2012-10-05 09:07:27 +04:00
# include "gpmc.h"
2012-08-31 21:59:07 +04:00
# include "soc.h"
2012-09-29 09:02:42 +04:00
# include "gpmc-onenand.h"
2012-08-31 21:59:07 +04:00
2012-08-30 23:53:23 +04:00
# define ONENAND_IO_SIZE SZ_128K
2012-06-05 08:41:48 +04:00
# 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-29 00:23:52 +04:00
static struct omap_onenand_platform_data * gpmc_onenand_data ;
2012-08-30 23:53:23 +04:00
static struct resource gpmc_onenand_resource = {
. flags = IORESOURCE_MEM ,
} ;
2009-05-29 00:23:52 +04:00
static struct platform_device gpmc_onenand_device = {
. name = " omap2-onenand " ,
. id = - 1 ,
2012-08-30 23:53:23 +04:00
. num_resources = 1 ,
. resource = & gpmc_onenand_resource ,
2009-05-29 00:23:52 +04:00
} ;
2013-02-21 23:46:22 +04:00
static struct gpmc_settings onenand_async = {
2013-02-21 22:42:22 +04:00
. device_width = GPMC_DEVWIDTH_16BIT ,
2013-02-21 23:46:22 +04:00
. mux_add_data = GPMC_MUX_AD ,
} ;
static struct gpmc_settings onenand_sync = {
. burst_read = true ,
2013-02-21 22:42:22 +04:00
. burst_wrap = true ,
. burst_len = GPMC_BURST_16 ,
. device_width = GPMC_DEVWIDTH_16BIT ,
2013-02-21 23:46:22 +04:00
. mux_add_data = GPMC_MUX_AD ,
2013-02-21 22:42:22 +04:00
. wait_pin = 0 ,
2013-02-21 23:46:22 +04:00
} ;
2013-02-22 01:20:53 +04:00
static void omap2_onenand_calc_async_timings ( struct gpmc_timings * t )
2009-05-29 00:23:52 +04:00
{
2012-11-09 16:35:17 +04:00
struct gpmc_device_timings dev_t ;
2009-05-29 00:23:52 +04:00
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 16:35:17 +04:00
memset ( & dev_t , 0 , sizeof ( dev_t ) ) ;
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 ;
2013-02-21 23:46:22 +04:00
gpmc_calc_timings ( t , & onenand_async , & dev_t ) ;
2012-06-05 08:41:48 +04:00
}
static void omap2_onenand_set_async_mode ( void __iomem * onenand_base )
{
u32 reg ;
2009-06-23 14:30:24 +04: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-29 00:23:52 +04:00
}
2012-06-05 08:41:48 +04:00
static void set_onenand_cfg ( void __iomem * onenand_base )
2009-05-29 00:23:52 +04: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 08:41:48 +04:00
if ( onenand_flags & ONENAND_FLAG_SYNCREAD )
2009-05-29 00:23:52 +04:00
reg | = ONENAND_SYS_CFG1_SYNC_READ ;
else
reg & = ~ ONENAND_SYS_CFG1_SYNC_READ ;
2012-06-05 08:41:48 +04:00
if ( onenand_flags & ONENAND_FLAG_SYNCWRITE )
2009-05-29 00:23:52 +04:00
reg | = ONENAND_SYS_CFG1_SYNC_WRITE ;
else
reg & = ~ ONENAND_SYS_CFG1_SYNC_WRITE ;
2012-06-05 08:41:48 +04:00
if ( onenand_flags & ONENAND_FLAG_HF )
2009-05-29 00:23:52 +04:00
reg | = ONENAND_SYS_CFG1_HF ;
else
reg & = ~ ONENAND_SYS_CFG1_HF ;
2012-06-05 08:41:48 +04:00
if ( onenand_flags & ONENAND_FLAG_VHF )
2011-02-07 11:46:58 +03:00
reg | = ONENAND_SYS_CFG1_VHF ;
else
reg & = ~ ONENAND_SYS_CFG1_VHF ;
2009-05-29 00:23:52 +04:00
writew ( reg , onenand_base + ONENAND_REG_SYS_CFG1 ) ;
}
2011-02-07 11:47:00 +03:00
static int omap2_onenand_get_freq ( struct omap_onenand_platform_data * cfg ,
2012-06-28 22:41:29 +04:00
void __iomem * onenand_base )
2011-02-07 11:47:00 +03:00
{
u16 ver = readw ( onenand_base + ONENAND_REG_VERSION_ID ) ;
2012-06-28 22:41:29 +04:00
int freq ;
2011-02-07 11:47:00 +03: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 ;
}
2013-02-22 01:20:53 +04:00
static void omap2_onenand_calc_sync_timings ( struct gpmc_timings * t ,
unsigned int flags ,
int freq )
2009-05-29 00:23:52 +04:00
{
2012-11-09 16:35:17 +04:00
struct gpmc_device_timings dev_t ;
2009-05-29 00:23:52 +04:00
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 16:35:17 +04:00
int div , gpmc_clk_ns ;
2009-05-29 00:23:52 +04:00
2013-02-22 01:20:53 +04:00
if ( flags & ONENAND_SYNC_READ )
2012-06-05 08:41:48 +04:00
onenand_flags = ONENAND_FLAG_SYNCREAD ;
2013-02-22 01:20:53 +04:00
else if ( flags & ONENAND_SYNC_READWRITE )
2012-06-05 08:41:48 +04:00
onenand_flags = ONENAND_FLAG_SYNCREAD | ONENAND_FLAG_SYNCWRITE ;
2009-05-29 00:23:52 +04:00
switch ( freq ) {
2010-12-09 12:22:50 +03: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 11:46:58 +03:00
t_rdyo = 6 ;
2010-12-09 12:22:50 +03:00
break ;
2009-05-29 00:23:52 +04:00
case 83 :
2010-12-09 11:48:27 +03:00
min_gpmc_clk_period = 12000 ; /* 83 MHz */
2009-05-29 00:23:52 +04: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 11:48:27 +03:00
min_gpmc_clk_period = 15000 ; /* 66 MHz */
2009-05-29 00:23:52 +04:00
t_ces = 6 ;
t_avds = 5 ;
t_avdh = 2 ;
t_ach = 6 ;
t_aavdh = 6 ;
t_rdyo = 11 ;
break ;
default :
2010-12-09 11:48:27 +03:00
min_gpmc_clk_period = 18500 ; /* 54 MHz */
2009-05-29 00:23:52 +04:00
t_ces = 7 ;
t_avds = 7 ;
t_avdh = 7 ;
t_ach = 9 ;
t_aavdh = 7 ;
t_rdyo = 15 ;
2012-06-05 08:41:48 +04:00
onenand_flags & = ~ ONENAND_FLAG_SYNCWRITE ;
2009-05-29 00:23:52 +04:00
break ;
}
2012-08-19 16:59:45 +04:00
div = gpmc_calc_divider ( min_gpmc_clk_period ) ;
2009-05-29 00:23:52 +04:00
gpmc_clk_ns = gpmc_ticks_to_ns ( div ) ;
if ( gpmc_clk_ns < 15 ) /* >66Mhz */
2012-06-05 08:41:48 +04:00
onenand_flags | = ONENAND_FLAG_HF ;
else
onenand_flags & = ~ ONENAND_FLAG_HF ;
2011-02-07 11:46:58 +03:00
if ( gpmc_clk_ns < 12 ) /* >83Mhz */
2012-06-05 08:41:48 +04:00
onenand_flags | = ONENAND_FLAG_VHF ;
else
onenand_flags & = ~ ONENAND_FLAG_VHF ;
if ( onenand_flags & ONENAND_FLAG_VHF )
2011-02-07 11:46:58 +03:00
latency = 8 ;
2012-06-05 08:41:48 +04:00
else if ( onenand_flags & ONENAND_FLAG_HF )
2009-05-29 00:23:52 +04:00
latency = 6 ;
else if ( gpmc_clk_ns > = 25 ) /* 40 MHz*/
latency = 3 ;
else
latency = 4 ;
2012-06-05 08:41:48 +04:00
/* Set synchronous read timings */
2012-11-09 16:35:17 +04:00
memset ( & dev_t , 0 , sizeof ( dev_t ) ) ;
2009-05-29 00:23:52 +04:00
2013-02-21 22:42:22 +04:00
if ( onenand_flags & ONENAND_FLAG_SYNCREAD )
onenand_sync . sync_read = true ;
2012-06-05 08:41:48 +04:00
if ( onenand_flags & ONENAND_FLAG_SYNCWRITE ) {
2013-02-21 23:46:22 +04:00
onenand_sync . sync_write = true ;
2013-02-21 22:42:22 +04:00
onenand_sync . burst_write = true ;
2009-05-29 00:23:52 +04:00
} else {
2012-11-09 16:35:17 +04:00
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-29 00:23:52 +04:00
}
2012-11-09 16:35:17 +04:00
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 ;
2013-02-21 23:46:22 +04:00
gpmc_calc_timings ( t , & onenand_sync , & dev_t ) ;
2012-06-05 08:41:48 +04:00
}
static int omap2_onenand_setup_async ( void __iomem * onenand_base )
{
struct gpmc_timings t ;
int ret ;
ARM: OMAP2: gpmc-onenand: fix sync mode setup with DT
With DT-based boot, the GPMC OneNAND sync mode setup does not work
correctly. During the async mode setup, sync flags gets incorrectly
set in the onenand_async data and the system crashes during the async
setup. Also, the sync mode never gets set in gpmc_onenand_data->flags, so
even without the crash, the actual sync mode setup would never be called.
The patch fixes this by adjusting the gpmc_onenand_data->flags when the
data is read from the DT. Also while doing this we force the onenand_async
to be always async.
The patch enables to use the following DTS chunk (that should correspond
the arch/arm/mach-omap2/board-rm680.c board file setup) with Nokia N950,
which currently crashes with 3.12-rc1. The crash output can be also
found below.
&gpmc {
ranges = <0 0 0x04000000 0x20000000>;
onenand@0,0 {
#address-cells = <1>;
#size-cells = <1>;
reg = <0 0 0x20000000>;
gpmc,sync-read;
gpmc,sync-write;
gpmc,burst-length = <16>;
gpmc,burst-read;
gpmc,burst-wrap;
gpmc,burst-write;
gpmc,device-width = <2>;
gpmc,mux-add-data = <2>;
gpmc,cs-on-ns = <0>;
gpmc,cs-rd-off-ns = <87>;
gpmc,cs-wr-off-ns = <87>;
gpmc,adv-on-ns = <0>;
gpmc,adv-rd-off-ns = <10>;
gpmc,adv-wr-off-ns = <10>;
gpmc,oe-on-ns = <15>;
gpmc,oe-off-ns = <87>;
gpmc,we-on-ns = <0>;
gpmc,we-off-ns = <87>;
gpmc,rd-cycle-ns = <112>;
gpmc,wr-cycle-ns = <112>;
gpmc,access-ns = <81>;
gpmc,page-burst-access-ns = <15>;
gpmc,bus-turnaround-ns = <0>;
gpmc,cycle2cycle-delay-ns = <0>;
gpmc,wait-monitoring-ns = <0>;
gpmc,clk-activation-ns = <5>;
gpmc,wr-data-mux-bus-ns = <30>;
gpmc,wr-access-ns = <81>;
gpmc,sync-clk-ps = <15000>;
};
};
[ 1.467559] GPMC CS0: cs_on : 0 ticks, 0 ns (was 0 ticks) 0 ns
[ 1.474822] GPMC CS0: cs_rd_off : 1 ticks, 5 ns (was 24 ticks) 5 ns
[ 1.482116] GPMC CS0: cs_wr_off : 14 ticks, 71 ns (was 24 ticks) 71 ns
[ 1.489349] GPMC CS0: adv_on : 0 ticks, 0 ns (was 0 ticks) 0 ns
[ 1.496582] GPMC CS0: adv_rd_off: 3 ticks, 15 ns (was 3 ticks) 15 ns
[ 1.503845] GPMC CS0: adv_wr_off: 3 ticks, 15 ns (was 3 ticks) 15 ns
[ 1.511077] GPMC CS0: oe_on : 3 ticks, 15 ns (was 4 ticks) 15 ns
[ 1.518310] GPMC CS0: oe_off : 1 ticks, 5 ns (was 24 ticks) 5 ns
[ 1.525543] GPMC CS0: we_on : 0 ticks, 0 ns (was 0 ticks) 0 ns
[ 1.532806] GPMC CS0: we_off : 8 ticks, 40 ns (was 24 ticks) 40 ns
[ 1.540039] GPMC CS0: rd_cycle : 4 ticks, 20 ns (was 29 ticks) 20 ns
[ 1.547302] GPMC CS0: wr_cycle : 4 ticks, 20 ns (was 29 ticks) 20 ns
[ 1.554504] GPMC CS0: access : 0 ticks, 0 ns (was 23 ticks) 0 ns
[ 1.561767] GPMC CS0: page_burst_access: 0 ticks, 0 ns (was 3 ticks) 0 ns
[ 1.569641] GPMC CS0: bus_turnaround: 0 ticks, 0 ns (was 0 ticks) 0 ns
[ 1.577270] GPMC CS0: cycle2cycle_delay: 0 ticks, 0 ns (was 0 ticks) 0 ns
[ 1.585144] GPMC CS0: wait_monitoring: 0 ticks, 0 ns (was 0 ticks) 0 ns
[ 1.592834] GPMC CS0: clk_activation: 0 ticks, 0 ns (was 0 ticks) 0 ns
[ 1.600463] GPMC CS0: wr_data_mux_bus: 5 ticks, 25 ns (was 8 ticks) 25 ns
[ 1.608154] GPMC CS0: wr_access : 0 ticks, 0 ns (was 23 ticks) 0 ns
[ 1.615386] GPMC CS0 CLK period is 5 ns (div 1)
[ 1.625122] Unhandled fault: external abort on non-linefetch (0x1008) at 0xf009e442
[ 1.633178] Internal error: : 1008 [#1] ARM
[ 1.637573] Modules linked in:
[ 1.640777] CPU: 0 PID: 1 Comm: swapper Not tainted 3.12.0-rc1-n9xx-los.git-5318619-00006-g4baa700-dirty #26
[ 1.651123] task: ef04c000 ti: ef050000 task.ti: ef050000
[ 1.656799] PC is at gpmc_onenand_setup+0x98/0x1e0
[ 1.661865] LR is at gpmc_cs_set_timings+0x494/0x5a4
[ 1.667083] pc : [<c002e040>] lr : [<c001f384>] psr: 60000113
[ 1.667083] sp : ef051d10 ip : ef051ce0 fp : ef051d94
[ 1.679138] r10: c0caaf60 r9 : ef050000 r8 : ef18b32c
[ 1.684631] r7 : f0080000 r6 : c0caaf60 r5 : 00000000 r4 : f009e400
[ 1.691497] r3 : f009e442 r2 : 80050000 r1 : 00000014 r0 : 00000000
[ 1.698333] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel
[ 1.706024] Control: 10c5387d Table: af290019 DAC: 00000015
[ 1.712066] Process swapper (pid: 1, stack limit = 0xef050240)
[ 1.718200] Stack: (0xef051d10 to 0xef052000)
[ 1.722778] 1d00: 00004000 00001402 00000000 00000005
[ 1.731384] 1d20: 00000047 00000000 0000000f 0000000f 00000000 00000028 0000000f 00000005
[ 1.739990] 1d40: 00000000 00000000 00000014 00000014 00000000 00000000 00000000 00000000
[ 1.748596] 1d60: 00000000 00000019 00000000 00000000 ef18b000 ef099c50 c0c8cb30 00000000
[ 1.757171] 1d80: c0488074 c048f868 ef051dcc ef051d98 c024447c c002dfb4 00000000 c048f868
[ 1.765777] 1da0: 00000000 00000000 c010e4a4 c0dbbb7c c0c8cb40 00000000 c0ca2500 c0488074
[ 1.774383] 1dc0: ef051ddc ef051dd0 c01fd508 c0244370 ef051dfc ef051de0 c01fc204 c01fd4f4
[ 1.782989] 1de0: c0c8cb40 c0ca2500 c0c8cb74 00000000 ef051e1c ef051e00 c01fc3b0 c01fc104
[ 1.791595] 1e00: ef0983bc 00000000 c0ca2500 c01fc31c ef051e44 ef051e20 c01fa794 c01fc328
[ 1.800201] 1e20: ef03634c ef0983b0 ef27d534 c0ca2500 ef27d500 c0c9a2f8 ef051e54 ef051e48
[ 1.808807] 1e40: c01fbcfc c01fa744 ef051e84 ef051e58 c01fb838 c01fbce4 c0411df8 c0caa040
[ 1.817413] 1e60: ef051e84 c0ca2500 00000006 c0caa040 00000066 c0488074 ef051e9c ef051e88
[ 1.825988] 1e80: c01fca30 c01fb768 c04975b8 00000006 ef051eac ef051ea0 c01fd728 c01fc9bc
[ 1.834594] 1ea0: ef051ebc ef051eb0 c048808c c01fd6e4 ef051f4c ef051ec0 c0008888 c0488080
[ 1.843200] 1ec0: 0000006f c046bae8 00000000 00000000 ef051efc ef051ee0 ef051f04 ef051ee8
[ 1.851806] 1ee0: c046d400 c0181218 c046d410 c18da8d5 c036a8e4 00000066 ef051f4c ef051f08
[ 1.860412] 1f00: c004b9a8 c046d41c c048f840 00000006 00000006 c046b488 00000000 c043ec08
[ 1.869018] 1f20: ef051f4c c04975b8 00000006 c0caa040 00000066 c046d410 c048f85c c048f868
[ 1.877593] 1f40: ef051f94 ef051f50 c046db8c c00087a0 00000006 00000006 c046d410 ffffffff
[ 1.886199] 1f60: ffffffff ffffffff ffffffff 00000000 c0348fd0 00000000 00000000 00000000
[ 1.894805] 1f80: 00000000 00000000 ef051fac ef051f98 c0348fe0 c046daa8 00000000 00000000
[ 1.903411] 1fa0: 00000000 ef051fb0 c000e7f8 c0348fdc 00000000 00000000 00000000 00000000
[ 1.912017] 1fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 1.920623] 1fe0: 00000000 00000000 00000000 00000000 00000013 00000000 ffffffff ffffffff
[ 1.929199] Backtrace:
[ 1.931793] [<c002dfa8>] (gpmc_onenand_setup+0x0/0x1e0) from [<c024447c>] (omap2_onenand_probe+0x118/0x49c)
[ 1.942047] [<c0244364>] (omap2_onenand_probe+0x0/0x49c) from [<c01fd508>] (platform_drv_probe+0x20/0x24)
[ 1.952117] r8:c0488074 r7:c0ca2500 r6:00000000 r5:c0c8cb40 r4:c0dbbb7c
[ 1.959197] [<c01fd4e8>] (platform_drv_probe+0x0/0x24) from [<c01fc204>] (driver_probe_device+0x10c/0x224)
[ 1.969360] [<c01fc0f8>] (driver_probe_device+0x0/0x224) from [<c01fc3b0>] (__driver_attach+0x94/0x98)
[ 1.979125] r7:00000000 r6:c0c8cb74 r5:c0ca2500 r4:c0c8cb40
[ 1.985107] [<c01fc31c>] (__driver_attach+0x0/0x98) from [<c01fa794>] (bus_for_each_dev+0x5c/0x90)
[ 1.994506] r6:c01fc31c r5:c0ca2500 r4:00000000 r3:ef0983bc
[ 2.000488] [<c01fa738>] (bus_for_each_dev+0x0/0x90) from [<c01fbcfc>] (driver_attach+0x24/0x28)
[ 2.009735] r6:c0c9a2f8 r5:ef27d500 r4:c0ca2500
[ 2.014587] [<c01fbcd8>] (driver_attach+0x0/0x28) from [<c01fb838>] (bus_add_driver+0xdc/0x260)
[ 2.023742] [<c01fb75c>] (bus_add_driver+0x0/0x260) from [<c01fca30>] (driver_register+0x80/0xfc)
[ 2.033081] r8:c0488074 r7:00000066 r6:c0caa040 r5:00000006 r4:c0ca2500
[ 2.040161] [<c01fc9b0>] (driver_register+0x0/0xfc) from [<c01fd728>] (__platform_driver_register+0x50/0x64)
[ 2.050476] r5:00000006 r4:c04975b8
[ 2.054260] [<c01fd6d8>] (__platform_driver_register+0x0/0x64) from [<c048808c>] (omap2_onenand_driver_init+0x18/0x20)
[ 2.065490] [<c0488074>] (omap2_onenand_driver_init+0x0/0x20) from [<c0008888>] (do_one_initcall+0xf4/0x150)
[ 2.075836] [<c0008794>] (do_one_initcall+0x0/0x150) from [<c046db8c>] (kernel_init_freeable+0xf0/0x1b4)
[ 2.085815] [<c046da9c>] (kernel_init_freeable+0x0/0x1b4) from [<c0348fe0>] (kernel_init+0x10/0xec)
[ 2.095336] [<c0348fd0>] (kernel_init+0x0/0xec) from [<c000e7f8>] (ret_from_fork+0x14/0x3c)
[ 2.104125] r4:00000000 r3:00000000
[ 2.107879] Code: ebffc3ae e2505000 ba00002e e2843042 (e1d320b0)
[ 2.114318] ---[ end trace b8ee3e3e5e002451 ]---
Signed-off-by: Aaro Koskinen <aaro.koskinen@iki.fi>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2013-09-21 00:01:06 +04:00
if ( gpmc_onenand_data - > of_node ) {
2013-02-25 21:37:58 +04:00
gpmc_read_settings_dt ( gpmc_onenand_data - > of_node ,
& onenand_async ) ;
ARM: OMAP2: gpmc-onenand: fix sync mode setup with DT
With DT-based boot, the GPMC OneNAND sync mode setup does not work
correctly. During the async mode setup, sync flags gets incorrectly
set in the onenand_async data and the system crashes during the async
setup. Also, the sync mode never gets set in gpmc_onenand_data->flags, so
even without the crash, the actual sync mode setup would never be called.
The patch fixes this by adjusting the gpmc_onenand_data->flags when the
data is read from the DT. Also while doing this we force the onenand_async
to be always async.
The patch enables to use the following DTS chunk (that should correspond
the arch/arm/mach-omap2/board-rm680.c board file setup) with Nokia N950,
which currently crashes with 3.12-rc1. The crash output can be also
found below.
&gpmc {
ranges = <0 0 0x04000000 0x20000000>;
onenand@0,0 {
#address-cells = <1>;
#size-cells = <1>;
reg = <0 0 0x20000000>;
gpmc,sync-read;
gpmc,sync-write;
gpmc,burst-length = <16>;
gpmc,burst-read;
gpmc,burst-wrap;
gpmc,burst-write;
gpmc,device-width = <2>;
gpmc,mux-add-data = <2>;
gpmc,cs-on-ns = <0>;
gpmc,cs-rd-off-ns = <87>;
gpmc,cs-wr-off-ns = <87>;
gpmc,adv-on-ns = <0>;
gpmc,adv-rd-off-ns = <10>;
gpmc,adv-wr-off-ns = <10>;
gpmc,oe-on-ns = <15>;
gpmc,oe-off-ns = <87>;
gpmc,we-on-ns = <0>;
gpmc,we-off-ns = <87>;
gpmc,rd-cycle-ns = <112>;
gpmc,wr-cycle-ns = <112>;
gpmc,access-ns = <81>;
gpmc,page-burst-access-ns = <15>;
gpmc,bus-turnaround-ns = <0>;
gpmc,cycle2cycle-delay-ns = <0>;
gpmc,wait-monitoring-ns = <0>;
gpmc,clk-activation-ns = <5>;
gpmc,wr-data-mux-bus-ns = <30>;
gpmc,wr-access-ns = <81>;
gpmc,sync-clk-ps = <15000>;
};
};
[ 1.467559] GPMC CS0: cs_on : 0 ticks, 0 ns (was 0 ticks) 0 ns
[ 1.474822] GPMC CS0: cs_rd_off : 1 ticks, 5 ns (was 24 ticks) 5 ns
[ 1.482116] GPMC CS0: cs_wr_off : 14 ticks, 71 ns (was 24 ticks) 71 ns
[ 1.489349] GPMC CS0: adv_on : 0 ticks, 0 ns (was 0 ticks) 0 ns
[ 1.496582] GPMC CS0: adv_rd_off: 3 ticks, 15 ns (was 3 ticks) 15 ns
[ 1.503845] GPMC CS0: adv_wr_off: 3 ticks, 15 ns (was 3 ticks) 15 ns
[ 1.511077] GPMC CS0: oe_on : 3 ticks, 15 ns (was 4 ticks) 15 ns
[ 1.518310] GPMC CS0: oe_off : 1 ticks, 5 ns (was 24 ticks) 5 ns
[ 1.525543] GPMC CS0: we_on : 0 ticks, 0 ns (was 0 ticks) 0 ns
[ 1.532806] GPMC CS0: we_off : 8 ticks, 40 ns (was 24 ticks) 40 ns
[ 1.540039] GPMC CS0: rd_cycle : 4 ticks, 20 ns (was 29 ticks) 20 ns
[ 1.547302] GPMC CS0: wr_cycle : 4 ticks, 20 ns (was 29 ticks) 20 ns
[ 1.554504] GPMC CS0: access : 0 ticks, 0 ns (was 23 ticks) 0 ns
[ 1.561767] GPMC CS0: page_burst_access: 0 ticks, 0 ns (was 3 ticks) 0 ns
[ 1.569641] GPMC CS0: bus_turnaround: 0 ticks, 0 ns (was 0 ticks) 0 ns
[ 1.577270] GPMC CS0: cycle2cycle_delay: 0 ticks, 0 ns (was 0 ticks) 0 ns
[ 1.585144] GPMC CS0: wait_monitoring: 0 ticks, 0 ns (was 0 ticks) 0 ns
[ 1.592834] GPMC CS0: clk_activation: 0 ticks, 0 ns (was 0 ticks) 0 ns
[ 1.600463] GPMC CS0: wr_data_mux_bus: 5 ticks, 25 ns (was 8 ticks) 25 ns
[ 1.608154] GPMC CS0: wr_access : 0 ticks, 0 ns (was 23 ticks) 0 ns
[ 1.615386] GPMC CS0 CLK period is 5 ns (div 1)
[ 1.625122] Unhandled fault: external abort on non-linefetch (0x1008) at 0xf009e442
[ 1.633178] Internal error: : 1008 [#1] ARM
[ 1.637573] Modules linked in:
[ 1.640777] CPU: 0 PID: 1 Comm: swapper Not tainted 3.12.0-rc1-n9xx-los.git-5318619-00006-g4baa700-dirty #26
[ 1.651123] task: ef04c000 ti: ef050000 task.ti: ef050000
[ 1.656799] PC is at gpmc_onenand_setup+0x98/0x1e0
[ 1.661865] LR is at gpmc_cs_set_timings+0x494/0x5a4
[ 1.667083] pc : [<c002e040>] lr : [<c001f384>] psr: 60000113
[ 1.667083] sp : ef051d10 ip : ef051ce0 fp : ef051d94
[ 1.679138] r10: c0caaf60 r9 : ef050000 r8 : ef18b32c
[ 1.684631] r7 : f0080000 r6 : c0caaf60 r5 : 00000000 r4 : f009e400
[ 1.691497] r3 : f009e442 r2 : 80050000 r1 : 00000014 r0 : 00000000
[ 1.698333] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel
[ 1.706024] Control: 10c5387d Table: af290019 DAC: 00000015
[ 1.712066] Process swapper (pid: 1, stack limit = 0xef050240)
[ 1.718200] Stack: (0xef051d10 to 0xef052000)
[ 1.722778] 1d00: 00004000 00001402 00000000 00000005
[ 1.731384] 1d20: 00000047 00000000 0000000f 0000000f 00000000 00000028 0000000f 00000005
[ 1.739990] 1d40: 00000000 00000000 00000014 00000014 00000000 00000000 00000000 00000000
[ 1.748596] 1d60: 00000000 00000019 00000000 00000000 ef18b000 ef099c50 c0c8cb30 00000000
[ 1.757171] 1d80: c0488074 c048f868 ef051dcc ef051d98 c024447c c002dfb4 00000000 c048f868
[ 1.765777] 1da0: 00000000 00000000 c010e4a4 c0dbbb7c c0c8cb40 00000000 c0ca2500 c0488074
[ 1.774383] 1dc0: ef051ddc ef051dd0 c01fd508 c0244370 ef051dfc ef051de0 c01fc204 c01fd4f4
[ 1.782989] 1de0: c0c8cb40 c0ca2500 c0c8cb74 00000000 ef051e1c ef051e00 c01fc3b0 c01fc104
[ 1.791595] 1e00: ef0983bc 00000000 c0ca2500 c01fc31c ef051e44 ef051e20 c01fa794 c01fc328
[ 1.800201] 1e20: ef03634c ef0983b0 ef27d534 c0ca2500 ef27d500 c0c9a2f8 ef051e54 ef051e48
[ 1.808807] 1e40: c01fbcfc c01fa744 ef051e84 ef051e58 c01fb838 c01fbce4 c0411df8 c0caa040
[ 1.817413] 1e60: ef051e84 c0ca2500 00000006 c0caa040 00000066 c0488074 ef051e9c ef051e88
[ 1.825988] 1e80: c01fca30 c01fb768 c04975b8 00000006 ef051eac ef051ea0 c01fd728 c01fc9bc
[ 1.834594] 1ea0: ef051ebc ef051eb0 c048808c c01fd6e4 ef051f4c ef051ec0 c0008888 c0488080
[ 1.843200] 1ec0: 0000006f c046bae8 00000000 00000000 ef051efc ef051ee0 ef051f04 ef051ee8
[ 1.851806] 1ee0: c046d400 c0181218 c046d410 c18da8d5 c036a8e4 00000066 ef051f4c ef051f08
[ 1.860412] 1f00: c004b9a8 c046d41c c048f840 00000006 00000006 c046b488 00000000 c043ec08
[ 1.869018] 1f20: ef051f4c c04975b8 00000006 c0caa040 00000066 c046d410 c048f85c c048f868
[ 1.877593] 1f40: ef051f94 ef051f50 c046db8c c00087a0 00000006 00000006 c046d410 ffffffff
[ 1.886199] 1f60: ffffffff ffffffff ffffffff 00000000 c0348fd0 00000000 00000000 00000000
[ 1.894805] 1f80: 00000000 00000000 ef051fac ef051f98 c0348fe0 c046daa8 00000000 00000000
[ 1.903411] 1fa0: 00000000 ef051fb0 c000e7f8 c0348fdc 00000000 00000000 00000000 00000000
[ 1.912017] 1fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 1.920623] 1fe0: 00000000 00000000 00000000 00000000 00000013 00000000 ffffffff ffffffff
[ 1.929199] Backtrace:
[ 1.931793] [<c002dfa8>] (gpmc_onenand_setup+0x0/0x1e0) from [<c024447c>] (omap2_onenand_probe+0x118/0x49c)
[ 1.942047] [<c0244364>] (omap2_onenand_probe+0x0/0x49c) from [<c01fd508>] (platform_drv_probe+0x20/0x24)
[ 1.952117] r8:c0488074 r7:c0ca2500 r6:00000000 r5:c0c8cb40 r4:c0dbbb7c
[ 1.959197] [<c01fd4e8>] (platform_drv_probe+0x0/0x24) from [<c01fc204>] (driver_probe_device+0x10c/0x224)
[ 1.969360] [<c01fc0f8>] (driver_probe_device+0x0/0x224) from [<c01fc3b0>] (__driver_attach+0x94/0x98)
[ 1.979125] r7:00000000 r6:c0c8cb74 r5:c0ca2500 r4:c0c8cb40
[ 1.985107] [<c01fc31c>] (__driver_attach+0x0/0x98) from [<c01fa794>] (bus_for_each_dev+0x5c/0x90)
[ 1.994506] r6:c01fc31c r5:c0ca2500 r4:00000000 r3:ef0983bc
[ 2.000488] [<c01fa738>] (bus_for_each_dev+0x0/0x90) from [<c01fbcfc>] (driver_attach+0x24/0x28)
[ 2.009735] r6:c0c9a2f8 r5:ef27d500 r4:c0ca2500
[ 2.014587] [<c01fbcd8>] (driver_attach+0x0/0x28) from [<c01fb838>] (bus_add_driver+0xdc/0x260)
[ 2.023742] [<c01fb75c>] (bus_add_driver+0x0/0x260) from [<c01fca30>] (driver_register+0x80/0xfc)
[ 2.033081] r8:c0488074 r7:00000066 r6:c0caa040 r5:00000006 r4:c0ca2500
[ 2.040161] [<c01fc9b0>] (driver_register+0x0/0xfc) from [<c01fd728>] (__platform_driver_register+0x50/0x64)
[ 2.050476] r5:00000006 r4:c04975b8
[ 2.054260] [<c01fd6d8>] (__platform_driver_register+0x0/0x64) from [<c048808c>] (omap2_onenand_driver_init+0x18/0x20)
[ 2.065490] [<c0488074>] (omap2_onenand_driver_init+0x0/0x20) from [<c0008888>] (do_one_initcall+0xf4/0x150)
[ 2.075836] [<c0008794>] (do_one_initcall+0x0/0x150) from [<c046db8c>] (kernel_init_freeable+0xf0/0x1b4)
[ 2.085815] [<c046da9c>] (kernel_init_freeable+0x0/0x1b4) from [<c0348fe0>] (kernel_init+0x10/0xec)
[ 2.095336] [<c0348fd0>] (kernel_init+0x0/0xec) from [<c000e7f8>] (ret_from_fork+0x14/0x3c)
[ 2.104125] r4:00000000 r3:00000000
[ 2.107879] Code: ebffc3ae e2505000 ba00002e e2843042 (e1d320b0)
[ 2.114318] ---[ end trace b8ee3e3e5e002451 ]---
Signed-off-by: Aaro Koskinen <aaro.koskinen@iki.fi>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2013-09-21 00:01:06 +04:00
if ( onenand_async . sync_read | | onenand_async . sync_write ) {
if ( onenand_async . sync_write )
gpmc_onenand_data - > flags | =
ONENAND_SYNC_READWRITE ;
else
gpmc_onenand_data - > flags | = ONENAND_SYNC_READ ;
onenand_async . sync_read = false ;
onenand_async . sync_write = false ;
}
}
2013-02-25 21:37:58 +04:00
2012-06-05 08:41:48 +04:00
omap2_onenand_set_async_mode ( onenand_base ) ;
2013-02-22 01:20:53 +04:00
omap2_onenand_calc_async_timings ( & t ) ;
2012-06-05 08:41:48 +04:00
2013-02-21 22:42:22 +04:00
ret = gpmc_cs_program_settings ( gpmc_onenand_data - > cs , & onenand_async ) ;
if ( ret < 0 )
return ret ;
ret = gpmc_cs_set_timings ( 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-14 00:44:21 +04:00
if ( ret < 0 )
2012-06-05 08:41:48 +04:00
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 22:41:29 +04:00
freq = omap2_onenand_get_freq ( gpmc_onenand_data , onenand_base ) ;
2012-06-05 08:41:48 +04:00
set_onenand_cfg ( onenand_base ) ;
}
2013-02-25 21:37:58 +04:00
if ( gpmc_onenand_data - > of_node ) {
gpmc_read_settings_dt ( gpmc_onenand_data - > of_node ,
& onenand_sync ) ;
} else {
/*
* FIXME : Appears to be legacy code from initial ONENAND commit .
* Unclear what boards this is for and if this can be removed .
*/
if ( ! cpu_is_omap34xx ( ) )
onenand_sync . wait_on_read = true ;
}
2013-02-21 22:42:22 +04:00
2013-02-22 01:20:53 +04:00
omap2_onenand_calc_sync_timings ( & t , gpmc_onenand_data - > flags , freq ) ;
2009-05-29 00:23:52 +04:00
2013-02-21 22:42:22 +04:00
ret = gpmc_cs_program_settings ( gpmc_onenand_data - > cs , & onenand_sync ) ;
if ( ret < 0 )
return ret ;
ret = gpmc_cs_set_timings ( 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-14 00:44:21 +04:00
if ( ret < 0 )
2012-06-05 08:41:48 +04:00
return ret ;
set_onenand_cfg ( onenand_base ) ;
2009-05-29 00:23:52 +04:00
2011-02-07 11:46:59 +03:00
* freq_ptr = freq ;
2009-05-29 00:23:52 +04:00
return 0 ;
}
2011-02-07 11:46:59 +03:00
static int gpmc_onenand_setup ( void __iomem * onenand_base , int * freq_ptr )
2009-05-29 00:23:52 +04:00
{
struct device * dev = & gpmc_onenand_device . dev ;
2012-06-05 08:41:48 +04:00
unsigned l = ONENAND_SYNC_READ | ONENAND_SYNC_READWRITE ;
int ret ;
2009-05-29 00:23:52 +04:00
2012-06-05 08:41:48 +04:00
ret = omap2_onenand_setup_async ( onenand_base ) ;
if ( ret ) {
dev_err ( dev , " unable to set to async mode \n " ) ;
return ret ;
2009-05-29 00:23:52 +04:00
}
2012-06-05 08:41:48 +04:00
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-29 00:23:52 +04:00
}
2013-01-25 16:23:10 +04:00
void gpmc_onenand_init ( struct omap_onenand_platform_data * _onenand_data )
2009-05-29 00:23:52 +04:00
{
2012-08-30 23:53:23 +04:00
int err ;
2013-02-12 23:22:22 +04:00
struct device * dev = & gpmc_onenand_device . dev ;
2012-08-30 23:53:23 +04:00
2009-05-29 00:23:52 +04: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 23:22:23 +04:00
dev_warn ( dev , " OneNAND using only SYNC_READ on 24xx \n " ) ;
2009-05-29 00:23:52 +04:00
gpmc_onenand_data - > flags & = ~ ONENAND_SYNC_READWRITE ;
gpmc_onenand_data - > flags | = ONENAND_SYNC_READ ;
}
2012-10-05 10:09:54 +04:00
if ( cpu_is_omap34xx ( ) )
gpmc_onenand_data - > flags | = ONENAND_IN_OMAP34XX ;
else
gpmc_onenand_data - > flags & = ~ ONENAND_IN_OMAP34XX ;
2012-08-30 23:53:23 +04:00
err = gpmc_cs_request ( gpmc_onenand_data - > cs , ONENAND_IO_SIZE ,
( unsigned long * ) & gpmc_onenand_resource . start ) ;
if ( err < 0 ) {
2013-02-12 23:22:22 +04:00
dev_err ( dev , " Cannot request GPMC CS %d, error %d \n " ,
gpmc_onenand_data - > cs , err ) ;
2012-08-30 23:53:23 +04:00
return ;
}
gpmc_onenand_resource . end = gpmc_onenand_resource . start +
ONENAND_IO_SIZE - 1 ;
2009-05-29 00:23:52 +04:00
if ( platform_device_register ( & gpmc_onenand_device ) < 0 ) {
2013-02-12 23:22:22 +04:00
dev_err ( dev , " Unable to register OneNAND device \n " ) ;
2012-08-30 23:53:23 +04:00
gpmc_cs_free ( gpmc_onenand_data - > cs ) ;
2009-05-29 00:23:52 +04:00
return ;
}
}