2011-10-05 17:22:34 +04:00
/*
* Handles the M - Systems DiskOnChip G3 chip
*
* Copyright ( C ) 2011 Robert Jarzmik
*
* 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/kernel.h>
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/platform_device.h>
# include <linux/string.h>
# include <linux/slab.h>
# include <linux/io.h>
# include <linux/delay.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/partitions.h>
2011-11-19 19:02:55 +04:00
# include <linux/bitmap.h>
# include <linux/bitrev.h>
# include <linux/bch.h>
2011-10-05 17:22:34 +04:00
# include <linux/debugfs.h>
# include <linux/seq_file.h>
# define CREATE_TRACE_POINTS
# include "docg3.h"
/*
* This driver handles the DiskOnChip G3 flash memory .
*
* As no specification is available from M - Systems / Sandisk , this drivers lacks
* several functions available on the chip , as :
* - IPL write
* - powerdown / powerup
*
* The bus data width ( 8 bits versus 16 bits ) is not handled ( if_cfg flag ) , and
* the driver assumes a 16 bits data bus .
*
* DocG3 relies on 2 ECC algorithms , which are handled in hardware :
* - a 1 byte Hamming code stored in the OOB for each page
* - a 7 bytes BCH code stored in the OOB for each page
2011-11-19 19:02:55 +04:00
* The BCH ECC is :
2011-10-05 17:22:34 +04:00
* - BCH is in GF ( 2 ^ 14 )
* - BCH is over data of 520 bytes ( 512 page + 7 page_info bytes
* + 1 hamming byte )
* - BCH can correct up to 4 bits ( t = 4 )
* - BCH syndroms are calculated in hardware , and checked in hardware as well
*
*/
2011-11-19 19:02:49 +04:00
/**
* struct docg3_oobinfo - DiskOnChip G3 OOB layout
* @ eccbytes : 8 bytes are used ( 1 for Hamming ECC , 7 for BCH ECC )
* @ eccpos : ecc positions ( byte 7 is Hamming ECC , byte 8 - 14 are BCH ECC )
* @ oobfree : free pageinfo bytes ( byte 0 until byte 6 , byte 15
* @ oobavail : 8 available bytes remaining after ECC toll
*/
static struct nand_ecclayout docg3_oobinfo = {
. eccbytes = 8 ,
. eccpos = { 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 } ,
. oobfree = { { 0 , 7 } , { 15 , 1 } } ,
. oobavail = 8 ,
} ;
2011-11-19 19:02:55 +04:00
/**
* struct docg3_bch - BCH engine
*/
static struct bch_control * docg3_bch ;
2011-10-05 17:22:34 +04:00
static inline u8 doc_readb ( struct docg3 * docg3 , u16 reg )
{
u8 val = readb ( docg3 - > base + reg ) ;
trace_docg3_io ( 0 , 8 , reg , ( int ) val ) ;
return val ;
}
static inline u16 doc_readw ( struct docg3 * docg3 , u16 reg )
{
u16 val = readw ( docg3 - > base + reg ) ;
trace_docg3_io ( 0 , 16 , reg , ( int ) val ) ;
return val ;
}
static inline void doc_writeb ( struct docg3 * docg3 , u8 val , u16 reg )
{
writeb ( val , docg3 - > base + reg ) ;
2011-11-19 19:02:44 +04:00
trace_docg3_io ( 1 , 8 , reg , val ) ;
2011-10-05 17:22:34 +04:00
}
static inline void doc_writew ( struct docg3 * docg3 , u16 val , u16 reg )
{
writew ( val , docg3 - > base + reg ) ;
trace_docg3_io ( 1 , 16 , reg , val ) ;
}
static inline void doc_flash_command ( struct docg3 * docg3 , u8 cmd )
{
doc_writeb ( docg3 , cmd , DOC_FLASHCOMMAND ) ;
}
static inline void doc_flash_sequence ( struct docg3 * docg3 , u8 seq )
{
doc_writeb ( docg3 , seq , DOC_FLASHSEQUENCE ) ;
}
static inline void doc_flash_address ( struct docg3 * docg3 , u8 addr )
{
doc_writeb ( docg3 , addr , DOC_FLASHADDRESS ) ;
}
static char const * part_probes [ ] = { " cmdlinepart " , " saftlpart " , NULL } ;
static int doc_register_readb ( struct docg3 * docg3 , int reg )
{
u8 val ;
doc_writew ( docg3 , reg , DOC_READADDRESS ) ;
val = doc_readb ( docg3 , reg ) ;
doc_vdbg ( " Read register %04x : %02x \n " , reg , val ) ;
return val ;
}
static int doc_register_readw ( struct docg3 * docg3 , int reg )
{
u16 val ;
doc_writew ( docg3 , reg , DOC_READADDRESS ) ;
val = doc_readw ( docg3 , reg ) ;
doc_vdbg ( " Read register %04x : %04x \n " , reg , val ) ;
return val ;
}
/**
* doc_delay - delay docg3 operations
* @ docg3 : the device
* @ nbNOPs : the number of NOPs to issue
*
* As no specification is available , the right timings between chip commands are
* unknown . The only available piece of information are the observed nops on a
* working docg3 chip .
* Therefore , doc_delay relies on a busy loop of NOPs , instead of scheduler
* friendlier msleep ( ) functions or blocking mdelay ( ) .
*/
static void doc_delay ( struct docg3 * docg3 , int nbNOPs )
{
int i ;
2011-11-19 19:02:43 +04:00
doc_vdbg ( " NOP x %d \n " , nbNOPs ) ;
2011-10-05 17:22:34 +04:00
for ( i = 0 ; i < nbNOPs ; i + + )
doc_writeb ( docg3 , 0 , DOC_NOP ) ;
}
static int is_prot_seq_error ( struct docg3 * docg3 )
{
int ctrl ;
ctrl = doc_register_readb ( docg3 , DOC_FLASHCONTROL ) ;
return ctrl & ( DOC_CTRL_PROTECTION_ERROR | DOC_CTRL_SEQUENCE_ERROR ) ;
}
static int doc_is_ready ( struct docg3 * docg3 )
{
int ctrl ;
ctrl = doc_register_readb ( docg3 , DOC_FLASHCONTROL ) ;
return ctrl & DOC_CTRL_FLASHREADY ;
}
static int doc_wait_ready ( struct docg3 * docg3 )
{
int maxWaitCycles = 100 ;
do {
doc_delay ( docg3 , 4 ) ;
cpu_relax ( ) ;
} while ( ! doc_is_ready ( docg3 ) & & maxWaitCycles - - ) ;
doc_delay ( docg3 , 2 ) ;
if ( maxWaitCycles > 0 )
return 0 ;
else
return - EIO ;
}
static int doc_reset_seq ( struct docg3 * docg3 )
{
int ret ;
doc_writeb ( docg3 , 0x10 , DOC_FLASHCONTROL ) ;
doc_flash_sequence ( docg3 , DOC_SEQ_RESET ) ;
doc_flash_command ( docg3 , DOC_CMD_RESET ) ;
doc_delay ( docg3 , 2 ) ;
ret = doc_wait_ready ( docg3 ) ;
doc_dbg ( " doc_reset_seq() -> isReady=%s \n " , ret ? " false " : " true " ) ;
return ret ;
}
/**
* doc_read_data_area - Read data from data area
* @ docg3 : the device
2011-11-19 19:02:47 +04:00
* @ buf : the buffer to fill in ( might be NULL is dummy reads )
* @ len : the length to read
2011-10-05 17:22:34 +04:00
* @ first : first time read , DOC_READADDRESS should be set
*
* Reads bytes from flash data . Handles the single byte / even bytes reads .
*/
static void doc_read_data_area ( struct docg3 * docg3 , void * buf , int len ,
int first )
{
int i , cdr , len4 ;
u16 data16 , * dst16 ;
u8 data8 , * dst8 ;
doc_dbg ( " doc_read_data_area(buf=%p, len=%d) \n " , buf , len ) ;
cdr = len & 0x3 ;
len4 = len - cdr ;
if ( first )
doc_writew ( docg3 , DOC_IOSPACE_DATA , DOC_READADDRESS ) ;
dst16 = buf ;
for ( i = 0 ; i < len4 ; i + = 2 ) {
data16 = doc_readw ( docg3 , DOC_IOSPACE_DATA ) ;
2011-11-19 19:02:47 +04:00
if ( dst16 ) {
* dst16 = data16 ;
dst16 + + ;
}
2011-10-05 17:22:34 +04:00
}
if ( cdr ) {
doc_writew ( docg3 , DOC_IOSPACE_DATA | DOC_READADDR_ONE_BYTE ,
DOC_READADDRESS ) ;
doc_delay ( docg3 , 1 ) ;
dst8 = ( u8 * ) dst16 ;
for ( i = 0 ; i < cdr ; i + + ) {
data8 = doc_readb ( docg3 , DOC_IOSPACE_DATA ) ;
2011-11-19 19:02:47 +04:00
if ( dst8 ) {
* dst8 = data8 ;
dst8 + + ;
}
2011-10-05 17:22:34 +04:00
}
}
}
2011-11-19 19:02:52 +04:00
/**
* doc_write_data_area - Write data into data area
* @ docg3 : the device
* @ buf : the buffer to get input bytes from
* @ len : the length to write
*
* Writes bytes into flash data . Handles the single byte / even bytes writes .
*/
static void doc_write_data_area ( struct docg3 * docg3 , const void * buf , int len )
{
int i , cdr , len4 ;
u16 * src16 ;
u8 * src8 ;
doc_dbg ( " doc_write_data_area(buf=%p, len=%d) \n " , buf , len ) ;
cdr = len & 0x3 ;
len4 = len - cdr ;
doc_writew ( docg3 , DOC_IOSPACE_DATA , DOC_READADDRESS ) ;
src16 = ( u16 * ) buf ;
for ( i = 0 ; i < len4 ; i + = 2 ) {
doc_writew ( docg3 , * src16 , DOC_IOSPACE_DATA ) ;
src16 + + ;
}
src8 = ( u8 * ) src16 ;
for ( i = 0 ; i < cdr ; i + + ) {
doc_writew ( docg3 , DOC_IOSPACE_DATA | DOC_READADDR_ONE_BYTE ,
DOC_READADDRESS ) ;
doc_writeb ( docg3 , * src8 , DOC_IOSPACE_DATA ) ;
src8 + + ;
}
}
2011-10-05 17:22:34 +04:00
/**
* doc_set_data_mode - Sets the flash to reliable data mode
* @ docg3 : the device
*
* The reliable data mode is a bit slower than the fast mode , but less errors
* occur . Entering the reliable mode cannot be done without entering the fast
* mode first .
*/
static void doc_set_reliable_mode ( struct docg3 * docg3 )
{
doc_dbg ( " doc_set_reliable_mode() \n " ) ;
doc_flash_sequence ( docg3 , DOC_SEQ_SET_MODE ) ;
doc_flash_command ( docg3 , DOC_CMD_FAST_MODE ) ;
doc_flash_command ( docg3 , DOC_CMD_RELIABLE_MODE ) ;
doc_delay ( docg3 , 2 ) ;
}
/**
* doc_set_asic_mode - Set the ASIC mode
* @ docg3 : the device
* @ mode : the mode
*
* The ASIC can work in 3 modes :
* - RESET : all registers are zeroed
* - NORMAL : receives and handles commands
* - POWERDOWN : minimal poweruse , flash parts shut off
*/
static void doc_set_asic_mode ( struct docg3 * docg3 , u8 mode )
{
int i ;
for ( i = 0 ; i < 12 ; i + + )
doc_readb ( docg3 , DOC_IOSPACE_IPL ) ;
mode | = DOC_ASICMODE_MDWREN ;
doc_dbg ( " doc_set_asic_mode(%02x) \n " , mode ) ;
doc_writeb ( docg3 , mode , DOC_ASICMODE ) ;
doc_writeb ( docg3 , ~ mode , DOC_ASICMODECONFIRM ) ;
doc_delay ( docg3 , 1 ) ;
}
/**
* doc_set_device_id - Sets the devices id for cascaded G3 chips
* @ docg3 : the device
* @ id : the chip to select ( amongst 0 , 1 , 2 , 3 )
*
* There can be 4 cascaded G3 chips . This function selects the one which will
* should be the active one .
*/
static void doc_set_device_id ( struct docg3 * docg3 , int id )
{
u8 ctrl ;
doc_dbg ( " doc_set_device_id(%d) \n " , id ) ;
doc_writeb ( docg3 , id , DOC_DEVICESELECT ) ;
ctrl = doc_register_readb ( docg3 , DOC_FLASHCONTROL ) ;
ctrl & = ~ DOC_CTRL_VIOLATION ;
ctrl | = DOC_CTRL_CE ;
doc_writeb ( docg3 , ctrl , DOC_FLASHCONTROL ) ;
}
/**
* doc_set_extra_page_mode - Change flash page layout
* @ docg3 : the device
*
* Normally , the flash page is split into the data ( 512 bytes ) and the out of
* band data ( 16 bytes ) . For each , 4 more bytes can be accessed , where the wear
* leveling counters are stored . To access this last area of 4 bytes , a special
* mode must be input to the flash ASIC .
*
* Returns 0 if no error occured , - EIO else .
*/
static int doc_set_extra_page_mode ( struct docg3 * docg3 )
{
int fctrl ;
doc_dbg ( " doc_set_extra_page_mode() \n " ) ;
doc_flash_sequence ( docg3 , DOC_SEQ_PAGE_SIZE_532 ) ;
doc_flash_command ( docg3 , DOC_CMD_PAGE_SIZE_532 ) ;
doc_delay ( docg3 , 2 ) ;
fctrl = doc_register_readb ( docg3 , DOC_FLASHCONTROL ) ;
if ( fctrl & ( DOC_CTRL_PROTECTION_ERROR | DOC_CTRL_SEQUENCE_ERROR ) )
return - EIO ;
else
return 0 ;
}
2011-11-19 19:02:52 +04:00
/**
* doc_setup_addr_sector - Setup blocks / page / ofs address for one plane
* @ docg3 : the device
* @ sector : the sector
*/
static void doc_setup_addr_sector ( struct docg3 * docg3 , int sector )
{
doc_delay ( docg3 , 1 ) ;
doc_flash_address ( docg3 , sector & 0xff ) ;
doc_flash_address ( docg3 , ( sector > > 8 ) & 0xff ) ;
doc_flash_address ( docg3 , ( sector > > 16 ) & 0xff ) ;
doc_delay ( docg3 , 1 ) ;
}
/**
* doc_setup_writeaddr_sector - Setup blocks / page / ofs address for one plane
* @ docg3 : the device
* @ sector : the sector
* @ ofs : the offset in the page , between 0 and ( 512 + 16 + 512 )
*/
static void doc_setup_writeaddr_sector ( struct docg3 * docg3 , int sector , int ofs )
{
ofs = ofs > > 2 ;
doc_delay ( docg3 , 1 ) ;
doc_flash_address ( docg3 , ofs & 0xff ) ;
doc_flash_address ( docg3 , sector & 0xff ) ;
doc_flash_address ( docg3 , ( sector > > 8 ) & 0xff ) ;
doc_flash_address ( docg3 , ( sector > > 16 ) & 0xff ) ;
doc_delay ( docg3 , 1 ) ;
}
2011-10-05 17:22:34 +04:00
/**
* doc_seek - Set both flash planes to the specified block , page for reading
* @ docg3 : the device
* @ block0 : the first plane block index
* @ block1 : the second plane block index
* @ page : the page index within the block
* @ wear : if true , read will occur on the 4 extra bytes of the wear area
* @ ofs : offset in page to read
*
* Programs the flash even and odd planes to the specific block and page .
* Alternatively , programs the flash to the wear area of the specified page .
*/
static int doc_read_seek ( struct docg3 * docg3 , int block0 , int block1 , int page ,
int wear , int ofs )
{
int sector , ret = 0 ;
doc_dbg ( " doc_seek(blocks=(%d,%d), page=%d, ofs=%d, wear=%d) \n " ,
block0 , block1 , page , ofs , wear ) ;
if ( ! wear & & ( ofs < 2 * DOC_LAYOUT_PAGE_SIZE ) ) {
doc_flash_sequence ( docg3 , DOC_SEQ_SET_PLANE1 ) ;
doc_flash_command ( docg3 , DOC_CMD_READ_PLANE1 ) ;
doc_delay ( docg3 , 2 ) ;
} else {
doc_flash_sequence ( docg3 , DOC_SEQ_SET_PLANE2 ) ;
doc_flash_command ( docg3 , DOC_CMD_READ_PLANE2 ) ;
doc_delay ( docg3 , 2 ) ;
}
doc_set_reliable_mode ( docg3 ) ;
if ( wear )
ret = doc_set_extra_page_mode ( docg3 ) ;
if ( ret )
goto out ;
doc_flash_sequence ( docg3 , DOC_SEQ_READ ) ;
2011-11-19 19:02:52 +04:00
sector = ( block0 < < DOC_ADDR_BLOCK_SHIFT ) + ( page & DOC_ADDR_PAGE_MASK ) ;
2011-10-05 17:22:34 +04:00
doc_flash_command ( docg3 , DOC_CMD_PROG_BLOCK_ADDR ) ;
2011-11-19 19:02:52 +04:00
doc_setup_addr_sector ( docg3 , sector ) ;
2011-10-05 17:22:34 +04:00
sector = ( block1 < < DOC_ADDR_BLOCK_SHIFT ) + ( page & DOC_ADDR_PAGE_MASK ) ;
doc_flash_command ( docg3 , DOC_CMD_PROG_BLOCK_ADDR ) ;
2011-11-19 19:02:52 +04:00
doc_setup_addr_sector ( docg3 , sector ) ;
2011-10-05 17:22:34 +04:00
doc_delay ( docg3 , 1 ) ;
2011-11-19 19:02:52 +04:00
out :
return ret ;
}
/**
* doc_write_seek - Set both flash planes to the specified block , page for writing
* @ docg3 : the device
* @ block0 : the first plane block index
* @ block1 : the second plane block index
* @ page : the page index within the block
* @ ofs : offset in page to write
*
* Programs the flash even and odd planes to the specific block and page .
* Alternatively , programs the flash to the wear area of the specified page .
*/
static int doc_write_seek ( struct docg3 * docg3 , int block0 , int block1 , int page ,
int ofs )
{
int ret = 0 , sector ;
doc_dbg ( " doc_write_seek(blocks=(%d,%d), page=%d, ofs=%d) \n " ,
block0 , block1 , page , ofs ) ;
doc_set_reliable_mode ( docg3 ) ;
if ( ofs < 2 * DOC_LAYOUT_PAGE_SIZE ) {
doc_flash_sequence ( docg3 , DOC_SEQ_SET_PLANE1 ) ;
doc_flash_command ( docg3 , DOC_CMD_READ_PLANE1 ) ;
doc_delay ( docg3 , 2 ) ;
} else {
doc_flash_sequence ( docg3 , DOC_SEQ_SET_PLANE2 ) ;
doc_flash_command ( docg3 , DOC_CMD_READ_PLANE2 ) ;
doc_delay ( docg3 , 2 ) ;
}
doc_flash_sequence ( docg3 , DOC_SEQ_PAGE_SETUP ) ;
doc_flash_command ( docg3 , DOC_CMD_PROG_CYCLE1 ) ;
sector = ( block0 < < DOC_ADDR_BLOCK_SHIFT ) + ( page & DOC_ADDR_PAGE_MASK ) ;
doc_setup_writeaddr_sector ( docg3 , sector , ofs ) ;
doc_flash_command ( docg3 , DOC_CMD_PROG_CYCLE3 ) ;
2011-10-05 17:22:34 +04:00
doc_delay ( docg3 , 2 ) ;
2011-11-19 19:02:52 +04:00
ret = doc_wait_ready ( docg3 ) ;
if ( ret )
goto out ;
doc_flash_command ( docg3 , DOC_CMD_PROG_CYCLE1 ) ;
sector = ( block1 < < DOC_ADDR_BLOCK_SHIFT ) + ( page & DOC_ADDR_PAGE_MASK ) ;
doc_setup_writeaddr_sector ( docg3 , sector , ofs ) ;
doc_delay ( docg3 , 1 ) ;
2011-10-05 17:22:34 +04:00
out :
return ret ;
}
2011-11-19 19:02:52 +04:00
2011-10-05 17:22:34 +04:00
/**
* doc_read_page_ecc_init - Initialize hardware ECC engine
* @ docg3 : the device
* @ len : the number of bytes covered by the ECC ( BCH covered )
*
* The function does initialize the hardware ECC engine to compute the Hamming
* ECC ( on 1 byte ) and the BCH Syndroms ( on 7 bytes ) .
*
* Return 0 if succeeded , - EIO on error
*/
static int doc_read_page_ecc_init ( struct docg3 * docg3 , int len )
{
doc_writew ( docg3 , DOC_ECCCONF0_READ_MODE
| DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE
| ( len & DOC_ECCCONF0_DATA_BYTES_MASK ) ,
DOC_ECCCONF0 ) ;
doc_delay ( docg3 , 4 ) ;
doc_register_readb ( docg3 , DOC_FLASHCONTROL ) ;
return doc_wait_ready ( docg3 ) ;
}
2011-11-19 19:02:52 +04:00
/**
* doc_write_page_ecc_init - Initialize hardware BCH ECC engine
* @ docg3 : the device
* @ len : the number of bytes covered by the ECC ( BCH covered )
*
* The function does initialize the hardware ECC engine to compute the Hamming
* ECC ( on 1 byte ) and the BCH Syndroms ( on 7 bytes ) .
*
* Return 0 if succeeded , - EIO on error
*/
static int doc_write_page_ecc_init ( struct docg3 * docg3 , int len )
{
doc_writew ( docg3 , ! DOC_ECCCONF0_READ_MODE
| DOC_ECCCONF0_BCH_ENABLE | DOC_ECCCONF0_HAMMING_ENABLE
| ( len & DOC_ECCCONF0_DATA_BYTES_MASK ) ,
DOC_ECCCONF0 ) ;
doc_delay ( docg3 , 4 ) ;
doc_register_readb ( docg3 , DOC_FLASHCONTROL ) ;
return doc_wait_ready ( docg3 ) ;
}
/**
* doc_ecc_disable - Disable Hamming and BCH ECC hardware calculator
* @ docg3 : the device
*
* Disables the hardware ECC generator and checker , for unchecked reads ( as when
* reading OOB only or write status byte ) .
*/
static void doc_ecc_disable ( struct docg3 * docg3 )
{
doc_writew ( docg3 , DOC_ECCCONF0_READ_MODE , DOC_ECCCONF0 ) ;
doc_delay ( docg3 , 4 ) ;
}
/**
* doc_hamming_ecc_init - Initialize hardware Hamming ECC engine
* @ docg3 : the device
* @ nb_bytes : the number of bytes covered by the ECC ( Hamming covered )
*
* This function programs the ECC hardware to compute the hamming code on the
* last provided N bytes to the hardware generator .
*/
static void doc_hamming_ecc_init ( struct docg3 * docg3 , int nb_bytes )
{
u8 ecc_conf1 ;
ecc_conf1 = doc_register_readb ( docg3 , DOC_ECCCONF1 ) ;
ecc_conf1 & = ~ DOC_ECCCONF1_HAMMING_BITS_MASK ;
ecc_conf1 | = ( nb_bytes & DOC_ECCCONF1_HAMMING_BITS_MASK ) ;
doc_writeb ( docg3 , ecc_conf1 , DOC_ECCCONF1 ) ;
}
2011-11-19 19:02:55 +04:00
/**
* doc_correct_data - Fix if need be read data from flash
* @ docg3 : the device
* @ buf : the buffer of read data ( 512 + 7 + 1 bytes )
* @ hwecc : the hardware calculated ECC .
* It ' s in fact recv_ecc ^ calc_ecc , where recv_ecc was read from OOB
* area data , and calc_ecc the ECC calculated by the hardware generator .
*
* Checks if the received data matches the ECC , and if an error is detected ,
* tries to fix the bit flips ( at most 4 ) in the buffer buf . As the docg3
* understands the ( data , ecc , syndroms ) in an inverted order in comparison to
* the BCH library , the function reverses the order of bits ( ie . bit7 and bit0 ,
* bit6 and bit 1 , . . . ) for all ECC data .
*
* The hardware ecc unit produces oob_ecc ^ calc_ecc . The kernel ' s bch
* algorithm is used to decode this . However the hw operates on page
* data in a bit order that is the reverse of that of the bch alg ,
* requiring that the bits be reversed on the result . Thanks to Ivan
* Djelic for his analysis .
*
* Returns number of fixed bits ( 0 , 1 , 2 , 3 , 4 ) or - EBADMSG if too many bit
* errors were detected and cannot be fixed .
*/
static int doc_ecc_bch_fix_data ( struct docg3 * docg3 , void * buf , u8 * hwecc )
{
u8 ecc [ DOC_ECC_BCH_SIZE ] ;
int errorpos [ DOC_ECC_BCH_T ] , i , numerrs ;
for ( i = 0 ; i < DOC_ECC_BCH_SIZE ; i + + )
ecc [ i ] = bitrev8 ( hwecc [ i ] ) ;
numerrs = decode_bch ( docg3_bch , NULL , DOC_ECC_BCH_COVERED_BYTES ,
NULL , ecc , NULL , errorpos ) ;
BUG_ON ( numerrs = = - EINVAL ) ;
if ( numerrs < 0 )
goto out ;
for ( i = 0 ; i < numerrs ; i + + )
errorpos [ i ] = ( errorpos [ i ] & ~ 7 ) | ( 7 - ( errorpos [ i ] & 7 ) ) ;
for ( i = 0 ; i < numerrs ; i + + )
if ( errorpos [ i ] < DOC_ECC_BCH_COVERED_BYTES * 8 )
/* error is located in data, correct it */
change_bit ( errorpos [ i ] , buf ) ;
out :
doc_dbg ( " doc_ecc_bch_fix_data: flipped %d bits \n " , numerrs ) ;
return numerrs ;
}
2011-10-05 17:22:34 +04:00
/**
* doc_read_page_prepare - Prepares reading data from a flash page
* @ docg3 : the device
* @ block0 : the first plane block index on flash memory
* @ block1 : the second plane block index on flash memory
* @ page : the page index in the block
* @ offset : the offset in the page ( must be a multiple of 4 )
*
* Prepares the page to be read in the flash memory :
* - tell ASIC to map the flash pages
* - tell ASIC to be in read mode
*
* After a call to this method , a call to doc_read_page_finish is mandatory ,
* to end the read cycle of the flash .
*
* Read data from a flash page . The length to be read must be between 0 and
* ( page_size + oob_size + wear_size ) , ie . 532 , and a multiple of 4 ( because
* the extra bytes reading is not implemented ) .
*
* As pages are grouped by 2 ( in 2 planes ) , reading from a page must be done
* in two steps :
* - one read of 512 bytes at offset 0
* - one read of 512 bytes at offset 512 + 16
*
* Returns 0 if successful , - EIO if a read error occured .
*/
static int doc_read_page_prepare ( struct docg3 * docg3 , int block0 , int block1 ,
int page , int offset )
{
int wear_area = 0 , ret = 0 ;
doc_dbg ( " doc_read_page_prepare(blocks=(%d,%d), page=%d, ofsInPage=%d) \n " ,
block0 , block1 , page , offset ) ;
if ( offset > = DOC_LAYOUT_WEAR_OFFSET )
wear_area = 1 ;
if ( ! wear_area & & offset > ( DOC_LAYOUT_PAGE_OOB_SIZE * 2 ) )
return - EINVAL ;
doc_set_device_id ( docg3 , docg3 - > device_id ) ;
ret = doc_reset_seq ( docg3 ) ;
if ( ret )
goto err ;
/* Program the flash address block and page */
ret = doc_read_seek ( docg3 , block0 , block1 , page , wear_area , offset ) ;
if ( ret )
goto err ;
doc_flash_command ( docg3 , DOC_CMD_READ_ALL_PLANES ) ;
doc_delay ( docg3 , 2 ) ;
doc_wait_ready ( docg3 ) ;
doc_flash_command ( docg3 , DOC_CMD_SET_ADDR_READ ) ;
doc_delay ( docg3 , 1 ) ;
if ( offset > = DOC_LAYOUT_PAGE_SIZE * 2 )
offset - = 2 * DOC_LAYOUT_PAGE_SIZE ;
doc_flash_address ( docg3 , offset > > 2 ) ;
doc_delay ( docg3 , 1 ) ;
doc_wait_ready ( docg3 ) ;
doc_flash_command ( docg3 , DOC_CMD_READ_FLASH ) ;
return 0 ;
err :
doc_writeb ( docg3 , 0 , DOC_DATAEND ) ;
doc_delay ( docg3 , 2 ) ;
return - EIO ;
}
/**
* doc_read_page_getbytes - Reads bytes from a prepared page
* @ docg3 : the device
* @ len : the number of bytes to be read ( must be a multiple of 4 )
* @ buf : the buffer to be filled in
* @ first : 1 if first time read , DOC_READADDRESS should be set
*
*/
static int doc_read_page_getbytes ( struct docg3 * docg3 , int len , u_char * buf ,
int first )
{
doc_read_data_area ( docg3 , buf , len , first ) ;
doc_delay ( docg3 , 2 ) ;
return len ;
}
2011-11-19 19:02:52 +04:00
/**
* doc_write_page_putbytes - Writes bytes into a prepared page
* @ docg3 : the device
* @ len : the number of bytes to be written
* @ buf : the buffer of input bytes
*
*/
static void doc_write_page_putbytes ( struct docg3 * docg3 , int len ,
const u_char * buf )
{
doc_write_data_area ( docg3 , buf , len ) ;
doc_delay ( docg3 , 2 ) ;
}
2011-10-05 17:22:34 +04:00
/**
* doc_get_hw_bch_syndroms - Get hardware calculated BCH syndroms
* @ docg3 : the device
* @ syns : the array of 7 integers where the syndroms will be stored
*/
2011-11-19 19:02:52 +04:00
static void doc_get_hw_bch_syndroms ( struct docg3 * docg3 , u8 * syns )
2011-10-05 17:22:34 +04:00
{
int i ;
for ( i = 0 ; i < DOC_ECC_BCH_SIZE ; i + + )
syns [ i ] = doc_register_readb ( docg3 , DOC_BCH_SYNDROM ( i ) ) ;
}
2011-11-19 19:02:52 +04:00
/**
* doc_page_finish - Ends reading / writing of a flash page
* @ docg3 : the device
*/
static void doc_page_finish ( struct docg3 * docg3 )
{
doc_writeb ( docg3 , 0 , DOC_DATAEND ) ;
doc_delay ( docg3 , 2 ) ;
}
2011-10-05 17:22:34 +04:00
/**
* doc_read_page_finish - Ends reading of a flash page
* @ docg3 : the device
*
* As a side effect , resets the chip selector to 0. This ensures that after each
* read operation , the floor 0 is selected . Therefore , if the systems halts , the
* reboot will boot on floor 0 , where the IPL is .
*/
static void doc_read_page_finish ( struct docg3 * docg3 )
{
2011-11-19 19:02:52 +04:00
doc_page_finish ( docg3 ) ;
2011-10-05 17:22:34 +04:00
doc_set_device_id ( docg3 , 0 ) ;
}
/**
* calc_block_sector - Calculate blocks , pages and ofs .
* @ from : offset in flash
* @ block0 : first plane block index calculated
* @ block1 : second plane block index calculated
* @ page : page calculated
* @ ofs : offset in page
*/
static void calc_block_sector ( loff_t from , int * block0 , int * block1 , int * page ,
int * ofs )
{
uint sector ;
sector = from / DOC_LAYOUT_PAGE_SIZE ;
* block0 = sector / ( DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_NBPLANES )
* DOC_LAYOUT_NBPLANES ;
* block1 = * block0 + 1 ;
* page = sector % ( DOC_LAYOUT_PAGES_PER_BLOCK * DOC_LAYOUT_NBPLANES ) ;
* page / = DOC_LAYOUT_NBPLANES ;
if ( sector % 2 )
* ofs = DOC_LAYOUT_PAGE_OOB_SIZE ;
else
* ofs = 0 ;
}
/**
2011-11-19 19:02:47 +04:00
* doc_read_oob - Read out of band bytes from flash
2011-10-05 17:22:34 +04:00
* @ mtd : the device
* @ from : the offset from first block and first page , in bytes , aligned on page
* size
2011-11-19 19:02:47 +04:00
* @ ops : the mtd oob structure
2011-10-05 17:22:34 +04:00
*
2011-11-19 19:02:47 +04:00
* Reads flash memory OOB area of pages .
2011-10-05 17:22:34 +04:00
*
* Returns 0 if read successfull , of - EIO , - EINVAL if an error occured
*/
2011-11-19 19:02:47 +04:00
static int doc_read_oob ( struct mtd_info * mtd , loff_t from ,
struct mtd_oob_ops * ops )
2011-10-05 17:22:34 +04:00
{
struct docg3 * docg3 = mtd - > priv ;
2011-11-19 19:02:47 +04:00
int block0 , block1 , page , ret , ofs = 0 ;
u8 * oobbuf = ops - > oobbuf ;
u8 * buf = ops - > datbuf ;
size_t len , ooblen , nbdata , nboob ;
2011-11-19 19:02:55 +04:00
u8 hwecc [ DOC_ECC_BCH_SIZE ] , eccconf1 ;
2011-11-19 19:02:47 +04:00
if ( buf )
len = ops - > len ;
else
len = 0 ;
if ( oobbuf )
ooblen = ops - > ooblen ;
else
ooblen = 0 ;
if ( oobbuf & & ops - > mode = = MTD_OPS_PLACE_OOB )
oobbuf + = ops - > ooboffs ;
doc_dbg ( " doc_read_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu)) \n " ,
from , ops - > mode , buf , len , oobbuf , ooblen ) ;
if ( ( len % DOC_LAYOUT_PAGE_SIZE ) | | ( ooblen % DOC_LAYOUT_OOB_SIZE ) | |
( from % DOC_LAYOUT_PAGE_SIZE ) )
return - EINVAL ;
2011-10-05 17:22:34 +04:00
ret = - EINVAL ;
2011-11-19 19:02:47 +04:00
calc_block_sector ( from + len , & block0 , & block1 , & page , & ofs ) ;
2011-10-05 17:22:34 +04:00
if ( block1 > docg3 - > max_block )
goto err ;
2011-11-19 19:02:47 +04:00
ops - > oobretlen = 0 ;
ops - > retlen = 0 ;
2011-10-05 17:22:34 +04:00
ret = 0 ;
2011-11-19 19:02:47 +04:00
while ( ! ret & & ( len > 0 | | ooblen > 0 ) ) {
calc_block_sector ( from , & block0 , & block1 , & page , & ofs ) ;
nbdata = min_t ( size_t , len , ( size_t ) DOC_LAYOUT_PAGE_SIZE ) ;
nboob = min_t ( size_t , ooblen , ( size_t ) DOC_LAYOUT_OOB_SIZE ) ;
2011-10-05 17:22:34 +04:00
ret = doc_read_page_prepare ( docg3 , block0 , block1 , page , ofs ) ;
if ( ret < 0 )
goto err ;
2011-11-19 19:02:55 +04:00
ret = doc_read_page_ecc_init ( docg3 , DOC_ECC_BCH_TOTAL_BYTES ) ;
2011-10-05 17:22:34 +04:00
if ( ret < 0 )
goto err_in_read ;
2011-11-19 19:02:47 +04:00
ret = doc_read_page_getbytes ( docg3 , nbdata , buf , 1 ) ;
if ( ret < nbdata )
2011-10-05 17:22:34 +04:00
goto err_in_read ;
2011-11-19 19:02:47 +04:00
doc_read_page_getbytes ( docg3 , DOC_LAYOUT_PAGE_SIZE - nbdata ,
NULL , 0 ) ;
ret = doc_read_page_getbytes ( docg3 , nboob , oobbuf , 0 ) ;
if ( ret < nboob )
2011-10-05 17:22:34 +04:00
goto err_in_read ;
2011-11-19 19:02:47 +04:00
doc_read_page_getbytes ( docg3 , DOC_LAYOUT_OOB_SIZE - nboob ,
NULL , 0 ) ;
2011-10-05 17:22:34 +04:00
2011-11-19 19:02:55 +04:00
doc_get_hw_bch_syndroms ( docg3 , hwecc ) ;
2011-10-05 17:22:34 +04:00
eccconf1 = doc_register_readb ( docg3 , DOC_ECCCONF1 ) ;
2011-11-19 19:02:47 +04:00
if ( nboob > = DOC_LAYOUT_OOB_SIZE ) {
doc_dbg ( " OOB - INFO: %02x:%02x:%02x:%02x:%02x:%02x:%02x \n " ,
oobbuf [ 0 ] , oobbuf [ 1 ] , oobbuf [ 2 ] , oobbuf [ 3 ] ,
oobbuf [ 4 ] , oobbuf [ 5 ] , oobbuf [ 6 ] ) ;
doc_dbg ( " OOB - HAMMING: %02x \n " , oobbuf [ 7 ] ) ;
doc_dbg ( " OOB - BCH_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x \n " ,
oobbuf [ 8 ] , oobbuf [ 9 ] , oobbuf [ 10 ] , oobbuf [ 11 ] ,
oobbuf [ 12 ] , oobbuf [ 13 ] , oobbuf [ 14 ] ) ;
doc_dbg ( " OOB - UNUSED: %02x \n " , oobbuf [ 15 ] ) ;
}
2011-10-05 17:22:34 +04:00
doc_dbg ( " ECC checks: ECCConf1=%x \n " , eccconf1 ) ;
2011-11-19 19:02:55 +04:00
doc_dbg ( " ECC HW_ECC: %02x:%02x:%02x:%02x:%02x:%02x:%02x \n " ,
hwecc [ 0 ] , hwecc [ 1 ] , hwecc [ 2 ] , hwecc [ 3 ] , hwecc [ 4 ] ,
hwecc [ 5 ] , hwecc [ 6 ] ) ;
ret = - EIO ;
if ( is_prot_seq_error ( docg3 ) )
goto err_in_read ;
ret = 0 ;
if ( ( block0 > = DOC_LAYOUT_BLOCK_FIRST_DATA ) & &
( eccconf1 & DOC_ECCCONF1_BCH_SYNDROM_ERR ) & &
( eccconf1 & DOC_ECCCONF1_PAGE_IS_WRITTEN ) & &
( ops - > mode ! = MTD_OPS_RAW ) & &
( nbdata = = DOC_LAYOUT_PAGE_SIZE ) ) {
ret = doc_ecc_bch_fix_data ( docg3 , buf , hwecc ) ;
if ( ret < 0 ) {
mtd - > ecc_stats . failed + + ;
ret = - EBADMSG ;
}
if ( ret > 0 ) {
mtd - > ecc_stats . corrected + = ret ;
ret = - EUCLEAN ;
}
2011-10-05 17:22:34 +04:00
}
2011-11-19 19:02:47 +04:00
2011-10-05 17:22:34 +04:00
doc_read_page_finish ( docg3 ) ;
2011-11-19 19:02:47 +04:00
ops - > retlen + = nbdata ;
ops - > oobretlen + = nboob ;
buf + = nbdata ;
oobbuf + = nboob ;
len - = nbdata ;
ooblen - = nboob ;
from + = DOC_LAYOUT_PAGE_SIZE ;
2011-10-05 17:22:34 +04:00
}
2011-11-19 19:02:55 +04:00
return ret ;
2011-10-05 17:22:34 +04:00
err_in_read :
doc_read_page_finish ( docg3 ) ;
err :
return ret ;
}
/**
2011-11-19 19:02:47 +04:00
* doc_read - Read bytes from flash
2011-10-05 17:22:34 +04:00
* @ mtd : the device
* @ from : the offset from first block and first page , in bytes , aligned on page
* size
2011-11-19 19:02:47 +04:00
* @ len : the number of bytes to read ( must be a multiple of 4 )
* @ retlen : the number of bytes actually read
* @ buf : the filled in buffer
2011-10-05 17:22:34 +04:00
*
2011-11-19 19:02:47 +04:00
* Reads flash memory pages . This function does not read the OOB chunk , but only
* the page data .
2011-10-05 17:22:34 +04:00
*
* Returns 0 if read successfull , of - EIO , - EINVAL if an error occured
*/
2011-11-19 19:02:47 +04:00
static int doc_read ( struct mtd_info * mtd , loff_t from , size_t len ,
size_t * retlen , u_char * buf )
2011-10-05 17:22:34 +04:00
{
2011-11-19 19:02:47 +04:00
struct mtd_oob_ops ops ;
size_t ret ;
2011-10-05 17:22:34 +04:00
2011-11-19 19:02:47 +04:00
memset ( & ops , 0 , sizeof ( ops ) ) ;
ops . datbuf = buf ;
ops . len = len ;
ops . mode = MTD_OPS_AUTO_OOB ;
2011-10-05 17:22:34 +04:00
2011-11-19 19:02:47 +04:00
ret = doc_read_oob ( mtd , from , & ops ) ;
* retlen = ops . retlen ;
return ret ;
2011-10-05 17:22:34 +04:00
}
static int doc_reload_bbt ( struct docg3 * docg3 )
{
int block = DOC_LAYOUT_BLOCK_BBT ;
int ret = 0 , nbpages , page ;
u_char * buf = docg3 - > bbt ;
nbpages = DIV_ROUND_UP ( docg3 - > max_block + 1 , 8 * DOC_LAYOUT_PAGE_SIZE ) ;
for ( page = 0 ; ! ret & & ( page < nbpages ) ; page + + ) {
ret = doc_read_page_prepare ( docg3 , block , block + 1 ,
page + DOC_LAYOUT_PAGE_BBT , 0 ) ;
if ( ! ret )
ret = doc_read_page_ecc_init ( docg3 ,
DOC_LAYOUT_PAGE_SIZE ) ;
if ( ! ret )
doc_read_page_getbytes ( docg3 , DOC_LAYOUT_PAGE_SIZE ,
buf , 1 ) ;
buf + = DOC_LAYOUT_PAGE_SIZE ;
}
doc_read_page_finish ( docg3 ) ;
return ret ;
}
/**
* doc_block_isbad - Checks whether a block is good or not
* @ mtd : the device
* @ from : the offset to find the correct block
*
* Returns 1 if block is bad , 0 if block is good
*/
static int doc_block_isbad ( struct mtd_info * mtd , loff_t from )
{
struct docg3 * docg3 = mtd - > priv ;
int block0 , block1 , page , ofs , is_good ;
calc_block_sector ( from , & block0 , & block1 , & page , & ofs ) ;
doc_dbg ( " doc_block_isbad(from=%lld) => block=(%d,%d), page=%d, ofs=%d \n " ,
from , block0 , block1 , page , ofs ) ;
if ( block0 < DOC_LAYOUT_BLOCK_FIRST_DATA )
return 0 ;
if ( block1 > docg3 - > max_block )
return - EINVAL ;
is_good = docg3 - > bbt [ block0 > > 3 ] & ( 1 < < ( block0 & 0x7 ) ) ;
return ! is_good ;
}
/**
* doc_get_erase_count - Get block erase count
* @ docg3 : the device
* @ from : the offset in which the block is .
*
* Get the number of times a block was erased . The number is the maximum of
* erase times between first and second plane ( which should be equal normally ) .
*
* Returns The number of erases , or - EINVAL or - EIO on error .
*/
static int doc_get_erase_count ( struct docg3 * docg3 , loff_t from )
{
u8 buf [ DOC_LAYOUT_WEAR_SIZE ] ;
int ret , plane1_erase_count , plane2_erase_count ;
int block0 , block1 , page , ofs ;
doc_dbg ( " doc_get_erase_count(from=%lld, buf=%p) \n " , from , buf ) ;
if ( from % DOC_LAYOUT_PAGE_SIZE )
return - EINVAL ;
calc_block_sector ( from , & block0 , & block1 , & page , & ofs ) ;
if ( block1 > docg3 - > max_block )
return - EINVAL ;
ret = doc_reset_seq ( docg3 ) ;
if ( ! ret )
ret = doc_read_page_prepare ( docg3 , block0 , block1 , page ,
ofs + DOC_LAYOUT_WEAR_OFFSET ) ;
if ( ! ret )
ret = doc_read_page_getbytes ( docg3 , DOC_LAYOUT_WEAR_SIZE ,
buf , 1 ) ;
doc_read_page_finish ( docg3 ) ;
if ( ret | | ( buf [ 0 ] ! = DOC_ERASE_MARK ) | | ( buf [ 2 ] ! = DOC_ERASE_MARK ) )
return - EIO ;
plane1_erase_count = ( u8 ) ( ~ buf [ 1 ] ) | ( ( u8 ) ( ~ buf [ 4 ] ) < < 8 )
| ( ( u8 ) ( ~ buf [ 5 ] ) < < 16 ) ;
plane2_erase_count = ( u8 ) ( ~ buf [ 3 ] ) | ( ( u8 ) ( ~ buf [ 6 ] ) < < 8 )
| ( ( u8 ) ( ~ buf [ 7 ] ) < < 16 ) ;
return max ( plane1_erase_count , plane2_erase_count ) ;
}
2011-11-19 19:02:52 +04:00
/**
* doc_get_op_status - get erase / write operation status
* @ docg3 : the device
*
* Queries the status from the chip , and returns it
*
* Returns the status ( bits DOC_PLANES_STATUS_ * )
*/
static int doc_get_op_status ( struct docg3 * docg3 )
{
u8 status ;
doc_flash_sequence ( docg3 , DOC_SEQ_PLANES_STATUS ) ;
doc_flash_command ( docg3 , DOC_CMD_PLANES_STATUS ) ;
doc_delay ( docg3 , 5 ) ;
doc_ecc_disable ( docg3 ) ;
doc_read_data_area ( docg3 , & status , 1 , 1 ) ;
return status ;
}
/**
* doc_write_erase_wait_status - wait for write or erase completion
* @ docg3 : the device
*
* Wait for the chip to be ready again after erase or write operation , and check
* erase / write status .
*
* Returns 0 if erase successfull , - EIO if erase / write issue , - ETIMEOUT if
* timeout
*/
static int doc_write_erase_wait_status ( struct docg3 * docg3 )
{
int status , ret = 0 ;
if ( ! doc_is_ready ( docg3 ) )
usleep_range ( 3000 , 3000 ) ;
if ( ! doc_is_ready ( docg3 ) ) {
doc_dbg ( " Timeout reached and the chip is still not ready \n " ) ;
ret = - EAGAIN ;
goto out ;
}
status = doc_get_op_status ( docg3 ) ;
if ( status & DOC_PLANES_STATUS_FAIL ) {
doc_dbg ( " Erase/Write failed on (a) plane(s), status = %x \n " ,
status ) ;
ret = - EIO ;
}
out :
doc_page_finish ( docg3 ) ;
return ret ;
}
2011-11-19 19:02:53 +04:00
/**
* doc_erase_block - Erase a couple of blocks
* @ docg3 : the device
* @ block0 : the first block to erase ( leftmost plane )
* @ block1 : the second block to erase ( rightmost plane )
*
* Erase both blocks , and return operation status
*
* Returns 0 if erase successful , - EIO if erase issue , - ETIMEOUT if chip not
* ready for too long
*/
static int doc_erase_block ( struct docg3 * docg3 , int block0 , int block1 )
{
int ret , sector ;
doc_dbg ( " doc_erase_block(blocks=(%d,%d)) \n " , block0 , block1 ) ;
ret = doc_reset_seq ( docg3 ) ;
if ( ret )
return - EIO ;
doc_set_reliable_mode ( docg3 ) ;
doc_flash_sequence ( docg3 , DOC_SEQ_ERASE ) ;
sector = block0 < < DOC_ADDR_BLOCK_SHIFT ;
doc_flash_command ( docg3 , DOC_CMD_PROG_BLOCK_ADDR ) ;
doc_setup_addr_sector ( docg3 , sector ) ;
sector = block1 < < DOC_ADDR_BLOCK_SHIFT ;
doc_flash_command ( docg3 , DOC_CMD_PROG_BLOCK_ADDR ) ;
doc_setup_addr_sector ( docg3 , sector ) ;
doc_delay ( docg3 , 1 ) ;
doc_flash_command ( docg3 , DOC_CMD_ERASECYCLE2 ) ;
doc_delay ( docg3 , 2 ) ;
if ( is_prot_seq_error ( docg3 ) ) {
doc_err ( " Erase blocks %d,%d error \n " , block0 , block1 ) ;
return - EIO ;
}
return doc_write_erase_wait_status ( docg3 ) ;
}
/**
* doc_erase - Erase a portion of the chip
* @ mtd : the device
* @ info : the erase info
*
* Erase a bunch of contiguous blocks , by pairs , as a " mtd " page of 1024 is
* split into 2 pages of 512 bytes on 2 contiguous blocks .
*
* Returns 0 if erase successful , - EINVAL if adressing error , - EIO if erase
* issue
*/
static int doc_erase ( struct mtd_info * mtd , struct erase_info * info )
{
struct docg3 * docg3 = mtd - > priv ;
uint64_t len ;
int block0 , block1 , page , ret , ofs = 0 ;
doc_dbg ( " doc_erase(from=%lld, len=%lld \n " , info - > addr , info - > len ) ;
doc_set_device_id ( docg3 , docg3 - > device_id ) ;
info - > state = MTD_ERASE_PENDING ;
calc_block_sector ( info - > addr + info - > len ,
& block0 , & block1 , & page , & ofs ) ;
ret = - EINVAL ;
if ( block1 > docg3 - > max_block | | page | | ofs )
goto reset_err ;
ret = 0 ;
calc_block_sector ( info - > addr , & block0 , & block1 , & page , & ofs ) ;
doc_set_reliable_mode ( docg3 ) ;
for ( len = info - > len ; ! ret & & len > 0 ; len - = mtd - > erasesize ) {
info - > state = MTD_ERASING ;
ret = doc_erase_block ( docg3 , block0 , block1 ) ;
block0 + = 2 ;
block1 + = 2 ;
}
if ( ret )
goto reset_err ;
info - > state = MTD_ERASE_DONE ;
return 0 ;
reset_err :
info - > state = MTD_ERASE_FAILED ;
return ret ;
}
2011-11-19 19:02:52 +04:00
/**
* doc_write_page - Write a single page to the chip
* @ docg3 : the device
* @ to : the offset from first block and first page , in bytes , aligned on page
* size
* @ buf : buffer to get bytes from
* @ oob : buffer to get out of band bytes from ( can be NULL if no OOB should be
* written )
* @ autoecc : if 0 , all 16 bytes from OOB are taken , regardless of HW Hamming or
* BCH computations . If 1 , only bytes 0 - 7 and byte 15 are taken ,
* remaining ones are filled with hardware Hamming and BCH
* computations . Its value is not meaningfull is oob = = NULL .
*
* Write one full page ( ie . 1 page split on two planes ) , of 512 bytes , with the
* OOB data . The OOB ECC is automatically computed by the hardware Hamming and
* BCH generator if autoecc is not null .
*
* Returns 0 if write successful , - EIO if write error , - EAGAIN if timeout
*/
static int doc_write_page ( struct docg3 * docg3 , loff_t to , const u_char * buf ,
const u_char * oob , int autoecc )
{
int block0 , block1 , page , ret , ofs = 0 ;
u8 syn [ DOC_ECC_BCH_SIZE ] , hamming ;
doc_dbg ( " doc_write_page(to=%lld) \n " , to ) ;
calc_block_sector ( to , & block0 , & block1 , & page , & ofs ) ;
doc_set_device_id ( docg3 , docg3 - > device_id ) ;
ret = doc_reset_seq ( docg3 ) ;
if ( ret )
goto err ;
/* Program the flash address block and page */
ret = doc_write_seek ( docg3 , block0 , block1 , page , ofs ) ;
if ( ret )
goto err ;
2011-11-19 19:02:55 +04:00
doc_write_page_ecc_init ( docg3 , DOC_ECC_BCH_TOTAL_BYTES ) ;
2011-11-19 19:02:52 +04:00
doc_delay ( docg3 , 2 ) ;
doc_write_page_putbytes ( docg3 , DOC_LAYOUT_PAGE_SIZE , buf ) ;
if ( oob & & autoecc ) {
doc_write_page_putbytes ( docg3 , DOC_LAYOUT_OOB_PAGEINFO_SZ , oob ) ;
doc_delay ( docg3 , 2 ) ;
oob + = DOC_LAYOUT_OOB_UNUSED_OFS ;
hamming = doc_register_readb ( docg3 , DOC_HAMMINGPARITY ) ;
doc_delay ( docg3 , 2 ) ;
doc_write_page_putbytes ( docg3 , DOC_LAYOUT_OOB_HAMMING_SZ ,
& hamming ) ;
doc_delay ( docg3 , 2 ) ;
doc_get_hw_bch_syndroms ( docg3 , syn ) ;
doc_write_page_putbytes ( docg3 , DOC_LAYOUT_OOB_BCH_SZ , syn ) ;
doc_delay ( docg3 , 2 ) ;
doc_write_page_putbytes ( docg3 , DOC_LAYOUT_OOB_UNUSED_SZ , oob ) ;
}
if ( oob & & ! autoecc )
doc_write_page_putbytes ( docg3 , DOC_LAYOUT_OOB_SIZE , oob ) ;
doc_delay ( docg3 , 2 ) ;
doc_page_finish ( docg3 ) ;
doc_delay ( docg3 , 2 ) ;
doc_flash_command ( docg3 , DOC_CMD_PROG_CYCLE2 ) ;
doc_delay ( docg3 , 2 ) ;
/*
* The wait status will perform another doc_page_finish ( ) call , but that
* seems to please the docg3 , so leave it .
*/
ret = doc_write_erase_wait_status ( docg3 ) ;
return ret ;
err :
doc_read_page_finish ( docg3 ) ;
return ret ;
}
/**
* doc_guess_autoecc - Guess autoecc mode from mbd_oob_ops
* @ ops : the oob operations
*
* Returns 0 or 1 if success , - EINVAL if invalid oob mode
*/
static int doc_guess_autoecc ( struct mtd_oob_ops * ops )
{
int autoecc ;
switch ( ops - > mode ) {
case MTD_OPS_PLACE_OOB :
case MTD_OPS_AUTO_OOB :
autoecc = 1 ;
break ;
case MTD_OPS_RAW :
autoecc = 0 ;
break ;
default :
autoecc = - EINVAL ;
}
return autoecc ;
}
/**
* doc_fill_autooob - Fill a 16 bytes OOB from 8 non - ECC bytes
* @ dst : the target 16 bytes OOB buffer
* @ oobsrc : the source 8 bytes non - ECC OOB buffer
*
*/
static void doc_fill_autooob ( u8 * dst , u8 * oobsrc )
{
memcpy ( dst , oobsrc , DOC_LAYOUT_OOB_PAGEINFO_SZ ) ;
dst [ DOC_LAYOUT_OOB_UNUSED_OFS ] = oobsrc [ DOC_LAYOUT_OOB_PAGEINFO_SZ ] ;
}
/**
* doc_backup_oob - Backup OOB into docg3 structure
* @ docg3 : the device
* @ to : the page offset in the chip
* @ ops : the OOB size and buffer
*
* As the docg3 should write a page with its OOB in one pass , and some userland
* applications do write_oob ( ) to setup the OOB and then write ( ) , store the OOB
* into a temporary storage . This is very dangerous , as 2 concurrent
* applications could store an OOB , and then write their pages ( which will
* result into one having its OOB corrupted ) .
*
* The only reliable way would be for userland to call doc_write_oob ( ) with both
* the page data _and_ the OOB area .
*
* Returns 0 if success , - EINVAL if ops content invalid
*/
static int doc_backup_oob ( struct docg3 * docg3 , loff_t to ,
struct mtd_oob_ops * ops )
{
int ooblen = ops - > ooblen , autoecc ;
if ( ooblen ! = DOC_LAYOUT_OOB_SIZE )
return - EINVAL ;
autoecc = doc_guess_autoecc ( ops ) ;
if ( autoecc < 0 )
return autoecc ;
docg3 - > oob_write_ofs = to ;
docg3 - > oob_autoecc = autoecc ;
if ( ops - > mode = = MTD_OPS_AUTO_OOB ) {
doc_fill_autooob ( docg3 - > oob_write_buf , ops - > oobbuf ) ;
ops - > oobretlen = 8 ;
} else {
memcpy ( docg3 - > oob_write_buf , ops - > oobbuf , DOC_LAYOUT_OOB_SIZE ) ;
ops - > oobretlen = DOC_LAYOUT_OOB_SIZE ;
}
return 0 ;
}
/**
* doc_write_oob - Write out of band bytes to flash
* @ mtd : the device
* @ ofs : the offset from first block and first page , in bytes , aligned on page
* size
* @ ops : the mtd oob structure
*
* Either write OOB data into a temporary buffer , for the subsequent write
* page . The provided OOB should be 16 bytes long . If a data buffer is provided
* as well , issue the page write .
* Or provide data without OOB , and then a all zeroed OOB will be used ( ECC will
* still be filled in if asked for ) .
*
* Returns 0 is successfull , EINVAL if length is not 14 bytes
*/
static int doc_write_oob ( struct mtd_info * mtd , loff_t ofs ,
struct mtd_oob_ops * ops )
{
struct docg3 * docg3 = mtd - > priv ;
int block0 , block1 , page , ret , pofs = 0 , autoecc , oobdelta ;
u8 * oobbuf = ops - > oobbuf ;
u8 * buf = ops - > datbuf ;
size_t len , ooblen ;
u8 oob [ DOC_LAYOUT_OOB_SIZE ] ;
if ( buf )
len = ops - > len ;
else
len = 0 ;
if ( oobbuf )
ooblen = ops - > ooblen ;
else
ooblen = 0 ;
if ( oobbuf & & ops - > mode = = MTD_OPS_PLACE_OOB )
oobbuf + = ops - > ooboffs ;
doc_dbg ( " doc_write_oob(from=%lld, mode=%d, data=(%p:%zu), oob=(%p:%zu)) \n " ,
ofs , ops - > mode , buf , len , oobbuf , ooblen ) ;
switch ( ops - > mode ) {
case MTD_OPS_PLACE_OOB :
case MTD_OPS_RAW :
oobdelta = mtd - > oobsize ;
break ;
case MTD_OPS_AUTO_OOB :
oobdelta = mtd - > ecclayout - > oobavail ;
break ;
default :
oobdelta = 0 ;
}
if ( ( len % DOC_LAYOUT_PAGE_SIZE ) | | ( ooblen % oobdelta ) | |
( ofs % DOC_LAYOUT_PAGE_SIZE ) )
return - EINVAL ;
if ( len & & ooblen & &
( len / DOC_LAYOUT_PAGE_SIZE ) ! = ( ooblen / oobdelta ) )
return - EINVAL ;
ret = - EINVAL ;
calc_block_sector ( ofs + len , & block0 , & block1 , & page , & pofs ) ;
if ( block1 > docg3 - > max_block )
goto err ;
ops - > oobretlen = 0 ;
ops - > retlen = 0 ;
ret = 0 ;
if ( len = = 0 & & ooblen = = 0 )
return - EINVAL ;
if ( len = = 0 & & ooblen > 0 )
return doc_backup_oob ( docg3 , ofs , ops ) ;
autoecc = doc_guess_autoecc ( ops ) ;
if ( autoecc < 0 )
return autoecc ;
while ( ! ret & & len > 0 ) {
memset ( oob , 0 , sizeof ( oob ) ) ;
if ( ofs = = docg3 - > oob_write_ofs )
memcpy ( oob , docg3 - > oob_write_buf , DOC_LAYOUT_OOB_SIZE ) ;
else if ( ooblen > 0 & & ops - > mode = = MTD_OPS_AUTO_OOB )
doc_fill_autooob ( oob , oobbuf ) ;
else if ( ooblen > 0 )
memcpy ( oob , oobbuf , DOC_LAYOUT_OOB_SIZE ) ;
ret = doc_write_page ( docg3 , ofs , buf , oob , autoecc ) ;
ofs + = DOC_LAYOUT_PAGE_SIZE ;
len - = DOC_LAYOUT_PAGE_SIZE ;
buf + = DOC_LAYOUT_PAGE_SIZE ;
if ( ooblen ) {
oobbuf + = oobdelta ;
ooblen - = oobdelta ;
ops - > oobretlen + = oobdelta ;
}
ops - > retlen + = DOC_LAYOUT_PAGE_SIZE ;
}
err :
doc_set_device_id ( docg3 , 0 ) ;
return ret ;
}
/**
* doc_write - Write a buffer to the chip
* @ mtd : the device
* @ to : the offset from first block and first page , in bytes , aligned on page
* size
* @ len : the number of bytes to write ( must be a full page size , ie . 512 )
* @ retlen : the number of bytes actually written ( 0 or 512 )
* @ buf : the buffer to get bytes from
*
* Writes data to the chip .
*
* Returns 0 if write successful , - EIO if write error
*/
static int doc_write ( struct mtd_info * mtd , loff_t to , size_t len ,
size_t * retlen , const u_char * buf )
{
struct docg3 * docg3 = mtd - > priv ;
int ret ;
struct mtd_oob_ops ops ;
doc_dbg ( " doc_write(to=%lld, len=%zu) \n " , to , len ) ;
ops . datbuf = ( char * ) buf ;
ops . len = len ;
ops . mode = MTD_OPS_PLACE_OOB ;
ops . oobbuf = NULL ;
ops . ooblen = 0 ;
ops . ooboffs = 0 ;
ret = doc_write_oob ( mtd , to , & ops ) ;
* retlen = ops . retlen ;
return ret ;
}
2011-10-05 17:22:34 +04:00
/*
* Debug sysfs entries
*/
static int dbg_flashctrl_show ( struct seq_file * s , void * p )
{
struct docg3 * docg3 = ( struct docg3 * ) s - > private ;
int pos = 0 ;
u8 fctrl = doc_register_readb ( docg3 , DOC_FLASHCONTROL ) ;
pos + = seq_printf ( s ,
" FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s) \n " ,
fctrl ,
fctrl & DOC_CTRL_VIOLATION ? " protocol violation " : " - " ,
fctrl & DOC_CTRL_CE ? " active " : " inactive " ,
fctrl & DOC_CTRL_PROTECTION_ERROR ? " protection error " : " - " ,
fctrl & DOC_CTRL_SEQUENCE_ERROR ? " sequence error " : " - " ,
fctrl & DOC_CTRL_FLASHREADY ? " ready " : " not ready " ) ;
return pos ;
}
DEBUGFS_RO_ATTR ( flashcontrol , dbg_flashctrl_show ) ;
static int dbg_asicmode_show ( struct seq_file * s , void * p )
{
struct docg3 * docg3 = ( struct docg3 * ) s - > private ;
int pos = 0 ;
int pctrl = doc_register_readb ( docg3 , DOC_ASICMODE ) ;
int mode = pctrl & 0x03 ;
pos + = seq_printf ( s ,
" %04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d ( " ,
pctrl ,
pctrl & DOC_ASICMODE_RAM_WE ? 1 : 0 ,
pctrl & DOC_ASICMODE_RSTIN_RESET ? 1 : 0 ,
pctrl & DOC_ASICMODE_BDETCT_RESET ? 1 : 0 ,
pctrl & DOC_ASICMODE_MDWREN ? 1 : 0 ,
pctrl & DOC_ASICMODE_POWERDOWN ? 1 : 0 ,
mode > > 1 , mode & 0x1 ) ;
switch ( mode ) {
case DOC_ASICMODE_RESET :
pos + = seq_printf ( s , " reset " ) ;
break ;
case DOC_ASICMODE_NORMAL :
pos + = seq_printf ( s , " normal " ) ;
break ;
case DOC_ASICMODE_POWERDOWN :
pos + = seq_printf ( s , " powerdown " ) ;
break ;
}
pos + = seq_printf ( s , " ) \n " ) ;
return pos ;
}
DEBUGFS_RO_ATTR ( asic_mode , dbg_asicmode_show ) ;
static int dbg_device_id_show ( struct seq_file * s , void * p )
{
struct docg3 * docg3 = ( struct docg3 * ) s - > private ;
int pos = 0 ;
int id = doc_register_readb ( docg3 , DOC_DEVICESELECT ) ;
pos + = seq_printf ( s , " DeviceId = %d \n " , id ) ;
return pos ;
}
DEBUGFS_RO_ATTR ( device_id , dbg_device_id_show ) ;
static int dbg_protection_show ( struct seq_file * s , void * p )
{
struct docg3 * docg3 = ( struct docg3 * ) s - > private ;
int pos = 0 ;
2011-11-19 19:02:45 +04:00
int protect , dps0 , dps0_low , dps0_high , dps1 , dps1_low , dps1_high ;
protect = doc_register_readb ( docg3 , DOC_PROTECTION ) ;
dps0 = doc_register_readb ( docg3 , DOC_DPS0_STATUS ) ;
dps0_low = doc_register_readw ( docg3 , DOC_DPS0_ADDRLOW ) ;
dps0_high = doc_register_readw ( docg3 , DOC_DPS0_ADDRHIGH ) ;
dps1 = doc_register_readb ( docg3 , DOC_DPS1_STATUS ) ;
dps1_low = doc_register_readw ( docg3 , DOC_DPS1_ADDRLOW ) ;
dps1_high = doc_register_readw ( docg3 , DOC_DPS1_ADDRHIGH ) ;
2011-10-05 17:22:34 +04:00
pos + = seq_printf ( s , " Protection = 0x%02x ( " ,
protect ) ;
if ( protect & DOC_PROTECT_FOUNDRY_OTP_LOCK )
pos + = seq_printf ( s , " FOUNDRY_OTP_LOCK, " ) ;
if ( protect & DOC_PROTECT_CUSTOMER_OTP_LOCK )
pos + = seq_printf ( s , " CUSTOMER_OTP_LOCK, " ) ;
if ( protect & DOC_PROTECT_LOCK_INPUT )
pos + = seq_printf ( s , " LOCK_INPUT, " ) ;
if ( protect & DOC_PROTECT_STICKY_LOCK )
pos + = seq_printf ( s , " STICKY_LOCK, " ) ;
if ( protect & DOC_PROTECT_PROTECTION_ENABLED )
pos + = seq_printf ( s , " PROTECTION ON, " ) ;
if ( protect & DOC_PROTECT_IPL_DOWNLOAD_LOCK )
pos + = seq_printf ( s , " IPL_DOWNLOAD_LOCK, " ) ;
if ( protect & DOC_PROTECT_PROTECTION_ERROR )
pos + = seq_printf ( s , " PROTECT_ERR, " ) ;
else
pos + = seq_printf ( s , " NO_PROTECT_ERR " ) ;
pos + = seq_printf ( s , " ) \n " ) ;
pos + = seq_printf ( s , " DPS0 = 0x%02x : "
" Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, "
" WRITE=%d, HW_LOCK=%d, KEY_OK=%d \n " ,
dps0 , dps0_low , dps0_high ,
! ! ( dps0 & DOC_DPS_OTP_PROTECTED ) ,
! ! ( dps0 & DOC_DPS_READ_PROTECTED ) ,
! ! ( dps0 & DOC_DPS_WRITE_PROTECTED ) ,
! ! ( dps0 & DOC_DPS_HW_LOCK_ENABLED ) ,
! ! ( dps0 & DOC_DPS_KEY_OK ) ) ;
pos + = seq_printf ( s , " DPS1 = 0x%02x : "
" Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, "
" WRITE=%d, HW_LOCK=%d, KEY_OK=%d \n " ,
dps1 , dps1_low , dps1_high ,
! ! ( dps1 & DOC_DPS_OTP_PROTECTED ) ,
! ! ( dps1 & DOC_DPS_READ_PROTECTED ) ,
! ! ( dps1 & DOC_DPS_WRITE_PROTECTED ) ,
! ! ( dps1 & DOC_DPS_HW_LOCK_ENABLED ) ,
! ! ( dps1 & DOC_DPS_KEY_OK ) ) ;
return pos ;
}
DEBUGFS_RO_ATTR ( protection , dbg_protection_show ) ;
static int __init doc_dbg_register ( struct docg3 * docg3 )
{
struct dentry * root , * entry ;
root = debugfs_create_dir ( " docg3 " , NULL ) ;
if ( ! root )
return - ENOMEM ;
entry = debugfs_create_file ( " flashcontrol " , S_IRUSR , root , docg3 ,
& flashcontrol_fops ) ;
if ( entry )
entry = debugfs_create_file ( " asic_mode " , S_IRUSR , root ,
docg3 , & asic_mode_fops ) ;
if ( entry )
entry = debugfs_create_file ( " device_id " , S_IRUSR , root ,
docg3 , & device_id_fops ) ;
if ( entry )
entry = debugfs_create_file ( " protection " , S_IRUSR , root ,
docg3 , & protection_fops ) ;
if ( entry ) {
docg3 - > debugfs_root = root ;
return 0 ;
} else {
debugfs_remove_recursive ( root ) ;
return - ENOMEM ;
}
}
static void __exit doc_dbg_unregister ( struct docg3 * docg3 )
{
debugfs_remove_recursive ( docg3 - > debugfs_root ) ;
}
/**
* doc_set_driver_info - Fill the mtd_info structure and docg3 structure
* @ chip_id : The chip ID of the supported chip
* @ mtd : The structure to fill
*/
static void __init doc_set_driver_info ( int chip_id , struct mtd_info * mtd )
{
struct docg3 * docg3 = mtd - > priv ;
int cfg ;
cfg = doc_register_readb ( docg3 , DOC_CONFIGURATION ) ;
docg3 - > if_cfg = ( cfg & DOC_CONF_IF_CFG ? 1 : 0 ) ;
switch ( chip_id ) {
case DOC_CHIPID_G3 :
2011-11-19 19:02:48 +04:00
mtd - > name = kasprintf ( GFP_KERNEL , " DiskOnChip G3 floor %d " ,
docg3 - > device_id ) ;
2011-10-05 17:22:34 +04:00
docg3 - > max_block = 2047 ;
break ;
}
mtd - > type = MTD_NANDFLASH ;
2011-11-19 19:02:54 +04:00
mtd - > flags = MTD_CAP_NANDFLASH ;
2011-10-05 17:22:34 +04:00
mtd - > size = ( docg3 - > max_block + 1 ) * DOC_LAYOUT_BLOCK_SIZE ;
mtd - > erasesize = DOC_LAYOUT_BLOCK_SIZE * DOC_LAYOUT_NBPLANES ;
mtd - > writesize = DOC_LAYOUT_PAGE_SIZE ;
mtd - > oobsize = DOC_LAYOUT_OOB_SIZE ;
mtd - > owner = THIS_MODULE ;
2011-11-19 19:02:54 +04:00
mtd - > erase = doc_erase ;
2011-10-05 17:22:34 +04:00
mtd - > point = NULL ;
mtd - > unpoint = NULL ;
mtd - > read = doc_read ;
2011-11-19 19:02:54 +04:00
mtd - > write = doc_write ;
2011-10-05 17:22:34 +04:00
mtd - > read_oob = doc_read_oob ;
2011-11-19 19:02:54 +04:00
mtd - > write_oob = doc_write_oob ;
2011-10-05 17:22:34 +04:00
mtd - > sync = NULL ;
mtd - > block_isbad = doc_block_isbad ;
2011-11-19 19:02:49 +04:00
mtd - > ecclayout = & docg3_oobinfo ;
2011-10-05 17:22:34 +04:00
}
/**
2011-11-19 19:02:48 +04:00
* doc_probe_device - Check if a device is available
* @ base : the io space where the device is probed
* @ floor : the floor of the probed device
* @ dev : the device
2011-10-05 17:22:34 +04:00
*
2011-11-19 19:02:48 +04:00
* Checks whether a device at the specified IO range , and floor is available .
2011-10-05 17:22:34 +04:00
*
2011-11-19 19:02:48 +04:00
* Returns a mtd_info struct if there is a device , ENODEV if none found , ENOMEM
* if a memory allocation failed . If floor 0 is checked , a reset of the ASIC is
* launched .
2011-10-05 17:22:34 +04:00
*/
2011-11-19 19:02:48 +04:00
static struct mtd_info * doc_probe_device ( void __iomem * base , int floor ,
struct device * dev )
2011-10-05 17:22:34 +04:00
{
int ret , bbt_nbpages ;
u16 chip_id , chip_id_inv ;
2011-11-19 19:02:48 +04:00
struct docg3 * docg3 ;
struct mtd_info * mtd ;
2011-10-05 17:22:34 +04:00
ret = - ENOMEM ;
docg3 = kzalloc ( sizeof ( struct docg3 ) , GFP_KERNEL ) ;
if ( ! docg3 )
goto nomem1 ;
mtd = kzalloc ( sizeof ( struct mtd_info ) , GFP_KERNEL ) ;
if ( ! mtd )
goto nomem2 ;
mtd - > priv = docg3 ;
2011-11-19 19:02:48 +04:00
bbt_nbpages = DIV_ROUND_UP ( docg3 - > max_block + 1 ,
8 * DOC_LAYOUT_PAGE_SIZE ) ;
docg3 - > bbt = kzalloc ( bbt_nbpages * DOC_LAYOUT_PAGE_SIZE , GFP_KERNEL ) ;
if ( ! docg3 - > bbt )
goto nomem3 ;
2011-10-05 17:22:34 +04:00
2011-11-19 19:02:48 +04:00
docg3 - > dev = dev ;
docg3 - > device_id = floor ;
docg3 - > base = base ;
2011-10-05 17:22:34 +04:00
doc_set_device_id ( docg3 , docg3 - > device_id ) ;
2011-11-19 19:02:48 +04:00
if ( ! floor )
doc_set_asic_mode ( docg3 , DOC_ASICMODE_RESET ) ;
2011-10-05 17:22:34 +04:00
doc_set_asic_mode ( docg3 , DOC_ASICMODE_NORMAL ) ;
chip_id = doc_register_readw ( docg3 , DOC_CHIPID ) ;
chip_id_inv = doc_register_readw ( docg3 , DOC_CHIPID_INV ) ;
2011-11-19 19:02:48 +04:00
ret = 0 ;
2011-10-05 17:22:34 +04:00
if ( chip_id ! = ( u16 ) ( ~ chip_id_inv ) ) {
2011-11-19 19:02:48 +04:00
goto nomem3 ;
2011-10-05 17:22:34 +04:00
}
switch ( chip_id ) {
case DOC_CHIPID_G3 :
2011-11-19 19:02:48 +04:00
doc_info ( " Found a G3 DiskOnChip at addr %p, floor %d \n " ,
base , floor ) ;
2011-10-05 17:22:34 +04:00
break ;
default :
doc_err ( " Chip id %04x is not a DiskOnChip G3 chip \n " , chip_id ) ;
2011-11-19 19:02:48 +04:00
goto nomem3 ;
2011-10-05 17:22:34 +04:00
}
doc_set_driver_info ( chip_id , mtd ) ;
2011-11-19 19:02:52 +04:00
doc_hamming_ecc_init ( docg3 , DOC_LAYOUT_OOB_PAGEINFO_SZ ) ;
2011-10-05 17:22:34 +04:00
doc_reload_bbt ( docg3 ) ;
2011-11-19 19:02:48 +04:00
return mtd ;
2011-10-05 17:22:34 +04:00
2011-11-19 19:02:48 +04:00
nomem3 :
2011-10-05 17:22:34 +04:00
kfree ( mtd ) ;
nomem2 :
kfree ( docg3 ) ;
nomem1 :
2011-11-19 19:02:48 +04:00
return ERR_PTR ( ret ) ;
}
/**
* doc_release_device - Release a docg3 floor
* @ mtd : the device
*/
static void doc_release_device ( struct mtd_info * mtd )
{
struct docg3 * docg3 = mtd - > priv ;
mtd_device_unregister ( mtd ) ;
kfree ( docg3 - > bbt ) ;
kfree ( docg3 ) ;
kfree ( mtd - > name ) ;
kfree ( mtd ) ;
}
/**
* doc_probe - Probe the IO space for a DiskOnChip G3 chip
* @ pdev : platform device
*
* Probes for a G3 chip at the specified IO space in the platform data
* ressources . The floor 0 must be available .
*
* Returns 0 on success , - ENOMEM , - ENXIO on error
*/
static int __init docg3_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct mtd_info * mtd ;
struct resource * ress ;
void __iomem * base ;
int ret , floor , found = 0 ;
struct mtd_info * * docg3_floors ;
ret = - ENXIO ;
ress = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! ress ) {
dev_err ( dev , " No I/O memory resource defined \n " ) ;
goto noress ;
}
base = ioremap ( ress - > start , DOC_IOSPACE_SIZE ) ;
ret = - ENOMEM ;
docg3_floors = kzalloc ( sizeof ( * docg3_floors ) * DOC_MAX_NBFLOORS ,
GFP_KERNEL ) ;
if ( ! docg3_floors )
2011-11-19 19:02:55 +04:00
goto nomem1 ;
docg3_bch = init_bch ( DOC_ECC_BCH_M , DOC_ECC_BCH_T ,
DOC_ECC_BCH_PRIMPOLY ) ;
if ( ! docg3_bch )
goto nomem2 ;
2011-11-19 19:02:48 +04:00
ret = 0 ;
for ( floor = 0 ; floor < DOC_MAX_NBFLOORS ; floor + + ) {
mtd = doc_probe_device ( base , floor , dev ) ;
if ( floor = = 0 & & ! mtd )
goto notfound ;
if ( ! IS_ERR_OR_NULL ( mtd ) )
ret = mtd_device_parse_register ( mtd , part_probes ,
NULL , NULL , 0 ) ;
else
ret = PTR_ERR ( mtd ) ;
docg3_floors [ floor ] = mtd ;
if ( ret )
goto err_probe ;
if ( mtd )
found + + ;
}
if ( ! found )
goto notfound ;
platform_set_drvdata ( pdev , docg3_floors ) ;
doc_dbg_register ( docg3_floors [ 0 ] - > priv ) ;
return 0 ;
notfound :
ret = - ENODEV ;
dev_info ( dev , " No supported DiskOnChip found \n " ) ;
err_probe :
2011-11-19 19:02:55 +04:00
free_bch ( docg3_bch ) ;
2011-11-19 19:02:48 +04:00
for ( floor = 0 ; floor < DOC_MAX_NBFLOORS ; floor + + )
if ( docg3_floors [ floor ] )
doc_release_device ( docg3_floors [ floor ] ) ;
2011-11-19 19:02:55 +04:00
nomem2 :
kfree ( docg3_floors ) ;
nomem1 :
2011-11-19 19:02:48 +04:00
iounmap ( base ) ;
noress :
2011-10-05 17:22:34 +04:00
return ret ;
}
/**
* docg3_release - Release the driver
* @ pdev : the platform device
*
* Returns 0
*/
static int __exit docg3_release ( struct platform_device * pdev )
{
2011-11-19 19:02:48 +04:00
struct mtd_info * * docg3_floors = platform_get_drvdata ( pdev ) ;
struct docg3 * docg3 = docg3_floors [ 0 ] - > priv ;
void __iomem * base = docg3 - > base ;
int floor ;
2011-10-05 17:22:34 +04:00
doc_dbg_unregister ( docg3 ) ;
2011-11-19 19:02:48 +04:00
for ( floor = 0 ; floor < DOC_MAX_NBFLOORS ; floor + + )
if ( docg3_floors [ floor ] )
doc_release_device ( docg3_floors [ floor ] ) ;
kfree ( docg3_floors ) ;
2011-11-19 19:02:55 +04:00
free_bch ( docg3_bch ) ;
2011-11-19 19:02:48 +04:00
iounmap ( base ) ;
2011-10-05 17:22:34 +04:00
return 0 ;
}
static struct platform_driver g3_driver = {
. driver = {
. name = " docg3 " ,
. owner = THIS_MODULE ,
} ,
. remove = __exit_p ( docg3_release ) ,
} ;
static int __init docg3_init ( void )
{
return platform_driver_probe ( & g3_driver , docg3_probe ) ;
}
module_init ( docg3_init ) ;
static void __exit docg3_exit ( void )
{
platform_driver_unregister ( & g3_driver ) ;
}
module_exit ( docg3_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Robert Jarzmik <robert.jarzmik@free.fr> " ) ;
MODULE_DESCRIPTION ( " MTD driver for DiskOnChip G3 " ) ;