Add the driver for onboard flash. The quality of this driver means that it has not been included in the upstream CVS. This implements the block device translation layer to match what the onboard firmware implements. diff -duNr linux-2.6.16-orig/drivers/block/Kconfig linux-2.6.16/drivers/block/Kconfig --- linux-2.6.16-orig/drivers/block/Kconfig 2006-06-29 16:12:57.000000000 +1000 +++ linux-2.6.16/drivers/block/Kconfig 2006-06-29 16:13:27.000000000 +1000 @@ -190,6 +190,13 @@ 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 -duNr linux-2.6.16-orig/drivers/block/Makefile linux-2.6.16/drivers/block/Makefile --- linux-2.6.16-orig/drivers/block/Makefile 2006-06-29 16:12:57.000000000 +1000 +++ linux-2.6.16/drivers/block/Makefile 2006-06-29 16:13:27.000000000 +1000 @@ -21,6 +21,7 @@ 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 -duNr linux-2.6.16-orig/drivers/block/ssfdc.c linux-2.6.16/drivers/block/ssfdc.c --- linux-2.6.16-orig/drivers/block/ssfdc.c 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.6.16/drivers/block/ssfdc.c 2006-06-29 16:13:50.000000000 +1000 @@ -0,0 +1,2742 @@ +/* $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 <linux/module.h> + +#include <asm/delay.h> +#include <linux/sched.h> +#include <linux/interrupt.h> + +#include <linux/fs.h> +#include <linux/hdreg.h> +#include <linux/file.h> +#include <linux/stat.h> +#include <linux/time.h> +#include <linux/errno.h> +#include <linux/major.h> +#include <linux/init.h> +#include <linux/devfs_fs_kernel.h> +#include <asm/uaccess.h> +#include <asm/hardirq.h> +#include <linux/bio.h> +#include <linux/blkdev.h> +#include <linux/slab.h> +#include <linux/highmem.h> + +#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_getgeo(struct block_device *bdev, struct hd_geometry *geo); +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, + .getgeo = ssfdc_getgeo, + .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<<bit); /* Put corrected data */ + return(1); + } + i=0; /* Clear count */ + d&=0x00ffffffL; /* Masking */ + while(d) { /* If d=0 finish counting */ + if (d&BIT0) ++i; /* Count number of 1 bit */ + d>>=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; i<count; i++) + *stringA++ = *stringB++; +} + +int StringCmp(char *stringA, char *stringB, int count) +{ + int i; + for (i=0;i<count;i++) + if (*stringA++ != *stringB++) return(ERROR); + return(SUCCESS); +} +/***************************************************************************/ +int CheckDataBlank(unsigned char *redundant) +{ + char i; + for(i=0; i<REDTSIZE; i++) + if(*redundant++!=0xFF) return(ERROR); + return(SUCCESS); +} + +int CheckFailBlock(unsigned char *redundant) +{ + redundant+=REDT_BLOCK; + if(*redundant==0xFF) return(SUCCESS); + if(! *redundant) return(ERROR); + if(BitCount(*redundant)<7) return(ERROR); + return(SUCCESS); +} + +int CheckCisBlock(unsigned char *redundant) +{ + if(! (*(redundant+REDT_ADDR1H)|*(redundant+REDT_ADDR1L))) + return(SUCCESS); + if(! (*(redundant+REDT_ADDR2H)|*(redundant+REDT_ADDR2L))) + return(SUCCESS); + return(ERROR); +} + +int CheckDataStatus(unsigned char *redundant) +{ + redundant+=REDT_DATA; + if(*redundant==0xFF) return(SUCCESS); + if(! *redundant) return(ERROR); + if(BitCount(*redundant)<5) return(ERROR); + return(SUCCESS); +} + +int LoadLogBlockAddr(ssfdc_dev *psm) +{ + unsigned int addr1,addr2; + addr1=*(psm->Redundant+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; i<REDTSIZE; i++) *(redundant+i)=0xFF; +} + +/***************************************************************************/ +void SetLogBlockAddr(ssfdc_dev *psm, unsigned char *redundant) +{ + unsigned int addr; + *(redundant+REDT_BLOCK)=0xFF; + *(redundant+REDT_DATA) =0xFF; + addr=psm->LogBlock*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; i<REDTSIZE; i++) + *redundant++=((i==REDT_BLOCK)?0xF0:0xFF); +} + +void SetDataStatus(unsigned char *redundant) +{ + redundant+=REDT_DATA; + *redundant=0x00; +} + + +/*************************************************************************** + NAND Memory (SmartMedia) Control Subroutine + ***************************************************************************/ +static void _SetSsfdcCmd(ssfdc_dev *psm, unsigned char cmd) +{ + _HwSetCmd(psm); + _HwOutData(psm,cmd); + _HwSetData(psm); +} + +static void _SetSsfdcAddr(ssfdc_dev *psm, unsigned char add) +{ + unsigned int addr; + +#if DEBUG_SSFDC_ADDR + printk(KERN_DEBUG "_SetSsfdcAddr() Zone %d LogBlock %d PhyBlock %d Sector %d\n", + psm->Zone,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; i<SSFDC_SECTSIZE; i++) + *buf++=DUMMY_DATA; + } + else { + // send our command + if (PrevBlock != psm->PhyBlock) { +#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->Zone<psm->MaxZones) { + 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->Sector<psm->MaxSectors) + return(SUCCESS); + psm->Sector=0; + if(++psm->LogBlock<psm->MaxLogBlocks) { + 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->Zone<psm->MaxZones) { + 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->WriteBlock<psm->MaxBlocks; 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->WriteBlock<psm->AssignStart[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->Sector<psm->MaxSectors) { + 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->Zone<psm->MaxZones; psm->Zone++) { + /* set all LogBlocks to NO_ASSIGN */ + for(psm->LogBlock=0; psm->LogBlock<psm->MaxLogBlocks; 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->PhyBlock<psm->MaxBlocks; psm->PhyBlock++) { + if((! psm->Zone) && (psm->PhyBlock<start)) { + SetBit(psm->Assign[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->Sector<psm->MaxSectors; 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 drive, int_val; + 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; + } + return -EINVAL; +} + + +static int ssfdc_getgeo(struct block_device *bdev, struct hd_geometry *geo) +{ + unsigned char heads, sectors; + unsigned int cylinders; + struct gendisk *disk = bdev->bd_disk; + ssfdc_dev *psm = disk->private_data; + int drive = psm->sm_minor; + int err; + + if (drive < 0 || drive >= MAX_SSFDC) + return -ENODEV; + + err = CheckLogCHS(psm, &cylinders, &heads, §ors); + if (err) + return ssfdc_maperror(err); + + geo->heads = heads; + geo->sectors = sectors; + geo->cylinders = cylinders; + return 0; +} + + +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; i<MAX_SSFDC; ++i) { + disks[i] = alloc_disk(1 << SSFDC_PARTN_BITS); + ssfdc[i] = kmalloc(sizeof(ssfdc_dev), GFP_KERNEL); + if (!disks[i] || !ssfdc[i]) { + err = -ENOMEM; + goto no_memory_error; + } + memset( ssfdc[i], 0, sizeof(ssfdc_dev)); + } + + if (register_blkdev(SSFDC_MAJOR, "smartmedia")) { + printk(KERN_ERR "Unable to get major number %d for ssfdc device\n", + SSFDC_MAJOR); + err = -EBUSY; + goto busy_error; + } + + devfs_mk_dir("sm"); + + for ( i=0; i < MAX_SSFDC; ++i) { + disks[i]->major = SSFDC_MAJOR; + disks[i]->first_minor = i << SSFDC_PARTN_BITS; + disks[i]->fops = &ssfdc_fops; + sprintf(disks[i]->disk_name, "sm%d", i); + sprintf(disks[i]->devfs_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"); + devfs_remove("sm"); +} + +#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 -duNr linux-2.6.16-orig/drivers/block/ssfdc.h linux-2.6.16/drivers/block/ssfdc.h --- linux-2.6.16-orig/drivers/block/ssfdc.h 1970-01-01 10:00:00.000000000 +1000 +++ linux-2.6.16/drivers/block/ssfdc.h 2006-06-29 16:13:27.000000000 +1000 @@ -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<<SSFDC_PARTN_BITS)-1) /* a useful bit mask */ +#define MAX_DRIVES MAX_SSFDC + +/*************************************************************************** + SmartMedia Controll Header + Rev 0.30('98-06-30) ***** BETA RELEASE ***** + Copyright (c) 1997-98, Toshiba Corporation. All rights reserved. + ***************************************************************************/ + +/*************************************************************************** + Define Difinetion + ***************************************************************************/ +#define SUCCESS 0 /* SUCCESS */ +#define ERROR -1 /* ERROR */ +#define CORRECT 1 /* CORRECTABLE */ + +/***************************************************************************/ +#define NO_ERROR 0x0000 /* NO ERROR */ +#define ERR_WriteFault 0x0003 /* Peripheral Device Write Fault */ +#define ERR_HwError 0x0004 /* Hardware Error */ +#define ERR_DataStatus 0x0010 /* DataStatus Error */ +#define ERR_EccReadErr 0x0011 /* Unrecovered Read Error */ +#define ERR_CorReadErr 0x0018 /* Recovered Read Data with ECC */ +#define ERR_OutOfLBA 0x0021 /* Illegal Logical Block Address */ +#define ERR_WrtProtect 0x0027 /* Write Protected */ +#define ERR_ChangedMedia 0x0028 /* Medium Changed */ +#define ERR_UnknownMedia 0x0030 /* Incompatible Medium Installed */ +#define ERR_IllegalFmt 0x0031 /* Medium Format Corrupted */ +#define ERR_NoSmartMedia 0x003A /* Medium Not Present */ + +/*************************************************************************** + Common Controll Header + Rev 1.30('98-06-30) ***** BETA RELEASE ***** + Copyright (c) 1997-98, Toshiba Corporation. All rights reserved. + ***************************************************************************/ + +/*************************************************************************** + SmartMedia Controller Definition + ***************************************************************************/ +/* I/O Mode Address */ +#define DATA(p) (p->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 */