2019-05-27 08:55:05 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2018-02-05 23:01:59 +01:00
/*
2008-05-20 17:32:27 +01:00
* Copyright © 2004 - 2008 Simtec Electronics
* http : //armlinux.simtec.co.uk/
2005-02-18 14:46:15 +00:00
* Ben Dooks < ben @ simtec . co . uk >
2005-04-16 15:20:36 -07:00
*
2008-05-20 17:32:27 +01:00
* Samsung S3C2410 / S3C2440 / S3C2412 NAND driver
2005-04-16 15:20:36 -07:00
*/
2012-07-16 16:02:23 +05:30
# define pr_fmt(fmt) "nand-s3c2410: " fmt
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_MTD_NAND_S3C2410_DEBUG
# define DEBUG
# endif
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/string.h>
2012-07-16 16:02:24 +05:30
# include <linux/io.h>
2005-04-16 15:20:36 -07:00
# include <linux/ioport.h>
2005-10-29 19:07:23 +01:00
# include <linux/platform_device.h>
2005-04-16 15:20:36 -07:00
# include <linux/delay.h>
# include <linux/err.h>
2005-10-30 15:03:48 -08:00
# include <linux/slab.h>
2006-01-07 16:15:52 +00:00
# include <linux/clk.h>
2008-07-15 11:58:31 +01:00
# include <linux/cpufreq.h>
2016-10-26 21:59:55 -02:00
# include <linux/of.h>
# include <linux/of_device.h>
2005-04-16 15:20:36 -07:00
# include <linux/mtd/mtd.h>
2017-08-04 17:29:10 +02:00
# include <linux/mtd/rawnand.h>
2005-04-16 15:20:36 -07:00
# include <linux/mtd/partitions.h>
2012-08-24 15:22:12 +02:00
# include <linux/platform_data/mtd-nand-s3c2410.h>
2005-04-16 15:20:36 -07:00
2014-01-10 11:24:13 +05:30
# define S3C2410_NFREG(x) (x)
# define S3C2410_NFCONF S3C2410_NFREG(0x00)
# define S3C2410_NFCMD S3C2410_NFREG(0x04)
# define S3C2410_NFADDR S3C2410_NFREG(0x08)
# define S3C2410_NFDATA S3C2410_NFREG(0x0C)
# define S3C2410_NFSTAT S3C2410_NFREG(0x10)
# define S3C2410_NFECC S3C2410_NFREG(0x14)
# define S3C2440_NFCONT S3C2410_NFREG(0x04)
# define S3C2440_NFCMD S3C2410_NFREG(0x08)
# define S3C2440_NFADDR S3C2410_NFREG(0x0C)
# define S3C2440_NFDATA S3C2410_NFREG(0x10)
# define S3C2440_NFSTAT S3C2410_NFREG(0x20)
# define S3C2440_NFMECC0 S3C2410_NFREG(0x2C)
# define S3C2412_NFSTAT S3C2410_NFREG(0x28)
# define S3C2412_NFMECC0 S3C2410_NFREG(0x34)
# define S3C2410_NFCONF_EN (1<<15)
# define S3C2410_NFCONF_INITECC (1<<12)
# define S3C2410_NFCONF_nFCE (1<<11)
# define S3C2410_NFCONF_TACLS(x) ((x)<<8)
# define S3C2410_NFCONF_TWRPH0(x) ((x)<<4)
# define S3C2410_NFCONF_TWRPH1(x) ((x)<<0)
# define S3C2410_NFSTAT_BUSY (1<<0)
# define S3C2440_NFCONF_TACLS(x) ((x)<<12)
# define S3C2440_NFCONF_TWRPH0(x) ((x)<<8)
# define S3C2440_NFCONF_TWRPH1(x) ((x)<<4)
# define S3C2440_NFCONT_INITECC (1<<4)
# define S3C2440_NFCONT_nFCE (1<<1)
# define S3C2440_NFCONT_ENABLE (1<<0)
# define S3C2440_NFSTAT_READY (1<<0)
# define S3C2412_NFCONF_NANDBOOT (1<<31)
# define S3C2412_NFCONT_INIT_MAIN_ECC (1<<5)
# define S3C2412_NFCONT_nFCE0 (1<<1)
# define S3C2412_NFSTAT_READY (1<<0)
2005-04-16 15:20:36 -07:00
/* new oob placement block for use with hardware ecc generation
*/
2016-02-03 20:03:30 +01:00
static int s3c2410_ooblayout_ecc ( struct mtd_info * mtd , int section ,
struct mtd_oob_region * oobregion )
{
if ( section )
return - ERANGE ;
oobregion - > offset = 0 ;
oobregion - > length = 3 ;
return 0 ;
}
static int s3c2410_ooblayout_free ( struct mtd_info * mtd , int section ,
struct mtd_oob_region * oobregion )
{
if ( section )
return - ERANGE ;
oobregion - > offset = 8 ;
oobregion - > length = 8 ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
2016-02-03 20:03:30 +01:00
static const struct mtd_ooblayout_ops s3c2410_ooblayout_ops = {
. ecc = s3c2410_ooblayout_ecc ,
. free = s3c2410_ooblayout_free ,
2005-04-16 15:20:36 -07:00
} ;
/* controller and mtd information */
struct s3c2410_nand_info ;
2009-05-30 17:18:15 +01:00
/**
* struct s3c2410_nand_mtd - driver MTD structure
* @ mtd : The MTD instance to pass to the MTD layer .
* @ chip : The NAND chip information .
* @ set : The platform information supplied for this set of NAND chips .
* @ info : Link back to the hardware information .
*/
2005-04-16 15:20:36 -07:00
struct s3c2410_nand_mtd {
struct nand_chip chip ;
struct s3c2410_nand_set * set ;
struct s3c2410_nand_info * info ;
} ;
2006-06-27 14:35:46 +01:00
enum s3c_cpu_type {
TYPE_S3C2410 ,
TYPE_S3C2412 ,
TYPE_S3C2440 ,
} ;
2011-04-13 11:59:30 +02:00
enum s3c_nand_clk_state {
CLOCK_DISABLE = 0 ,
CLOCK_ENABLE ,
CLOCK_SUSPEND ,
} ;
2005-04-16 15:20:36 -07:00
/* overview of the s3c2410 nand state */
2009-05-30 17:18:15 +01:00
/**
* struct s3c2410_nand_info - NAND controller state .
2020-11-09 18:22:00 +00:00
* @ controller : Base controller structure .
* @ mtds : An array of MTD instances on this controller .
2009-05-30 17:18:15 +01:00
* @ platform : The platform data for this board .
* @ device : The platform device we bound to .
* @ clk : The clock resource for this controller .
2012-08-21 14:24:09 +05:30
* @ regs : The area mapped for the hardware registers .
2009-05-30 17:18:15 +01:00
* @ sel_reg : Pointer to the register controlling the NAND selection .
* @ sel_bit : The bit in @ sel_reg to select the NAND chip .
* @ mtd_count : The number of MTDs created from this controller .
* @ save_sel : The contents of @ sel_reg to be saved over suspend .
* @ clk_rate : The clock rate from @ clk .
2011-04-13 11:59:30 +02:00
* @ clk_state : The current clock state .
2009-05-30 17:18:15 +01:00
* @ cpu_type : The exact type of this controller .
2020-11-09 18:22:00 +00:00
* @ freq_transition : CPUFreq notifier block
2009-05-30 17:18:15 +01:00
*/
2005-04-16 15:20:36 -07:00
struct s3c2410_nand_info {
/* mtd info */
2018-07-17 09:08:02 +02:00
struct nand_controller controller ;
2005-04-16 15:20:36 -07:00
struct s3c2410_nand_mtd * mtds ;
struct s3c2410_platform_nand * platform ;
/* device info */
struct device * device ;
struct clk * clk ;
2005-02-18 14:46:15 +00:00
void __iomem * regs ;
2006-06-27 14:35:46 +01:00
void __iomem * sel_reg ;
int sel_bit ;
2005-04-16 15:20:36 -07:00
int mtd_count ;
2008-04-15 11:36:18 +01:00
unsigned long save_sel ;
2008-07-15 11:58:31 +01:00
unsigned long clk_rate ;
2011-04-13 11:59:30 +02:00
enum s3c_nand_clk_state clk_state ;
2007-11-19 23:28:07 +00:00
2006-06-27 14:35:46 +01:00
enum s3c_cpu_type cpu_type ;
2008-07-15 11:58:31 +01:00
2016-06-27 14:51:38 +02:00
# ifdef CONFIG_ARM_S3C24XX_CPUFREQ
2008-07-15 11:58:31 +01:00
struct notifier_block freq_transition ;
# endif
2005-04-16 15:20:36 -07:00
} ;
2016-10-26 21:59:55 -02:00
struct s3c24XX_nand_devtype_data {
enum s3c_cpu_type type ;
} ;
static const struct s3c24XX_nand_devtype_data s3c2410_nand_devtype_data = {
. type = TYPE_S3C2410 ,
} ;
static const struct s3c24XX_nand_devtype_data s3c2412_nand_devtype_data = {
. type = TYPE_S3C2412 ,
} ;
static const struct s3c24XX_nand_devtype_data s3c2440_nand_devtype_data = {
. type = TYPE_S3C2440 ,
} ;
2005-04-16 15:20:36 -07:00
/* conversion functions */
static struct s3c2410_nand_mtd * s3c2410_nand_mtd_toours ( struct mtd_info * mtd )
{
2015-12-10 09:00:22 +01:00
return container_of ( mtd_to_nand ( mtd ) , struct s3c2410_nand_mtd ,
chip ) ;
2005-04-16 15:20:36 -07:00
}
static struct s3c2410_nand_info * s3c2410_nand_mtd_toinfo ( struct mtd_info * mtd )
{
return s3c2410_nand_mtd_toours ( mtd ) - > info ;
}
2005-11-09 22:32:44 +00:00
static struct s3c2410_nand_info * to_nand_info ( struct platform_device * dev )
2005-04-16 15:20:36 -07:00
{
2005-11-09 22:32:44 +00:00
return platform_get_drvdata ( dev ) ;
2005-04-16 15:20:36 -07:00
}
2005-11-09 22:32:44 +00:00
static struct s3c2410_platform_nand * to_nand_plat ( struct platform_device * dev )
2005-04-16 15:20:36 -07:00
{
2013-07-30 17:18:33 +09:00
return dev_get_platdata ( & dev - > dev ) ;
2005-04-16 15:20:36 -07:00
}
2011-04-13 11:59:30 +02:00
static inline int allow_clk_suspend ( struct s3c2410_nand_info * info )
2006-06-19 09:29:38 +01:00
{
2012-07-16 16:02:25 +05:30
# ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP
return 1 ;
# else
return 0 ;
# endif
2006-06-19 09:29:38 +01:00
}
2011-04-13 11:59:30 +02:00
/**
* s3c2410_nand_clk_set_state - Enable , disable or suspend NAND clock .
* @ info : The controller instance .
* @ new_state : State to which clock should be set .
*/
static void s3c2410_nand_clk_set_state ( struct s3c2410_nand_info * info ,
enum s3c_nand_clk_state new_state )
{
if ( ! allow_clk_suspend ( info ) & & new_state = = CLOCK_SUSPEND )
return ;
if ( info - > clk_state = = CLOCK_ENABLE ) {
if ( new_state ! = CLOCK_ENABLE )
2014-06-30 22:12:16 +03:00
clk_disable_unprepare ( info - > clk ) ;
2011-04-13 11:59:30 +02:00
} else {
if ( new_state = = CLOCK_ENABLE )
2014-06-30 22:12:16 +03:00
clk_prepare_enable ( info - > clk ) ;
2011-04-13 11:59:30 +02:00
}
info - > clk_state = new_state ;
}
2005-04-16 15:20:36 -07:00
/* timing calculations */
2005-10-20 22:22:58 +01:00
# define NS_IN_KHZ 1000000
2005-04-16 15:20:36 -07:00
2009-05-30 17:18:15 +01:00
/**
* s3c_nand_calc_rate - calculate timing data .
* @ wanted : The cycle time in nanoseconds .
* @ clk : The clock rate in kHz .
* @ max : The maximum divider value .
*
* Calculate the timing value from the given parameters .
*/
2006-06-27 14:35:46 +01:00
static int s3c_nand_calc_rate ( int wanted , unsigned long clk , int max )
2005-04-16 15:20:36 -07:00
{
int result ;
2009-05-30 18:34:16 +01:00
result = DIV_ROUND_UP ( ( wanted * clk ) , NS_IN_KHZ ) ;
2005-04-16 15:20:36 -07:00
pr_debug ( " result %d from %ld, %d \n " , result , clk , wanted ) ;
if ( result > max ) {
2012-07-16 16:02:23 +05:30
pr_err ( " %d ns is too big for current clock rate %ld \n " ,
wanted , clk ) ;
2005-04-16 15:20:36 -07:00
return - 1 ;
}
if ( result < 1 )
result = 1 ;
return result ;
}
2012-07-16 16:02:26 +05:30
# define to_ns(ticks, clk) (((ticks) * NS_IN_KHZ) / (unsigned int)(clk))
2005-04-16 15:20:36 -07:00
/* controller setup */
2009-05-30 17:18:15 +01:00
/**
* s3c2410_nand_setrate - setup controller timing information .
* @ info : The controller instance .
*
* Given the information supplied by the platform , calculate and set
* the necessary timing registers in the hardware to generate the
* necessary timing cycles to the hardware .
*/
2008-07-15 11:58:31 +01:00
static int s3c2410_nand_setrate ( struct s3c2410_nand_info * info )
2005-04-16 15:20:36 -07:00
{
2008-07-15 11:58:31 +01:00
struct s3c2410_platform_nand * plat = info - > platform ;
2006-06-27 14:35:46 +01:00
int tacls_max = ( info - > cpu_type = = TYPE_S3C2412 ) ? 8 : 4 ;
2005-10-20 22:22:58 +01:00
int tacls , twrph0 , twrph1 ;
2008-07-15 11:58:31 +01:00
unsigned long clkrate = clk_get_rate ( info - > clk ) ;
treewide: Remove uninitialized_var() usage
Using uninitialized_var() is dangerous as it papers over real bugs[1]
(or can in the future), and suppresses unrelated compiler warnings
(e.g. "unused variable"). If the compiler thinks it is uninitialized,
either simply initialize the variable or make compiler changes.
In preparation for removing[2] the[3] macro[4], remove all remaining
needless uses with the following script:
git grep '\buninitialized_var\b' | cut -d: -f1 | sort -u | \
xargs perl -pi -e \
's/\buninitialized_var\(([^\)]+)\)/\1/g;
s:\s*/\* (GCC be quiet|to make compiler happy) \*/$::g;'
drivers/video/fbdev/riva/riva_hw.c was manually tweaked to avoid
pathological white-space.
No outstanding warnings were found building allmodconfig with GCC 9.3.0
for x86_64, i386, arm64, arm, powerpc, powerpc64le, s390x, mips, sparc64,
alpha, and m68k.
[1] https://lore.kernel.org/lkml/20200603174714.192027-1-glider@google.com/
[2] https://lore.kernel.org/lkml/CA+55aFw+Vbj0i=1TGqCR5vQkCzWJ0QxK6CernOU6eedsudAixw@mail.gmail.com/
[3] https://lore.kernel.org/lkml/CA+55aFwgbgqhbp1fkxvRKEpzyR5J8n1vKT1VZdz9knmPuXhOeg@mail.gmail.com/
[4] https://lore.kernel.org/lkml/CA+55aFz2500WfbKXAx8s67wrm9=yVJu65TpLgN_ybYNv0VEOKA@mail.gmail.com/
Reviewed-by: Leon Romanovsky <leonro@mellanox.com> # drivers/infiniband and mlx4/mlx5
Acked-by: Jason Gunthorpe <jgg@mellanox.com> # IB
Acked-by: Kalle Valo <kvalo@codeaurora.org> # wireless drivers
Reviewed-by: Chao Yu <yuchao0@huawei.com> # erofs
Signed-off-by: Kees Cook <keescook@chromium.org>
2020-06-03 13:09:38 -07:00
unsigned long set , cfg , mask ;
2008-07-15 11:58:31 +01:00
unsigned long flags ;
2005-04-16 15:20:36 -07:00
/* calculate the timing information for the controller */
2008-07-15 11:58:31 +01:00
info - > clk_rate = clkrate ;
2005-10-20 22:22:58 +01:00
clkrate / = 1000 ; /* turn clock into kHz for ease of use */
2005-04-16 15:20:36 -07:00
if ( plat ! = NULL ) {
2006-06-27 14:35:46 +01:00
tacls = s3c_nand_calc_rate ( plat - > tacls , clkrate , tacls_max ) ;
twrph0 = s3c_nand_calc_rate ( plat - > twrph0 , clkrate , 8 ) ;
twrph1 = s3c_nand_calc_rate ( plat - > twrph1 , clkrate , 8 ) ;
2005-04-16 15:20:36 -07:00
} else {
/* default timings */
2006-06-27 14:35:46 +01:00
tacls = tacls_max ;
2005-04-16 15:20:36 -07:00
twrph0 = 8 ;
twrph1 = 8 ;
}
2005-11-07 11:15:49 +00:00
2005-04-16 15:20:36 -07:00
if ( tacls < 0 | | twrph0 < 0 | | twrph1 < 0 ) {
2006-06-21 15:43:05 +01:00
dev_err ( info - > device , " cannot get suitable timings \n " ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
2006-06-21 15:43:05 +01:00
dev_info ( info - > device , " Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns \n " ,
2012-07-16 16:02:26 +05:30
tacls , to_ns ( tacls , clkrate ) , twrph0 , to_ns ( twrph0 , clkrate ) ,
twrph1 , to_ns ( twrph1 , clkrate ) ) ;
2005-04-16 15:20:36 -07:00
2008-07-15 11:58:31 +01:00
switch ( info - > cpu_type ) {
case TYPE_S3C2410 :
mask = ( S3C2410_NFCONF_TACLS ( 3 ) |
S3C2410_NFCONF_TWRPH0 ( 7 ) |
S3C2410_NFCONF_TWRPH1 ( 7 ) ) ;
set = S3C2410_NFCONF_EN ;
set | = S3C2410_NFCONF_TACLS ( tacls - 1 ) ;
set | = S3C2410_NFCONF_TWRPH0 ( twrph0 - 1 ) ;
set | = S3C2410_NFCONF_TWRPH1 ( twrph1 - 1 ) ;
break ;
case TYPE_S3C2440 :
case TYPE_S3C2412 :
2009-06-03 13:46:54 +02:00
mask = ( S3C2440_NFCONF_TACLS ( tacls_max - 1 ) |
S3C2440_NFCONF_TWRPH0 ( 7 ) |
S3C2440_NFCONF_TWRPH1 ( 7 ) ) ;
2008-07-15 11:58:31 +01:00
set = S3C2440_NFCONF_TACLS ( tacls - 1 ) ;
set | = S3C2440_NFCONF_TWRPH0 ( twrph0 - 1 ) ;
set | = S3C2440_NFCONF_TWRPH1 ( twrph1 - 1 ) ;
break ;
default :
BUG ( ) ;
}
local_irq_save ( flags ) ;
cfg = readl ( info - > regs + S3C2410_NFCONF ) ;
cfg & = ~ mask ;
cfg | = set ;
writel ( cfg , info - > regs + S3C2410_NFCONF ) ;
local_irq_restore ( flags ) ;
2009-05-10 15:42:02 -05:00
dev_dbg ( info - > device , " NF_CONF is 0x%lx \n " , cfg ) ;
2008-07-15 11:58:31 +01:00
return 0 ;
}
2009-05-30 17:18:15 +01:00
/**
* s3c2410_nand_inithw - basic hardware initialisation
* @ info : The hardware state .
*
* Do the basic initialisation of the hardware , using s3c2410_nand_setrate ( )
* to setup the hardware access speeds and set the controller to be enabled .
*/
2008-07-15 11:58:31 +01:00
static int s3c2410_nand_inithw ( struct s3c2410_nand_info * info )
{
int ret ;
ret = s3c2410_nand_setrate ( info ) ;
if ( ret < 0 )
return ret ;
2012-07-16 16:02:26 +05:30
switch ( info - > cpu_type ) {
case TYPE_S3C2410 :
2008-07-15 11:58:31 +01:00
default :
2006-06-27 14:35:46 +01:00
break ;
2012-07-16 16:02:26 +05:30
case TYPE_S3C2440 :
case TYPE_S3C2412 :
2006-06-19 09:29:38 +01:00
/* enable the controller and de-assert nFCE */
2006-06-27 14:35:46 +01:00
writel ( S3C2440_NFCONT_ENABLE , info - > regs + S3C2440_NFCONT ) ;
2005-06-20 12:48:25 +01:00
}
2005-04-16 15:20:36 -07:00
return 0 ;
}
2009-05-30 17:18:15 +01:00
/**
* s3c2410_nand_select_chip - select the given nand chip
2018-09-06 14:05:24 +02:00
* @ this : NAND chip object .
2009-05-30 17:18:15 +01:00
* @ chip : The chip number .
*
* This is called by the MTD layer to either select a given chip for the
* @ mtd instance , or to indicate that the access has finished and the
* chip can be de - selected .
*
* The routine ensures that the nFCE line is correctly setup , and any
* platform specific selection code is called to route nFCE to the specific
* chip .
*/
2018-09-06 14:05:24 +02:00
static void s3c2410_nand_select_chip ( struct nand_chip * this , int chip )
2005-04-16 15:20:36 -07:00
{
struct s3c2410_nand_info * info ;
2005-11-07 11:15:49 +00:00
struct s3c2410_nand_mtd * nmtd ;
2005-04-16 15:20:36 -07:00
unsigned long cur ;
2015-12-10 09:00:41 +01:00
nmtd = nand_get_controller_data ( this ) ;
2005-04-16 15:20:36 -07:00
info = nmtd - > info ;
2011-04-13 11:59:30 +02:00
if ( chip ! = - 1 )
s3c2410_nand_clk_set_state ( info , CLOCK_ENABLE ) ;
2006-06-19 09:29:38 +01:00
2006-06-27 14:35:46 +01:00
cur = readl ( info - > sel_reg ) ;
2005-04-16 15:20:36 -07:00
if ( chip = = - 1 ) {
2006-06-27 14:35:46 +01:00
cur | = info - > sel_bit ;
2005-04-16 15:20:36 -07:00
} else {
2005-07-06 21:05:10 +01:00
if ( nmtd - > set ! = NULL & & chip > nmtd - > set - > nr_chips ) {
2006-06-21 15:43:05 +01:00
dev_err ( info - > device , " invalid chip %d \n " , chip ) ;
2005-04-16 15:20:36 -07:00
return ;
}
if ( info - > platform ! = NULL ) {
if ( info - > platform - > select_chip ! = NULL )
2006-05-13 18:07:53 +01:00
( info - > platform - > select_chip ) ( nmtd - > set , chip ) ;
2005-04-16 15:20:36 -07:00
}
2006-06-27 14:35:46 +01:00
cur & = ~ info - > sel_bit ;
2005-04-16 15:20:36 -07:00
}
2006-06-27 14:35:46 +01:00
writel ( cur , info - > sel_reg ) ;
2006-06-19 09:29:38 +01:00
2011-04-13 11:59:30 +02:00
if ( chip = = - 1 )
s3c2410_nand_clk_set_state ( info , CLOCK_SUSPEND ) ;
2005-04-16 15:20:36 -07:00
}
2006-06-19 09:43:23 +01:00
/* s3c2410_nand_hwcontrol
2005-06-20 12:48:25 +01:00
*
2006-06-19 09:43:23 +01:00
* Issue command and address cycles to the chip
2005-06-20 12:48:25 +01:00
*/
2005-04-16 15:20:36 -07:00
2018-09-06 14:05:26 +02:00
static void s3c2410_nand_hwcontrol ( struct nand_chip * chip , int cmd ,
2006-06-10 00:53:16 +01:00
unsigned int ctrl )
2005-04-16 15:20:36 -07:00
{
2018-09-06 14:05:26 +02:00
struct mtd_info * mtd = nand_to_mtd ( chip ) ;
2005-04-16 15:20:36 -07:00
struct s3c2410_nand_info * info = s3c2410_nand_mtd_toinfo ( mtd ) ;
2006-11-30 08:17:38 +00:00
2006-05-23 23:25:53 +02:00
if ( cmd = = NAND_CMD_NONE )
return ;
2006-06-10 00:53:16 +01:00
if ( ctrl & NAND_CLE )
2006-05-23 23:25:53 +02:00
writeb ( cmd , info - > regs + S3C2410_NFCMD ) ;
else
writeb ( cmd , info - > regs + S3C2410_NFADDR ) ;
2005-06-20 12:48:25 +01:00
}
/* command and control functions */
2018-09-06 14:05:26 +02:00
static void s3c2440_nand_hwcontrol ( struct nand_chip * chip , int cmd ,
2006-06-10 00:53:16 +01:00
unsigned int ctrl )
2005-06-20 12:48:25 +01:00
{
2018-09-06 14:05:26 +02:00
struct mtd_info * mtd = nand_to_mtd ( chip ) ;
2005-06-20 12:48:25 +01:00
struct s3c2410_nand_info * info = s3c2410_nand_mtd_toinfo ( mtd ) ;
2005-04-16 15:20:36 -07:00
2006-05-23 23:25:53 +02:00
if ( cmd = = NAND_CMD_NONE )
return ;
2006-06-10 00:53:16 +01:00
if ( ctrl & NAND_CLE )
2006-05-23 23:25:53 +02:00
writeb ( cmd , info - > regs + S3C2440_NFCMD ) ;
else
writeb ( cmd , info - > regs + S3C2440_NFADDR ) ;
2005-04-16 15:20:36 -07:00
}
/* s3c2410_nand_devready()
*
* returns 0 if the nand is busy , 1 if it is ready
*/
2018-09-06 14:05:27 +02:00
static int s3c2410_nand_devready ( struct nand_chip * chip )
2005-04-16 15:20:36 -07:00
{
2018-09-06 14:05:27 +02:00
struct mtd_info * mtd = nand_to_mtd ( chip ) ;
2005-04-16 15:20:36 -07:00
struct s3c2410_nand_info * info = s3c2410_nand_mtd_toinfo ( mtd ) ;
return readb ( info - > regs + S3C2410_NFSTAT ) & S3C2410_NFSTAT_BUSY ;
}
2018-09-06 14:05:27 +02:00
static int s3c2440_nand_devready ( struct nand_chip * chip )
2006-06-27 14:35:46 +01:00
{
2018-09-06 14:05:27 +02:00
struct mtd_info * mtd = nand_to_mtd ( chip ) ;
2006-06-27 14:35:46 +01:00
struct s3c2410_nand_info * info = s3c2410_nand_mtd_toinfo ( mtd ) ;
return readb ( info - > regs + S3C2440_NFSTAT ) & S3C2440_NFSTAT_READY ;
}
2018-09-06 14:05:27 +02:00
static int s3c2412_nand_devready ( struct nand_chip * chip )
2006-06-27 14:35:46 +01:00
{
2018-09-06 14:05:27 +02:00
struct mtd_info * mtd = nand_to_mtd ( chip ) ;
2006-06-27 14:35:46 +01:00
struct s3c2410_nand_info * info = s3c2410_nand_mtd_toinfo ( mtd ) ;
return readb ( info - > regs + S3C2412_NFSTAT ) & S3C2412_NFSTAT_READY ;
}
2005-04-16 15:20:36 -07:00
/* ECC handling functions */
2018-09-06 14:05:19 +02:00
static int s3c2410_nand_correct_data ( struct nand_chip * chip , u_char * dat ,
2006-06-27 14:35:46 +01:00
u_char * read_ecc , u_char * calc_ecc )
2005-04-16 15:20:36 -07:00
{
2018-09-06 14:05:19 +02:00
struct mtd_info * mtd = nand_to_mtd ( chip ) ;
2007-02-02 16:59:33 +00:00
struct s3c2410_nand_info * info = s3c2410_nand_mtd_toinfo ( mtd ) ;
unsigned int diff0 , diff1 , diff2 ;
unsigned int bit , byte ;
pr_debug ( " %s(%p,%p,%p,%p) \n " , __func__ , mtd , dat , read_ecc , calc_ecc ) ;
diff0 = read_ecc [ 0 ] ^ calc_ecc [ 0 ] ;
diff1 = read_ecc [ 1 ] ^ calc_ecc [ 1 ] ;
diff2 = read_ecc [ 2 ] ^ calc_ecc [ 2 ] ;
2012-08-02 16:06:47 +03:00
pr_debug ( " %s: rd %*phN calc %*phN diff %02x%02x%02x \n " ,
__func__ , 3 , read_ecc , 3 , calc_ecc ,
2007-02-02 16:59:33 +00:00
diff0 , diff1 , diff2 ) ;
if ( diff0 = = 0 & & diff1 = = 0 & & diff2 = = 0 )
return 0 ; /* ECC is ok */
2008-04-15 11:36:20 +01:00
/* sometimes people do not think about using the ECC, so check
* to see if we have an 0xff , 0xff , 0xff read ECC and then ignore
* the error , on the assumption that this is an un - eccd page .
*/
if ( read_ecc [ 0 ] = = 0xff & & read_ecc [ 1 ] = = 0xff & & read_ecc [ 2 ] = = 0xff
& & info - > platform - > ignore_unset_ecc )
return 0 ;
2007-02-02 16:59:33 +00:00
/* Can we correct this ECC (ie, one row and column change).
* Note , this is similar to the 256 error code on smartmedia */
if ( ( ( diff0 ^ ( diff0 > > 1 ) ) & 0x55 ) = = 0x55 & &
( ( diff1 ^ ( diff1 > > 1 ) ) & 0x55 ) = = 0x55 & &
( ( diff2 ^ ( diff2 > > 1 ) ) & 0x55 ) = = 0x55 ) {
/* calculate the bit position of the error */
2007-10-18 18:02:43 -07:00
bit = ( ( diff2 > > 3 ) & 1 ) |
( ( diff2 > > 4 ) & 2 ) |
( ( diff2 > > 5 ) & 4 ) ;
2005-04-16 15:20:36 -07:00
2007-02-02 16:59:33 +00:00
/* calculate the byte position of the error */
2005-04-16 15:20:36 -07:00
2007-10-18 18:02:43 -07:00
byte = ( ( diff2 < < 7 ) & 0x100 ) |
( ( diff1 < < 0 ) & 0x80 ) |
( ( diff1 < < 1 ) & 0x40 ) |
( ( diff1 < < 2 ) & 0x20 ) |
( ( diff1 < < 3 ) & 0x10 ) |
( ( diff0 > > 4 ) & 0x08 ) |
( ( diff0 > > 3 ) & 0x04 ) |
( ( diff0 > > 2 ) & 0x02 ) |
( ( diff0 > > 1 ) & 0x01 ) ;
2007-02-02 16:59:33 +00:00
dev_dbg ( info - > device , " correcting error bit %d, byte %d \n " ,
bit , byte ) ;
dat [ byte ] ^ = ( 1 < < bit ) ;
return 1 ;
}
/* if there is only one bit difference in the ECC, then
* one of only a row or column parity has changed , which
* means the error is most probably in the ECC itself */
diff0 | = ( diff1 < < 8 ) ;
diff0 | = ( diff2 < < 16 ) ;
2016-04-12 15:30:35 +08:00
/* equal to "(diff0 & ~(1 << __ffs(diff0)))" */
if ( ( diff0 & ( diff0 - 1 ) ) = = 0 )
2007-02-02 16:59:33 +00:00
return 1 ;
2007-10-18 18:02:44 -07:00
return - 1 ;
2005-04-16 15:20:36 -07:00
}
2005-06-20 12:48:25 +01:00
/* ECC functions
*
* These allow the s3c2410 and s3c2440 to use the controller ' s ECC
* generator block to ECC the data as it passes through ]
*/
2018-09-06 14:05:17 +02:00
static void s3c2410_nand_enable_hwecc ( struct nand_chip * chip , int mode )
2005-04-16 15:20:36 -07:00
{
2018-09-06 14:05:17 +02:00
struct s3c2410_nand_info * info ;
2005-04-16 15:20:36 -07:00
unsigned long ctrl ;
2018-09-06 14:05:17 +02:00
info = s3c2410_nand_mtd_toinfo ( nand_to_mtd ( chip ) ) ;
2005-04-16 15:20:36 -07:00
ctrl = readl ( info - > regs + S3C2410_NFCONF ) ;
ctrl | = S3C2410_NFCONF_INITECC ;
writel ( ctrl , info - > regs + S3C2410_NFCONF ) ;
}
2018-09-06 14:05:17 +02:00
static void s3c2412_nand_enable_hwecc ( struct nand_chip * chip , int mode )
2007-02-13 12:30:38 +01:00
{
2018-09-06 14:05:17 +02:00
struct s3c2410_nand_info * info ;
2007-02-13 12:30:38 +01:00
unsigned long ctrl ;
2018-09-06 14:05:17 +02:00
info = s3c2410_nand_mtd_toinfo ( nand_to_mtd ( chip ) ) ;
2007-02-13 12:30:38 +01:00
ctrl = readl ( info - > regs + S3C2440_NFCONT ) ;
2012-08-21 10:21:15 +05:30
writel ( ctrl | S3C2412_NFCONT_INIT_MAIN_ECC ,
info - > regs + S3C2440_NFCONT ) ;
2007-02-13 12:30:38 +01:00
}
2018-09-06 14:05:17 +02:00
static void s3c2440_nand_enable_hwecc ( struct nand_chip * chip , int mode )
2005-06-20 12:48:25 +01:00
{
2018-09-06 14:05:17 +02:00
struct s3c2410_nand_info * info ;
2005-06-20 12:48:25 +01:00
unsigned long ctrl ;
2018-09-06 14:05:17 +02:00
info = s3c2410_nand_mtd_toinfo ( nand_to_mtd ( chip ) ) ;
2005-06-20 12:48:25 +01:00
ctrl = readl ( info - > regs + S3C2440_NFCONT ) ;
writel ( ctrl | S3C2440_NFCONT_INITECC , info - > regs + S3C2440_NFCONT ) ;
}
2018-09-06 14:05:18 +02:00
static int s3c2410_nand_calculate_ecc ( struct nand_chip * chip ,
const u_char * dat , u_char * ecc_code )
2005-04-16 15:20:36 -07:00
{
2018-09-06 14:05:18 +02:00
struct mtd_info * mtd = nand_to_mtd ( chip ) ;
2005-04-16 15:20:36 -07:00
struct s3c2410_nand_info * info = s3c2410_nand_mtd_toinfo ( mtd ) ;
ecc_code [ 0 ] = readb ( info - > regs + S3C2410_NFECC + 0 ) ;
ecc_code [ 1 ] = readb ( info - > regs + S3C2410_NFECC + 1 ) ;
ecc_code [ 2 ] = readb ( info - > regs + S3C2410_NFECC + 2 ) ;
2012-08-02 16:06:47 +03:00
pr_debug ( " %s: returning ecc %*phN \n " , __func__ , 3 , ecc_code ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2018-09-06 14:05:18 +02:00
static int s3c2412_nand_calculate_ecc ( struct nand_chip * chip ,
const u_char * dat , u_char * ecc_code )
2007-02-13 12:30:38 +01:00
{
2018-09-06 14:05:18 +02:00
struct mtd_info * mtd = nand_to_mtd ( chip ) ;
2007-02-13 12:30:38 +01:00
struct s3c2410_nand_info * info = s3c2410_nand_mtd_toinfo ( mtd ) ;
unsigned long ecc = readl ( info - > regs + S3C2412_NFMECC0 ) ;
ecc_code [ 0 ] = ecc ;
ecc_code [ 1 ] = ecc > > 8 ;
ecc_code [ 2 ] = ecc > > 16 ;
2012-08-02 16:06:47 +03:00
pr_debug ( " %s: returning ecc %*phN \n " , __func__ , 3 , ecc_code ) ;
2007-02-13 12:30:38 +01:00
return 0 ;
}
2018-09-06 14:05:18 +02:00
static int s3c2440_nand_calculate_ecc ( struct nand_chip * chip ,
const u_char * dat , u_char * ecc_code )
2005-06-20 12:48:25 +01:00
{
2018-09-06 14:05:18 +02:00
struct mtd_info * mtd = nand_to_mtd ( chip ) ;
2005-06-20 12:48:25 +01:00
struct s3c2410_nand_info * info = s3c2410_nand_mtd_toinfo ( mtd ) ;
unsigned long ecc = readl ( info - > regs + S3C2440_NFMECC0 ) ;
ecc_code [ 0 ] = ecc ;
ecc_code [ 1 ] = ecc > > 8 ;
ecc_code [ 2 ] = ecc > > 16 ;
2008-04-15 11:36:19 +01:00
pr_debug ( " %s: returning ecc %06lx \n " , __func__ , ecc & 0xffffff ) ;
2005-06-20 12:48:25 +01:00
return 0 ;
}
/* over-ride the standard functions for a little more speed. We can
* use read / write block to move the data buffers to / from the controller
*/
2005-04-16 15:20:36 -07:00
2018-09-06 14:05:22 +02:00
static void s3c2410_nand_read_buf ( struct nand_chip * this , u_char * buf , int len )
2005-04-16 15:20:36 -07:00
{
2018-09-07 00:38:34 +02:00
readsb ( this - > legacy . IO_ADDR_R , buf , len ) ;
2005-04-16 15:20:36 -07:00
}
2018-09-06 14:05:22 +02:00
static void s3c2440_nand_read_buf ( struct nand_chip * this , u_char * buf , int len )
2007-10-18 17:43:07 -07:00
{
2018-09-06 14:05:22 +02:00
struct mtd_info * mtd = nand_to_mtd ( this ) ;
2007-10-18 17:43:07 -07:00
struct s3c2410_nand_info * info = s3c2410_nand_mtd_toinfo ( mtd ) ;
2009-05-30 18:30:18 +01:00
readsl ( info - > regs + S3C2440_NFDATA , buf , len > > 2 ) ;
/* cleanup if we've got less than a word to do */
if ( len & 3 ) {
buf + = len & ~ 3 ;
for ( ; len & 3 ; len - - )
* buf + + = readb ( info - > regs + S3C2440_NFDATA ) ;
}
2007-10-18 17:43:07 -07:00
}
2018-09-06 14:05:23 +02:00
static void s3c2410_nand_write_buf ( struct nand_chip * this , const u_char * buf ,
2012-08-21 10:21:15 +05:30
int len )
2005-04-16 15:20:36 -07:00
{
2018-09-07 00:38:34 +02:00
writesb ( this - > legacy . IO_ADDR_W , buf , len ) ;
2005-04-16 15:20:36 -07:00
}
2018-09-06 14:05:23 +02:00
static void s3c2440_nand_write_buf ( struct nand_chip * this , const u_char * buf ,
2012-08-21 10:21:15 +05:30
int len )
2007-10-18 17:43:07 -07:00
{
2018-09-06 14:05:23 +02:00
struct mtd_info * mtd = nand_to_mtd ( this ) ;
2007-10-18 17:43:07 -07:00
struct s3c2410_nand_info * info = s3c2410_nand_mtd_toinfo ( mtd ) ;
2009-05-30 18:30:18 +01:00
writesl ( info - > regs + S3C2440_NFDATA , buf , len > > 2 ) ;
/* cleanup any fractional write */
if ( len & 3 ) {
buf + = len & ~ 3 ;
for ( ; len & 3 ; len - - , buf + + )
writeb ( * buf , info - > regs + S3C2440_NFDATA ) ;
}
2007-10-18 17:43:07 -07:00
}
2008-07-15 11:58:31 +01:00
/* cpufreq driver support */
2016-06-27 14:51:38 +02:00
# ifdef CONFIG_ARM_S3C24XX_CPUFREQ
2008-07-15 11:58:31 +01:00
static int s3c2410_nand_cpufreq_transition ( struct notifier_block * nb ,
unsigned long val , void * data )
{
struct s3c2410_nand_info * info ;
unsigned long newclk ;
info = container_of ( nb , struct s3c2410_nand_info , freq_transition ) ;
newclk = clk_get_rate ( info - > clk ) ;
if ( ( val = = CPUFREQ_POSTCHANGE & & newclk < info - > clk_rate ) | |
( val = = CPUFREQ_PRECHANGE & & newclk > info - > clk_rate ) ) {
s3c2410_nand_setrate ( info ) ;
}
return 0 ;
}
static inline int s3c2410_nand_cpufreq_register ( struct s3c2410_nand_info * info )
{
info - > freq_transition . notifier_call = s3c2410_nand_cpufreq_transition ;
return cpufreq_register_notifier ( & info - > freq_transition ,
CPUFREQ_TRANSITION_NOTIFIER ) ;
}
2012-08-21 10:21:15 +05:30
static inline void
s3c2410_nand_cpufreq_deregister ( struct s3c2410_nand_info * info )
2008-07-15 11:58:31 +01:00
{
cpufreq_unregister_notifier ( & info - > freq_transition ,
CPUFREQ_TRANSITION_NOTIFIER ) ;
}
# else
static inline int s3c2410_nand_cpufreq_register ( struct s3c2410_nand_info * info )
{
return 0 ;
}
2012-08-21 10:21:15 +05:30
static inline void
s3c2410_nand_cpufreq_deregister ( struct s3c2410_nand_info * info )
2008-07-15 11:58:31 +01:00
{
}
# endif
2005-04-16 15:20:36 -07:00
/* device management functions */
2009-05-30 16:55:29 +01:00
static int s3c24xx_nand_remove ( struct platform_device * pdev )
2005-04-16 15:20:36 -07:00
{
2005-11-09 22:32:44 +00:00
struct s3c2410_nand_info * info = to_nand_info ( pdev ) ;
2005-04-16 15:20:36 -07:00
2005-11-07 11:15:49 +00:00
if ( info = = NULL )
2005-04-16 15:20:36 -07:00
return 0 ;
2008-07-15 11:58:31 +01:00
s3c2410_nand_cpufreq_deregister ( info ) ;
/* Release all our mtds and their partitions, then go through
* freeing the resources used
2005-04-16 15:20:36 -07:00
*/
2005-11-07 11:15:49 +00:00
2005-04-16 15:20:36 -07:00
if ( info - > mtds ! = NULL ) {
struct s3c2410_nand_mtd * ptr = info - > mtds ;
int mtdno ;
for ( mtdno = 0 ; mtdno < info - > mtd_count ; mtdno + + , ptr + + ) {
pr_debug ( " releasing mtd %d (%p) \n " , mtdno , ptr ) ;
2020-05-19 15:00:19 +02:00
WARN_ON ( mtd_device_unregister ( nand_to_mtd ( & ptr - > chip ) ) ) ;
nand_cleanup ( & ptr - > chip ) ;
2005-04-16 15:20:36 -07:00
}
}
/* free the common resources */
2012-08-21 14:24:09 +05:30
if ( ! IS_ERR ( info - > clk ) )
2011-04-13 11:59:30 +02:00
s3c2410_nand_clk_set_state ( info , CLOCK_DISABLE ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static int s3c2410_nand_add_partition ( struct s3c2410_nand_info * info ,
struct s3c2410_nand_mtd * mtd ,
struct s3c2410_nand_set * set )
{
2012-11-16 16:08:22 +05:30
if ( set ) {
2015-12-10 09:00:22 +01:00
struct mtd_info * mtdinfo = nand_to_mtd ( & mtd - > chip ) ;
2009-05-10 15:42:09 -05:00
2015-12-10 09:00:22 +01:00
mtdinfo - > name = set - > name ;
2018-07-13 11:27:31 +02:00
return mtd_device_register ( mtdinfo , set - > partitions ,
set - > nr_partitions ) ;
2012-11-16 16:08:22 +05:30
}
return - ENODEV ;
2005-04-16 15:20:36 -07:00
}
2020-05-29 13:13:13 +02:00
static int s3c2410_nand_setup_interface ( struct nand_chip * chip , int csline ,
const struct nand_interface_config * conf )
2016-10-26 21:59:55 -02:00
{
2018-09-06 14:05:33 +02:00
struct mtd_info * mtd = nand_to_mtd ( chip ) ;
2016-10-26 21:59:55 -02:00
struct s3c2410_nand_info * info = s3c2410_nand_mtd_toinfo ( mtd ) ;
struct s3c2410_platform_nand * pdata = info - > platform ;
const struct nand_sdr_timings * timings ;
int tacls ;
timings = nand_get_sdr_timings ( conf ) ;
if ( IS_ERR ( timings ) )
return - ENOTSUPP ;
tacls = timings - > tCLS_min - timings - > tWP_min ;
if ( tacls < 0 )
tacls = 0 ;
pdata - > tacls = DIV_ROUND_UP ( tacls , 1000 ) ;
pdata - > twrph0 = DIV_ROUND_UP ( timings - > tWP_min , 1000 ) ;
pdata - > twrph1 = DIV_ROUND_UP ( timings - > tCLH_min , 1000 ) ;
return s3c2410_nand_setrate ( info ) ;
}
2009-05-30 17:18:15 +01:00
/**
* s3c2410_nand_init_chip - initialise a single instance of an chip
* @ info : The base NAND controller the chip is on .
* @ nmtd : The new controller MTD instance to fill in .
* @ set : The information passed from the board specific platform data .
2005-04-16 15:20:36 -07:00
*
2009-05-30 17:18:15 +01:00
* Initialise the given @ nmtd from the information in @ info and @ set . This
* readies the structure for use with the MTD layer functions by ensuring
* all pointers are setup and the necessary control routines selected .
*/
2005-04-16 15:20:36 -07:00
static void s3c2410_nand_init_chip ( struct s3c2410_nand_info * info ,
struct s3c2410_nand_mtd * nmtd ,
struct s3c2410_nand_set * set )
{
2016-10-26 21:59:55 -02:00
struct device_node * np = info - > device - > of_node ;
2005-04-16 15:20:36 -07:00
struct nand_chip * chip = & nmtd - > chip ;
2006-06-27 14:35:46 +01:00
void __iomem * regs = info - > regs ;
2005-04-16 15:20:36 -07:00
2016-10-26 21:59:55 -02:00
nand_set_flash_node ( chip , set - > of_node ) ;
2018-09-07 00:38:35 +02:00
chip - > legacy . write_buf = s3c2410_nand_write_buf ;
chip - > legacy . read_buf = s3c2410_nand_read_buf ;
2018-11-11 08:55:22 +01:00
chip - > legacy . select_chip = s3c2410_nand_select_chip ;
2018-09-07 00:38:41 +02:00
chip - > legacy . chip_delay = 50 ;
2015-12-10 09:00:41 +01:00
nand_set_controller_data ( chip , nmtd ) ;
2009-11-02 18:12:51 +00:00
chip - > options = set - > options ;
2005-04-16 15:20:36 -07:00
chip - > controller = & info - > controller ;
2016-10-26 21:59:55 -02:00
/*
* let ' s keep behavior unchanged for legacy boards booting via pdata and
* auto - detect timings only when booting with a device tree .
*/
2018-11-11 08:55:24 +01:00
if ( ! np )
chip - > options | = NAND_KEEP_TIMINGS ;
2016-10-26 21:59:55 -02:00
2006-06-27 14:35:46 +01:00
switch ( info - > cpu_type ) {
case TYPE_S3C2410 :
2018-09-07 00:38:34 +02:00
chip - > legacy . IO_ADDR_W = regs + S3C2410_NFDATA ;
2006-06-27 14:35:46 +01:00
info - > sel_reg = regs + S3C2410_NFCONF ;
info - > sel_bit = S3C2410_NFCONF_nFCE ;
2018-09-07 00:38:36 +02:00
chip - > legacy . cmd_ctrl = s3c2410_nand_hwcontrol ;
2018-09-07 00:38:37 +02:00
chip - > legacy . dev_ready = s3c2410_nand_devready ;
2006-06-27 14:35:46 +01:00
break ;
case TYPE_S3C2440 :
2018-09-07 00:38:34 +02:00
chip - > legacy . IO_ADDR_W = regs + S3C2440_NFDATA ;
2006-06-27 14:35:46 +01:00
info - > sel_reg = regs + S3C2440_NFCONT ;
info - > sel_bit = S3C2440_NFCONT_nFCE ;
2018-09-07 00:38:36 +02:00
chip - > legacy . cmd_ctrl = s3c2440_nand_hwcontrol ;
2018-09-07 00:38:37 +02:00
chip - > legacy . dev_ready = s3c2440_nand_devready ;
2018-09-07 00:38:35 +02:00
chip - > legacy . read_buf = s3c2440_nand_read_buf ;
chip - > legacy . write_buf = s3c2440_nand_write_buf ;
2006-06-27 14:35:46 +01:00
break ;
case TYPE_S3C2412 :
2018-09-07 00:38:34 +02:00
chip - > legacy . IO_ADDR_W = regs + S3C2440_NFDATA ;
2006-06-27 14:35:46 +01:00
info - > sel_reg = regs + S3C2440_NFCONT ;
info - > sel_bit = S3C2412_NFCONT_nFCE0 ;
2018-09-07 00:38:36 +02:00
chip - > legacy . cmd_ctrl = s3c2440_nand_hwcontrol ;
2018-09-07 00:38:37 +02:00
chip - > legacy . dev_ready = s3c2412_nand_devready ;
2006-06-27 14:35:46 +01:00
if ( readl ( regs + S3C2410_NFCONF ) & S3C2412_NFCONF_NANDBOOT )
dev_info ( info - > device , " System booted from NAND \n " ) ;
break ;
2012-07-16 16:02:26 +05:30
}
2006-06-27 14:35:46 +01:00
2018-09-07 00:38:34 +02:00
chip - > legacy . IO_ADDR_R = chip - > legacy . IO_ADDR_W ;
2005-06-20 12:48:25 +01:00
2005-04-16 15:20:36 -07:00
nmtd - > info = info ;
nmtd - > set = set ;
2020-08-27 10:51:58 +02:00
chip - > ecc . engine_type = info - > platform - > engine_type ;
2009-05-13 16:54:14 +01:00
2016-10-26 21:59:55 -02:00
/*
* If you use u - boot BBT creation code , specifying this flag will
* let the kernel fish out the BBT from the NAND .
*/
if ( set - > flash_bbt )
2011-05-31 16:31:23 -07:00
chip - > bbt_options | = NAND_BBT_USE_FLASH ;
2005-04-16 15:20:36 -07:00
}
2009-05-30 17:18:15 +01:00
/**
2018-07-20 17:15:10 +02:00
* s3c2410_nand_attach_chip - Init the ECC engine after NAND scan
* @ chip : The NAND chip
2008-04-15 11:36:19 +01:00
*
2018-07-20 17:15:10 +02:00
* This hook is called by the core after the identification of the NAND chip ,
* once the relevant per - chip information is up to date . . This call ensure that
2009-05-30 17:18:15 +01:00
* we update the internal state accordingly .
*
* The internal state is currently limited to the ECC state information .
*/
2018-07-20 17:15:10 +02:00
static int s3c2410_nand_attach_chip ( struct nand_chip * chip )
2008-04-15 11:36:19 +01:00
{
2018-07-20 17:15:10 +02:00
struct mtd_info * mtd = nand_to_mtd ( chip ) ;
struct s3c2410_nand_info * info = s3c2410_nand_mtd_toinfo ( mtd ) ;
2008-04-15 11:36:19 +01:00
2020-08-27 10:51:58 +02:00
switch ( chip - > ecc . engine_type ) {
2008-04-15 11:36:19 +01:00
2020-08-27 10:51:58 +02:00
case NAND_ECC_ENGINE_TYPE_NONE :
2016-10-20 19:42:44 -02:00
dev_info ( info - > device , " ECC disabled \n " ) ;
break ;
2020-08-27 10:51:58 +02:00
case NAND_ECC_ENGINE_TYPE_SOFT :
2016-10-20 19:42:44 -02:00
/*
2020-08-27 10:51:58 +02:00
* This driver expects Hamming based ECC when engine_type is set
* to NAND_ECC_ENGINE_TYPE_SOFT . Force ecc . algo to
* NAND_ECC_ALGO_HAMMING to avoid adding an extra ecc_algo field
* to s3c2410_platform_nand .
2016-10-20 19:42:44 -02:00
*/
2020-08-27 10:51:50 +02:00
chip - > ecc . algo = NAND_ECC_ALGO_HAMMING ;
2016-10-20 19:42:44 -02:00
dev_info ( info - > device , " soft ECC \n " ) ;
break ;
2020-08-27 10:51:58 +02:00
case NAND_ECC_ENGINE_TYPE_ON_HOST :
2016-10-20 19:42:44 -02:00
chip - > ecc . calculate = s3c2410_nand_calculate_ecc ;
chip - > ecc . correct = s3c2410_nand_correct_data ;
chip - > ecc . strength = 1 ;
switch ( info - > cpu_type ) {
case TYPE_S3C2410 :
chip - > ecc . hwctl = s3c2410_nand_enable_hwecc ;
chip - > ecc . calculate = s3c2410_nand_calculate_ecc ;
break ;
case TYPE_S3C2412 :
chip - > ecc . hwctl = s3c2412_nand_enable_hwecc ;
chip - > ecc . calculate = s3c2412_nand_calculate_ecc ;
break ;
case TYPE_S3C2440 :
chip - > ecc . hwctl = s3c2440_nand_enable_hwecc ;
chip - > ecc . calculate = s3c2440_nand_calculate_ecc ;
break ;
}
dev_dbg ( info - > device , " chip %p => page shift %d \n " ,
chip , chip - > page_shift ) ;
2009-05-10 15:41:25 -05:00
2012-09-19 21:48:00 -04:00
/* change the behaviour depending on whether we are using
2008-04-15 11:36:19 +01:00
* the large or small page nand device */
2016-10-20 19:42:44 -02:00
if ( chip - > page_shift > 10 ) {
chip - > ecc . size = 256 ;
chip - > ecc . bytes = 3 ;
} else {
chip - > ecc . size = 512 ;
chip - > ecc . bytes = 3 ;
mtd_set_ooblayout ( nand_to_mtd ( chip ) ,
& s3c2410_ooblayout_ops ) ;
}
2008-04-15 11:36:19 +01:00
2016-10-20 19:42:44 -02:00
dev_info ( info - > device , " hardware ECC \n " ) ;
break ;
default :
dev_err ( info - > device , " invalid ECC mode! \n " ) ;
return - EINVAL ;
2008-04-15 11:36:19 +01:00
}
2016-10-20 19:42:44 -02:00
2016-10-26 21:59:55 -02:00
if ( chip - > bbt_options & NAND_BBT_USE_FLASH )
chip - > options | = NAND_SKIP_BBTSCAN ;
return 0 ;
}
2018-07-20 17:15:10 +02:00
static const struct nand_controller_ops s3c24xx_nand_controller_ops = {
. attach_chip = s3c2410_nand_attach_chip ,
2020-05-29 13:13:13 +02:00
. setup_interface = s3c2410_nand_setup_interface ,
2018-07-20 17:15:10 +02:00
} ;
2016-10-26 21:59:55 -02:00
static const struct of_device_id s3c24xx_nand_dt_ids [ ] = {
{
. compatible = " samsung,s3c2410-nand " ,
. data = & s3c2410_nand_devtype_data ,
} , {
/* also compatible with s3c6400 */
. compatible = " samsung,s3c2412-nand " ,
. data = & s3c2412_nand_devtype_data ,
} , {
. compatible = " samsung,s3c2440-nand " ,
. data = & s3c2440_nand_devtype_data ,
} ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , s3c24xx_nand_dt_ids ) ;
static int s3c24xx_nand_probe_dt ( struct platform_device * pdev )
{
const struct s3c24XX_nand_devtype_data * devtype_data ;
struct s3c2410_platform_nand * pdata ;
struct s3c2410_nand_info * info = platform_get_drvdata ( pdev ) ;
struct device_node * np = pdev - > dev . of_node , * child ;
struct s3c2410_nand_set * sets ;
devtype_data = of_device_get_match_data ( & pdev - > dev ) ;
if ( ! devtype_data )
return - ENODEV ;
info - > cpu_type = devtype_data - > type ;
pdata = devm_kzalloc ( & pdev - > dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return - ENOMEM ;
pdev - > dev . platform_data = pdata ;
pdata - > nr_sets = of_get_child_count ( np ) ;
if ( ! pdata - > nr_sets )
return 0 ;
treewide: devm_kzalloc() -> devm_kcalloc()
The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc().
This patch replaces cases of:
devm_kzalloc(handle, a * b, gfp)
with:
devm_kcalloc(handle, a * b, gfp)
as well as handling cases of:
devm_kzalloc(handle, a * b * c, gfp)
with:
devm_kzalloc(handle, array3_size(a, b, c), gfp)
as it's slightly less ugly than:
devm_kcalloc(handle, array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
devm_kzalloc(handle, 4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
Some manual whitespace fixes were needed in this patch, as Coccinelle
really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...".
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
expression HANDLE;
type TYPE;
expression THING, E;
@@
(
devm_kzalloc(HANDLE,
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
devm_kzalloc(HANDLE,
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression HANDLE;
expression COUNT;
typedef u8;
typedef __u8;
@@
(
devm_kzalloc(HANDLE,
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
expression HANDLE;
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
expression HANDLE;
identifier SIZE, COUNT;
@@
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression HANDLE;
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression HANDLE;
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
expression HANDLE;
identifier STRIDE, SIZE, COUNT;
@@
(
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression HANDLE;
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE,
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression HANDLE;
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, sizeof(THING) * C2, ...)
|
devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...)
|
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE, C1 * C2, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * E2
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * (E2)
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-12 14:07:58 -07:00
sets = devm_kcalloc ( & pdev - > dev , pdata - > nr_sets , sizeof ( * sets ) ,
2016-10-26 21:59:55 -02:00
GFP_KERNEL ) ;
if ( ! sets )
return - ENOMEM ;
pdata - > sets = sets ;
for_each_available_child_of_node ( np , child ) {
sets - > name = ( char * ) child - > name ;
sets - > of_node = child ;
sets - > nr_chips = 1 ;
of_node_get ( child ) ;
sets + + ;
}
return 0 ;
}
static int s3c24xx_nand_probe_pdata ( struct platform_device * pdev )
{
struct s3c2410_nand_info * info = platform_get_drvdata ( pdev ) ;
info - > cpu_type = platform_get_device_id ( pdev ) - > driver_data ;
2016-10-20 19:42:44 -02:00
return 0 ;
2008-04-15 11:36:19 +01:00
}
2009-05-30 16:55:29 +01:00
/* s3c24xx_nand_probe
2005-04-16 15:20:36 -07:00
*
* called by device layer when it finds a device matching
* one our driver can handled . This code checks to see if
* it can allocate all necessary resources then calls the
* nand layer to look for devices
*/
2009-05-30 16:55:29 +01:00
static int s3c24xx_nand_probe ( struct platform_device * pdev )
2005-04-16 15:20:36 -07:00
{
2016-10-26 21:59:55 -02:00
struct s3c2410_platform_nand * plat ;
2005-04-16 15:20:36 -07:00
struct s3c2410_nand_info * info ;
struct s3c2410_nand_mtd * nmtd ;
struct s3c2410_nand_set * sets ;
struct resource * res ;
int err = 0 ;
int size ;
int nr_sets ;
int setno ;
2012-08-21 14:24:09 +05:30
info = devm_kzalloc ( & pdev - > dev , sizeof ( * info ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( info = = NULL ) {
err = - ENOMEM ;
goto exit_error ;
}
2005-11-09 22:32:44 +00:00
platform_set_drvdata ( pdev , info ) ;
2005-04-16 15:20:36 -07:00
2018-07-17 09:08:02 +02:00
nand_controller_init ( & info - > controller ) ;
2018-07-20 17:15:10 +02:00
info - > controller . ops = & s3c24xx_nand_controller_ops ;
2005-04-16 15:20:36 -07:00
/* get the clock source and enable it */
2012-08-21 14:24:09 +05:30
info - > clk = devm_clk_get ( & pdev - > dev , " nand " ) ;
2005-04-16 15:20:36 -07:00
if ( IS_ERR ( info - > clk ) ) {
2007-10-18 03:06:30 -07:00
dev_err ( & pdev - > dev , " failed to get clock \n " ) ;
2005-04-16 15:20:36 -07:00
err = - ENOENT ;
goto exit_error ;
}
2011-04-13 11:59:30 +02:00
s3c2410_nand_clk_set_state ( info , CLOCK_ENABLE ) ;
2005-04-16 15:20:36 -07:00
2016-10-26 21:59:55 -02:00
if ( pdev - > dev . of_node )
err = s3c24xx_nand_probe_dt ( pdev ) ;
else
err = s3c24xx_nand_probe_pdata ( pdev ) ;
if ( err )
goto exit_error ;
plat = to_nand_plat ( pdev ) ;
2005-04-16 15:20:36 -07:00
/* allocate and map the resource */
2005-06-20 12:48:25 +01:00
/* currently we assume we have the one resource */
2012-08-21 14:24:09 +05:30
res = pdev - > resource ;
2009-12-14 16:56:22 -05:00
size = resource_size ( res ) ;
2005-04-16 15:20:36 -07:00
2012-08-21 14:24:09 +05:30
info - > device = & pdev - > dev ;
info - > platform = plat ;
2005-04-16 15:20:36 -07:00
2013-01-21 11:09:12 +01:00
info - > regs = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( info - > regs ) ) {
err = PTR_ERR ( info - > regs ) ;
2005-04-16 15:20:36 -07:00
goto exit_error ;
2005-11-07 11:15:49 +00:00
}
2005-04-16 15:20:36 -07:00
2005-11-09 22:32:44 +00:00
dev_dbg ( & pdev - > dev , " mapped registers at %p \n " , info - > regs ) ;
2005-04-16 15:20:36 -07:00
2018-07-19 22:53:50 +02:00
if ( ! plat - > sets | | plat - > nr_sets < 1 ) {
err = - EINVAL ;
goto exit_error ;
}
sets = plat - > sets ;
nr_sets = plat - > nr_sets ;
2005-04-16 15:20:36 -07:00
info - > mtd_count = nr_sets ;
/* allocate our information */
size = nr_sets * sizeof ( * info - > mtds ) ;
2012-08-21 14:24:09 +05:30
info - > mtds = devm_kzalloc ( & pdev - > dev , size , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( info - > mtds = = NULL ) {
err = - ENOMEM ;
goto exit_error ;
}
/* initialise all possible chips */
nmtd = info - > mtds ;
2018-07-19 22:53:50 +02:00
for ( setno = 0 ; setno < nr_sets ; setno + + , nmtd + + , sets + + ) {
2015-12-10 09:00:22 +01:00
struct mtd_info * mtd = nand_to_mtd ( & nmtd - > chip ) ;
2012-08-21 10:21:15 +05:30
pr_debug ( " initialising set %d (%p, info %p) \n " ,
setno , nmtd , info ) ;
2005-11-07 11:15:49 +00:00
2015-12-10 09:00:22 +01:00
mtd - > dev . parent = & pdev - > dev ;
2005-04-16 15:20:36 -07:00
s3c2410_nand_init_chip ( info , nmtd , sets ) ;
2018-09-06 14:05:14 +02:00
err = nand_scan ( & nmtd - > chip , sets ? sets - > nr_chips : 1 ) ;
2018-03-21 14:01:57 +01:00
if ( err )
goto exit_error ;
s3c2410_nand_add_partition ( info , nmtd , sets ) ;
2005-04-16 15:20:36 -07:00
}
2005-11-07 11:15:49 +00:00
2016-10-26 21:59:55 -02:00
/* initialise the hardware */
err = s3c2410_nand_inithw ( info ) ;
if ( err ! = 0 )
goto exit_error ;
2008-07-15 11:58:31 +01:00
err = s3c2410_nand_cpufreq_register ( info ) ;
if ( err < 0 ) {
dev_err ( & pdev - > dev , " failed to init cpufreq support \n " ) ;
goto exit_error ;
}
2011-04-13 11:59:30 +02:00
if ( allow_clk_suspend ( info ) ) {
2006-06-19 09:29:38 +01:00
dev_info ( & pdev - > dev , " clock idle support enabled \n " ) ;
2011-04-13 11:59:30 +02:00
s3c2410_nand_clk_set_state ( info , CLOCK_SUSPEND ) ;
2006-06-19 09:29:38 +01:00
}
2005-04-16 15:20:36 -07:00
return 0 ;
exit_error :
2009-05-30 16:55:29 +01:00
s3c24xx_nand_remove ( pdev ) ;
2005-04-16 15:20:36 -07:00
if ( err = = 0 )
err = - EINVAL ;
return err ;
}
2006-06-19 09:29:38 +01:00
/* PM Support */
# ifdef CONFIG_PM
static int s3c24xx_nand_suspend ( struct platform_device * dev , pm_message_t pm )
{
struct s3c2410_nand_info * info = platform_get_drvdata ( dev ) ;
if ( info ) {
2008-04-15 11:36:18 +01:00
info - > save_sel = readl ( info - > sel_reg ) ;
2007-11-19 23:28:07 +00:00
/* For the moment, we must ensure nFCE is high during
* the time we are suspended . This really should be
* handled by suspending the MTDs we are using , but
* that is currently not the case . */
2008-04-15 11:36:18 +01:00
writel ( info - > save_sel | info - > sel_bit , info - > sel_reg ) ;
2007-11-19 23:28:07 +00:00
2011-04-13 11:59:30 +02:00
s3c2410_nand_clk_set_state ( info , CLOCK_DISABLE ) ;
2006-06-19 09:29:38 +01:00
}
return 0 ;
}
static int s3c24xx_nand_resume ( struct platform_device * dev )
{
struct s3c2410_nand_info * info = platform_get_drvdata ( dev ) ;
2008-04-15 11:36:18 +01:00
unsigned long sel ;
2006-06-19 09:29:38 +01:00
if ( info ) {
2011-04-13 11:59:30 +02:00
s3c2410_nand_clk_set_state ( info , CLOCK_ENABLE ) ;
2008-07-15 11:58:31 +01:00
s3c2410_nand_inithw ( info ) ;
2006-06-19 09:29:38 +01:00
2007-11-19 23:28:07 +00:00
/* Restore the state of the nFCE line. */
2008-04-15 11:36:18 +01:00
sel = readl ( info - > sel_reg ) ;
sel & = ~ info - > sel_bit ;
sel | = info - > save_sel & info - > sel_bit ;
writel ( sel , info - > sel_reg ) ;
2007-11-19 23:28:07 +00:00
2011-04-13 11:59:30 +02:00
s3c2410_nand_clk_set_state ( info , CLOCK_SUSPEND ) ;
2006-06-19 09:29:38 +01:00
}
return 0 ;
}
# else
# define s3c24xx_nand_suspend NULL
# define s3c24xx_nand_resume NULL
# endif
2005-06-20 12:48:25 +01:00
/* driver device registration */
2015-05-02 00:50:02 +09:00
static const struct platform_device_id s3c24xx_driver_ids [ ] = {
2009-05-30 16:55:29 +01:00
{
. name = " s3c2410-nand " ,
. driver_data = TYPE_S3C2410 ,
} , {
. name = " s3c2440-nand " ,
. driver_data = TYPE_S3C2440 ,
} , {
. name = " s3c2412-nand " ,
. driver_data = TYPE_S3C2412 ,
2009-06-07 06:04:23 -07:00
} , {
. name = " s3c6400-nand " ,
. driver_data = TYPE_S3C2412 , /* compatible with 2412 */
2005-11-09 22:32:44 +00:00
} ,
2009-05-30 16:55:29 +01:00
{ }
2005-04-16 15:20:36 -07:00
} ;
2009-05-30 16:55:29 +01:00
MODULE_DEVICE_TABLE ( platform , s3c24xx_driver_ids ) ;
2005-06-20 12:48:25 +01:00
2009-05-30 16:55:29 +01:00
static struct platform_driver s3c24xx_nand_driver = {
. probe = s3c24xx_nand_probe ,
. remove = s3c24xx_nand_remove ,
2006-06-27 14:35:46 +01:00
. suspend = s3c24xx_nand_suspend ,
. resume = s3c24xx_nand_resume ,
2009-05-30 16:55:29 +01:00
. id_table = s3c24xx_driver_ids ,
2006-06-27 14:35:46 +01:00
. driver = {
2009-05-30 16:55:29 +01:00
. name = " s3c24xx-nand " ,
2016-10-26 21:59:55 -02:00
. of_match_table = s3c24xx_nand_dt_ids ,
2006-06-27 14:35:46 +01:00
} ,
} ;
2012-07-16 16:02:22 +05:30
module_platform_driver ( s3c24xx_nand_driver ) ;
2005-04-16 15:20:36 -07:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Ben Dooks <ben@simtec.co.uk> " ) ;
2005-06-20 12:48:25 +01:00
MODULE_DESCRIPTION ( " S3C24XX MTD NAND driver " ) ;