2007-10-03 00:56:05 +04:00
/* linux/drivers/mtd/nand/bf5xx_nand.c
*
2008-04-25 08:07:31 +04:00
* Copyright 2006 - 2008 Analog Devices Inc .
2007-10-03 00:56:05 +04:00
* http : //blackfin.uclinux.org/
* Bryan Wu < bryan . wu @ analog . com >
*
2008-02-03 18:22:34 +03:00
* Blackfin BF5xx on - chip NAND flash controller driver
2007-10-03 00:56:05 +04:00
*
* Derived from drivers / mtd / nand / s3c2410 . c
* Copyright ( c ) 2007 Ben Dooks < ben @ simtec . co . uk >
*
* Derived from drivers / mtd / nand / cafe . c
* Copyright © 2006 Red Hat , Inc .
* Copyright © 2006 David Woodhouse < dwmw2 @ infradead . org >
*
* Changelog :
* 12 - Jun - 2007 Bryan Wu : Initial version
* 18 - Jul - 2007 Bryan Wu :
* - ECC_HW and ECC_SW supported
* - DMA supported in ECC_HW
* - YAFFS tested as rootfs in both ECC_HW and ECC_SW
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/ioport.h>
# include <linux/platform_device.h>
# include <linux/delay.h>
# include <linux/dma-mapping.h>
# include <linux/err.h>
# include <linux/slab.h>
# include <linux/io.h>
# include <linux/bitops.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/nand.h>
# include <linux/mtd/nand_ecc.h>
# include <linux/mtd/partitions.h>
# include <asm/blackfin.h>
# include <asm/dma.h>
# include <asm/cacheflush.h>
# include <asm/nand.h>
# include <asm/portmux.h>
# define DRV_NAME "bf5xx-nand"
# define DRV_VERSION "1.2"
# define DRV_AUTHOR "Bryan Wu <bryan.wu@analog.com>"
# define DRV_DESC "BF5xx on-chip NAND FLash Controller Driver"
2010-03-09 19:05:48 +03:00
/* NFC_STAT Masks */
# define NBUSY 0x01 /* Not Busy */
# define WB_FULL 0x02 /* Write Buffer Full */
# define PG_WR_STAT 0x04 /* Page Write Pending */
# define PG_RD_STAT 0x08 /* Page Read Pending */
# define WB_EMPTY 0x10 /* Write Buffer Empty */
/* NFC_IRQSTAT Masks */
# define NBUSYIRQ 0x01 /* Not Busy IRQ */
# define WB_OVF 0x02 /* Write Buffer Overflow */
# define WB_EDGE 0x04 /* Write Buffer Edge Detect */
# define RD_RDY 0x08 /* Read Data Ready */
# define WR_DONE 0x10 /* Page Write Done */
/* NFC_RST Masks */
# define ECC_RST 0x01 /* ECC (and NFC counters) Reset */
/* NFC_PGCTL Masks */
# define PG_RD_START 0x01 /* Page Read Start */
# define PG_WR_START 0x02 /* Page Write Start */
2007-10-03 00:56:05 +04:00
# ifdef CONFIG_MTD_NAND_BF5XX_HWECC
static int hardware_ecc = 1 ;
# else
static int hardware_ecc ;
# endif
2008-04-25 08:07:31 +04:00
static const unsigned short bfin_nfc_pin_req [ ] =
2007-10-30 12:08:29 +03:00
{ P_NAND_CE ,
P_NAND_RB ,
P_NAND_D0 ,
P_NAND_D1 ,
P_NAND_D2 ,
P_NAND_D3 ,
P_NAND_D4 ,
P_NAND_D5 ,
P_NAND_D6 ,
P_NAND_D7 ,
P_NAND_WE ,
P_NAND_RE ,
P_NAND_CLE ,
P_NAND_ALE ,
0 } ;
2007-10-03 00:56:05 +04:00
2008-07-30 23:35:01 +04:00
# ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
2016-02-03 21:59:25 +03:00
static int bootrom_ooblayout_ecc ( struct mtd_info * mtd , int section ,
struct mtd_oob_region * oobregion )
{
if ( section > 7 )
return - ERANGE ;
oobregion - > offset = section * 8 ;
oobregion - > length = 3 ;
return 0 ;
}
static int bootrom_ooblayout_free ( struct mtd_info * mtd , int section ,
struct mtd_oob_region * oobregion )
{
if ( section > 7 )
return - ERANGE ;
oobregion - > offset = ( section * 8 ) + 3 ;
oobregion - > length = 5 ;
return 0 ;
}
static const struct mtd_ooblayout_ops bootrom_ooblayout_ops = {
. ecc = bootrom_ooblayout_ecc ,
. free = bootrom_ooblayout_free ,
2008-07-30 23:35:01 +04:00
} ;
# endif
2007-10-03 00:56:05 +04:00
/*
* Data structures for bf5xx nand flash controller driver
*/
/* bf5xx nand info */
struct bf5xx_nand_info {
/* mtd info */
struct nand_hw_control controller ;
struct nand_chip chip ;
/* platform info */
struct bf5xx_nand_platform * platform ;
/* device info */
struct device * device ;
/* DMA stuff */
struct completion dma_completion ;
} ;
/*
* Conversion functions
*/
static struct bf5xx_nand_info * mtd_to_nand_info ( struct mtd_info * mtd )
{
2015-12-10 10:59:53 +03:00
return container_of ( mtd_to_nand ( mtd ) , struct bf5xx_nand_info ,
chip ) ;
2007-10-03 00:56:05 +04:00
}
static struct bf5xx_nand_info * to_nand_info ( struct platform_device * pdev )
{
return platform_get_drvdata ( pdev ) ;
}
static struct bf5xx_nand_platform * to_nand_plat ( struct platform_device * pdev )
{
2013-07-30 12:18:33 +04:00
return dev_get_platdata ( & pdev - > dev ) ;
2007-10-03 00:56:05 +04:00
}
/*
* struct nand_chip interface function pointers
*/
/*
* bf5xx_nand_hwcontrol
*
* Issue command and address cycles to the chip
*/
static void bf5xx_nand_hwcontrol ( struct mtd_info * mtd , int cmd ,
unsigned int ctrl )
{
if ( cmd = = NAND_CMD_NONE )
return ;
while ( bfin_read_NFC_STAT ( ) & WB_FULL )
cpu_relax ( ) ;
if ( ctrl & NAND_CLE )
bfin_write_NFC_CMD ( cmd ) ;
2010-08-05 19:07:42 +04:00
else if ( ctrl & NAND_ALE )
2007-10-03 00:56:05 +04:00
bfin_write_NFC_ADDR ( cmd ) ;
SSYNC ( ) ;
}
/*
* bf5xx_nand_devready ( )
*
* returns 0 if the nand is busy , 1 if it is ready
*/
static int bf5xx_nand_devready ( struct mtd_info * mtd )
{
2010-08-05 19:07:38 +04:00
unsigned short val = bfin_read_NFC_STAT ( ) ;
2007-10-03 00:56:05 +04:00
2010-08-05 19:07:38 +04:00
if ( ( val & NBUSY ) = = NBUSY )
2007-10-03 00:56:05 +04:00
return 1 ;
else
return 0 ;
}
/*
* ECC functions
* These allow the bf5xx to use the controller ' s ECC
* generator block to ECC the data as it passes through
*/
/*
* ECC error correction function
*/
static int bf5xx_nand_correct_data_256 ( struct mtd_info * mtd , u_char * dat ,
u_char * read_ecc , u_char * calc_ecc )
{
struct bf5xx_nand_info * info = mtd_to_nand_info ( mtd ) ;
u32 syndrome [ 5 ] ;
u32 calced , stored ;
int i ;
unsigned short failing_bit , failing_byte ;
u_char data ;
calced = calc_ecc [ 0 ] | ( calc_ecc [ 1 ] < < 8 ) | ( calc_ecc [ 2 ] < < 16 ) ;
stored = read_ecc [ 0 ] | ( read_ecc [ 1 ] < < 8 ) | ( read_ecc [ 2 ] < < 16 ) ;
syndrome [ 0 ] = ( calced ^ stored ) ;
/*
* syndrome 0 : all zero
* No error in data
* No action
*/
if ( ! syndrome [ 0 ] | | ! calced | | ! stored )
return 0 ;
/*
* sysdrome 0 : only one bit is one
* ECC data was incorrect
* No action
*/
if ( hweight32 ( syndrome [ 0 ] ) = = 1 ) {
dev_err ( info - > device , " ECC data was incorrect! \n " ) ;
2015-12-30 22:32:03 +03:00
return - EBADMSG ;
2007-10-03 00:56:05 +04:00
}
syndrome [ 1 ] = ( calced & 0x7FF ) ^ ( stored & 0x7FF ) ;
syndrome [ 2 ] = ( calced & 0x7FF ) ^ ( ( calced > > 11 ) & 0x7FF ) ;
syndrome [ 3 ] = ( stored & 0x7FF ) ^ ( ( stored > > 11 ) & 0x7FF ) ;
syndrome [ 4 ] = syndrome [ 2 ] ^ syndrome [ 3 ] ;
for ( i = 0 ; i < 5 ; i + + )
dev_info ( info - > device , " syndrome[%d] 0x%08x \n " , i , syndrome [ i ] ) ;
dev_info ( info - > device ,
" calced[0x%08x], stored[0x%08x] \n " ,
calced , stored ) ;
/*
* sysdrome 0 : exactly 11 bits are one , each parity
* and parity ' pair is 1 & 0 or 0 & 1.
* 1 - bit correctable error
* Correct the error
*/
if ( hweight32 ( syndrome [ 0 ] ) = = 11 & & syndrome [ 4 ] = = 0x7FF ) {
dev_info ( info - > device ,
" 1-bit correctable error, correct it. \n " ) ;
dev_info ( info - > device ,
" syndrome[1] 0x%08x \n " , syndrome [ 1 ] ) ;
failing_bit = syndrome [ 1 ] & 0x7 ;
failing_byte = syndrome [ 1 ] > > 0x3 ;
data = * ( dat + failing_byte ) ;
data = data ^ ( 0x1 < < failing_bit ) ;
* ( dat + failing_byte ) = data ;
2015-12-30 22:32:03 +03:00
return 1 ;
2007-10-03 00:56:05 +04:00
}
/*
* sysdrome 0 : random data
* More than 1 - bit error , non - correctable error
* Discard data , mark bad block
*/
dev_err ( info - > device ,
" More than 1-bit error, non-correctable error. \n " ) ;
dev_err ( info - > device ,
" Please discard data, mark bad block \n " ) ;
2015-12-30 22:32:03 +03:00
return - EBADMSG ;
2007-10-03 00:56:05 +04:00
}
static int bf5xx_nand_correct_data ( struct mtd_info * mtd , u_char * dat ,
u_char * read_ecc , u_char * calc_ecc )
{
2015-12-01 14:03:04 +03:00
struct nand_chip * chip = mtd_to_nand ( mtd ) ;
2015-12-30 22:32:03 +03:00
int ret , bitflips = 0 ;
2007-10-03 00:56:05 +04:00
ret = bf5xx_nand_correct_data_256 ( mtd , dat , read_ecc , calc_ecc ) ;
2015-12-30 22:32:03 +03:00
if ( ret < 0 )
return ret ;
bitflips = ret ;
2007-10-03 00:56:05 +04:00
2010-08-05 19:07:43 +04:00
/* If ecc size is 512, correct second 256 bytes */
if ( chip - > ecc . size = = 512 ) {
2007-10-03 00:56:05 +04:00
dat + = 256 ;
2010-08-05 19:07:43 +04:00
read_ecc + = 3 ;
calc_ecc + = 3 ;
2015-12-30 22:32:03 +03:00
ret = bf5xx_nand_correct_data_256 ( mtd , dat , read_ecc , calc_ecc ) ;
if ( ret < 0 )
return ret ;
bitflips + = ret ;
2007-10-03 00:56:05 +04:00
}
2015-12-30 22:32:03 +03:00
return bitflips ;
2007-10-03 00:56:05 +04:00
}
static void bf5xx_nand_enable_hwecc ( struct mtd_info * mtd , int mode )
{
return ;
}
static int bf5xx_nand_calculate_ecc ( struct mtd_info * mtd ,
const u_char * dat , u_char * ecc_code )
{
struct bf5xx_nand_info * info = mtd_to_nand_info ( mtd ) ;
2015-12-01 14:03:04 +03:00
struct nand_chip * chip = mtd_to_nand ( mtd ) ;
2007-10-03 00:56:05 +04:00
u16 ecc0 , ecc1 ;
u32 code [ 2 ] ;
u8 * p ;
2010-08-05 19:07:43 +04:00
/* first 3 bytes ECC code for 256 page size */
2007-10-03 00:56:05 +04:00
ecc0 = bfin_read_NFC_ECC0 ( ) ;
ecc1 = bfin_read_NFC_ECC1 ( ) ;
2008-07-30 23:35:00 +04:00
code [ 0 ] = ( ecc0 & 0x7ff ) | ( ( ecc1 & 0x7ff ) < < 11 ) ;
2007-10-03 00:56:05 +04:00
dev_dbg ( info - > device , " returning ecc 0x%08x \n " , code [ 0 ] ) ;
2008-01-30 12:18:18 +03:00
p = ( u8 * ) code ;
memcpy ( ecc_code , p , 3 ) ;
2010-08-05 19:07:43 +04:00
/* second 3 bytes ECC code for 512 ecc size */
if ( chip - > ecc . size = = 512 ) {
2007-10-03 00:56:05 +04:00
ecc0 = bfin_read_NFC_ECC2 ( ) ;
ecc1 = bfin_read_NFC_ECC3 ( ) ;
2008-07-30 23:35:00 +04:00
code [ 1 ] = ( ecc0 & 0x7ff ) | ( ( ecc1 & 0x7ff ) < < 11 ) ;
2008-01-30 12:18:18 +03:00
/* second 3 bytes in ecc_code for second 256
* bytes of 512 page size
*/
p = ( u8 * ) ( code + 1 ) ;
memcpy ( ( ecc_code + 3 ) , p , 3 ) ;
2007-10-03 00:56:05 +04:00
dev_dbg ( info - > device , " returning ecc 0x%08x \n " , code [ 1 ] ) ;
}
return 0 ;
}
/*
* PIO mode for buffer writing and reading
*/
static void bf5xx_nand_read_buf ( struct mtd_info * mtd , uint8_t * buf , int len )
{
int i ;
unsigned short val ;
/*
* Data reads are requested by first writing to NFC_DATA_RD
* and then reading back from NFC_READ .
*/
for ( i = 0 ; i < len ; i + + ) {
while ( bfin_read_NFC_STAT ( ) & WB_FULL )
cpu_relax ( ) ;
/* Contents do not matter */
bfin_write_NFC_DATA_RD ( 0x0000 ) ;
SSYNC ( ) ;
while ( ( bfin_read_NFC_IRQSTAT ( ) & RD_RDY ) ! = RD_RDY )
cpu_relax ( ) ;
buf [ i ] = bfin_read_NFC_READ ( ) ;
val = bfin_read_NFC_IRQSTAT ( ) ;
val | = RD_RDY ;
bfin_write_NFC_IRQSTAT ( val ) ;
SSYNC ( ) ;
}
}
static uint8_t bf5xx_nand_read_byte ( struct mtd_info * mtd )
{
uint8_t val ;
bf5xx_nand_read_buf ( mtd , & val , 1 ) ;
return val ;
}
static void bf5xx_nand_write_buf ( struct mtd_info * mtd ,
const uint8_t * buf , int len )
{
int i ;
for ( i = 0 ; i < len ; i + + ) {
while ( bfin_read_NFC_STAT ( ) & WB_FULL )
cpu_relax ( ) ;
bfin_write_NFC_DATA_WR ( buf [ i ] ) ;
SSYNC ( ) ;
}
}
static void bf5xx_nand_read_buf16 ( struct mtd_info * mtd , uint8_t * buf , int len )
{
int i ;
u16 * p = ( u16 * ) buf ;
len > > = 1 ;
/*
* Data reads are requested by first writing to NFC_DATA_RD
* and then reading back from NFC_READ .
*/
bfin_write_NFC_DATA_RD ( 0x5555 ) ;
SSYNC ( ) ;
for ( i = 0 ; i < len ; i + + )
p [ i ] = bfin_read_NFC_READ ( ) ;
}
static void bf5xx_nand_write_buf16 ( struct mtd_info * mtd ,
const uint8_t * buf , int len )
{
int i ;
u16 * p = ( u16 * ) buf ;
len > > = 1 ;
for ( i = 0 ; i < len ; i + + )
bfin_write_NFC_DATA_WR ( p [ i ] ) ;
SSYNC ( ) ;
}
/*
* DMA functions for buffer writing and reading
*/
static irqreturn_t bf5xx_nand_dma_irq ( int irq , void * dev_id )
{
struct bf5xx_nand_info * info = dev_id ;
clear_dma_irqstat ( CH_NFC ) ;
disable_dma ( CH_NFC ) ;
complete ( & info - > dma_completion ) ;
return IRQ_HANDLED ;
}
2009-05-26 14:24:13 +04:00
static void bf5xx_nand_dma_rw ( struct mtd_info * mtd ,
2007-10-03 00:56:05 +04:00
uint8_t * buf , int is_read )
{
struct bf5xx_nand_info * info = mtd_to_nand_info ( mtd ) ;
2015-12-01 14:03:04 +03:00
struct nand_chip * chip = mtd_to_nand ( mtd ) ;
2007-10-03 00:56:05 +04:00
unsigned short val ;
dev_dbg ( info - > device , " mtd->%p, buf->%p, is_read %d \n " ,
mtd , buf , is_read ) ;
/*
* Before starting a dma transfer , be sure to invalidate / flush
* the cache over the address range of your DMA buffer to
* prevent cache coherency problems . Otherwise very subtle bugs
* can be introduced to your driver .
*/
if ( is_read )
invalidate_dcache_range ( ( unsigned int ) buf ,
2010-08-05 19:07:43 +04:00
( unsigned int ) ( buf + chip - > ecc . size ) ) ;
2007-10-03 00:56:05 +04:00
else
flush_dcache_range ( ( unsigned int ) buf ,
2010-08-05 19:07:43 +04:00
( unsigned int ) ( buf + chip - > ecc . size ) ) ;
2007-10-03 00:56:05 +04:00
/*
* This register must be written before each page is
* transferred to generate the correct ECC register
* values .
*/
2010-03-09 19:05:48 +03:00
bfin_write_NFC_RST ( ECC_RST ) ;
2007-10-03 00:56:05 +04:00
SSYNC ( ) ;
2010-08-05 19:07:41 +04:00
while ( bfin_read_NFC_RST ( ) & ECC_RST )
cpu_relax ( ) ;
2007-10-03 00:56:05 +04:00
disable_dma ( CH_NFC ) ;
clear_dma_irqstat ( CH_NFC ) ;
/* setup DMA register with Blackfin DMA API */
set_dma_config ( CH_NFC , 0x0 ) ;
set_dma_start_addr ( CH_NFC , ( unsigned long ) buf ) ;
2009-05-26 14:24:14 +04:00
2010-03-09 19:05:48 +03:00
/* The DMAs have different size on BF52x and BF54x */
2009-05-26 14:24:14 +04:00
# ifdef CONFIG_BF52x
2010-08-05 19:07:43 +04:00
set_dma_x_count ( CH_NFC , ( chip - > ecc . size > > 1 ) ) ;
2009-05-26 14:24:14 +04:00
set_dma_x_modify ( CH_NFC , 2 ) ;
val = DI_EN | WDSIZE_16 ;
# endif
# ifdef CONFIG_BF54x
2010-08-05 19:07:43 +04:00
set_dma_x_count ( CH_NFC , ( chip - > ecc . size > > 2 ) ) ;
2007-10-03 00:56:05 +04:00
set_dma_x_modify ( CH_NFC , 4 ) ;
val = DI_EN | WDSIZE_32 ;
2009-05-26 14:24:14 +04:00
# endif
/* setup write or read operation */
2007-10-03 00:56:05 +04:00
if ( is_read )
val | = WNR ;
set_dma_config ( CH_NFC , val ) ;
enable_dma ( CH_NFC ) ;
/* Start PAGE read/write operation */
if ( is_read )
2010-03-09 19:05:48 +03:00
bfin_write_NFC_PGCTL ( PG_RD_START ) ;
2007-10-03 00:56:05 +04:00
else
2010-03-09 19:05:48 +03:00
bfin_write_NFC_PGCTL ( PG_WR_START ) ;
2007-10-03 00:56:05 +04:00
wait_for_completion ( & info - > dma_completion ) ;
}
static void bf5xx_nand_dma_read_buf ( struct mtd_info * mtd ,
uint8_t * buf , int len )
{
struct bf5xx_nand_info * info = mtd_to_nand_info ( mtd ) ;
2015-12-01 14:03:04 +03:00
struct nand_chip * chip = mtd_to_nand ( mtd ) ;
2007-10-03 00:56:05 +04:00
dev_dbg ( info - > device , " mtd->%p, buf->%p, int %d \n " , mtd , buf , len ) ;
2010-08-05 19:07:43 +04:00
if ( len = = chip - > ecc . size )
2007-10-03 00:56:05 +04:00
bf5xx_nand_dma_rw ( mtd , buf , 1 ) ;
else
bf5xx_nand_read_buf ( mtd , buf , len ) ;
}
static void bf5xx_nand_dma_write_buf ( struct mtd_info * mtd ,
const uint8_t * buf , int len )
{
struct bf5xx_nand_info * info = mtd_to_nand_info ( mtd ) ;
2015-12-01 14:03:04 +03:00
struct nand_chip * chip = mtd_to_nand ( mtd ) ;
2007-10-03 00:56:05 +04:00
dev_dbg ( info - > device , " mtd->%p, buf->%p, len %d \n " , mtd , buf , len ) ;
2010-08-05 19:07:43 +04:00
if ( len = = chip - > ecc . size )
2007-10-03 00:56:05 +04:00
bf5xx_nand_dma_rw ( mtd , ( uint8_t * ) buf , 0 ) ;
else
bf5xx_nand_write_buf ( mtd , buf , len ) ;
}
2010-08-05 19:07:44 +04:00
static int bf5xx_nand_read_page_raw ( struct mtd_info * mtd , struct nand_chip * chip ,
2012-05-02 21:14:55 +04:00
uint8_t * buf , int oob_required , int page )
2010-08-05 19:07:44 +04:00
{
bf5xx_nand_read_buf ( mtd , buf , mtd - > writesize ) ;
bf5xx_nand_read_buf ( mtd , chip - > oob_poi , mtd - > oobsize ) ;
return 0 ;
}
2012-06-25 14:07:45 +04:00
static int bf5xx_nand_write_page_raw ( struct mtd_info * mtd ,
2015-10-13 12:22:18 +03:00
struct nand_chip * chip , const uint8_t * buf , int oob_required ,
int page )
2010-08-05 19:07:44 +04:00
{
bf5xx_nand_write_buf ( mtd , buf , mtd - > writesize ) ;
bf5xx_nand_write_buf ( mtd , chip - > oob_poi , mtd - > oobsize ) ;
2012-06-25 14:07:45 +04:00
return 0 ;
2010-08-05 19:07:44 +04:00
}
2007-10-03 00:56:05 +04:00
/*
* System initialization functions
*/
static int bf5xx_nand_dma_init ( struct bf5xx_nand_info * info )
{
int ret ;
/* Do not use dma */
if ( ! hardware_ecc )
return 0 ;
init_completion ( & info - > dma_completion ) ;
/* Request NFC DMA channel */
ret = request_dma ( CH_NFC , " BF5XX NFC driver " ) ;
if ( ret < 0 ) {
dev_err ( info - > device , " unable to get DMA channel \n " ) ;
return ret ;
}
2009-03-04 23:01:29 +03:00
# ifdef CONFIG_BF54x
/* Setup DMAC1 channel mux for NFC which shared with SDH */
bfin_write_DMAC1_PERIMUX ( bfin_read_DMAC1_PERIMUX ( ) & ~ 1 ) ;
SSYNC ( ) ;
# endif
2009-03-04 23:01:30 +03:00
set_dma_callback ( CH_NFC , bf5xx_nand_dma_irq , info ) ;
2007-10-03 00:56:05 +04:00
/* Turn off the DMA channel first */
disable_dma ( CH_NFC ) ;
return 0 ;
}
2008-07-30 23:35:04 +04:00
static void bf5xx_nand_dma_remove ( struct bf5xx_nand_info * info )
{
/* Free NFC DMA channel */
if ( hardware_ecc )
free_dma ( CH_NFC ) ;
}
2007-10-03 00:56:05 +04:00
/*
* BF5XX NFC hardware initialization
* - pin mux setup
* - clear interrupt status
*/
static int bf5xx_nand_hw_init ( struct bf5xx_nand_info * info )
{
int err = 0 ;
unsigned short val ;
struct bf5xx_nand_platform * plat = info - > platform ;
/* setup NFC_CTL register */
dev_info ( info - > device ,
2010-08-05 19:07:43 +04:00
" data_width=%d, wr_dly=%d, rd_dly=%d \n " ,
2007-10-03 00:56:05 +04:00
( plat - > data_width ? 16 : 8 ) ,
plat - > wr_dly , plat - > rd_dly ) ;
2010-08-05 19:07:43 +04:00
val = ( 1 < < NFC_PG_SIZE_OFFSET ) |
2007-10-03 00:56:05 +04:00
( plat - > data_width < < NFC_NWIDTH_OFFSET ) |
( plat - > rd_dly < < NFC_RDDLY_OFFSET ) |
2010-08-05 19:07:40 +04:00
( plat - > wr_dly < < NFC_WRDLY_OFFSET ) ;
2007-10-03 00:56:05 +04:00
dev_dbg ( info - > device , " NFC_CTL is 0x%04x \n " , val ) ;
bfin_write_NFC_CTL ( val ) ;
SSYNC ( ) ;
/* clear interrupt status */
bfin_write_NFC_IRQMASK ( 0x0 ) ;
SSYNC ( ) ;
val = bfin_read_NFC_IRQSTAT ( ) ;
bfin_write_NFC_IRQSTAT ( val ) ;
SSYNC ( ) ;
/* DMA initialization */
if ( bf5xx_nand_dma_init ( info ) )
err = - ENXIO ;
return err ;
}
/*
* Device management interface
*/
2012-11-19 22:23:07 +04:00
static int bf5xx_nand_add_partition ( struct bf5xx_nand_info * info )
2007-10-03 00:56:05 +04:00
{
2015-12-10 10:59:53 +03:00
struct mtd_info * mtd = nand_to_mtd ( & info - > chip ) ;
2007-10-03 00:56:05 +04:00
struct mtd_partition * parts = info - > platform - > partitions ;
int nr = info - > platform - > nr_partitions ;
2011-05-23 13:23:15 +04:00
return mtd_device_register ( mtd , parts , nr ) ;
2007-10-03 00:56:05 +04:00
}
2012-11-19 22:26:04 +04:00
static int bf5xx_nand_remove ( struct platform_device * pdev )
2007-10-03 00:56:05 +04:00
{
struct bf5xx_nand_info * info = to_nand_info ( pdev ) ;
/* first thing we need to do is release all our mtds
* and their partitions , then go through freeing the
* resources used
*/
2015-12-10 10:59:53 +03:00
nand_release ( nand_to_mtd ( & info - > chip ) ) ;
2007-10-03 00:56:05 +04:00
peripheral_free_list ( bfin_nfc_pin_req ) ;
2008-07-30 23:35:04 +04:00
bf5xx_nand_dma_remove ( info ) ;
2007-10-03 00:56:05 +04:00
return 0 ;
}
2010-08-05 19:07:43 +04:00
static int bf5xx_nand_scan ( struct mtd_info * mtd )
{
2015-12-01 14:03:04 +03:00
struct nand_chip * chip = mtd_to_nand ( mtd ) ;
2010-08-05 19:07:43 +04:00
int ret ;
2010-08-28 09:45:00 +04:00
ret = nand_scan_ident ( mtd , 1 , NULL ) ;
2010-08-05 19:07:43 +04:00
if ( ret )
return ret ;
if ( hardware_ecc ) {
/*
* for nand with page size > 512 B , think it as several sections with 512 B
*/
if ( likely ( mtd - > writesize > = 512 ) ) {
chip - > ecc . size = 512 ;
chip - > ecc . bytes = 6 ;
2012-03-12 01:21:11 +04:00
chip - > ecc . strength = 2 ;
2010-08-05 19:07:43 +04:00
} else {
chip - > ecc . size = 256 ;
chip - > ecc . bytes = 3 ;
2012-03-12 01:21:11 +04:00
chip - > ecc . strength = 1 ;
2010-08-05 19:07:43 +04:00
bfin_write_NFC_CTL ( bfin_read_NFC_CTL ( ) & ~ ( 1 < < NFC_PG_SIZE_OFFSET ) ) ;
SSYNC ( ) ;
}
}
return nand_scan_tail ( mtd ) ;
}
2007-10-03 00:56:05 +04:00
/*
* bf5xx_nand_probe
*
* 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
*/
2012-11-19 22:23:07 +04:00
static int bf5xx_nand_probe ( struct platform_device * pdev )
2007-10-03 00:56:05 +04:00
{
struct bf5xx_nand_platform * plat = to_nand_plat ( pdev ) ;
struct bf5xx_nand_info * info = NULL ;
struct nand_chip * chip = NULL ;
struct mtd_info * mtd = NULL ;
int err = 0 ;
dev_dbg ( & pdev - > dev , " (%p) \n " , pdev ) ;
2008-07-30 23:35:04 +04:00
if ( ! plat ) {
dev_err ( & pdev - > dev , " no platform specific information \n " ) ;
return - EINVAL ;
}
2008-04-25 08:07:31 +04:00
if ( peripheral_request_list ( bfin_nfc_pin_req , DRV_NAME ) ) {
2008-07-30 23:35:03 +04:00
dev_err ( & pdev - > dev , " requesting Peripherals failed \n " ) ;
2008-04-25 08:07:31 +04:00
return - EFAULT ;
}
mtd: bf5xx_nand: use the managed version of kzalloc
This patch moves data allocated using kzalloc to managed data allocated
using devm_kzalloc and cleans now unnecessary kfrees in probe and remove
functions. Also, the now unnecessary label out_err_hw_init is done away
with and the label out_err_kzalloc is renamed to out_err.
The following Coccinelle semantic patch was used for making the change:
@platform@
identifier p, probefn, removefn;
@@
struct platform_driver p = {
.probe = probefn,
.remove = removefn,
};
@prb@
identifier platform.probefn, pdev;
expression e, e1, e2;
@@
probefn(struct platform_device *pdev, ...) {
<+...
- e = kzalloc(e1, e2)
+ e = devm_kzalloc(&pdev->dev, e1, e2)
...
?-kfree(e);
...+>
}
@rem depends on prb@
identifier platform.removefn;
expression e;
@@
removefn(...) {
<...
- kfree(e);
...>
}
Signed-off-by: Himangi Saraogi <himangi774@gmail.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
2014-05-22 22:58:48 +04:00
info = devm_kzalloc ( & pdev - > dev , sizeof ( * info ) , GFP_KERNEL ) ;
2007-10-03 00:56:05 +04:00
if ( info = = NULL ) {
err = - ENOMEM ;
mtd: bf5xx_nand: use the managed version of kzalloc
This patch moves data allocated using kzalloc to managed data allocated
using devm_kzalloc and cleans now unnecessary kfrees in probe and remove
functions. Also, the now unnecessary label out_err_hw_init is done away
with and the label out_err_kzalloc is renamed to out_err.
The following Coccinelle semantic patch was used for making the change:
@platform@
identifier p, probefn, removefn;
@@
struct platform_driver p = {
.probe = probefn,
.remove = removefn,
};
@prb@
identifier platform.probefn, pdev;
expression e, e1, e2;
@@
probefn(struct platform_device *pdev, ...) {
<+...
- e = kzalloc(e1, e2)
+ e = devm_kzalloc(&pdev->dev, e1, e2)
...
?-kfree(e);
...+>
}
@rem depends on prb@
identifier platform.removefn;
expression e;
@@
removefn(...) {
<...
- kfree(e);
...>
}
Signed-off-by: Himangi Saraogi <himangi774@gmail.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
2014-05-22 22:58:48 +04:00
goto out_err ;
2007-10-03 00:56:05 +04:00
}
platform_set_drvdata ( pdev , info ) ;
spin_lock_init ( & info - > controller . lock ) ;
init_waitqueue_head ( & info - > controller . wq ) ;
info - > device = & pdev - > dev ;
info - > platform = plat ;
/* initialise chip data struct */
chip = & info - > chip ;
2015-12-10 10:59:53 +03:00
mtd = nand_to_mtd ( & info - > chip ) ;
2007-10-03 00:56:05 +04:00
if ( plat - > data_width )
chip - > options | = NAND_BUSWIDTH_16 ;
chip - > options | = NAND_CACHEPRG | NAND_SKIP_BBTSCAN ;
chip - > read_buf = ( plat - > data_width ) ?
bf5xx_nand_read_buf16 : bf5xx_nand_read_buf ;
chip - > write_buf = ( plat - > data_width ) ?
bf5xx_nand_write_buf16 : bf5xx_nand_write_buf ;
chip - > read_byte = bf5xx_nand_read_byte ;
chip - > cmd_ctrl = bf5xx_nand_hwcontrol ;
chip - > dev_ready = bf5xx_nand_devready ;
2015-12-10 11:00:41 +03:00
nand_set_controller_data ( chip , mtd ) ;
2007-10-03 00:56:05 +04:00
chip - > controller = & info - > controller ;
chip - > IO_ADDR_R = ( void __iomem * ) NFC_READ ;
chip - > IO_ADDR_W = ( void __iomem * ) NFC_DATA_WR ;
chip - > chip_delay = 0 ;
/* initialise mtd info data struct */
2015-06-10 23:38:39 +03:00
mtd - > dev . parent = & pdev - > dev ;
2007-10-03 00:56:05 +04:00
/* initialise the hardware */
err = bf5xx_nand_hw_init ( info ) ;
2008-07-30 23:35:04 +04:00
if ( err )
mtd: bf5xx_nand: use the managed version of kzalloc
This patch moves data allocated using kzalloc to managed data allocated
using devm_kzalloc and cleans now unnecessary kfrees in probe and remove
functions. Also, the now unnecessary label out_err_hw_init is done away
with and the label out_err_kzalloc is renamed to out_err.
The following Coccinelle semantic patch was used for making the change:
@platform@
identifier p, probefn, removefn;
@@
struct platform_driver p = {
.probe = probefn,
.remove = removefn,
};
@prb@
identifier platform.probefn, pdev;
expression e, e1, e2;
@@
probefn(struct platform_device *pdev, ...) {
<+...
- e = kzalloc(e1, e2)
+ e = devm_kzalloc(&pdev->dev, e1, e2)
...
?-kfree(e);
...+>
}
@rem depends on prb@
identifier platform.removefn;
expression e;
@@
removefn(...) {
<...
- kfree(e);
...>
}
Signed-off-by: Himangi Saraogi <himangi774@gmail.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
2014-05-22 22:58:48 +04:00
goto out_err ;
2007-10-03 00:56:05 +04:00
/* setup hardware ECC data struct */
if ( hardware_ecc ) {
2008-07-30 23:35:01 +04:00
# ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
2016-02-03 21:59:25 +03:00
mtd_set_ooblayout ( mtd , & bootrom_ooblayout_ops ) ;
2008-07-30 23:35:01 +04:00
# endif
2007-10-03 00:56:05 +04:00
chip - > read_buf = bf5xx_nand_dma_read_buf ;
chip - > write_buf = bf5xx_nand_dma_write_buf ;
chip - > ecc . calculate = bf5xx_nand_calculate_ecc ;
chip - > ecc . correct = bf5xx_nand_correct_data ;
chip - > ecc . mode = NAND_ECC_HW ;
chip - > ecc . hwctl = bf5xx_nand_enable_hwecc ;
2010-08-05 19:07:44 +04:00
chip - > ecc . read_page_raw = bf5xx_nand_read_page_raw ;
chip - > ecc . write_page_raw = bf5xx_nand_write_page_raw ;
2007-10-03 00:56:05 +04:00
} else {
chip - > ecc . mode = NAND_ECC_SOFT ;
2016-04-17 23:52:57 +03:00
chip - > ecc . algo = NAND_ECC_HAMMING ;
2007-10-03 00:56:05 +04:00
}
/* scan hardware nand chip and setup mtd info data struct */
2010-08-05 19:07:43 +04:00
if ( bf5xx_nand_scan ( mtd ) ) {
2007-10-03 00:56:05 +04:00
err = - ENXIO ;
2008-07-30 23:35:04 +04:00
goto out_err_nand_scan ;
2007-10-03 00:56:05 +04:00
}
2010-10-17 02:26:59 +04:00
# ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
chip - > badblockpos = 63 ;
# endif
2007-10-03 00:56:05 +04:00
/* add NAND partition */
bf5xx_nand_add_partition ( info ) ;
dev_dbg ( & pdev - > dev , " initialised ok \n " ) ;
return 0 ;
2008-07-30 23:35:04 +04:00
out_err_nand_scan :
bf5xx_nand_dma_remove ( info ) ;
mtd: bf5xx_nand: use the managed version of kzalloc
This patch moves data allocated using kzalloc to managed data allocated
using devm_kzalloc and cleans now unnecessary kfrees in probe and remove
functions. Also, the now unnecessary label out_err_hw_init is done away
with and the label out_err_kzalloc is renamed to out_err.
The following Coccinelle semantic patch was used for making the change:
@platform@
identifier p, probefn, removefn;
@@
struct platform_driver p = {
.probe = probefn,
.remove = removefn,
};
@prb@
identifier platform.probefn, pdev;
expression e, e1, e2;
@@
probefn(struct platform_device *pdev, ...) {
<+...
- e = kzalloc(e1, e2)
+ e = devm_kzalloc(&pdev->dev, e1, e2)
...
?-kfree(e);
...+>
}
@rem depends on prb@
identifier platform.removefn;
expression e;
@@
removefn(...) {
<...
- kfree(e);
...>
}
Signed-off-by: Himangi Saraogi <himangi774@gmail.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
2014-05-22 22:58:48 +04:00
out_err :
2008-07-30 23:35:04 +04:00
peripheral_free_list ( bfin_nfc_pin_req ) ;
2007-10-03 00:56:05 +04:00
return err ;
}
/* driver device registration */
static struct platform_driver bf5xx_nand_driver = {
. probe = bf5xx_nand_probe ,
2012-11-19 22:21:24 +04:00
. remove = bf5xx_nand_remove ,
2007-10-03 00:56:05 +04:00
. driver = {
. name = DRV_NAME ,
} ,
} ;
2013-03-18 15:16:49 +04:00
module_platform_driver ( bf5xx_nand_driver ) ;
2007-10-03 00:56:05 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( DRV_AUTHOR ) ;
MODULE_DESCRIPTION ( DRV_DESC ) ;
2008-04-19 00:44:27 +04:00
MODULE_ALIAS ( " platform: " DRV_NAME ) ;