summaryrefslogtreecommitdiff
path: root/packages/linux/linux-titan-sh4/titan-flash.patch
diff options
context:
space:
mode:
authorJamie Lenehan <lenehan@twibble.org>2006-07-06 14:41:19 +0000
committerOpenEmbedded Project <openembedded-devel@lists.openembedded.org>2006-07-06 14:41:19 +0000
commit4777a70d6665e319ae3723b5e75f67ed0f10997d (patch)
treed3166fa743d0506deb0825d1027c22c1e48f897e /packages/linux/linux-titan-sh4/titan-flash.patch
parent9bfbf27d78e0edd5f639c2eaee98f5e506ce7cc6 (diff)
linux/linux-titan-sh4: Upgrade the titan kernel from 2.6.16 to 2.6.17 now
that the sh tree has been upgraded to 2.6.17.
Diffstat (limited to 'packages/linux/linux-titan-sh4/titan-flash.patch')
-rw-r--r--packages/linux/linux-titan-sh4/titan-flash.patch3157
1 files changed, 3157 insertions, 0 deletions
diff --git a/packages/linux/linux-titan-sh4/titan-flash.patch b/packages/linux/linux-titan-sh4/titan-flash.patch
new file mode 100644
index 0000000000..fc633f3e05
--- /dev/null
+++ b/packages/linux/linux-titan-sh4/titan-flash.patch
@@ -0,0 +1,3157 @@
+
+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,&sector)
++ && WriteReqInCurrBlk(psm,curr_sector, &sector))
++ {
++#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, &sector))
++ {
++ // 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(&current->sighand->siglock,flags); // _irq
++ sigfillset(&current->blocked);
++ recalc_sigpending();
++ spin_unlock_irqrestore(&current->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, &sectors);
++ 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 */