2006-10-19 20:24:35 +04:00
/*
* Copyright ( C ) 2003 Rick Bronson
*
* Derived from drivers / mtd / nand / autcpu12 . c
* Copyright ( c ) 2001 Thomas Gleixner ( gleixner @ autronix . de )
*
* Derived from drivers / mtd / spia . c
* Copyright ( C ) 2000 Steven J . Hill ( sjhill @ cotw . com )
*
2008-04-23 21:51:14 +04:00
*
* Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
* Richard Genoud ( richard . genoud @ gmail . com ) , Adeneo Copyright ( C ) 2007
*
* Derived from Das U - Boot source code
* ( u - boot - 1.1 .5 / board / atmel / at91sam9263ek / nand . c )
* ( C ) Copyright 2006 ATMEL Rousset , Lacressonniere Nicolas
*
*
2006-10-19 20:24:35 +04:00
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
*/
# include <linux/slab.h>
# include <linux/module.h>
2009-05-27 19:19:39 +04:00
# include <linux/moduleparam.h>
2006-10-19 20:24:35 +04:00
# include <linux/platform_device.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/nand.h>
# include <linux/mtd/partitions.h>
2008-06-07 11:49:00 +04:00
# include <linux/gpio.h>
# include <linux/io.h>
2006-10-19 20:24:35 +04:00
2008-08-05 19:14:15 +04:00
# include <mach/board.h>
# include <mach/cpu.h>
2006-10-19 20:24:35 +04:00
2008-06-06 20:04:52 +04:00
# ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW
2008-04-23 21:51:14 +04:00
# define hard_ecc 1
# else
# define hard_ecc 0
# endif
2008-06-06 20:04:52 +04:00
# ifdef CONFIG_MTD_NAND_ATMEL_ECC_NONE
2008-04-23 21:51:14 +04:00
# define no_ecc 1
# else
# define no_ecc 0
# endif
2009-05-27 19:19:39 +04:00
static int on_flash_bbt = 0 ;
module_param ( on_flash_bbt , int , 0 ) ;
2008-04-23 21:51:14 +04:00
/* Register access macros */
# define ecc_readl(add, reg) \
2008-06-06 20:04:53 +04:00
__raw_readl ( add + ATMEL_ECC_ # # reg )
2008-04-23 21:51:14 +04:00
# define ecc_writel(add, reg, value) \
2008-06-06 20:04:53 +04:00
__raw_writel ( ( value ) , add + ATMEL_ECC_ # # reg )
2008-04-23 21:51:14 +04:00
2008-06-06 20:04:52 +04:00
# include "atmel_nand_ecc.h" /* Hardware ECC registers */
2008-04-23 21:51:14 +04:00
/* oob layout for large page size
* bad block info is on bytes 0 and 1
* the bytes have to be consecutives to avoid
* several NAND_CMD_RNDOUT during read
*/
2008-06-06 20:04:53 +04:00
static struct nand_ecclayout atmel_oobinfo_large = {
2008-04-23 21:51:14 +04:00
. eccbytes = 4 ,
. eccpos = { 60 , 61 , 62 , 63 } ,
. oobfree = {
{ 2 , 58 }
} ,
} ;
/* oob layout for small page size
* bad block info is on bytes 4 and 5
* the bytes have to be consecutives to avoid
* several NAND_CMD_RNDOUT during read
*/
2008-06-06 20:04:53 +04:00
static struct nand_ecclayout atmel_oobinfo_small = {
2008-04-23 21:51:14 +04:00
. eccbytes = 4 ,
. eccpos = { 0 , 1 , 2 , 3 } ,
. oobfree = {
{ 6 , 10 }
} ,
} ;
2008-06-06 20:04:53 +04:00
struct atmel_nand_host {
2006-10-19 20:24:35 +04:00
struct nand_chip nand_chip ;
struct mtd_info mtd ;
void __iomem * io_base ;
2008-06-06 20:04:53 +04:00
struct atmel_nand_data * board ;
2008-04-23 21:51:14 +04:00
struct device * dev ;
void __iomem * ecc ;
2006-10-19 20:24:35 +04:00
} ;
2008-04-26 20:51:12 +04:00
/*
* Enable NAND .
*/
2008-06-06 20:04:53 +04:00
static void atmel_nand_enable ( struct atmel_nand_host * host )
2008-04-26 20:51:12 +04:00
{
if ( host - > board - > enable_pin )
2008-06-06 20:04:51 +04:00
gpio_set_value ( host - > board - > enable_pin , 0 ) ;
2008-04-26 20:51:12 +04:00
}
/*
* Disable NAND .
*/
2008-06-06 20:04:53 +04:00
static void atmel_nand_disable ( struct atmel_nand_host * host )
2008-04-26 20:51:12 +04:00
{
if ( host - > board - > enable_pin )
2008-06-06 20:04:51 +04:00
gpio_set_value ( host - > board - > enable_pin , 1 ) ;
2008-04-26 20:51:12 +04:00
}
2006-10-19 20:24:35 +04:00
/*
* Hardware specific access to control - lines
*/
2008-06-06 20:04:53 +04:00
static void atmel_nand_cmd_ctrl ( struct mtd_info * mtd , int cmd , unsigned int ctrl )
2006-10-19 20:24:35 +04:00
{
struct nand_chip * nand_chip = mtd - > priv ;
2008-06-06 20:04:53 +04:00
struct atmel_nand_host * host = nand_chip - > priv ;
2006-10-19 20:24:35 +04:00
2008-04-26 20:51:12 +04:00
if ( ctrl & NAND_CTRL_CHANGE ) {
2008-04-24 18:51:29 +04:00
if ( ctrl & NAND_NCE )
2008-06-06 20:04:53 +04:00
atmel_nand_enable ( host ) ;
2008-04-24 18:51:29 +04:00
else
2008-06-06 20:04:53 +04:00
atmel_nand_disable ( host ) ;
2008-04-24 18:51:29 +04:00
}
2006-10-19 20:24:35 +04:00
if ( cmd = = NAND_CMD_NONE )
return ;
if ( ctrl & NAND_CLE )
writeb ( cmd , host - > io_base + ( 1 < < host - > board - > cle ) ) ;
else
writeb ( cmd , host - > io_base + ( 1 < < host - > board - > ale ) ) ;
}
/*
* Read the Device Ready pin .
*/
2008-06-06 20:04:53 +04:00
static int atmel_nand_device_ready ( struct mtd_info * mtd )
2006-10-19 20:24:35 +04:00
{
struct nand_chip * nand_chip = mtd - > priv ;
2008-06-06 20:04:53 +04:00
struct atmel_nand_host * host = nand_chip - > priv ;
2006-10-19 20:24:35 +04:00
2009-02-16 23:21:47 +03:00
return gpio_get_value ( host - > board - > rdy_pin ) ^
! ! host - > board - > rdy_pin_active_low ;
2006-10-19 20:24:35 +04:00
}
2008-07-04 10:40:16 +04:00
/*
* Minimal - overhead PIO for data access .
*/
static void atmel_read_buf ( struct mtd_info * mtd , u8 * buf , int len )
{
struct nand_chip * nand_chip = mtd - > priv ;
__raw_readsb ( nand_chip - > IO_ADDR_R , buf , len ) ;
}
static void atmel_read_buf16 ( struct mtd_info * mtd , u8 * buf , int len )
{
struct nand_chip * nand_chip = mtd - > priv ;
__raw_readsw ( nand_chip - > IO_ADDR_R , buf , len / 2 ) ;
}
static void atmel_write_buf ( struct mtd_info * mtd , const u8 * buf , int len )
{
struct nand_chip * nand_chip = mtd - > priv ;
__raw_writesb ( nand_chip - > IO_ADDR_W , buf , len ) ;
}
static void atmel_write_buf16 ( struct mtd_info * mtd , const u8 * buf , int len )
{
struct nand_chip * nand_chip = mtd - > priv ;
__raw_writesw ( nand_chip - > IO_ADDR_W , buf , len / 2 ) ;
}
2008-04-23 21:51:14 +04:00
/*
* Calculate HW ECC
*
* function called after a write
*
* mtd : MTD block structure
* dat : raw data ( unused )
* ecc_code : buffer for ECC
*/
2008-06-06 20:04:53 +04:00
static int atmel_nand_calculate ( struct mtd_info * mtd ,
2008-04-23 21:51:14 +04:00
const u_char * dat , unsigned char * ecc_code )
{
struct nand_chip * nand_chip = mtd - > priv ;
2008-06-06 20:04:53 +04:00
struct atmel_nand_host * host = nand_chip - > priv ;
2008-04-23 21:51:14 +04:00
unsigned int ecc_value ;
/* get the first 2 ECC bytes */
2008-04-25 11:32:26 +04:00
ecc_value = ecc_readl ( host - > ecc , PR ) ;
2008-04-23 21:51:14 +04:00
2008-10-12 10:42:28 +04:00
ecc_code [ 0 ] = ecc_value & 0xFF ;
ecc_code [ 1 ] = ( ecc_value > > 8 ) & 0xFF ;
2008-04-23 21:51:14 +04:00
/* get the last 2 ECC bytes */
2008-06-06 20:04:53 +04:00
ecc_value = ecc_readl ( host - > ecc , NPR ) & ATMEL_ECC_NPARITY ;
2008-04-23 21:51:14 +04:00
2008-10-12 10:42:28 +04:00
ecc_code [ 2 ] = ecc_value & 0xFF ;
ecc_code [ 3 ] = ( ecc_value > > 8 ) & 0xFF ;
2008-04-23 21:51:14 +04:00
return 0 ;
}
/*
* HW ECC read page function
*
* mtd : mtd info structure
* chip : nand chip info structure
* buf : buffer to store read data
*/
2008-06-06 20:04:53 +04:00
static int atmel_nand_read_page ( struct mtd_info * mtd ,
2009-09-18 23:51:46 +04:00
struct nand_chip * chip , uint8_t * buf , int page )
2008-04-23 21:51:14 +04:00
{
int eccsize = chip - > ecc . size ;
int eccbytes = chip - > ecc . bytes ;
uint32_t * eccpos = chip - > ecc . layout - > eccpos ;
uint8_t * p = buf ;
uint8_t * oob = chip - > oob_poi ;
uint8_t * ecc_pos ;
int stat ;
2008-07-04 10:40:18 +04:00
/*
* Errata : ALE is incorrectly wired up to the ECC controller
* on the AP7000 , so it will include the address cycles in the
* ECC calculation .
*
* Workaround : Reset the parity registers before reading the
* actual data .
*/
if ( cpu_is_at32ap7000 ( ) ) {
struct atmel_nand_host * host = chip - > priv ;
ecc_writel ( host - > ecc , CR , ATMEL_ECC_RST ) ;
}
2008-04-23 21:51:14 +04:00
/* read the page */
chip - > read_buf ( mtd , p , eccsize ) ;
/* move to ECC position if needed */
if ( eccpos [ 0 ] ! = 0 ) {
/* This only works on large pages
* because the ECC controller waits for
* NAND_CMD_RNDOUTSTART after the
* NAND_CMD_RNDOUT .
* anyway , for small pages , the eccpos [ 0 ] = = 0
*/
chip - > cmdfunc ( mtd , NAND_CMD_RNDOUT ,
mtd - > writesize + eccpos [ 0 ] , - 1 ) ;
}
/* the ECC controller needs to read the ECC just after the data */
ecc_pos = oob + eccpos [ 0 ] ;
chip - > read_buf ( mtd , ecc_pos , eccbytes ) ;
/* check if there's an error */
stat = chip - > ecc . correct ( mtd , p , oob , NULL ) ;
if ( stat < 0 )
mtd - > ecc_stats . failed + + ;
else
mtd - > ecc_stats . corrected + = stat ;
/* get back to oob start (end of page) */
chip - > cmdfunc ( mtd , NAND_CMD_RNDOUT , mtd - > writesize , - 1 ) ;
/* read the oob */
chip - > read_buf ( mtd , oob , mtd - > oobsize ) ;
return 0 ;
}
/*
* HW ECC Correction
*
* function called after a read
*
* mtd : MTD block structure
* dat : raw data read from the chip
* read_ecc : ECC from the chip ( unused )
* isnull : unused
*
* Detect and correct a 1 bit error for a page
*/
2008-06-06 20:04:53 +04:00
static int atmel_nand_correct ( struct mtd_info * mtd , u_char * dat ,
2008-04-23 21:51:14 +04:00
u_char * read_ecc , u_char * isnull )
{
struct nand_chip * nand_chip = mtd - > priv ;
2008-06-06 20:04:53 +04:00
struct atmel_nand_host * host = nand_chip - > priv ;
2008-04-23 21:51:14 +04:00
unsigned int ecc_status ;
unsigned int ecc_word , ecc_bit ;
/* get the status from the Status Register */
ecc_status = ecc_readl ( host - > ecc , SR ) ;
/* if there's no error */
2008-06-06 20:04:53 +04:00
if ( likely ( ! ( ecc_status & ATMEL_ECC_RECERR ) ) )
2008-04-23 21:51:14 +04:00
return 0 ;
/* get error bit offset (4 bits) */
2008-06-06 20:04:53 +04:00
ecc_bit = ecc_readl ( host - > ecc , PR ) & ATMEL_ECC_BITADDR ;
2008-04-23 21:51:14 +04:00
/* get word address (12 bits) */
2008-06-06 20:04:53 +04:00
ecc_word = ecc_readl ( host - > ecc , PR ) & ATMEL_ECC_WORDADDR ;
2008-04-23 21:51:14 +04:00
ecc_word > > = 4 ;
/* if there are multiple errors */
2008-06-06 20:04:53 +04:00
if ( ecc_status & ATMEL_ECC_MULERR ) {
2008-04-23 21:51:14 +04:00
/* check if it is a freshly erased block
* ( filled with 0xff ) */
2008-06-06 20:04:53 +04:00
if ( ( ecc_bit = = ATMEL_ECC_BITADDR )
& & ( ecc_word = = ( ATMEL_ECC_WORDADDR > > 4 ) ) ) {
2008-04-23 21:51:14 +04:00
/* the block has just been erased, return OK */
return 0 ;
}
/* it doesn't seems to be a freshly
* erased block .
* We can ' t correct so many errors */
2008-06-06 20:04:53 +04:00
dev_dbg ( host - > dev , " atmel_nand : multiple errors detected. "
2008-04-23 21:51:14 +04:00
" Unable to correct. \n " ) ;
return - EIO ;
}
/* if there's a single bit error : we can correct it */
2008-06-06 20:04:53 +04:00
if ( ecc_status & ATMEL_ECC_ECCERR ) {
2008-04-23 21:51:14 +04:00
/* there's nothing much to do here.
* the bit error is on the ECC itself .
*/
2008-06-06 20:04:53 +04:00
dev_dbg ( host - > dev , " atmel_nand : one bit error on ECC code. "
2008-04-23 21:51:14 +04:00
" Nothing to correct \n " ) ;
return 0 ;
}
2008-06-06 20:04:53 +04:00
dev_dbg ( host - > dev , " atmel_nand : one bit error on data. "
2008-04-23 21:51:14 +04:00
" (word offset in the page : "
" 0x%x bit offset : 0x%x) \n " ,
ecc_word , ecc_bit ) ;
/* correct the error */
if ( nand_chip - > options & NAND_BUSWIDTH_16 ) {
/* 16 bits words */
( ( unsigned short * ) dat ) [ ecc_word ] ^ = ( 1 < < ecc_bit ) ;
} else {
/* 8 bits words */
dat [ ecc_word ] ^ = ( 1 < < ecc_bit ) ;
}
2008-06-06 20:04:53 +04:00
dev_dbg ( host - > dev , " atmel_nand : error corrected \n " ) ;
2008-04-23 21:51:14 +04:00
return 1 ;
}
/*
2008-07-04 10:40:18 +04:00
* Enable HW ECC : unused on most chips
2008-04-23 21:51:14 +04:00
*/
2008-07-04 10:40:18 +04:00
static void atmel_nand_hwctl ( struct mtd_info * mtd , int mode )
{
if ( cpu_is_at32ap7000 ( ) ) {
struct nand_chip * nand_chip = mtd - > priv ;
struct atmel_nand_host * host = nand_chip - > priv ;
ecc_writel ( host - > ecc , CR , ATMEL_ECC_RST ) ;
}
}
2008-04-23 21:51:14 +04:00
2010-08-05 14:38:41 +04:00
# ifdef CONFIG_MTD_CMDLINE_PARTS
2008-03-30 16:59:37 +04:00
static const char * part_probes [ ] = { " cmdlinepart " , NULL } ;
2007-05-03 10:16:44 +04:00
# endif
2006-10-19 20:24:35 +04:00
/*
* Probe for the NAND device .
*/
2008-06-06 20:04:53 +04:00
static int __init atmel_nand_probe ( struct platform_device * pdev )
2006-10-19 20:24:35 +04:00
{
2008-06-06 20:04:53 +04:00
struct atmel_nand_host * host ;
2006-10-19 20:24:35 +04:00
struct mtd_info * mtd ;
struct nand_chip * nand_chip ;
2008-04-23 21:51:14 +04:00
struct resource * regs ;
struct resource * mem ;
2006-10-19 20:24:35 +04:00
int res ;
# ifdef CONFIG_MTD_PARTITIONS
struct mtd_partition * partitions = NULL ;
int num_partitions = 0 ;
# endif
2008-06-06 20:04:54 +04:00
mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! mem ) {
printk ( KERN_ERR " atmel_nand: can't get I/O resource mem \n " ) ;
return - ENXIO ;
}
2006-10-19 20:24:35 +04:00
/* Allocate memory for the device structure (and zero it) */
2008-06-06 20:04:53 +04:00
host = kzalloc ( sizeof ( struct atmel_nand_host ) , GFP_KERNEL ) ;
2006-10-19 20:24:35 +04:00
if ( ! host ) {
2008-06-06 20:04:53 +04:00
printk ( KERN_ERR " atmel_nand: failed to allocate device structure. \n " ) ;
2006-10-19 20:24:35 +04:00
return - ENOMEM ;
}
2008-04-23 21:51:14 +04:00
host - > io_base = ioremap ( mem - > start , mem - > end - mem - > start + 1 ) ;
2006-10-19 20:24:35 +04:00
if ( host - > io_base = = NULL ) {
2008-06-06 20:04:53 +04:00
printk ( KERN_ERR " atmel_nand: ioremap failed \n " ) ;
2008-06-06 20:04:54 +04:00
res = - EIO ;
goto err_nand_ioremap ;
2006-10-19 20:24:35 +04:00
}
mtd = & host - > mtd ;
nand_chip = & host - > nand_chip ;
host - > board = pdev - > dev . platform_data ;
2008-04-23 21:51:14 +04:00
host - > dev = & pdev - > dev ;
2006-10-19 20:24:35 +04:00
nand_chip - > priv = host ; /* link the private data structures */
mtd - > priv = nand_chip ;
mtd - > owner = THIS_MODULE ;
/* Set address of NAND IO lines */
nand_chip - > IO_ADDR_R = host - > io_base ;
nand_chip - > IO_ADDR_W = host - > io_base ;
2008-06-06 20:04:53 +04:00
nand_chip - > cmd_ctrl = atmel_nand_cmd_ctrl ;
2007-05-24 15:35:58 +04:00
if ( host - > board - > rdy_pin )
2008-06-06 20:04:53 +04:00
nand_chip - > dev_ready = atmel_nand_device_ready ;
2007-05-24 15:35:58 +04:00
2008-04-23 21:51:14 +04:00
regs = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
if ( ! regs & & hard_ecc ) {
2008-06-06 20:04:53 +04:00
printk ( KERN_ERR " atmel_nand: can't get I/O resource "
2008-04-23 21:51:14 +04:00
" regs \n Falling back on software ECC \n " ) ;
}
2006-10-19 20:24:35 +04:00
nand_chip - > ecc . mode = NAND_ECC_SOFT ; /* enable ECC */
2008-04-23 21:51:14 +04:00
if ( no_ecc )
nand_chip - > ecc . mode = NAND_ECC_NONE ;
if ( hard_ecc & & regs ) {
host - > ecc = ioremap ( regs - > start , regs - > end - regs - > start + 1 ) ;
if ( host - > ecc = = NULL ) {
2008-06-06 20:04:53 +04:00
printk ( KERN_ERR " atmel_nand: ioremap failed \n " ) ;
2008-04-23 21:51:14 +04:00
res = - EIO ;
goto err_ecc_ioremap ;
}
2008-10-12 10:42:28 +04:00
nand_chip - > ecc . mode = NAND_ECC_HW ;
2008-06-06 20:04:53 +04:00
nand_chip - > ecc . calculate = atmel_nand_calculate ;
nand_chip - > ecc . correct = atmel_nand_correct ;
nand_chip - > ecc . hwctl = atmel_nand_hwctl ;
nand_chip - > ecc . read_page = atmel_nand_read_page ;
2008-04-23 21:51:14 +04:00
nand_chip - > ecc . bytes = 4 ;
}
2006-10-19 20:24:35 +04:00
nand_chip - > chip_delay = 20 ; /* 20us command delay time */
2008-07-04 10:40:16 +04:00
if ( host - > board - > bus_width_16 ) { /* 16-bit bus width */
2006-12-08 14:49:42 +03:00
nand_chip - > options | = NAND_BUSWIDTH_16 ;
2008-07-04 10:40:16 +04:00
nand_chip - > read_buf = atmel_read_buf16 ;
nand_chip - > write_buf = atmel_write_buf16 ;
} else {
nand_chip - > read_buf = atmel_read_buf ;
nand_chip - > write_buf = atmel_write_buf ;
}
2006-12-08 14:49:42 +03:00
2006-10-19 20:24:35 +04:00
platform_set_drvdata ( pdev , host ) ;
2008-06-06 20:04:53 +04:00
atmel_nand_enable ( host ) ;
2006-10-19 20:24:35 +04:00
if ( host - > board - > det_pin ) {
2008-06-06 20:04:51 +04:00
if ( gpio_get_value ( host - > board - > det_pin ) ) {
2009-05-27 19:19:39 +04:00
printk ( KERN_INFO " No SmartMedia card inserted. \n " ) ;
2009-11-11 23:47:06 +03:00
res = - ENXIO ;
2008-06-06 20:04:54 +04:00
goto err_no_card ;
2006-10-19 20:24:35 +04:00
}
}
2009-05-27 19:19:39 +04:00
if ( on_flash_bbt ) {
printk ( KERN_INFO " atmel_nand: Use On Flash BBT \n " ) ;
nand_chip - > options | = NAND_USE_FLASH_BBT ;
}
2008-04-23 21:51:14 +04:00
/* first scan to find the device and get the page size */
2010-02-26 21:32:56 +03:00
if ( nand_scan_ident ( mtd , 1 , NULL ) ) {
2008-04-23 21:51:14 +04:00
res = - ENXIO ;
2008-06-06 20:04:54 +04:00
goto err_scan_ident ;
2008-04-23 21:51:14 +04:00
}
2008-10-12 10:42:28 +04:00
if ( nand_chip - > ecc . mode = = NAND_ECC_HW ) {
2008-04-23 21:51:14 +04:00
/* ECC is calculated for the whole page (1 step) */
nand_chip - > ecc . size = mtd - > writesize ;
/* set ECC page size and oob layout */
switch ( mtd - > writesize ) {
case 512 :
2008-06-06 20:04:53 +04:00
nand_chip - > ecc . layout = & atmel_oobinfo_small ;
ecc_writel ( host - > ecc , MR , ATMEL_ECC_PAGESIZE_528 ) ;
2008-04-23 21:51:14 +04:00
break ;
case 1024 :
2008-06-06 20:04:53 +04:00
nand_chip - > ecc . layout = & atmel_oobinfo_large ;
ecc_writel ( host - > ecc , MR , ATMEL_ECC_PAGESIZE_1056 ) ;
2008-04-23 21:51:14 +04:00
break ;
case 2048 :
2008-06-06 20:04:53 +04:00
nand_chip - > ecc . layout = & atmel_oobinfo_large ;
ecc_writel ( host - > ecc , MR , ATMEL_ECC_PAGESIZE_2112 ) ;
2008-04-23 21:51:14 +04:00
break ;
case 4096 :
2008-06-06 20:04:53 +04:00
nand_chip - > ecc . layout = & atmel_oobinfo_large ;
ecc_writel ( host - > ecc , MR , ATMEL_ECC_PAGESIZE_4224 ) ;
2008-04-23 21:51:14 +04:00
break ;
default :
/* page size not handled by HW ECC */
/* switching back to soft ECC */
nand_chip - > ecc . mode = NAND_ECC_SOFT ;
nand_chip - > ecc . calculate = NULL ;
nand_chip - > ecc . correct = NULL ;
nand_chip - > ecc . hwctl = NULL ;
nand_chip - > ecc . read_page = NULL ;
nand_chip - > ecc . postpad = 0 ;
nand_chip - > ecc . prepad = 0 ;
nand_chip - > ecc . bytes = 0 ;
break ;
}
}
/* second phase scan */
if ( nand_scan_tail ( mtd ) ) {
2006-10-19 20:24:35 +04:00
res = - ENXIO ;
2008-06-06 20:04:54 +04:00
goto err_scan_tail ;
2006-10-19 20:24:35 +04:00
}
# ifdef CONFIG_MTD_PARTITIONS
2007-05-03 10:16:44 +04:00
# ifdef CONFIG_MTD_CMDLINE_PARTS
2008-06-06 20:04:53 +04:00
mtd - > name = " atmel_nand " ;
2008-01-29 16:28:22 +03:00
num_partitions = parse_mtd_partitions ( mtd , part_probes ,
& partitions , 0 ) ;
2007-05-03 10:16:44 +04:00
# endif
2008-01-29 16:28:22 +03:00
if ( num_partitions < = 0 & & host - > board - > partition_info )
partitions = host - > board - > partition_info ( mtd - > size ,
& num_partitions ) ;
2006-10-19 20:24:35 +04:00
if ( ( ! partitions ) | | ( num_partitions = = 0 ) ) {
2009-06-25 01:40:46 +04:00
printk ( KERN_ERR " atmel_nand: No partitions defined, or unsupported device. \n " ) ;
2009-11-11 23:47:06 +03:00
res = - ENXIO ;
2008-06-06 20:04:54 +04:00
goto err_no_partitions ;
2006-10-19 20:24:35 +04:00
}
res = add_mtd_partitions ( mtd , partitions , num_partitions ) ;
# else
res = add_mtd_device ( mtd ) ;
# endif
if ( ! res )
return res ;
2008-04-23 21:51:14 +04:00
# ifdef CONFIG_MTD_PARTITIONS
2008-06-06 20:04:54 +04:00
err_no_partitions :
2008-04-23 21:51:14 +04:00
# endif
2006-10-19 20:24:35 +04:00
nand_release ( mtd ) ;
2008-06-06 20:04:54 +04:00
err_scan_tail :
err_scan_ident :
err_no_card :
2008-06-06 20:04:53 +04:00
atmel_nand_disable ( host ) ;
2006-10-19 20:24:35 +04:00
platform_set_drvdata ( pdev , NULL ) ;
2008-06-06 20:04:54 +04:00
if ( host - > ecc )
iounmap ( host - > ecc ) ;
err_ecc_ioremap :
2006-10-19 20:24:35 +04:00
iounmap ( host - > io_base ) ;
2008-06-06 20:04:54 +04:00
err_nand_ioremap :
2006-10-19 20:24:35 +04:00
kfree ( host ) ;
return res ;
}
/*
* Remove a NAND device .
*/
2008-07-04 10:40:16 +04:00
static int __exit atmel_nand_remove ( struct platform_device * pdev )
2006-10-19 20:24:35 +04:00
{
2008-06-06 20:04:53 +04:00
struct atmel_nand_host * host = platform_get_drvdata ( pdev ) ;
2006-10-19 20:24:35 +04:00
struct mtd_info * mtd = & host - > mtd ;
nand_release ( mtd ) ;
2008-06-06 20:04:53 +04:00
atmel_nand_disable ( host ) ;
2006-10-19 20:24:35 +04:00
2008-06-06 20:04:54 +04:00
if ( host - > ecc )
iounmap ( host - > ecc ) ;
2006-10-19 20:24:35 +04:00
iounmap ( host - > io_base ) ;
kfree ( host ) ;
return 0 ;
}
2008-06-06 20:04:53 +04:00
static struct platform_driver atmel_nand_driver = {
2008-07-04 10:40:16 +04:00
. remove = __exit_p ( atmel_nand_remove ) ,
2006-10-19 20:24:35 +04:00
. driver = {
2008-06-06 20:04:53 +04:00
. name = " atmel_nand " ,
2006-10-19 20:24:35 +04:00
. owner = THIS_MODULE ,
} ,
} ;
2008-06-06 20:04:53 +04:00
static int __init atmel_nand_init ( void )
2006-10-19 20:24:35 +04:00
{
2008-07-04 10:40:16 +04:00
return platform_driver_probe ( & atmel_nand_driver , atmel_nand_probe ) ;
2006-10-19 20:24:35 +04:00
}
2008-06-06 20:04:53 +04:00
static void __exit atmel_nand_exit ( void )
2006-10-19 20:24:35 +04:00
{
2008-06-06 20:04:53 +04:00
platform_driver_unregister ( & atmel_nand_driver ) ;
2006-10-19 20:24:35 +04:00
}
2008-06-06 20:04:53 +04:00
module_init ( atmel_nand_init ) ;
module_exit ( atmel_nand_exit ) ;
2006-10-19 20:24:35 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Rick Bronson " ) ;
2008-06-06 20:04:52 +04:00
MODULE_DESCRIPTION ( " NAND/SmartMedia driver for AT91 / AVR32 " ) ;
2008-06-06 20:04:53 +04:00
MODULE_ALIAS ( " platform:atmel_nand " ) ;