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; + continue; + } + if(psm->LogBlock>=psm->MaxLogBlocks) + continue; + ++blk_assigned; + if(psm->Log2Phy[psm->Zone][psm->LogBlock]==NO_ASSIGN) { +#if DEBUG_SSFDC_ASSIGNRELEASE + if (psm->LogBlock == 0) + printk(KERN_DEBUG "MakeLogTable() LogBlock %d = PhyBlock %d\n", + psm->LogBlock, psm->PhyBlock); +#endif + psm->Log2Phy[psm->Zone][psm->LogBlock]=psm->PhyBlock; + continue; + } + psm->Sector=psm->MaxSectors-1; + if(SsfdcReadRedtData(psm,psm->Redundant)) { + SsfdcReset(psm); +#if 0 + printk(KERN_ERR "error 2\n"); +#endif + return(ERROR); + } + psm->Sector=0; + block=psm->LogBlock; + if(! LoadLogBlockAddr(psm)) + if(psm->LogBlock==block) { +#ifdef L2P_ERR_ERASE /***************************************************/ + block=psm->Log2Phy[psm->Zone][psm->LogBlock]; + psm->Log2Phy[psm->Zone][psm->LogBlock]=psm->PhyBlock; + psm->PhyBlock=block; + if(!(psm->Attribute &MWP)) { + SsfdcReset(psm); + if(SsfdcEraseBlock(psm)) { + printk(KERN_ERR "error 3\n"); + return(ERROR); + } + if(SsfdcCheckStatus(psm)) { + if(MarkFailPhyOneBlock(psm)) { + printk(KERN_ERR "error 4\n"); + return(ERROR); + } + } + else ClrBit(psm->Assign[psm->Zone],psm->PhyBlock); + } + psm->PhyBlock=psm->Log2Phy[psm->Zone][psm->LogBlock]; +#else /*******************************************************************/ + psm->Log2Phy[psm->Zone][psm->LogBlock]=psm->PhyBlock; +#endif /*******************************************************************/ + continue; + } +#ifdef L2P_ERR_ERASE /***************************************************/ + if(!(psm->Attribute &MWP)) { + SsfdcReset(psm); + if(SsfdcEraseBlock(psm)) { + printk(KERN_ERR "error 5\n"); + return(ERROR); + } + if(SsfdcCheckStatus(psm)) { + if(MarkFailPhyOneBlock(psm)) { + printk(KERN_ERR "error 6\n"); + return(ERROR); + } + } + else ClrBit(psm->Assign[psm->Zone],psm->PhyBlock); + } +#endif /*******************************************************************/ + } + psm->AssignStart[psm->Zone]=0; + } + SsfdcReset(psm); +#if 0 + printk("MakeLogTable()\n"); + printk("\t%d failed\n", blk_fail); + printk("\t%d blank\n", blk_blank); + printk("\t%d assigned\n", blk_assigned); + printk("\t%d no logical addr\n", blk_nologaddr); + printk("\n\t%d total\n", blk_total); + printk("\t%d sum total\n", blk_fail + blk_blank + blk_assigned + blk_nologaddr); +#endif + return(SUCCESS); +} + +/***************************************************************************/ +static int MarkFailPhyOneBlock(ssfdc_dev *psm) +{ + unsigned char sect; + sect=psm->Sector; + SetFailBlock(psm->WorkRedund); + SsfdcWriteRedtMode(psm); + for(psm->Sector=0; psm->SectorMaxSectors; psm->Sector++) + if(SsfdcWriteRedtData(psm,psm->WorkRedund)) { + SsfdcReset(psm); + psm->Sector=sect; + psm->ErrCode=ERR_HwError; + return(ERROR); + } /* NO Status Check */ + SsfdcReset(psm); + psm->Sector=sect; + return(SUCCESS); +} + +/*************************************************************************** + SmartMedia Control subroutine + Rev 0.30('98-06-30) ***** BETA RELEASE ***** + Copyright (c) 1997-98, Toshiba Corporation. All rights reserved. + ***************************************************************************/ + + +/* Linux Driver Modifications */ +/* +dump_ssfdc_state +*/ +#if DEBUG_SSFDC +void dump_ssfdc_state(ssfdc_dev * psm) +{ +#if DEBUG_SSFDC_STRUCT + // misc structure dump information + printk(KERN_DEBUG "psm->\n"); + /* unsigned long */ printk(KERN_DEBUG "\t address %x\n", psm->address); + /* int */ printk(KERN_DEBUG "\t sm_minor %d\n",psm->sm_minor); + /* struct dentry printk(KERN_DEBUG "\t *sm_dentry %x\n",psm->sm_dentry );*/ + /* kdev_t */ printk(KERN_DEBUG "\t sm_device %x\n",psm->sm_device); + /* int */ printk(KERN_DEBUG "\t sm_flags %x\n",psm->sm_flags); + /* unsigned int */ printk(KERN_DEBUG "\t UseCount %d\n",psm->UseCount); + /* unsigned int */ printk(KERN_DEBUG "\t ErrCode %d\n",psm->ErrCode); + /* unsigned int */ printk(KERN_DEBUG "\t MediaChange %d\n",psm->MediaChange); + /* unsigned int */ printk(KERN_DEBUG "\t SectCopyMode %d\n",psm->SectCopyMode); + /* unsigned int */ printk(KERN_DEBUG "\t HostCyl %d\n",psm->HostCyl ); + /* unsigned char */ printk(KERN_DEBUG "\t HostHead %d\n",psm->HostHead ); + /* unsigned char */ printk(KERN_DEBUG "\t HostSect %d\n",psm->HostSect ); + /* unsigned int */ printk(KERN_DEBUG "\t ReadBlock %d\n",psm->ReadBlock ); + /* unsigned int */ printk(KERN_DEBUG "\t WriteBlock %d\n",psm->WriteBlock ); + + /* Card attributes */ + /* unsigned char */ printk(KERN_DEBUG "\t Model %d\n",psm->Model ); + /* unsigned char */ printk(KERN_DEBUG "\t Attribute %x\n",psm->Attribute ); + /* unsigned char */ printk(KERN_DEBUG "\t MaxZones %d\n",psm->MaxZones ); + /* unsigned char */ printk(KERN_DEBUG "\t MaxSectors %d\n",psm->MaxSectors ); + /* unsigned int */ printk(KERN_DEBUG "\t MaxBlocks %d\n",psm->MaxBlocks ); + /* unsigned int */ printk(KERN_DEBUG "\t MaxLogBlocks %d\n",psm->MaxLogBlocks ); + /* unsigned char */ printk(KERN_DEBUG "\t Zone %d\n",psm->Zone ); + /* unsigned char */ printk(KERN_DEBUG "\t Sector %d\n",psm->Sector ); + /* unsigned int */ printk(KERN_DEBUG "\t PhyBlock %d\n",psm->PhyBlock ); + /* unsigned int */ printk(KERN_DEBUG "\t LogBlock %d\n",psm->LogBlock ); +#endif +} +#endif + +typedef struct { + int sm_error; + int lnx_error; + char *smerrstr; +} errmap; + +static errmap error_map_table [] = { + { NO_ERROR, 0x0000, ""}, + { ERR_WriteFault, EIO, "Peripheral Device Write Fault "}, + { ERR_HwError, EIO, "Hardware Error"}, + { ERR_DataStatus, EIO, "DataStatus Error"}, + { ERR_EccReadErr, EIO, "Unrecovered Read Error" }, + { ERR_CorReadErr, EIO, "Recovered Read Data with ECC" }, + { ERR_OutOfLBA, EIO, "Illegal Logical Block Address" }, + { ERR_WrtProtect, EROFS, "Write Protected" }, + { ERR_ChangedMedia, EIO, "Medium Changed" }, + { ERR_UnknownMedia, EIO, "Incompatible Medium Installed" }, + { ERR_IllegalFmt, EIO, "Medium Format Corrupted" }, + { ERR_NoSmartMedia, EIO, "Medium Not Present" } +}; + +static int ssfdc_maperror(int ssfdc_error) { + int loopus=0; + + if (!ssfdc_error) return 0; + + do { + if (error_map_table[loopus].sm_error == ssfdc_error) { + printk("%s\n", error_map_table[loopus].smerrstr); + return -error_map_table[loopus].lnx_error; + } + } while (++loopus < (sizeof(error_map_table) / (sizeof(errmap)))); + + printk(KERN_ERR "%s: error code %d is not mapped, EIO\n", MAJOR_NAME, ssfdc_error); + return -EIO; +} + +static int ssfdc_thread(void * arg) +{ + ssfdc_dev *psm = arg; + unsigned long flags; + + daemonize("sm%dd",psm->sm_minor); + + spin_lock_irqsave(¤t->sighand->siglock,flags); // _irq + sigfillset(¤t->blocked); + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock,flags); // _irq + + while (!psm->exiting) { + if ( ssfdc_get_request(psm) ) + initxfer(psm->sm_minor); + + spin_lock_irqsave(&psm->req_queue_lock,flags); // _irq + // bjm spin_lock(&psm->req_queue_lock); // _irq + psm->waiting = 0; + spin_unlock_irqrestore(&psm->req_queue_lock,flags); // _irq + // bjm spin_unlock(&psm->req_queue_lock); // _irq + if (wait_event_interruptible(psm->thread_wq,ssfdc_get_request(psm))) + printk("ssfdc_thread() interrupted\n"); + // wait_event(psm->thread_wq,ssfdc_get_request(psm)); + spin_lock_irqsave(&psm->req_queue_lock,flags); // _irq + // bjm spin_lock(&psm->req_queue_lock); // _irq + psm->waiting = 1; + spin_unlock_irqrestore(&psm->req_queue_lock,flags); // _irq + // bjm spin_unlock(&psm->req_queue_lock); // _irq + } + + printk("ssfdcd Exiting!\n"); + + complete_and_exit(&psm->thread_dead, 0); + +} + +/* +ssfdc_init_device(ssfdc_dev *, int minor, unsigned long baseaddr, int removable) + reset and initialize the ssfdc_dev structure +*/ +static int ssfdc_init_device(ssfdc_dev *psm, int minor, unsigned long baseaddr) +{ + int pid; + + // Establish ssfdc state + psm->XferState = xfer_idle; + psm->ErrCode = NO_ERROR; + psm->MediaChange = SUCCESS; + psm->SectCopyMode = COMPLETED; + psm->UseCount = 0; + psm->DataBuf_Valid = 0; + + // set minor number + psm->sm_minor = minor; + // io address + psm->address = baseaddr; + if (!request_region(psm->address, 3, "sm")) { + printk(KERN_ERR "sm: memory already in use!\n"); + return ERROR; + } + spin_lock_init(&psm->req_queue_lock); + + // thread related inititializations... + init_completion(&psm->thread_dead); + init_waitqueue_head(&psm->thread_wq); + + pid = kernel_thread(ssfdc_thread, psm, CLONE_KERNEL); + if (pid < 0) + printk("ssfdc: ERROR starting thread!\n"); + else + printk("ssfdc: started kernel thread sm%dd pid %d\n", psm->sm_minor, pid); + + // switch on power to device, and set basic attributes of card (no logical to phys mapping) + if ( ! CntPowerOn(psm) && ! CheckCardExist(psm) ) { + SetPhyFmtValue(psm); + } + else { + printk(KERN_ERR "ssfdc_init_device() unable to SetPhyFmtValue()\n"); + } + +#if DEBUG_SSFDC + dump_ssfdc_state(psm); +#endif + + return SUCCESS; +} + +static int ssfdc_dev_blk_size(ssfdc_dev *psm) +{ + if (!psm) + return 0; + + // because of the physical to logical block mapping, not as many blocks + // as expected... + switch(psm->Model) { + case SSFDC1MB: + return (250 * 8 * 512) / SSFDC_BLKSIZE; + case SSFDC2MB: + return (500 * 8 * 512) / SSFDC_BLKSIZE; + case SSFDC4MB: + return (500 * 16 * 512) / SSFDC_BLKSIZE; + case SSFDC8MB: + return (1000 * 16 * 512) / SSFDC_BLKSIZE; + case SSFDC16MB: + return (1000 * 32 * 512) / SSFDC_BLKSIZE; + case SSFDC32MB: + return (2000 * 32 * 512) / SSFDC_BLKSIZE; + case SSFDC64MB: + return (4000 * 32 * 512) / SSFDC_BLKSIZE; + case SSFDC128MB: + return (8000 * 32 * 512) / SSFDC_BLKSIZE; + default: + return 0; + } +} + +inline int ssfdc_dev_sectors(ssfdc_dev *psm) { + return ssfdc_dev_blk_size(psm) * (SSFDC_BLKSIZE/SSFDC_SECTSIZE); +} + +static int ssfdc_open(struct inode *in, struct file *fptr) +{ + int error_code=NO_ERROR; + ssfdc_dev *psm = in->i_bdev->bd_disk->private_data; + unsigned long flags; + +#if DEBUG_SSFDC + printk(KERN_DEBUG "+ssfdc_open()\n"); +#endif + + if (!fptr) { + return -EIO; + } + + spin_lock_irqsave(&psm->req_queue_lock,flags); + // bjm spin_lock(&psm->req_queue_lock); + // Power up smartmedia device, check for card, check media + if ((error_code=CntPowerOn(psm))) { + printk(KERN_ERR "%s PowerUP error\n", MAJOR_NAME); + } + else if ((error_code=CheckCardExist(psm))) {// Check the existence of a card + printk(KERN_ERR "%s No Card!\n", MAJOR_NAME); + } + else if ( ! psm->UseCount++ ) { + spin_unlock_irqrestore(&psm->req_queue_lock,flags); + // bjm spin_unlock(&psm->req_queue_lock); + check_disk_change(in->i_bdev); + spin_lock_irqsave(&psm->req_queue_lock,flags); + // bjm spin_lock(&psm->req_queue_lock); + } + + if ( ! psm->ErrCode ) { + // check our open mode against that of removable media's + if (WRITE_PROTECTED(psm)) { + printk(KERN_ERR "mount read only detected.\n"); + } + } + +#if DEBUG_SSFDC + dump_ssfdc_state(psm); + printk(KERN_DEBUG "-ssfdc_open() error_code %d\n", error_code); +#endif + + spin_unlock_irqrestore(&psm->req_queue_lock,flags); + +#if DEBUG_SSFDC + printk("-ssfdc_open()\n"); +#endif + + return ssfdc_maperror(error_code); +} + +static int ssfdc_release(struct inode *i_node, struct file *fptr) +{ + int drive; + ssfdc_dev *psm=NULL; + unsigned long flags; + +#if DEBUG_SSFDC + printk(KERN_DEBUG "+ssfdc_release("); +#endif + + psm = (ssfdc_dev *) i_node->i_bdev->bd_disk->private_data; + drive = psm->sm_minor; + +#if DEBUG_SSFDC + printk(KERN_DEBUG "%d)\n", drive); +#endif + if (drive < 0 || drive >= MAX_SSFDC) + return -ENODEV; + + spin_lock_irqsave(&psm->req_queue_lock,flags); + // bjm spin_lock(&psm->req_queue_lock); + + if (!psm->UseCount) + printk(KERN_ERR "sm: Zero use count!\n"); + else { + --psm->UseCount; + } + +#if DEBUG_SSFDC + dump_ssfdc_state(psm); +#endif + +#if DEBUG_SSFDC + printk(KERN_DEBUG "-ssfdc_release()\n"); +#endif + + spin_unlock_irqrestore(&psm->req_queue_lock,flags); + // bjm spin_unlock(&psm->req_queue_lock); + + return 0; +} + +static int ssfdc_ioctl(struct inode *i_node, struct file *fptr, unsigned cmd, unsigned long arg) +{ + int err, drive, int_val; + unsigned char heads, sectors; + unsigned int cylinders; + struct hd_geometry geo; + ssfdc_dev *psm; + +#if DEBUG_SSFDC + printk(KERN_DEBUG "ssfdc_ioctl(%d)", cmd); +#endif + + if (i_node == NULL) + return -EINVAL; + + psm = (ssfdc_dev *) i_node->i_bdev->bd_disk->private_data; + drive = psm->sm_minor; + + if (drive < 0 || drive >= MAX_SSFDC) + return -ENODEV; + switch(cmd) { + case BLKROSET: /* set device read-only (0 = read-write) */ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (copy_from_user((void *) &int_val, (int *)arg, sizeof(int_val))) + return -EFAULT; + if (int_val) + psm->Attribute |= MWP; + else + psm->Attribute &= ~MWP; + return 0; + + case BLKROGET:/* get read-only status (0 = read_write) */ + int_val = psm->Attribute & MWP; + copy_to_user(arg, (void *) &int_val, sizeof(int_val)); + return 0; + + /* case BLKRRPART: */ /* re-read partition table */ + + case BLKGETSIZE: +#if DEBUG_SSFDC + printk(KERN_DEBUG "BLKGETSIZE"); +#else + printk(KERN_DEBUG "ssfdc_ioctl(BLKGETSIZE) not handled.\n"); +#endif + break; + + case HDIO_GETGEO: + if ((void *) arg == NULL) return -EINVAL; + if (!access_ok(VERIFY_WRITE, (void __user *)arg, sizeof(geo))) + return -EFAULT; + if ((err=CheckLogCHS( psm, &cylinders, &heads, §ors))) + return ssfdc_maperror(err); + memset(&geo, 0, sizeof(geo)); + geo.cylinders = cylinders; + geo.heads = heads; + geo.sectors = sectors; + geo.start = get_start_sect(i_node->i_bdev); + if (copy_to_user((void *) arg, &geo, sizeof(geo))) + return -EFAULT; + if (copy_to_user((void *) arg, &geo, sizeof(geo))) + return -EFAULT; + return 0; + } + return -EINVAL; +} + + + +static int ssfdc_revalidate(struct gendisk *disk) +{ + unsigned int cis; + ssfdc_dev *psm=NULL; + int error_code=NO_ERROR; + unsigned long flags; + +#if DEBUG_SSFDC + printk(KERN_DEBUG "ssfdc_revalidate()\n"); +#endif + + psm = disk->private_data; + + spin_lock_irqsave(&psm->req_queue_lock,flags); + // bjm spin_lock(&psm->req_queue_lock); + + if ( ! psm->DataBuf_Valid ) { + if ((error_code=SetPhyFmtValue(psm))) + printk(KERN_ERR "ssfdc_revalidate() SetPhyFmtValue error\n"); + else if ((error_code=SearchCIS(psm,&cis))) + printk(KERN_ERR "ssfdc_revalidate() SearchCIS error\n"); + else if ((error_code=MakeLogTable(psm,cis+1))) + printk(KERN_ERR "ssfdc_revalidate() MakeLogTable error\n"); + } + spin_unlock_irqrestore(&psm->req_queue_lock,flags); + // bjm spin_unlock(&psm->req_queue_lock); + + return ssfdc_maperror(error_code); +} + +int __init ssfdc_init(void) +{ + int i; + int err = 0; + +#if DEBUG_SSFDC + printk(KERN_DEBUG "+ssfdc_init()\n"); +#endif + + memset(disks, 0, sizeof(struct gendisk *) * MAX_SSFDC); + memset(ssfdc, 0, sizeof(struct ssfdc_dev *) * MAX_SSFDC); + for (i=0; imajor = SSFDC_MAJOR; + disks[i]->first_minor = i << SSFDC_PARTN_BITS; + disks[i]->fops = &ssfdc_fops; + sprintf(disks[i]->disk_name, "sm%d", i); + disks[i]->private_data = ssfdc[i]; + ssfdc_init_device(ssfdc[i], i << SSFDC_PARTN_BITS, + CPLD_BASE_ADDRESS + SMART_MEDIA_ONE_OFFSET); + + disks[i]->queue = ssfdc[i]->req_queue = + blk_init_queue(do_ssfdc_request, &ssfdc[i]->req_queue_lock); + ssfdc[i]->req_queue->queuedata = ssfdc[i]; + + set_capacity(disks[i], ssfdc_dev_sectors(ssfdc[i])); + // bjm blk_queue_max_sectors(disks[i]->queue, 32); + // bjm blk_queue_max_phys_segments(disks[i]->queue, 4); + blk_queue_max_segment_size(disks[i]->queue, (ssfdc[i]->MaxSectors / 2) * K_BYTE); + add_disk(disks[i]); + } + +#if 0 // bjm debug +#ifndef CONFIG_SH_NIMBLE_MINI + mediachangetest(0L); +#else + mediachangetest(1L); +#endif +#endif // bjm debug + +#if DEBUG_SSFDC + printk(KERN_DEBUG "-ssfdc_init(0)\n"); +#endif + return 0; + +busy_error: +no_memory_error: + for (i=0; i < MAX_SSFDC; ++i) { + if (disks[i] && disks[i]->queue) + kfree(disks[i]->queue); + put_disk(disks[i]); + if (ssfdc[i]) + kfree(ssfdc[i]); + } +#if DEBUG_SSFDC + printk(KERN_DEBUG "-ssfdc_init(%d)\n", -ENOMEM); +#endif + return -ENOMEM; +} + +void __init ssfdc_clean(void) +{ + int i; + + printk(KERN_DEBUG "SSFDC exit code\n"); + + for (i=0; i < MAX_SSFDC; ++i) { + if (disks[i] != NULL) { + blk_cleanup_queue(disks[i]->queue); + del_gendisk(disks[i]); + put_disk(disks[i]); + } + + if (ssfdc[i]) { + // signal thread to exit... + ssfdc[i]->exiting = 1; + wake_up(&ssfdc[i]->thread_wq); + wait_for_completion(&ssfdc[i]->thread_dead); + + if (ssfdc[i]->address) + release_region(ssfdc[i]->address, 3); + kfree(ssfdc[i]); + } + } + + if (unregister_blkdev(SSFDC_MAJOR, "smartmedia")) + printk(KERN_WARNING "smartmedia: cannot unregister blkdev\n"); +} + +#if DEBUG_SSFDC +void dump_request(struct request *req) +{ +#if DEBUG_SSFDC_REQUEST && DEBUG_SSFDC_REQUEST + printk(KERN_DEBUG "req->\n"); + /* int */ printk(KERN_DEBUG "\t req->cmd %x\n", req->cmd); /* READ or WRITE */ + /* int errors */ printk(KERN_DEBUG "\t req->errors %d\n", req->errors); + /* unsigned long */ printk(KERN_DEBUG "\t req->sector %d\n", req->sector); + /* unsigned long */ printk(KERN_DEBUG "\t req->nr_sectors %d\n",req->nr_sectors); + /* unsigned long */ printk(KERN_DEBUG "\t req->hard_sector %d\n", req->hard_sector); + /* unsigned int */ printk(KERN_DEBUG "\t req->nr_hw_segments %d\n",req->nr_hw_segments); + /* unsigned long */ printk(KERN_DEBUG "\t req->current_nr_sectors %d\n",req->current_nr_sectors); + +#endif +} +#endif + +void do_ssfdc_request(request_queue_t * rq) +{ + ssfdc_dev *psm = rq->queuedata; + + if ( ! psm->waiting ) + wake_up(&psm->thread_wq); +} + +static struct request * ssfdc_get_request(ssfdc_dev *psm) +{ + struct request *treq; + unsigned long flags; + + spin_lock_irqsave(&psm->req_queue_lock, flags); + // bjm spin_lock(&psm->req_queue_lock); + treq = elv_next_request(psm->req_queue); + spin_unlock_irqrestore(&psm->req_queue_lock, flags); + // bjm spin_unlock(&psm->req_queue_lock); + + return treq; +} + +static void ssfdc_terminate_request(ssfdc_dev *psm, struct request *req) +{ + unsigned long flags; + + if (psm && req) { + spin_lock_irqsave(&psm->req_queue_lock, flags); + // bjm spin_lock(&psm->req_queue_lock); + end_request(req,0); + spin_unlock_irqrestore(&psm->req_queue_lock, flags); + // bjm spin_unlock(&psm->req_queue_lock); + } +} + + +static int ssfdc_end_request(ssfdc_dev *psm, struct request *req, int status) +{ + unsigned long flags; + + if (!psm || !req) { + printk(KERN_DEBUG "ssfdc_end_request() NULL psm or req!\n"); + return 0; + } + +#if DEBUG_SSFDC_REQUEST + printk("ssfdc_end_request(%p)\n", req); +#endif + + spin_lock_irqsave(&psm->req_queue_lock,flags); + // bjm spin_lock(&psm->req_queue_lock); + end_request(req,status); + spin_unlock_irqrestore(&psm->req_queue_lock,flags); + // bjm spin_unlock(&psm->req_queue_lock); + return 0; +} + +void initxfer(unsigned long dev_idx) +{ + ssfdc_dev *psm = ssfdc[dev_idx]; + struct request *req; + int error_code; + unsigned int cis; + +#if DEBUG_SSFDC + // printk(KERN_DEBUG "+initxfer(%d)", dev_idx); +#endif + // get device lock, and check idle flag, setting if not busy + + req = ssfdc_get_request(psm); + + // if the device is idle, setup a read or write operation + if (psm->XferState == xfer_idle) { + // get the current request from our device's request list. + if (!req) { +#if DEBUG_SSFDC +// printk(KERN_DEBUG "initxfer() terminate, no schedule.\n"); +#endif + } + // Absence of power indicates absence of card. + // terminate request and exit... + if ( ! _HwChkPower(psm) ) { + printk(KERN_DEBUG "initxfer() - Media power NOT!\n"); + ssfdc_terminate_request(psm,req); + return; + } + + // We have a request and we have a card. Is the Log2Phys mapping still valid? + if ( ! psm->DataBuf_Valid ) { + if ((error_code = SetPhyFmtValue(psm))) + printk(KERN_DEBUG "%s SetPhyFmtValue error\n", MAJOR_NAME); + else if ((error_code = SearchCIS(psm,&cis))) + printk(KERN_DEBUG "%s SearchCIS error\n", MAJOR_NAME); + else if ((error_code = MakeLogTable(psm,cis+1))) + printk(KERN_DEBUG "%s MakeLogTable error\n", MAJOR_NAME); + if (error_code) { + printk(KERN_DEBUG "%s error %d\n", MAJOR_NAME, error_code); + ssfdc_terminate_request(psm,req); + return; + } + } + + psm->XferState = xfer_busy; +#if DEBUG_SSFDC + printk(KERN_DEBUG " initxfer() - do the request %x\n", req); +#endif + ssfdc_rw_request(psm, req); + } +#if DEBUG_SSFDC + else { + printk(KERN_DEBUG "initxfer(%d) dev is busy, no reschedule.\n", dev_idx); + } +#endif + +} + + +void ssfdc_rw_request(ssfdc_dev *psm, struct request *req) +{ + int (*rwsector)(ssfdc_dev *, struct request *, char *, long, int); + unsigned sector, count; + int rw_return=1; + + if (rq_data_dir(req) == WRITE) + rwsector = MediaWriteSector; + else if (rq_data_dir(req) == READ) + rwsector = MediaReadSector; + else { + printk(KERN_DEBUG "%s: command %d not implemented!\n", MAJOR_NAME, (int) rq_data_dir(req)); + goto terminal_error; + } + + /* + */ + sector = req->sector; + count = req->nr_sectors; + + // check that the request does not extend past ssfdc's max size + if ( (sector + count) > ssfdc_dev_sectors(psm) ) { + printk(KERN_ERR "Attempt to read past end of device!"); + goto terminal_error; + } + else { + + // for each segment in each bio_vec R/W from/to device. + + count = req->current_nr_sectors; + rw_return = rwsector(psm, req, req->buffer, sector, count); + + if (rq_data_dir(req) == READ) + ssfdc_end_request(psm, req, rw_return); + } + + // things appear OK... + return; + +terminal_error: + ssfdc_terminate_request( psm, req); + psm->XferState = xfer_idle; +} + +module_init(ssfdc_init); +module_exit(ssfdc_clean); + +MODULE_LICENSE("GPL"); + +/* End of Linux Driver Modifications */ diff --git a/drivers/block/ssfdc.h b/drivers/block/ssfdc.h new file mode 100644 index 0000000..06f4a3c --- /dev/null +++ b/drivers/block/ssfdc.h @@ -0,0 +1,372 @@ +/* $id: $ */ +#ifndef _SSFDC_H +#define _SSFDC_H + +/* + Linux related defines +*/ + +#ifdef CONFIG_SH_NIMBLE_MINI +#define MAX_SSFDC 2 /* two drives */ +#else +#define MAX_SSFDC 1 /* only one drive */ +#endif + +#define SSFDC_MAJOR_NAME "sm" +#define MAJOR_NAME SSFDC_MAJOR_NAME +#define SSFDC_PARTN_BITS 4 /* number of minor dev bits for partitions */ +#define PARTN_MASK ((1<address+0x00) /* R/W Data Reg */ +#define STATUS(p) (p->address+0x02) /* R/- Status Reg */ +#define MODE(p) (p->address+0x02) /* -/W Mode Reg */ + +/* Controller Status Reg (Read Only) */ +#define STS_BUSY 0x80 +#define STS_VCC 0x10 +#define STS_SCHG 0x08 +#define STS_WP 0x01 +#define STS_CENB 0x04 + +#ifdef CONFIG_SH_TITAN +/* Controller Mode Reg (Write Only) */ +/* keep PCI clock running on bit 3 */ +/* CE# on bit 2, CLE on bit 1 and ALE on bit 0 */ +#define STANDBY (0x00 | 0x00 | 0x08) +#define WR_DATA (0x00 | 0x04 | 0x08) +#define WR_CMD (0x02 | 0x04 | 0x08) +#define WR_ADR (0x01 | 0x04 | 0x08) +#else +/* Controller Mode Reg (Write Only) */ +#define STANDBY 0x00 +#define WR_DATA 0x10 +#define WR_CMD 0x11 +#define WR_ADR 0x12 +#define PW_OFF 0x80 +#define PW_ON 0x88 +#endif + +/***************************************************************************/ +#define _HwSetData(p) ctrl_outb(WR_DATA,MODE(p)) +#define _HwSetCmd(p) ctrl_outb(WR_CMD,MODE(p)) +#define _HwSetAddr(p) ctrl_outb(WR_ADR,MODE(p)) +#define _HwSetStandby(p) ctrl_outb(STANDBY,MODE(p)) + +#define _HwInData(p) ctrl_inb(DATA(p)) +#define _HwOutData(p,a) ctrl_outb((a),DATA(p)) + +#ifdef CONFIG_SH_TITAN +#define _HwVccOn(p) +#define _HwVccOff(p) +#else +#define _HwVccOn(p) ctrl_outb(PW_ON,MODE(p)) +#define _HwVccOff(p) ctrl_outb(PW_OFF,MODE(p)) +#endif + +#ifdef CONFIG_SH_TITAN +#define _HwChkCardIn(p) (1) +#define _HwChkStatus(p) (0) +#define _HwChkWP(p) (0) +#define _HwChkPower(p) (1) +#define _HwChkBusy(p) (ctrl_inb(STATUS(p))&STS_BUSY) + +#else + +#define _HwChkCardIn(p) (ctrl_inb(STATUS(p))&STS_CENB) +#define _HwChkStatus(p) (ctrl_inb(STATUS(p))&(STS_SCHG)) +#define _HwChkWP(p) (ctrl_inb(STATUS(p))&(STS_WP)) +#define _HwChkPower(p) (ctrl_inb(STATUS(p))&(STS_VCC)) +#define _HwChkBusy(p) (ctrl_inb(STATUS(p))&STS_BUSY) + +#endif + +#define _HwRdStatus(p) (ctrl_inb(STATUS(p))) +/***************************************************************************/ +#ifdef CONFIG_SH_NIMBLE_MINI +#define CPLD_BASE_ADDRESS 0xB4030000L +#define SMART_MEDIA_ONE_OFFSET 0x08 // The "built-in" SmartMedia +#define SMART_MEDIA_TWO_OFFSET 0x00 // The "removable" SmartMedia +#elif CONFIG_SH_TITAN +#define CPLD_BASE_ADDRESS 0xA4000000L +#define SMART_MEDIA_ONE_OFFSET 0x00 +#else +#define CPLD_BASE_ADDRESS 0xB8030000L +#define SMART_MEDIA_ONE_OFFSET 0x00 // The "built-in" SmartMedia +#endif + +/*************************************************************************** + Program & Macro for SmartMedia Controller + Rev 0.30('98-06-30) ***** BETA RELEASE ***** + Copyright (c) 1997-98, Toshiba Corporation. All rights reserved. + ***************************************************************************/ +/*************************************************************************** + Define Definition + ***************************************************************************/ +#define K_BYTE 1024 /* Kilo Byte */ +#define SSFDC_SECTSIZE 512 /* Sector buffer size */ +#define SSFDC_BLKSIZE (K_BYTE * 4) +#define REDTSIZE 16 /* Redundant buffer size */ + +/***************************************************************************/ +#define DUMMY_DATA 0xFF /* No Assign Sector Read Data */ + +/*************************************************************************** + Max Zone/Block/Sectors Data Definition + ***************************************************************************/ +#define MAX_ZONENUM 0x08 /* Max Zone Numbers in a SmartMedia */ +#define MAX_BLOCKNUM 0x0400 /* Max Block Numbers in a Zone */ +#define MAX_SECTNUM 0x20 /* Max Sector Numbers in a Block */ +#define MAX_LOGBLOCK 1000 /* Max Logical Block Numbers in a Zone */ + +/*************************************************************************** + Logical to Physical Block Table Data Definition + ***************************************************************************/ +#define NO_ASSIGN 0xFFFF /* No Assign Logical Block Address */ + +/*************************************************************************** + 'SectCopyMode' Data + ***************************************************************************/ +#define COMPLETED 0 /* Sector Copy Completed */ +#define REQ_ERASE 1 /* Request Read Block Erase */ +#define REQ_FAIL 2 /* Request Read Block Failed */ + +/*************************************************************************** + Retry Counter Definition + ***************************************************************************/ +#define RDERR_REASSIGN 1 /* Reassign with Read Error */ +#define L2P_ERR_ERASE 1 /* BlockErase for Contradicted L2P Table */ + +/*************************************************************************** + SmartMedia Command & Status Definition + ***************************************************************************/ +/* SmartMedia Command */ +#define SSFDC_WRDATA 0x80 +#define SSFDC_READ 0x00 +#define SSFDC_READ_REDT 0x50 +#define SSFDC_READ1 0x00 +#define SSFDC_READ2 0x01 +#define SSFDC_READ3 0x50 +#define SSFDC_RST_CHIP 0xFF +#define SSFDC_WRITE 0x10 +#define SSFDC_DUMMY_WRITE 0x11 +#define SSFDC_MULTI_WRITE 0x15 +#define SSFDC_ERASE1 0x60 +#define SSFDC_ERASE2 0xD0 +#define SSFDC_RDSTATUS 0x70 +#define SSFDC_READ_ID 0x90 + +/* SmartMedia Status */ +#define WR_FAIL 0x01 /* 0:Pass, 1:Fail */ +#define SUSPENDED 0x20 /* 0:Not Suspended, 1:Suspended */ +#define READY 0x40 /* 0:Busy, 1:Ready */ +#define WR_PRTCT 0x80 /* 0:Protect, 1:Not Protect */ + +#define USEC 1 +#define MSEC 1000 * USEC +#define JIFFY_TICK_MS (MSEC / HZ) + +// #define BUSY_PROG 20 * MSEC /* 200-1000us ----- Program Time */ +#define BUSY_PROG 1000 * USEC /* 200-1000us ----- Program Time */ +#define BUSY_DUMMY_WRITE 10 * USEC /* 2-10us dummy write */ +#define BUSY_MULTI_WRITE 1000 * USEC /* 200 - 1000 usec */ +#define BUSY_ERASE 10 * MSEC /* 2-10ms ----- Block Erase Time */ +#define BUSY_READ 100 * USEC /* tR : 100us ----- Data transfer Time */ +#define BUSY_RESET 10 * USEC /* tRST : 10us ----- Device Resetting Time */ +#define BUSY_ADDR_SET 25 * USEC + +#define TIME_PON 30 /* 300ms ------ Power On Wait Time */ +#define TIME_CDCHK 2 /* 20ms ------ Card Check Interval Timer */ +#define TIME_WPCHK 1 /* 5ms ------ WP Check Interval Timer */ + +/* Power On Timeout */ +#define POWER_ON_TIMEOUT (HZ*2) + +/* Default retry limit for Read/Write */ +#define RD_RETRY_LIMIT 3 +#define WR_RETRY_LIMIT 4 +#define BLOCK_READ_RETRY_LIMIT 2 +#define BLOCK_WRITE_RETRY_LIMIT 3 +#define REASSIGN_RETRY_LIMIT 4 + +/*************************************************************************** + Redundant Data + ***************************************************************************/ +#define REDT_DATA 0x04 +#define REDT_BLOCK 0x05 + +#define REDT_ADDR1H 0x06 +#define REDT_ADDR1L 0x07 +#define REDT_ADDR2H 0x0B +#define REDT_ADDR2L 0x0C + +#define REDT_ECC10 0x0D +#define REDT_ECC11 0x0E +#define REDT_ECC12 0x0F + +#define REDT_ECC20 0x08 +#define REDT_ECC21 0x09 +#define REDT_ECC22 0x0A + +/*************************************************************************** + SmartMedia Model & Attribute + ***************************************************************************/ +/* SmartMedia Attribute */ +#define NOWP 0x00 /* 0... .... No Write Protect */ +#define WP 0x80 /* 1... .... Write Protected */ +#define MASK 0x00 /* .00. .... NAND MASK ROM Model */ +#define FLASH 0x20 /* .01. .... NAND Flash ROM Model */ +#define AD3CYC 0x00 /* ...0 .... Address 3-cycle */ +#define AD4CYC 0x10 /* ...1 .... Address 4-cycle */ +#define BS16 0x00 /* .... 00.. 16page/block */ +#define BS32 0x04 /* .... 01.. 32page/block */ +#define PS256 0x00 /* .... ..00 256byte/page */ +#define PS512 0x01 /* .... ..01 512byte/page */ + +#define MWP 0x80 /* WriteProtect mask */ +#define MFLASH 0x60 /* Flash Rom mask */ +#define MADC 0x10 /* Address Cycle */ +#define MBS 0x0C /* BlockSize mask */ +#define MPS 0x03 /* PageSize mask */ + +/* SmartMedia Model */ +#define NOSSFDC 0x00 /* NO SmartMedia */ +#define SSFDC1MB 0x01 /* 1MB SmartMedia */ +#define SSFDC2MB 0x02 /* 2MB SmartMedia */ +#define SSFDC4MB 0x03 /* 4MB SmartMedia */ +#define SSFDC8MB 0x04 /* 8MB SmartMedia */ +#define SSFDC16MB 0x05 /* 16MB SmartMedia */ +#define SSFDC32MB 0x06 /* 32MB SmartMedia */ +#define SSFDC64MB 0x07 /* 64MB SmartMedia */ +#define SSFDC128MB 0x08 /*128MB SmartMedia */ + +#define EVEN 0 /* Even Page for 256byte/page */ +#define ODD 1 /* Odd Page for 256byte/page */ + +/*************************************************************************** + Struct Definition + ***************************************************************************/ +/* Linux kernel additions */ + +/* device buffer xfer status */ +typedef enum { xfer_idle, xfer_busy} xfer_states; + +/* Smartmedia device structure */ +typedef struct { + unsigned long address; + int sm_minor; + int sm_flags; + int busy; + int waiting; + + /* queue of io requests for the device */ + spinlock_t req_queue_lock; + request_queue_t *req_queue; + + /* our thread related parameters */ + struct completion thread_dead; + int exiting; + wait_queue_head_t thread_wq; + + /* accounting variables for each buffer io operation + each request may have a chain of buffers, each of + which may require I/O of multiple sectors */ + unsigned int ReqSectorSize; + unsigned int BufIndex; + unsigned int SectorWriteIndex; + + /* CHS parameters */ + unsigned int HostCyl; + unsigned char HostHead; + unsigned char HostSect; + + /* State Information */ + xfer_states XferState; + unsigned int UseCount; + unsigned int RetryCount; + unsigned int ErrCode; + unsigned int MediaChange; + unsigned int CardPresent; + unsigned int SectCopyMode; + + /* Working Databuf Area */ + unsigned char SectBuf[SSFDC_SECTSIZE]; + unsigned char WorkBuf[SSFDC_SECTSIZE]; + unsigned char Redundant[REDTSIZE]; + unsigned char WorkRedund[REDTSIZE]; + unsigned int DataBuf_Valid; + unsigned int Log2Phy[MAX_ZONENUM][MAX_LOGBLOCK]; + unsigned char Assign[MAX_ZONENUM][MAX_BLOCKNUM/8]; + unsigned int AssignStart[MAX_ZONENUM]; + unsigned int ReadBlock; + unsigned int WriteBlock; + + /* Card attributes */ + unsigned char Model; + unsigned char Attribute; + unsigned char MaxZones; + unsigned char MaxSectors; + unsigned int MaxBlocks; + unsigned int MaxLogBlocks; + + /* Address of current access (Media) */ + unsigned char Zone; /* Zone Number */ + unsigned char Sector; /* Sector(512byte) Number on Block */ + unsigned int PhyBlock; /* Physical Block Number on Zone */ + unsigned int LogBlock; /* Logical Block Number of Zone */ + + /* device statistics */ + unsigned int Sector_reads; + unsigned int Sector_writes; + unsigned int Sect_rd_errs_ttl; + unsigned int Sect_wr_errs_ttl; + unsigned int Bad_blks_rd; + unsigned int Bad_blks_wr; + unsigned int Bad_blks_erase; +} ssfdc_dev; + + +/****************************************************************************/ +/* Handy defines */ +/****************************************************************************/ +#define WRITE_PROTECTED(p) (p->Attribute & WP) + +/* End of Linux kernel additions */ +#endif /* #ifndef _SSFDC_H */