Add the driver for onboard flash. The quality of this driver means that it has not been included in the upstream kernel sources. This implements a block device translation layer to match what the onboard firmware implements. diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 58c1deb..9668ac7 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -186,6 +186,13 @@ config BLK_DEV_DAC960 To compile this driver as a module, choose M here: the module will be called DAC960. +config BLK_SSFDC + tristate "SmartMedia(TM) Driver (sm)" + depends on SH_TITAN + help + Say Y here if you want the SmartMedia chip enabled. + Otherwise say N. + config BLK_DEV_UMEM tristate "Micro Memory MM5415 Battery Backed RAM support (EXPERIMENTAL)" depends on PCI && EXPERIMENTAL diff --git a/drivers/block/Makefile b/drivers/block/Makefile index dd88e33..37fc9e8 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_BLK_DEV_XD) += xd.o obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o +obj-$(CONFIG_BLK_SSFDC) += ssfdc.o obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o obj-$(CONFIG_BLK_DEV_UMEM) += umem.o diff --git a/drivers/block/ssfdc.c b/drivers/block/ssfdc.c new file mode 100644 index 0000000..482d617 --- /dev/null +++ b/drivers/block/ssfdc.c @@ -0,0 +1,2733 @@ +/* $id: $ +ssfdc.c - Solid State Flopyy Disk Card + +Original source curtesy of Toshiba Corporation. + +Modification for use by Linux provided by Nimble Microsystems Inc. + +TODO: + +Modification History: + + March 2001 - Initial port of Toshiba sources by Bill Mann + May 2001 - Debug of staticly linked ssfdc driver, Bill Mann + Nov 2001 - Reimplementation using tasklets and timers. + May 2002 - Partition support added. + Oct 2003 - Port to kernel 2.6.0 + Mar 2004 - Stabilization refinements... + +Overview: The kernel interfaces to the device via the "block_device_operations + ssfdc_fops", the device's request handling function + "do_ssfdc_request(request_queue_t * q)", or by the ioctl interface ssfdc_ioctl(). + + do_ssfdc_request() purpose is to kickstart ssfdc_thread via a wake_up call. ssfdc_thread + then processes requests from the queue. + + Blocks are mapped logically. So a sector read/write results in the determination + of the logical block address of the block containing the desired sector and the + corresponding physical block being accessed. Note the use of ReadBlock, WriteBlock, + and PhyBlock, Log2Phy[] etc. + + This driver implements a wear leveling strategy where sector writes to the + SmartMedia causes the block which is the target of the write to be copied into a + new block, the new data written and the old block erased. This makes the driver + more complicated than a straightforward sector read/write. + +*/ + +/* Section device headers */ +#define DEBUG_SSFDC 0 +#define DEBUG_SSFDC_STRUCT 0 +#define DEBUG_SSFDC_REQUEST 0 +#define DEBUG_SSFDC_READREDT 0 +#define DEBUG_SSFDC_WRITE 0 +#define DEBUG_SSFDC_WRITESECT 0 +#define DEBUG_SSFDC_WRITEBLKS 0 +#define DEBUG_SSFDC_READ 0 +#define DEBUG_SSFDC_ADDR 0 +#define DEBUG_SSFDC_ASSIGNRELEASE 0 +#define SSFDC_READINGTASKLET 0 +/* Edition Compilation Mode */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ssfdc.h" + +#define SSFDC_MAJOR 240 + +static int static_ssfdc_debug = 0; + +static DECLARE_WAIT_QUEUE_HEAD(ssfdc_wait); + +static struct gendisk *disks[MAX_SSFDC]; +static ssfdc_dev *ssfdc[MAX_SSFDC]; + +static int ssfdc_open(struct inode *i_node, struct file *fptr); +static int ssfdc_release(struct inode *i_node, struct file *fptr); +static int ssfdc_ioctl(struct inode *i_node, struct file *fptr, unsigned cmd, unsigned long arg); +static int ssfdc_revalidate(struct gendisk *disk); +void do_ssfdc_request(request_queue_t * q); + +static struct block_device_operations ssfdc_fops = { + .owner = THIS_MODULE, + .open = ssfdc_open, + .release = ssfdc_release, + .ioctl = ssfdc_ioctl, + // bjm out .revalidate_disk = ssfdc_revalidate, +}; + + +/*************************************************************************** + BIT Control Macro + ***************************************************************************/ +static char BitData[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 }; +#define SetBit(a,b) (a[(unsigned char)((b)/8)]|= BitData[(b)%8]) +#define ClrBit(a,b) (a[(unsigned char)((b)/8)]&=~BitData[(b)%8]) +#define ChkBit(a,b) (a[(unsigned char)((b)/8)] & BitData[(b)%8]) + +/***************************************************************************/ +static int MediaReadSector(ssfdc_dev *, struct request *, char *, long,int); +static int MediaWriteSector(ssfdc_dev *, struct request *, char *, long,int); + +/***************************************************************************/ +static int CheckLogCHS(ssfdc_dev *,unsigned int *,unsigned char *,unsigned char *); +static int CheckMediaWP(ssfdc_dev *); +static int ConvMediaAddr(ssfdc_dev *,long); +static int IncMediaAddr(ssfdc_dev *); +static int WriteReqInCurrBlk(ssfdc_dev *, long, int *); +/******************************************/ +/******************************************/ +static int AssignWriteBlock(ssfdc_dev *, int); +/******************************************/ +/******************************************/ +static int SetPhyFmtValue(ssfdc_dev *); +static int SearchCIS(ssfdc_dev *,unsigned int *); +static int MakeLogTable(ssfdc_dev *,unsigned int); +/******************************************/ +static int MarkFailPhyOneBlock(ssfdc_dev *); + +static void _ReadSsfdcBuf(ssfdc_dev *, unsigned char *databuf,unsigned char *redundant); +static void _WriteSsfdcBuf(ssfdc_dev *,unsigned char *,unsigned char *); +static void _ReadSsfdcWord(ssfdc_dev *,unsigned int *); +static void _ReadRedtSsfdcBuf(ssfdc_dev *, unsigned char *redundant); +static void _WriteRedtSsfdcBuf(ssfdc_dev*, unsigned char *redundant); + +/***************************************************************************/ +static void _SetSsfdcCmd(ssfdc_dev *, unsigned char); +static void _SetSsfdcAddr(ssfdc_dev *, unsigned char); +static void _SetSsfdcBlock(ssfdc_dev *); +static void _SetSsfdcChip(ssfdc_dev *); +static void _SetSsfdcStandby(ssfdc_dev *); +static int _CheckSsfdcBusy(ssfdc_dev *, unsigned int); +static int _CheckSsfdcStatus(ssfdc_dev *); +static void _ResetSsfdcErr(ssfdc_dev *psm); +static unsigned char _CheckDevCode(unsigned char); +void SsfdcReset(ssfdc_dev *); +void CntReset(ssfdc_dev *); + +static char BitCount(unsigned char); +static char BitCountWord(unsigned int); + +static void _WaitTimer(long int); +typedef void (*timeout_fn)(unsigned long); +static void ssfdc_rw_request(ssfdc_dev *psm, struct request *req); +static int ssfdc_end_request(ssfdc_dev *psm, struct request *req, int status); +static void ssfdc_terminate_request(ssfdc_dev *psm, struct request *req); +static struct request *ssfdc_get_request(ssfdc_dev *psm); + +/* debugging utils etc. */ + +#if DEBUG_SSFDC +static void dump_ssfdc_state(ssfdc_dev * psm); +#endif + +/* end of debugging utils etc. */ + +/* our tasklets */ +/* top level R/W initiation tasklet */ +static void initxfer(unsigned long); +#if 0 // use thread and not a tasklet +DECLARE_TASKLET(initxfer_tasklet0, initxfer, 0); +#ifdef CONFIG_SH_NIMBLE_MINI +DECLARE_TASKLET(initxfer_tasklet1, initxfer, 1); +#endif +#endif + +/* Sector Write Tasklets, This group includes a readcopy tasklet for block copies...*/ + +/* Tasklet to read a sector into a temporary buffer for later write */ + +/* power is turned on, and then left on for TIMER_ON_TIMEOUT */ +// bjm debug struct timer_list mediachange_timer; +// bjm debug static void mediachangetest(unsigned long); + +// bjm out +// bjm out struct timer_list waiting_timer; +static void waiting_timeout(unsigned long); + +/******************************************************************************/ +static void trans_result \ + (unsigned char,unsigned char,unsigned char *,unsigned char *); +static void calculate_ecc \ + (unsigned char *,unsigned char *,unsigned char *,unsigned char *,unsigned char *); +static unsigned char correct_data \ + (unsigned char *,unsigned char *,unsigned char,unsigned char,unsigned char); + + /* CP0-CP5 code table */ +static unsigned char ecctable[256] = { + 0x00,0x55,0x56,0x03,0x59,0x0C,0x0F,0x5A,0x5A,0x0F,0x0C,0x59,0x03,0x56,0x55,0x00, + 0x65,0x30,0x33,0x66,0x3C,0x69,0x6A,0x3F,0x3F,0x6A,0x69,0x3C,0x66,0x33,0x30,0x65, + 0x66,0x33,0x30,0x65,0x3F,0x6A,0x69,0x3C,0x3C,0x69,0x6A,0x3F,0x65,0x30,0x33,0x66, + 0x03,0x56,0x55,0x00,0x5A,0x0F,0x0C,0x59,0x59,0x0C,0x0F,0x5A,0x00,0x55,0x56,0x03, + 0x69,0x3C,0x3F,0x6A,0x30,0x65,0x66,0x33,0x33,0x66,0x65,0x30,0x6A,0x3F,0x3C,0x69, + 0x0C,0x59,0x5A,0x0F,0x55,0x00,0x03,0x56,0x56,0x03,0x00,0x55,0x0F,0x5A,0x59,0x0C, + 0x0F,0x5A,0x59,0x0C,0x56,0x03,0x00,0x55,0x55,0x00,0x03,0x56,0x0C,0x59,0x5A,0x0F, + 0x6A,0x3F,0x3C,0x69,0x33,0x66,0x65,0x30,0x30,0x65,0x66,0x33,0x69,0x3C,0x3F,0x6A, + 0x6A,0x3F,0x3C,0x69,0x33,0x66,0x65,0x30,0x30,0x65,0x66,0x33,0x69,0x3C,0x3F,0x6A, + 0x0F,0x5A,0x59,0x0C,0x56,0x03,0x00,0x55,0x55,0x00,0x03,0x56,0x0C,0x59,0x5A,0x0F, + 0x0C,0x59,0x5A,0x0F,0x55,0x00,0x03,0x56,0x56,0x03,0x00,0x55,0x0F,0x5A,0x59,0x0C, + 0x69,0x3C,0x3F,0x6A,0x30,0x65,0x66,0x33,0x33,0x66,0x65,0x30,0x6A,0x3F,0x3C,0x69, + 0x03,0x56,0x55,0x00,0x5A,0x0F,0x0C,0x59,0x59,0x0C,0x0F,0x5A,0x00,0x55,0x56,0x03, + 0x66,0x33,0x30,0x65,0x3F,0x6A,0x69,0x3C,0x3C,0x69,0x6A,0x3F,0x65,0x30,0x33,0x66, + 0x65,0x30,0x33,0x66,0x3C,0x69,0x6A,0x3F,0x3F,0x6A,0x69,0x3C,0x66,0x33,0x30,0x65, + 0x00,0x55,0x56,0x03,0x59,0x0C,0x0F,0x5A,0x5A,0x0F,0x0C,0x59,0x03,0x56,0x55,0x00 +}; + +#define BIT7 0x80 +#define BIT6 0x40 +#define BIT5 0x20 +#define BIT4 0x10 +#define BIT3 0x08 +#define BIT2 0x04 +#define BIT1 0x02 +#define BIT0 0x01 + +#define BIT1BIT0 0x03 +#define BIT23 0x00800000L +#define MASK_CPS 0x3f +#define CORRECTABLE 0x00555554L + +/* + Transfer result + LP14,12,10,... & LP15,13,11,... -> LP15,14,13,... & LP7,6,5,.. +*/ +static void trans_result(reg2,reg3,ecc1,ecc2) +unsigned char reg2; /* LP14,LP12,LP10,... */ +unsigned char reg3; /* LP15,LP13,LP11,... */ +unsigned char *ecc1; /* LP15,LP14,LP13,... */ +unsigned char *ecc2; /* LP07,LP06,LP05,... */ +{ + unsigned char a; /* Working for reg2,reg3 */ + unsigned char b; /* Working for ecc1,ecc2 */ + unsigned char i; /* For counting */ + + a=BIT7; b=BIT7; /* 80h=10000000b */ + *ecc1=*ecc2=0; /* Clear ecc1,ecc2 */ + for(i=0; i<4; ++i) { + if ((reg3&a)!=0) *ecc1|=b; /* LP15,13,11,9 -> ecc1 */ + b=b>>1; /* Right shift */ + if ((reg2&a)!=0) *ecc1|=b; /* LP14,12,10,8 -> ecc1 */ + b=b>>1; /* Right shift */ + a=a>>1; /* Right shift */ + } + b=BIT7; /* 80h=10000000b */ + for(i=0; i<4; ++i) { + if ((reg3&a)!=0) *ecc2|=b; /* LP7,5,3,1 -> ecc2 */ + b=b>>1; /* Right shift */ + if ((reg2&a)!=0) *ecc2|=b; /* LP6,4,2,0 -> ecc2 */ + b=b>>1; /* Right shift */ + a=a>>1; /* Right shift */ + } +} + + +/* + Calculating ECC + data[0-255] -> ecc1,ecc2,ecc3 using CP0-CP5 code table[0-255] +*/ +static void calculate_ecc(table,data,ecc1,ecc2,ecc3) +unsigned char *table; /* CP0-CP5 code table */ +unsigned char *data; /* DATA */ +unsigned char *ecc1; /* LP15,LP14,LP13,... */ +unsigned char *ecc2; /* LP07,LP06,LP05,... */ +unsigned char *ecc3; /* CP5,CP4,CP3,...,"1","1" */ +{ + unsigned int i; /* For counting */ + unsigned char a; /* Working for table */ + unsigned char reg1; /* D-all,CP5,CP4,CP3,... */ + unsigned char reg2; /* LP14,LP12,L10,... */ + unsigned char reg3; /* LP15,LP13,L11,... */ + + reg1=reg2=reg3=0; /* Clear parameter */ + + for(i=0; i<256; ++i) { + a=table[data[i]]; /* Get CP0-CP5 code from table */ + reg1^=(a&MASK_CPS); /* XOR with a */ + if ((a&BIT6)!=0) { /* If D_all(all bit XOR) = 1 */ + reg3^=(unsigned char)i; /* XOR with counter */ + reg2^=~((unsigned char)i); /* XOR with inv. of counter */ + } + } + + /* Trans LP14,12,10,... & LP15,13,11,... -> LP15,14,13,... & LP7,6,5,.. */ + trans_result(reg2,reg3,ecc1,ecc2); + + *ecc1=~(*ecc1); *ecc2=~(*ecc2); /* Inv. ecc2 & ecc3 */ + *ecc3=((~reg1)<<2)|BIT1BIT0; /* Make TEL format */ +} + +static unsigned char correct_data(data,eccdata,ecc1,ecc2,ecc3) +unsigned char *data; /* DATA */ +unsigned char *eccdata; /* ECC DATA */ +unsigned char ecc1; /* LP15,LP14,LP13,... */ +unsigned char ecc2; /* LP07,LP06,LP05,... */ +unsigned char ecc3; /* CP5,CP4,CP3,...,"1","1" */ +{ + unsigned long l; /* Working to check d */ + unsigned long d; /* Result of comparison */ + unsigned int i; /* For counting */ + unsigned char d1,d2,d3; /* Result of comparison */ + unsigned char a; /* Working for add */ + unsigned char add; /* Byte address of cor. DATA */ + unsigned char b; /* Working for bit */ + unsigned char bit; /* Bit address of cor. DATA */ + + d1=ecc1^eccdata[1]; d2=ecc2^eccdata[0]; /* Compare LP's */ + d3=ecc3^eccdata[2]; /* Comapre CP's */ + d=((unsigned long)d1<<16) /* Result of comparison */ + +((unsigned long)d2<<8) + +(unsigned long)d3; + + if (d==0) return(0); /* If No error, return */ + if (((d^(d>>1))&CORRECTABLE)==CORRECTABLE) { /* If correctable */ + l=BIT23; + add=0; /* Clear parameter */ + a=BIT7; + for(i=0; i<8; ++i) { /* Checking 8 bit */ + if ((d&l)!=0) add|=a; /* Make byte address from LP's */ + l>>=2; a>>=1; /* Right Shift */ + } + bit=0; /* Clear parameter */ + b=BIT2; + for(i=0; i<3; ++i) { /* Checking 3 bit */ + if ((d&l)!=0) bit|=b; /* Make bit address from CP's */ + l>>=2; b>>=1; /* Right shift */ + } + b=BIT0; + data[add]^=(b<>=1; /* Right shift */ + } + if (i==1) { /* If ECC error */ + eccdata[1]=ecc1; eccdata[0]=ecc2; /* Put right ECC code */ + eccdata[2]=ecc3; + return(2); + } + return(3); /* Uncorrectable error */ +} +/*************************************************************************** + Common Subroutine + ***************************************************************************/ +char BitCount(unsigned char cdata) +{ + char bitcount=0; + while(cdata) { + bitcount+=(cdata &0x01); + cdata /=2; + } + return(bitcount); +} + +char BitCountWord(unsigned int cdata) +{ + char bitcount=0; + while(cdata) { + bitcount+=(cdata &0x01); + cdata /=2; + } + return(bitcount); +} + +/***************************************************************************/ +void StringCopy(char *stringA, char *stringB, int count) +{ + int i; + for(i=0; iRedundant+REDT_ADDR1H)*0x100+*(psm->Redundant+REDT_ADDR1L); + addr2=*(psm->Redundant+REDT_ADDR2H)*0x100+*(psm->Redundant+REDT_ADDR2L); + if(addr1==addr2) + if((addr1 &0xF000)==0x1000) + { psm->LogBlock=(addr1 &0x0FFF)/2; return(SUCCESS); } + if(BitCountWord(addr1^addr2)>1) return(ERROR); + if((addr1 &0xF000)==0x1000) + if(! (BitCountWord(addr1) &0x0001)) + { psm->LogBlock=(addr1 &0x0FFF)/2; return(SUCCESS); } + if((addr2 &0xF000)==0x1000) + if(! (BitCountWord(addr2) &0x0001)) + { psm->LogBlock=(addr2 &0x0FFF)/2; return(SUCCESS); } + return(ERROR); +} +/***************************************************************************/ +void ClrRedundantData(unsigned char *redundant) +{ + char i; + for(i=0; iLogBlock*2+0x1000; + if((BitCountWord(addr)%2)) addr++; + *(redundant+REDT_ADDR1H)=*(redundant+REDT_ADDR2H)=addr/0x100; + *(redundant+REDT_ADDR1L)=*(redundant+REDT_ADDR2L)=(unsigned char)addr; +} + +void SetFailBlock(unsigned char *redundant) +{ + char i; + for(i=0; iZone,psm->LogBlock,psm->PhyBlock,psm->Sector); +#endif + addr=(unsigned int)psm->Zone*psm->MaxBlocks+psm->PhyBlock; + addr=addr*(unsigned int)psm->MaxSectors+psm->Sector; + if((psm->Attribute &MPS)==PS256) /* for 256byte/page */ + addr=addr*2+(unsigned int)add; +/*-----------------------------------------------*/ + _HwSetAddr(psm); + _HwOutData(psm,0x00); + _HwOutData(psm,(unsigned char)addr); + _HwOutData(psm,(unsigned char)(addr/0x0100)); + if((psm->Attribute &MADC)==AD4CYC) + _HwOutData(psm,(unsigned char)(psm->Zone/2)); /* Patch */ + _HwSetData(psm); +} + +static void _SetSsfdcBlock(ssfdc_dev *psm) +{ + unsigned int addr; +#if DEBUG_SSFDC_ASSIGNRELEASE + printk(KERN_DEBUG "_SetSsfdcBlock() set card addr to PhyBlock %d\n", psm->PhyBlock); +#endif + addr=(unsigned int)psm->Zone*psm->MaxBlocks+psm->PhyBlock; + addr=addr*(unsigned int)psm->MaxSectors; + if((psm->Attribute &MPS)==PS256) /* for 256byte/page */ + addr=addr*2; +/*-----------------------------------------------*/ + _HwSetAddr(psm); + _HwOutData(psm,(unsigned char)addr); + _HwOutData(psm,(unsigned char)(addr/0x0100)); + if((psm->Attribute &MADC)==AD4CYC) + _HwOutData(psm,(unsigned char)(psm->Zone/2)); /* Patch */ + _HwSetData(psm); +} + +static inline void _SetSsfdcStandby(ssfdc_dev *psm) +{ + _HwSetStandby(psm); +} + +static int _CheckSsfdcStatus(ssfdc_dev *psm) +{ + int status; + if((status=_HwInData(psm)) & WR_FAIL) { + printk(KERN_DEBUG "_CheckSsfdcStatus() error %x\n", status); + return(ERROR); + } + return(SUCCESS); +} + +static void _ResetSsfdcErr(ssfdc_dev *psm) +{ + _HwSetCmd(psm); + _HwOutData(psm,SSFDC_RST_CHIP); + _HwSetData(psm); + while(1) { + udelay(30); + if(! _HwChkBusy(psm)) break; + } + _HwSetStandby(psm); +} + +static void waiting_timeout(unsigned long psm) +{ + // enable the wakeup signal! + wake_up(&((ssfdc_dev *)psm)->thread_wq); +} + +/* + _CheckSsfdcBusy() + + set a timer in jiffies from int time x .1ms +*/ + +static int _CheckSsfdcBusy(ssfdc_dev *psm, unsigned int time) +{ + unsigned long incr_div = 4; + unsigned long incr_us = time / incr_div, + jticks=time/(MSEC * JIFFY_TICK_MS); + unsigned long tick_retried=0, wrap_flag, expires; + + if (!jticks) { + // small delay first to test completion + do { + udelay(incr_us); + if (!_HwChkBusy(psm)) + return(SUCCESS); + } while (incr_div--); + return(ERROR); + } + + // Block the wakeup signal? + +one_more_time: + expires = jiffies + jticks; + wrap_flag = ( expires < jiffies); + + do { + wait_event_interruptible_timeout(psm->thread_wq, 0, jticks); + if (!_HwChkBusy(psm)) { + return(SUCCESS); + } + } while (wrap_flag ? expires <= jiffies : expires >= jiffies); + +#if 1 + // Is the chip not busy? If so its an ERROR + if (!_HwChkBusy(psm)) { + return(SUCCESS); + } + else { + // if we came back, give us one more tick/time + if (! tick_retried ) { + tick_retried = 1; + jticks = 0; + printk("."); + goto one_more_time; + } + return(ERROR); + } +#endif +} + +static void _SetSsfdcChip(ssfdc_dev *psm) +{ + _HwSetAddr(psm); + _HwOutData(psm,0x00); + _HwSetData(psm); +} +/*************************************************************************** + NAND Memory (SmartMedia) Buffer Data Xfer Subroutine + ***************************************************************************/ +static void _ReadSsfdcBuf(ssfdc_dev *psm,unsigned char *databuf,unsigned char *redundant) +{ + int i; + for(i=0x00;i<(((psm->Attribute &MPS)==PS256)?0x100:0x200);i++) + *databuf++ =_HwInData(psm); + for(i=0x00;i<(((psm->Attribute &MPS)==PS256)?0x08:0x10);i++) + *redundant++ =_HwInData(psm); +} + +static void _WriteSsfdcBuf(ssfdc_dev *psm, unsigned char *databuf,unsigned char *redundant) +{ + int i; + for(i=0x00;i<(((psm->Attribute &MPS)==PS256)?0x100:0x200);i++) + _HwOutData(psm,*databuf++); + for(i=0x00;i<(((psm->Attribute &MPS)==PS256)?0x08:0x10);i++) + _HwOutData(psm,*redundant++); +} + +static void _ReadSsfdcWord(ssfdc_dev *psm, unsigned int *pdata) +{ + *pdata =_HwInData(psm)*0x100; + *pdata|=(unsigned char)_HwInData(psm); +} + +static void _ReadRedtSsfdcBuf(ssfdc_dev *psm,unsigned char *redundant) +{ + int i; + for(i=0x00;i<(((psm->Attribute &MPS)==PS256)?0x08:0x10);i++) + redundant[i] =_HwInData(psm); +} + +static void _WriteRedtSsfdcBuf(ssfdc_dev *psm, unsigned char *redundant) +{ + char i; + for(i=0x00;i<(((psm->Attribute &MPS)==PS256)?0x08:0x10);i++) + _HwOutData(psm,*redundant++); +} + +/*************************************************************************** + Timer Control Subroutine + ***************************************************************************/ +#define SHORT_DELAY 1 + + + + +void _GetDateTime(char *date) +{ +} + +/* +_WaitTimer(long time) time is in ticks. +*/ + +static inline void _WaitTimer(long time) +{ +} + +/*************************************************************************** + SmartMedia Function Command Subroutine + ***************************************************************************/ +void SsfdcReset(ssfdc_dev *psm) +{ + _SetSsfdcCmd(psm, SSFDC_RST_CHIP); + _CheckSsfdcBusy(psm,BUSY_RESET); + _SetSsfdcCmd(psm,SSFDC_READ); + _CheckSsfdcBusy(psm,BUSY_READ); + _SetSsfdcStandby(psm); +} + +void SsfdcWriteRedtMode(ssfdc_dev *psm) +{ + _SetSsfdcCmd(psm,SSFDC_RST_CHIP); + _CheckSsfdcBusy(psm,BUSY_RESET); + _SetSsfdcCmd(psm,SSFDC_READ_REDT); + _CheckSsfdcBusy(psm,BUSY_READ); + _SetSsfdcStandby(psm); +} + +void SsfdcReadID(ssfdc_dev *psm, unsigned int *pid) +{ + _SetSsfdcCmd(psm,SSFDC_READ_ID); + _SetSsfdcChip(psm); + _ReadSsfdcWord(psm,pid); + _SetSsfdcStandby(psm); +} + +int SsfdcCheckStatus(ssfdc_dev *psm) +{ + _SetSsfdcCmd(psm,SSFDC_RDSTATUS); + if(_CheckSsfdcStatus(psm)) + { _SetSsfdcStandby(psm); return(ERROR); } + _SetSsfdcStandby(psm); + return(SUCCESS); +} + +int SsfdcReadSect(ssfdc_dev *psm, unsigned char *buf,unsigned char *redundant) +{ +#if DEBUG_SSFDC_READ + printk(KERN_DEBUG "SsfdcReadSect() - Zone %d LogBlock %d, PhyBlock %d, Sector %d\n", + psm->Zone, psm->LogBlock, psm->PhyBlock, psm->Sector); +#endif + _SetSsfdcCmd(psm,SSFDC_READ); + _SetSsfdcAddr(psm, EVEN); + if(_CheckSsfdcBusy(psm,BUSY_READ)) + { _ResetSsfdcErr(psm); return(ERROR); } + _ReadSsfdcBuf(psm,buf,redundant); + if(_CheckSsfdcBusy(psm,BUSY_READ)) + { _ResetSsfdcErr(psm); return(ERROR); } + if((psm->Attribute &MPS)==PS256) { + _SetSsfdcCmd(psm,SSFDC_READ); + _SetSsfdcAddr(psm, ODD); + if(_CheckSsfdcBusy(psm,BUSY_READ)) + { _ResetSsfdcErr(psm); return(ERROR); } + _ReadSsfdcBuf(psm,buf+0x100,redundant+0x08); + if(_CheckSsfdcBusy(psm,BUSY_READ)) + { _ResetSsfdcErr(psm); return(ERROR); } + } + _SetSsfdcStandby(psm); + return(SUCCESS); +} + +int SsfdcWriteSect(ssfdc_dev *psm, unsigned char *buf, unsigned char *redundant) +{ +#if DEBUG_SSFDC_WRITESECT + printk(KERN_DEBUG "SsfdcWriteSect() - Zone %d LogBlock %d, PhyBlock %d, Sector %d\n", \ + psm->Zone, psm->LogBlock, psm->PhyBlock, psm->Sector); +#endif + _SetSsfdcCmd(psm,SSFDC_WRDATA); + _SetSsfdcAddr(psm,EVEN); + _WriteSsfdcBuf(psm,buf,redundant); + _SetSsfdcCmd(psm,SSFDC_WRITE); + if(_CheckSsfdcBusy(psm,BUSY_PROG)) + { _ResetSsfdcErr(psm); +#if DEBUG_SSFDC_WRITESECT + printk(KERN_DEBUG "SsfdcWriteSect() e 1\n"); +#endif + return(ERROR); } + if((psm->Attribute &MPS)==PS256) { + _SetSsfdcCmd(psm,SSFDC_RDSTATUS); + if(_CheckSsfdcStatus(psm)) + { _SetSsfdcStandby(psm); return(SUCCESS); } + _SetSsfdcCmd(psm,SSFDC_WRDATA); + _SetSsfdcAddr(psm,ODD); + _WriteSsfdcBuf(psm,buf+0x100,redundant+0x08); + _SetSsfdcCmd(psm,SSFDC_WRITE); + if(_CheckSsfdcBusy(psm,BUSY_PROG)) + { _ResetSsfdcErr(psm); +#if DEBUG_SSFDC_WRITESECT + printk(KERN_DEBUG "SsfdcWriteSect() e 2\n"); +#endif + return(ERROR); } + } + _SetSsfdcStandby(psm); + return(SUCCESS); +} + +int SsfdcEraseBlock(ssfdc_dev *psm) +{ + _SetSsfdcCmd(psm,SSFDC_ERASE1); + _SetSsfdcBlock(psm); + _SetSsfdcCmd(psm,SSFDC_ERASE2); + if(_CheckSsfdcBusy(psm,BUSY_ERASE) || SsfdcCheckStatus(psm)) { + _ResetSsfdcErr(psm); + return(ERROR); + } + _SetSsfdcStandby(psm); + return(SUCCESS); +} + +int SsfdcReadRedtData(ssfdc_dev *psm, unsigned char *redundant) +{ +#if DEBUG_SSFDC_READREDT + printk(KERN_DEBUG " +"); +#endif + _SetSsfdcCmd(psm,SSFDC_READ_REDT); + _SetSsfdcAddr(psm,EVEN); + if(_CheckSsfdcBusy(psm,BUSY_READ)) + { _ResetSsfdcErr(psm); +#if DEBUG_SSFDC_READREDT + printk(KERN_DEBUG " e 1\n"); +#endif + return(ERROR); } + _ReadRedtSsfdcBuf(psm, redundant); + if(_CheckSsfdcBusy(psm,BUSY_READ)) + { _ResetSsfdcErr(psm); +#if DEBUG_SSFDC_READREDT + printk(KERN_DEBUG " e 2\n"); +#endif + return(ERROR); } + if((psm->Attribute &MPS)==PS256) { + _SetSsfdcCmd(psm,SSFDC_READ_REDT); + _SetSsfdcAddr(psm,ODD); + if(_CheckSsfdcBusy(psm,BUSY_READ)) + { _ResetSsfdcErr(psm); +#if DEBUG_SSFDC_READREDT + printk(KERN_DEBUG " e 3\n"); +#endif + + return(ERROR); } + _ReadRedtSsfdcBuf(psm, redundant+0x08); + if(_CheckSsfdcBusy(psm,BUSY_READ)) + { _ResetSsfdcErr(psm); +#if DEBUG_SSFDC_READREDT + printk(KERN_DEBUG " e 4\n"); +#endif + return(ERROR); } + } + _SetSsfdcStandby(psm); +#if DEBUG_SSFDC_READREDT + printk(KERN_DEBUG " -\n"); +#endif + return(SUCCESS); +} + +int SsfdcWriteRedtData(ssfdc_dev *psm, unsigned char *redundant) +{ + _SetSsfdcCmd(psm,SSFDC_WRDATA); + _SetSsfdcAddr(psm,EVEN); + _WriteRedtSsfdcBuf(psm,redundant); + _SetSsfdcCmd(psm,SSFDC_WRITE); + if(_CheckSsfdcBusy(psm,BUSY_PROG)) + { _ResetSsfdcErr(psm); return(ERROR); } + if((psm->Attribute &MPS)==PS256) { + _SetSsfdcCmd(psm,SSFDC_RDSTATUS); + if(_CheckSsfdcStatus(psm)) + { _SetSsfdcStandby(psm); return(SUCCESS); } + _SetSsfdcCmd(psm,SSFDC_WRDATA); + _SetSsfdcAddr(psm,ODD); + _WriteRedtSsfdcBuf(psm,redundant+0x08); + _SetSsfdcCmd(psm,SSFDC_WRITE); + if(_CheckSsfdcBusy(psm,BUSY_PROG)) + { _ResetSsfdcErr(psm); return(ERROR); } + } + _SetSsfdcStandby(psm); + return(SUCCESS); +} + +/*************************************************************************** + SmartMedia ID Code Check & Mode Set Subroutine + ***************************************************************************/ +int SetSsfdcModel(ssfdc_dev *psm, unsigned char dcode) +{ + switch(_CheckDevCode(dcode)) { + case SSFDC1MB: + psm->Model = SSFDC1MB; + psm->Attribute = FLASH | AD3CYC | BS16 | PS256; + psm->MaxZones = 1; + psm->MaxBlocks = 256; + psm->MaxLogBlocks = 250; + psm->MaxSectors = 8; + break; + case SSFDC2MB: + psm->Model = SSFDC2MB; + psm->Attribute = FLASH | AD3CYC | BS16 | PS256; + psm->MaxZones = 1; + psm->MaxBlocks = 512; + psm->MaxLogBlocks = 500; + psm->MaxSectors = 8; + break; + case SSFDC4MB: + psm->Model = SSFDC4MB; + psm->Attribute = FLASH | AD3CYC | BS16 | PS512; + psm->MaxZones = 1; + psm->MaxBlocks = 512; + psm->MaxLogBlocks = 500; + psm->MaxSectors = 16; + break; + case SSFDC8MB: + psm->Model = SSFDC8MB; + psm->Attribute = FLASH | AD3CYC | BS16 | PS512; + psm->MaxZones = 1; + psm->MaxBlocks = 1024; + psm->MaxLogBlocks = 1000; + psm->MaxSectors = 16; + break; + case SSFDC16MB: + psm->Model = SSFDC16MB; + psm->Attribute = FLASH | AD3CYC | BS32 | PS512; + psm->MaxZones = 1; + psm->MaxBlocks = 1024; + psm->MaxLogBlocks = 1000; + psm->MaxSectors = 32; + break; + case SSFDC32MB: + psm->Model = SSFDC32MB; + psm->Attribute = FLASH | AD3CYC | BS32 | PS512; + psm->MaxZones = 2; + psm->MaxBlocks = 1024; + psm->MaxLogBlocks = 1000; + psm->MaxSectors = 32; + break; + case SSFDC64MB: + psm->Model = SSFDC64MB; + psm->Attribute = FLASH | AD4CYC | BS32 | PS512; + psm->MaxZones = 4; + psm->MaxBlocks = 1024; + psm->MaxLogBlocks = 1000; + psm->MaxSectors = 32; + break; + case SSFDC128MB: + psm->Model = SSFDC128MB; + psm->Attribute = FLASH | AD4CYC | BS32 | PS512; + psm->MaxZones = 8; + psm->MaxBlocks = 1024; + psm->MaxLogBlocks = 1000; + psm->MaxSectors = 32; + break; + default: + psm->Model = NOSSFDC; + return(ERROR); + } + return(SUCCESS); +} + +/***************************************************************************/ +static unsigned char _CheckDevCode(unsigned char dcode) +{ + switch(dcode){ + case 0x6E: + case 0xE8: + case 0xEC: return(SSFDC1MB); /* 8Mbit (1M) NAND */ + case 0x64: + case 0xEA: return(SSFDC2MB); /* 16Mbit (2M) NAND */ + case 0x6B: + case 0xE3: + case 0xE5: return(SSFDC4MB); /* 32Mbit (4M) NAND */ + case 0xE6: return(SSFDC8MB); /* 64Mbit (8M) NAND */ + case 0x73: return(SSFDC16MB); /*128Mbit (16M)NAND */ + case 0x75: return(SSFDC32MB); /*256Mbit (32M)NAND */ + case 0x76: return(SSFDC64MB); /*512Mbit (64M)NAND */ + case 0x79: return(SSFDC128MB); /* 1Gbit(128M)NAND */ + default: return(ERROR); + } +} +/*************************************************************************** + SmartMedia Power Control Subroutine + ***************************************************************************/ +void CntReset(ssfdc_dev *psm) +{ + _HwSetStandby(psm); + _HwVccOff(psm); +} + +int CntPowerOn(ssfdc_dev *psm) +{ + _HwVccOn(psm); + _WaitTimer(TIME_PON); + if(_HwChkPower(psm)) + return(SUCCESS); + _HwVccOff(psm); + return(ERROR); +} + +#if 0 // remove for now +static void mediachangetest(unsigned long dev_idx) +{ + ssfdc_dev *psm = ssfdc[dev_idx]; + unsigned int cardpresent; + unsigned long flags; + + spin_lock_irqsave( &psm->req_queue_lock, flags ); + // bjm spin_lock( &psm->req_queue_lock); + + del_timer(&mediachange_timer); + + // check current card presence + if ( ! (cardpresent = CntPowerOn(psm)) && psm->CardPresent ) { + psm->MediaChange = 1; + psm->DataBuf_Valid = 0; + } + psm->CardPresent = cardpresent; + + // set up to run again... + mediachange_timer.function = mediachangetest; + mediachange_timer.expires = jiffies + (HZ / 2); + mediachange_timer.data = dev_idx; + add_timer(&mediachange_timer); + + spin_unlock_irqrestore( &psm->req_queue_lock, flags ); + // bjm spin_unlock( &psm->req_queue_lock); +} +#endif + +int CheckCardExist(ssfdc_dev *psm) +{ + char i,j,k; + if(! _HwChkStatus(psm)) /***** Not Status Change *****/ + if(_HwChkCardIn(psm)) return(SUCCESS); /* Card exist in Slot */ + for(i=0,j=0,k=0; i<0x10; i++) { + if(_HwChkCardIn(psm)) /***** Status Change *****/ + { j++; k=0; } + else { j=0; k++; } + if(j>3) return(SUCCESS); /* Card exist in Slot */ + if(k>3) return(ERROR); /* NO Card exist in Slot */ + _WaitTimer(TIME_CDCHK); + } + return(ERROR); +} + +int CheckSsfdcWP(ssfdc_dev *psm) +{ /* ERROR: WP, SUCCESS: Not WP */ + char i; + for(i=0; i<0x08; i++) { + if(_HwChkWP(psm)) + return(ERROR); + _WaitTimer(TIME_WPCHK); + } + return(SUCCESS); +} + +/******************************************/ +int CheckCISdata(unsigned char *buf,unsigned char *redundant) +{ + static unsigned char cis[]={ 0x01,0x03,0xD9,0x01,0xFF,0x18,0x02,0xDF,0x01,0x20 }; + unsigned char ecc1,ecc2,ecc3; + unsigned int err; + calculate_ecc(ecctable,buf,&ecc1,&ecc2,&ecc3); + err=correct_data(buf,redundant+0x0D,ecc1,ecc2,ecc3); + if(err==0 || err==1 || err==2) + return(StringCmp(buf,cis,10)); + buf+=0x100; + calculate_ecc(ecctable,buf,&ecc1,&ecc2,&ecc3); + err=correct_data(buf,redundant+0x08,ecc1,ecc2,ecc3); + if(err==0 || err==1 || err==2) + return(StringCmp(buf,cis,10)); + return(ERROR); +} + +int CheckECCdata(unsigned char *buf,unsigned char *redundant) +{ + unsigned char ecc1,ecc2,ecc3; + unsigned int err, corr=SUCCESS; + calculate_ecc(ecctable,buf,&ecc1,&ecc2,&ecc3); + err=correct_data(buf,redundant+0x0D,ecc1,ecc2,ecc3); + if(err==1 || err==2) corr=CORRECT; + else if(err) return(ERROR); + buf+=0x100; + calculate_ecc(ecctable,buf,&ecc1,&ecc2,&ecc3); + err=correct_data(buf,redundant+0x08,ecc1,ecc2,ecc3); + if(err==1 || err==2) corr=CORRECT; + else if(err) return(ERROR); + return(corr); +} + +void SetECCdata(unsigned char *buf,unsigned char *redundant) +{ + calculate_ecc(ecctable,buf,redundant+0x0E,redundant+0x0D,redundant+0x0F); + buf+=0x100; + calculate_ecc(ecctable,buf,redundant+0x09,redundant+0x08,redundant+0x0A); +} + +/*************************************************************************** + Power Control & Media Exist Check Function + ***************************************************************************/ + +/*************************************************************************** + SmartMedia Read/Write/Erase Function + ***************************************************************************/ +static int MediaReadSector(ssfdc_dev *psm, struct request *req, + char * bbuf, long start,int count) +{ + char *buf; + int i, err, request_complete, + PrevBlock = NO_ASSIGN; + int read_status=0; + + if (ConvMediaAddr(psm, start)) { + printk(KERN_ERR "MediaReadSector() - bad address conversion\n"); + goto read_exit; + } + + psm->ReqSectorSize = count; + psm->BufIndex = 0; + psm->RetryCount = 0; + +#if DEBUG_SSFDC_READ + printk(KERN_DEBUG "MediaReadSector() - read %d sectors @ %d\n", psm->ReqSectorSize, start); +#endif + while (psm->ReqSectorSize) { + // if this PhyBlock is not assigned, fill with dummy data and return + // An assigned block results in a card access and readsector schedule... + if (psm->PhyBlock == NO_ASSIGN) { +#if DEBUG_SSFDC_READ + printk(KERN_DEBUG "Read NO_ASSIGN block %x\n", psm->PhyBlock); +#endif + buf = bbuf + psm->BufIndex; + for(i=0; iPhyBlock) { +#if DEBUG_SSFDC_READ + printk(KERN_DEBUG "Read block %x\n", psm->PhyBlock); +#endif + PrevBlock = psm->PhyBlock; + _SetSsfdcCmd(psm,SSFDC_READ); + _SetSsfdcAddr(psm, EVEN); + for (i=0; i<5; ++i) { + if (!_HwChkBusy(psm)) + break; + udelay(10); + } + } + + + if ( _HwChkBusy(psm) ) { + ++psm->Sect_rd_errs_ttl; +#if DEBUG_SSFDC_READ + printk(KERN_DEBUG "MediaReadSector() - Hardware busy!\n"); +#endif + } + else { + ++psm->Sector_reads; + _ReadSsfdcBuf( psm, psm->SectBuf, psm->Redundant); + + // verify the integrity of what was read + if (CheckDataStatus(psm->Redundant)) { +#if DEBUG_SSFDC_READ + printk(KERN_DEBUG "Bad Data Status\n"); +#endif + goto error_state; + } + + switch (err = CheckECCdata(psm->SectBuf,psm->Redundant)) + { + case CORRECT: + // Correctable data, fix and copy like SUCCESS + SetECCdata(psm->SectBuf,psm->Redundant); + case SUCCESS: + memcpy(bbuf + psm->BufIndex, psm->SectBuf, SSFDC_SECTSIZE); + break; + + case ERROR: +error_state: + ++psm->Sect_rd_errs_ttl; +#if DEBUG_SSFDC_READ + printk(KERN_DEBUG "readsector() - err == ERROR\n"); +#endif + _ResetSsfdcErr(psm); + if (++psm->RetryCount < RD_RETRY_LIMIT) { + continue; + } + break; + default: + ssfdc_terminate_request( psm, req); + break; + } + + } // if ( _HwChkBusy(psm) ) + } // if (psm->PhyBlock == NO_ASSIGN) + + // common req/buffer management code for either unassigned or assigned + // block from /dev/ssfdc + psm->RetryCount = 0; + psm->BufIndex += SSFDC_SECTSIZE; + request_complete = (--psm->ReqSectorSize == 0); + if (request_complete) { + // completed the read, req->buffer now has requested sector(s). + // End the request waking sleeping process and reschedule initxfer(). +#if DEBUG_SSFDC_READ + printk(KERN_DEBUG "readsector() - req %x complete\n", req); +#endif + read_status = 1; + } + else if (IncMediaAddr(psm)) { + printk(KERN_DEBUG "readsector() - IncMediaAddr() error.\n"); + goto read_exit; + } + } // while (psm->ReqSectorSize) + +read_exit: + psm->XferState = xfer_idle; + + return read_status; + +} + +/* + ReadBlkCopy(ssfdc_dev *psm, unsigned char *buf) +*/ +int ReadBlkCopy(ssfdc_dev *psm, unsigned char *buf, char *rd_sector_status) +{ + int err, read_error=0, rw_retry=0; + unsigned long PrevBlock=NO_ASSIGN; + + if ( ! buf ) { + printk(KERN_ERR "NULL buffer pointer\n"); + return ERROR; + } + + if (psm->PhyBlock == NO_ASSIGN) { + memset(buf, 0xff, psm->MaxSectors * SSFDC_SECTSIZE); + memset(rd_sector_status, 1, sizeof(char) * MAX_SECTNUM); + return SUCCESS; + } + +#if 0 + printk(KERN_ERR "ReadBlkCopy() - LogBlk %d\n", psm->LogBlock); +#endif + + for (psm->Sector = 0; + psm->PhyBlock != NO_ASSIGN && psm->Sector < psm->MaxSectors; + ++psm->Sector) + { + if (PrevBlock != psm->PhyBlock) { + _SetSsfdcCmd(psm,SSFDC_READ); + _SetSsfdcAddr(psm, EVEN); + PrevBlock = psm->PhyBlock; + _CheckSsfdcBusy(psm, BUSY_ADDR_SET); + } + + if ( _HwChkBusy(psm) ) { + printk(KERN_ERR "%s: HW busy during block copy!\n", MAJOR_NAME); + goto error_state; + } + + _ReadSsfdcBuf( psm, psm->SectBuf, psm->Redundant); + if (CheckDataStatus(psm->Redundant)) { + printk("KERN_ERR reading Block %d, sector %d\n", psm->PhyBlock, psm->Sector); + goto error_state; + } + + // Attempt to correct + switch (err = CheckECCdata(psm->SectBuf,psm->Redundant)) + { + case CORRECT: +#if DEBUG_SSFDC_WRITE + printk(KERN_DEBUG "ReadBlkCopy() - err == CORRECT\n"); +#endif + SetECCdata(psm->SectBuf,psm->Redundant); + case SUCCESS: + read_error = 0; + rw_retry = 0; + memcpy(buf + (psm->Sector * SSFDC_SECTSIZE), psm->SectBuf, SSFDC_SECTSIZE); + rd_sector_status[psm->Sector] = 1; + read_error = 0; + break; + + case ERROR: +error_state: +/*bjm*/ printk("ERR - ECC error reading Block %d, Sector %d\n", psm->PhyBlock,psm->Sector); +#if DEBUG_SSFDC_WRITE + printk(KERN_DEBUG "ReadBlkCopy() - err == ERROR\n"); + printk("_ResetSsfdcErr(psm)\n"); +#endif + _ResetSsfdcErr(psm); + PrevBlock = NO_ASSIGN; + if (++rw_retry < RD_RETRY_LIMIT) { + // retry current Sector/loop counter on next loop iteration. + --psm->Sector; + } + else { + // set sector data in copy buf to the unassigned value 0xFF + // next loop iteration will read next Sector, zero RetryCount + // for next sectors read + // map bad sector... + memset(buf + psm->Sector * SSFDC_SECTSIZE, 0xFF, SSFDC_SECTSIZE); + rw_retry = 0; + rd_sector_status[psm->Sector] = 0; + ++psm->Sect_rd_errs_ttl; + read_error = 1; + } +#if DEBUG_SSFDC_WRITE + printk(KERN_DEBUG "Unable to read Blk %d Sector %d\n", psm->PhyBlock, psm->Sector); +#endif + break; + } + } + if (!read_error) { + if (SsfdcEraseBlock(psm)) { + MarkFailPhyOneBlock(psm); + ++psm->Bad_blks_erase; + } + else { + ClrBit(psm->Assign[psm->Zone], psm->PhyBlock); + } + } + else { + printk("Read error block %d\n", psm->PhyBlock); + MarkFailPhyOneBlock(psm); + } + psm->Sector = 0; + return read_error ? ERROR : SUCCESS; +} + +/* + WriteBlock() +*/ +int WriteBlock(ssfdc_dev *psm, char *buf, char *wr_sector_status) +{ + int write_error=0, reassign_retry=0; + + for ( reassign_retry = 0; reassign_retry < REASSIGN_RETRY_LIMIT; ++reassign_retry) + { + /* + assign new write block for write req + - set new write address + - write buffer to new block + */ +#if DEBUG_SSFDC_WRITE + if (AssignWriteBlock(psm,1)) { +#else + if (AssignWriteBlock(psm,0)) { +#endif + write_error = 1; + printk(KERN_ERR "sm%dd Unable to assign new write block.\n", psm->sm_minor); + memset(wr_sector_status, 1, sizeof(char) * MAX_SECTNUM); + // ssfdc_terminate_request(psm, req); + return ERROR; + } + +#if 0 + printk(KERN_ERR "WriteBlock() - LogBlock %d\n", psm->LogBlock); +#endif + + for (psm->Sector = 0; psm->Sector < psm->MaxSectors; ++psm->Sector) + { + memcpy(psm->SectBuf,buf+psm->Sector*SSFDC_SECTSIZE,SSFDC_SECTSIZE); + + _SetSsfdcCmd(psm,SSFDC_WRDATA); + _SetSsfdcAddr(psm,EVEN); + ClrRedundantData(psm->Redundant); + SetLogBlockAddr(psm,psm->Redundant); + SetECCdata(psm->SectBuf,psm->Redundant); + _WriteSsfdcBuf(psm,psm->SectBuf,psm->Redundant); + +#if DEBUG_SSFDC_WRITE + printk("%d ", psm->Sector); +#endif + + _SetSsfdcCmd(psm, SSFDC_WRITE); + if ( ! _CheckSsfdcBusy(psm, BUSY_PROG) && !SsfdcCheckStatus(psm)) { +#if DEBUG_SSFDC_WRITE + printk("\nMulti-Sector write OK!\n"); +#endif + _SetSsfdcStandby(psm); + // mark status a success + wr_sector_status[psm->Sector] = 1; + +#if 0 // bjm removed + { unsigned char parbuf[SSFDC_SECTSIZE]; + unsigned char redtpar[REDTSIZE]; + + _SetSsfdcCmd(psm,SSFDC_READ); + _SetSsfdcAddr(psm, EVEN); + + udelay(30); + if ( _HwChkBusy(psm) ) { + _ResetSsfdcErr(psm); + printk("paranoid read failure\n"); + } + else { + _ReadSsfdcBuf(psm, parbuf, redtpar); + if (CheckDataStatus(redtpar)) { + printk("paranoid read, bad data status\n"); + } + else { + switch( err = CheckECCdata(parbuf,redtpar)) + { + case CORRECT: + printk("paranoid correctable\n"); + SetECCdata(parbuf,redtpar); + case SUCCESS: + if (memcmp(parbuf,psm->SectBuf, SSFDC_SECTSIZE)) + write_error = 1; + else + write_error = 0; + break; + case ERROR: + MarkFailPhyOneBlock(psm); + write_error=1; + break; + } + } + } + } // bjm end of paranoid read back test... +#endif + } + else { +#if DEBUG_SSFDC_WRITE + printk("\nMulti-Sector write FAILED!\n"); +#endif + // mark status a failure + wr_sector_status[psm->Sector] = 0; + _ResetSsfdcErr(psm); + write_error = 1; + break; + } // for (psm->Sector ...) + if (write_error) + break; + } + if ( ! write_error ) { + psm->Log2Phy[psm->Zone][psm->LogBlock] = psm->WriteBlock; + break; + } + } + psm->Sector = 0; + return write_error ? ERROR : SUCCESS; +} + +/* + MediaWriteSector() + +*/ +static int MediaWriteSector(ssfdc_dev *psm, struct request *req, char * bbuf, long start, int count) +{ + int write_error=0, unwritten_block=0; + char *buf; + unsigned long curr_sector, blksize, PrevBlock; + unsigned long writebuf_index, readbio_index; + int i, sector, rw_retry=0; + int sectors_in_block; + char rd_sector_status[MAX_SECTNUM]; + char wr_sector_status[MAX_SECTNUM]; + + // optimized write, new vars + int bio_endios=0; + int bio_bvecs=0; + struct bio *bio; + // optimized for write + +#if DEBUG_SSFDC_WRITE + printk(KERN_DEBUG "+MediaWriteSector()\n"); +#endif + + if (!count) { + printk("MediaWriteSector() count == 0!\n"); + ssfdc_end_request(psm, req, 0); + return 1; + } + + if (CheckMediaWP(psm)) { + printk(KERN_DEBUG "%s: write protected media.\n", MAJOR_NAME); + ssfdc_terminate_request( psm, req); + psm->XferState = xfer_idle; + return -EIO; + } + + // allocate block size buffer + blksize = psm->MaxSectors * SSFDC_SECTSIZE; +#if DEBUG_SSFDC_WRITE + printk(KERN_DEBUG "%s: Allocate %d sized block.\n", MAJOR_NAME, blksize); +#endif + if ((buf = kmalloc(blksize, GFP_ATOMIC)) == NULL) { + printk(KERN_ERR "%s: Null buffer allocated!\n", MAJOR_NAME); + ssfdc_terminate_request( psm, req); + goto the_exit; + } + + /* + Loop to handle a request at the curr_sector of count sectors. + The write operation my encompas more than one phys block. + */ + curr_sector = start; + sectors_in_block = 0; + // zero out our sector R/W status array + memset(rd_sector_status, 1, sizeof(char) * MAX_SECTNUM); + memset(wr_sector_status, 1, sizeof(char) * MAX_SECTNUM); + + // rangecheck this sector within the device. + if (ConvMediaAddr(psm, curr_sector)) { + ssfdc_terminate_request(psm, req); + printk(KERN_ERR "WriteSector: ConvMediaAddr() error\n"); +#if DEBUG_SSFDC_WRITE + printk(KERN_DEBUG "-MediaWriteSector()\n"); +#endif + return 0; + } + +#if DEBUG_SSFDC_WRITE + printk(KERN_DEBUG "MediaWriteSector() Zone %d, LogBlock %d PhyBlock %d, Sector %d\n", + psm->Zone,psm->LogBlock, psm->PhyBlock,psm->Sector); +#endif + + PrevBlock = NO_ASSIGN; + rw_retry = 0; + +#if DEBUG_SSFDC_WRITE + printk(KERN_DEBUG "Copy Zone %d, Phys %d\n", psm->Zone, psm->PhyBlock); +#endif + // As a technique for wear leveling, a write to the SM results in the contents + // of the block to be copied into a blocksize buffer, the write data of the + // write request being overlayed onto the buffer containing the copied block, + // a new logical to physical mapping defined, and the buffer written into this + // newly mapped (logically) physical block. + + // read the physical block into the buffer. +#if DEBUG_SSFDC_WRITE + printk(KERN_DEBUG "ReadBlock = %d LogBlock %d\n", psm->PhyBlock, psm->LogBlock); +#endif + if (ReadBlkCopy(psm, buf, rd_sector_status) != SUCCESS) { + printk(KERN_ERR "Unable to read block.\n"); + goto the_exit; + } + +#if DEBUG_SSFDC_WRITE + printk(KERN_DEBUG "Read from pending write request "); +#endif + +#if 0 + int bio_idx=0; +#endif + rq_for_each_bio(bio, req) { + struct bio_vec *bvec; + int i, break_flag=0; +#if 0 + int segment_idx; + printk(KERN_ERR "bio %d\n", bio_idx++); + + segment_idx = 0; +#endif + // bio_bvecs = 0; + bio_for_each_segment(bvec, bio, i) { +#if 0 + printk(KERN_ERR "segment %d\n", segment_idx++); +#endif + // The conditions... + // bio fits within block + if (WriteReqInCurrBlk(psm,curr_sector + (bvec->bv_len >> 9) - 1,§or) + && WriteReqInCurrBlk(psm,curr_sector, §or)) + { +#if 0 + printk(KERN_ERR "LogBlk %d: write at %d, %d sectors\n", + psm->LogBlock, curr_sector % psm->MaxSectors, bio_cur_sectors(bio)); +#endif + // write bio into copied block + ++bio_bvecs; + writebuf_index = sector * SSFDC_SECTSIZE; +#if 0 + printk(KERN_ERR "memcpy buf at 0x%x, 0x%x bytes\n", + writebuf_index, bvec->bv_len); +#endif + memcpy(buf + writebuf_index, + page_address(bvec->bv_page) + bvec->bv_offset, bvec->bv_len); + unwritten_block = 1; + curr_sector += bvec->bv_len >> 9; + } + // bio fits partially within block + else if (WriteReqInCurrBlk(psm,curr_sector, §or)) + { + // put portion of bio in block + ++bio_bvecs; + writebuf_index = sector * SSFDC_SECTSIZE; + sectors_in_block = psm->MaxSectors - sector; + readbio_index = sectors_in_block * SSFDC_SECTSIZE; +#if 0 + printk(KERN_ERR "memcpy buf at %x, %x bytes\n", + writebuf_index, readbio_index); +#endif + memcpy(buf + writebuf_index, + page_address(bvec->bv_page) + bvec->bv_offset, readbio_index); +#if 0 + printk(KERN_ERR "LogBlk %d: partial-write at %d, %d sectors first\n", + psm->LogBlock, curr_sector % psm->MaxSectors, sectors_in_block); +#endif + // write block + unwritten_block = 0; + if (WriteBlock(psm, buf, wr_sector_status) != SUCCESS) { + printk(KERN_ERR "Unable to write block %d\n", psm->LogBlock); + // write_error - writing this block failed + break_flag = 1; + break; + } + // incr addr & read next block, + curr_sector += sectors_in_block; + if (ConvMediaAddr(psm,curr_sector) != SUCCESS) { + printk(KERN_ERR "MediaWriteSector() IncMediaAddr() error!\n"); + // write_error - address into next block is bogus + write_error = 1; + break_flag = 1; + break; + } + if (ReadBlkCopy(psm, buf, rd_sector_status) != SUCCESS) { + printk(KERN_ERR "MediaWriteSector() ReadBlkCopy() error!\n"); + // write error - next block read error + write_error = 1; + break_flag =1; + break; + } + // write remainder of bio into block +#if 0 + printk(KERN_ERR "LogBlk %d: partial-write at %d, %d sectors, second write\n", + psm->LogBlock, curr_sector % psm->MaxSectors, (bvec->bv_len >> 9) - sectors_in_block); +#endif +#if 0 + printk(KERN_ERR "memcpy buf at 0x%x, from bio 0x%x, for 0x%x bytes\n", + 0, readbio_index, ((bvec->bv_len >> 9) - sectors_in_block) * SSFDC_SECTSIZE); +#endif + memcpy(buf, (page_address(bvec->bv_page) + bvec->bv_offset) + readbio_index, + ((bvec->bv_len >> 9) - sectors_in_block) * SSFDC_SECTSIZE); + writebuf_index = ((bvec->bv_len >> 9) - sectors_in_block) * SSFDC_SECTSIZE; + unwritten_block = 1; + curr_sector += (bvec->bv_len >> 9) - sectors_in_block; + } + // bio is not in block at all. coplete unwritten block and exit loop. + else { + // write current block +#if 0 + printk(KERN_ERR "bio no longer in block\n"); +#endif + if (unwritten_block) { + if (WriteBlock(psm, buf, wr_sector_status) != SUCCESS) { + printk(KERN_ERR "MediaWriteSector() WriteBlock() error!\n"); + // write_error + } + unwritten_block = 0; + } + break_flag = 1; + break; + } + } + // bjm if (bio_bvecs) +bio_bvecs; + + if (break_flag) + break; + } + + if (unwritten_block) { + if (WriteBlock(psm, buf, wr_sector_status) != SUCCESS) { + printk(KERN_ERR "MediaWriteSector() WriteBlock() error!\n"); + write_error = 1; + } + } + + if (!(bio_endios=bio_bvecs)) { + if (static_ssfdc_debug) + printk("no bios from request!\n"); + ++bio_endios; + write_error = 0; + } + +the_exit: + // log sector status for the copied/unmodified Sectors and flag any that have cpy errors + for (sector = 0; sector < psm->MaxSectors; ++sector) { + if ( ! rd_sector_status[sector] ) + printk(KERN_ERR "%s: READ sector %d invalid for block %d!\n", \ + MAJOR_NAME, sector, psm->LogBlock); + if ( ! wr_sector_status[sector]) + printk(KERN_ERR "%s: WRITTEN sector %d invalid for block %d!\n", \ + MAJOR_NAME, sector, psm->LogBlock); + } + + // free our prev allocated block for copy... + if (buf) + kfree(buf); + + psm->XferState = xfer_idle; + +#if DEBUG_SSFDC_WRITE + printk(KERN_DEBUG "-MediaWriteSector()\n"); +#endif + if (static_ssfdc_debug) + printk("end_request(%d) ", ! write_error); + for (i = 0; i < bio_endios; ++i) { + if (static_ssfdc_debug) + printk("%d ", i); + ssfdc_end_request(psm, req, ! write_error); + } + if (static_ssfdc_debug) + printk("\n"); + + return ! write_error; + +} + + +/*************************************************************************** + SmartMedia Logical Format Subroutine + ***************************************************************************/ +int CheckLogCHS(ssfdc_dev *psm, unsigned int *c,unsigned char *h,unsigned char *s) +{ + switch(psm->Model) { + case SSFDC1MB: *c=125; *h= 4; *s= 4; break; + case SSFDC2MB: *c=125; *h= 4; *s= 8; break; + case SSFDC4MB: *c=250; *h= 4; *s= 8; break; + case SSFDC8MB: *c=250; *h= 4; *s=16; break; + case SSFDC16MB: *c=500; *h= 4; *s=16; break; + case SSFDC32MB: *c=500; *h= 8; *s=16; break; + case SSFDC64MB: *c=500; *h= 8; *s=32; break; + case SSFDC128MB: *c=500; *h=16; *s=32; break; + default: + *c= 0; *h= 0; *s= 0; + psm->ErrCode=ERR_NoSmartMedia; + return(ERROR); + } + return(SUCCESS); +} +/*************************************************************************** + Power Control & Media Exist Check Subroutine + ***************************************************************************/ + +int CheckMediaWP(ssfdc_dev *psm) +{ + if(psm->Attribute &MWP) + { psm->ErrCode=ERR_WrtProtect; return(ERROR); } + return(SUCCESS); +} + +/*************************************************************************** + SmartMedia Physical Address Control Subroutine + ***************************************************************************/ +int ConvMediaAddr(ssfdc_dev *psm, long addr) +{ + long temp; + temp =addr/psm->MaxSectors; + psm->Sector =addr%psm->MaxSectors; + psm->LogBlock=temp%psm->MaxLogBlocks; + psm->Zone =temp/psm->MaxLogBlocks; + if(psm->ZoneMaxZones) { + ClrRedundantData(psm->Redundant); + SetLogBlockAddr(psm,psm->Redundant); + psm->PhyBlock=psm->Log2Phy[psm->Zone][psm->LogBlock]; +#if DEBUG_SSFDC_ASSIGNRELEASE + printk(KERN_DEBUG "ConvMediaAddr() LogBlock %d -> PhyBlock %d\n", + psm->LogBlock, psm->PhyBlock); +#endif + return(SUCCESS); + } + psm->ErrCode=ERR_OutOfLBA; + return(ERROR); +} + +int IncMediaAddr(ssfdc_dev *psm) +{ + if(++psm->SectorMaxSectors) + return(SUCCESS); + psm->Sector=0; + if(++psm->LogBlockMaxLogBlocks) { + ClrRedundantData(psm->Redundant); + SetLogBlockAddr(psm,psm->Redundant); + psm->PhyBlock=psm->Log2Phy[psm->Zone][psm->LogBlock]; +#if DEBUG_SSFDC_ASSIGNRELEASE + printk(KERN_DEBUG "IncMediaAddr() PhyBlock %d <- LogBlock %d\n", + psm->PhyBlock, psm->LogBlock); +#endif + return(SUCCESS); + } + psm->LogBlock=0; + if(++psm->ZoneMaxZones) { + ClrRedundantData(psm->Redundant); + SetLogBlockAddr(psm,psm->Redundant); + psm->PhyBlock=psm->Log2Phy[psm->Zone][psm->LogBlock]; +#if DEBUG_SSFDC_ASSIGNRELEASE + printk(KERN_DEBUG "IncMediaAddr() PhyBlock %d <- LogBlock %d\n", + psm->PhyBlock, psm->LogBlock); +#endif + return(SUCCESS); + } + psm->Zone=0; + psm->ErrCode=ERR_OutOfLBA; + return(ERROR); +} + +/***************************************************************************/ + + +static int WriteReqInCurrBlk(ssfdc_dev *psm, long sector, int *blksector) +{ + long temp; + unsigned char Zone; /* Zone Number */ + unsigned int LogBlock; /* Logical Block Number of Zone */ + + if (!psm) + return 0; + + temp = sector / psm->MaxSectors; + *blksector = sector % psm->MaxSectors; + LogBlock = temp % psm->MaxLogBlocks; + Zone = temp / psm->MaxLogBlocks; + + return (psm->LogBlock == LogBlock && psm->Zone == Zone); +} + +/*************************************************************************** + SmartMedia Read/Write Subroutine with Retry + ***************************************************************************/ + +/*************************************************************************** + SmartMedia Physical Block Assign/Release Subroutine + ***************************************************************************/ +int AssignWriteBlock(ssfdc_dev *psm, int verbose_flag) +{ + psm->ReadBlock=psm->PhyBlock; +#if DEBUG_SSFDC_WRITE + int Zonesave=psm->Zone, ZoneIndex; +#endif + +#if DEBUG_SSFDC_WRITE + if (verbose_flag) { + printk("AssignWriteBlock() verbose mode. psm->Zone %d\n",psm->Zone); + for (psm->Zone = 0; psm->Zone < psm->MaxZones; psm->Zone++) { + int free_blk=0; + printk("\tZone %d, AssignStart %d and ", psm->Zone, psm->AssignStart[psm->Zone]); + for (psm->WriteBlock=0; psm->WriteBlock < psm->MaxLogBlocks; psm->WriteBlock++) + if (! ChkBit(psm->Assign[psm->Zone],psm->WriteBlock)) ++free_blk; + printk("%d free blocks.\n", free_blk); + } + psm->Zone = Zonesave; + } +#endif + for(psm->WriteBlock=psm->AssignStart[psm->Zone]; psm->WriteBlockMaxBlocks; psm->WriteBlock++) + if(! ChkBit(psm->Assign[psm->Zone],psm->WriteBlock)) { + SetBit(psm->Assign[psm->Zone],psm->WriteBlock); + psm->AssignStart[psm->Zone]=psm->WriteBlock+1; + psm->PhyBlock=psm->WriteBlock; + psm->SectCopyMode=REQ_ERASE; +#if DEBUG_SSFDC_ASSIGNRELEASE + printk(KERN_DEBUG "AssignWriteBlock() - WriteBlock %d ReadBlock %d LogBlock %d\n", + psm->WriteBlock, psm->ReadBlock, psm->LogBlock); +#endif + return(SUCCESS); + } + for(psm->WriteBlock=0; psm->WriteBlockAssignStart[psm->Zone]; psm->WriteBlock++) + if(! ChkBit(psm->Assign[psm->Zone],psm->WriteBlock)) { + SetBit(psm->Assign[psm->Zone],psm->WriteBlock); + psm->AssignStart[psm->Zone]=psm->WriteBlock+1; + psm->PhyBlock=psm->WriteBlock; + psm->SectCopyMode=REQ_ERASE; +#if DEBUG_SSFDC_ASSIGNRELEASE + printk(KERN_DEBUG "AssignWriteBlock() - WriteBlock %d PhyBlock %d LogBlock %d\n", + psm->WriteBlock, psm->PhyBlock, psm->LogBlock); +#endif + return(SUCCESS); + } + psm->WriteBlock=NO_ASSIGN; + psm->ErrCode=ERR_WriteFault; + return(ERROR); +} + +/*************************************************************************** + SmartMedia Physical Format Check Local Subroutine + ***************************************************************************/ +static int SetPhyFmtValue(ssfdc_dev *psm) +{ + unsigned int idcode; + SsfdcReadID(psm, &idcode); + if(SetSsfdcModel(psm,(unsigned char)idcode)) + return(ERROR); + if(CheckSsfdcWP(psm)) + psm->Attribute|=WP; + return(SUCCESS); +} + +static int SearchCIS(ssfdc_dev *psm, unsigned int *pcis) +{ + psm->Zone=0; psm->Sector=0; + for(psm->PhyBlock=0; psm->PhyBlock<(psm->MaxBlocks-psm->MaxLogBlocks-1); psm->PhyBlock++) { + if(SsfdcReadRedtData(psm, psm->Redundant)) + { SsfdcReset(psm); return(ERROR); } + if(! CheckFailBlock(psm->Redundant)) { + if(CheckCisBlock(psm->Redundant)) + { SsfdcReset(psm); return(ERROR); } + break; + } + } + while(psm->SectorMaxSectors) { + if(psm->Sector) + if(SsfdcReadRedtData(psm, psm->Redundant)) + { SsfdcReset(psm); return(ERROR); } + if(! CheckDataStatus(psm->Redundant)) { + if(SsfdcReadSect(psm,psm->WorkBuf,psm->Redundant)) + { SsfdcReset(psm); return(ERROR); } + if(CheckCISdata(psm->WorkBuf,psm->Redundant)) + { SsfdcReset(psm); return(ERROR); } + *pcis=psm->PhyBlock; + SsfdcReset(psm); + return(SUCCESS); + } + psm->Sector++; + } + SsfdcReset(psm); + return(ERROR); +} + +/***************************************************************************/ +static int MakeLogTable(ssfdc_dev *psm, unsigned int start) +{ + unsigned int block; + unsigned int blk_total=0, blk_blank=0, blk_nologaddr=0, + blk_fail=0, blk_assigned=0; + +#if DEBUG_SSFDC + printk(KERN_DEBUG "MakeLogTable()\n"); +#endif + psm->DataBuf_Valid = 1; + psm->Sector=0; + for(psm->Zone=0; psm->ZoneMaxZones; psm->Zone++) { + /* set all LogBlocks to NO_ASSIGN */ + for(psm->LogBlock=0; psm->LogBlockMaxLogBlocks; psm->LogBlock++) + psm->Log2Phy[psm->Zone][psm->LogBlock]=NO_ASSIGN; + /* for all Assigns[zone][PhyBlock] = 0x00 */ + for(psm->PhyBlock=0; psm->PhyBlock<(MAX_BLOCKNUM/8); psm->PhyBlock++) + psm->Assign[psm->Zone][psm->PhyBlock]=0x00; + /*******************************************************************/ + for(psm->PhyBlock=0; psm->PhyBlockMaxBlocks; psm->PhyBlock++) { + if((! psm->Zone) && (psm->PhyBlockAssign[psm->Zone],psm->PhyBlock); + continue; + } + ++blk_total; + if(SsfdcReadRedtData(psm,psm->Redundant)) { + SsfdcReset(psm); +#if 0 + printk(KERN_ERR "error 1 PhyBlock %d\n", psm->PhyBlock); +#endif + return(ERROR); + } + if(! CheckDataBlank(psm->Redundant)) { + ++blk_blank; + continue; + } + SetBit(psm->Assign[psm->Zone],psm->PhyBlock); + if(CheckFailBlock(psm->Redundant)) { +#if 0 + printk("Zone %d, Block %d failed\n", psm->Zone, psm->PhyBlock); +#endif + ++blk_fail; + continue; + } + if(LoadLogBlockAddr(psm)) { + ++blk_nologaddr; + c