| /* | 
 |  * (C) Copyright 2001 | 
 |  * Denis Peter, MPL AG, d.peter@mpl.ch. | 
 |  * | 
 |  * See file CREDITS for list of people who contributed to this | 
 |  * project. | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU General Public License as | 
 |  * published by the Free Software Foundation; either version 2 of | 
 |  * the License, or (at your option) any later version. | 
 |  * | 
 |  * This program is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |  * GNU General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU General Public License | 
 |  * along with this program; if not, write to the Free Software | 
 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 
 |  * MA 02111-1307 USA | 
 |  * | 
 |  */ | 
 | /* | 
 |  * Floppy Disk support | 
 |  */ | 
 |  | 
 | #include <common.h> | 
 | #include <config.h> | 
 | #include <command.h> | 
 | #include <image.h> | 
 |  | 
 |  | 
 | #undef	FDC_DEBUG | 
 |  | 
 | #ifdef	FDC_DEBUG | 
 | #define	PRINTF(fmt,args...)	printf (fmt ,##args) | 
 | #else | 
 | #define PRINTF(fmt,args...) | 
 | #endif | 
 |  | 
 | #ifndef	TRUE | 
 | #define TRUE            1 | 
 | #endif | 
 | #ifndef FALSE | 
 | #define FALSE           0 | 
 | #endif | 
 |  | 
 | /*#if defined(CONFIG_CMD_DATE) */ | 
 | /*#include <rtc.h> */ | 
 | /*#endif */ | 
 |  | 
 | typedef struct { | 
 | 	int		flags;		/* connected drives ect */ | 
 | 	unsigned long	blnr;		/* Logical block nr */ | 
 | 	uchar		drive;		/* drive no */ | 
 | 	uchar		cmdlen;		/* cmd length */ | 
 | 	uchar		cmd[16];	/* cmd desc */ | 
 | 	uchar		dma;		/* if > 0 dma enabled */ | 
 | 	uchar		result[11];	/* status information */ | 
 | 	uchar		resultlen;	/* lenght of result */ | 
 | } FDC_COMMAND_STRUCT; | 
 |  | 
 | /* flags: only the lower 8bit used: | 
 |  * bit 0 if set drive 0 is present | 
 |  * bit 1 if set drive 1 is present | 
 |  * bit 2 if set drive 2 is present | 
 |  * bit 3 if set drive 3 is present | 
 |  * bit 4 if set disk in drive 0 is inserted | 
 |  * bit 5 if set disk in drive 1 is inserted | 
 |  * bit 6 if set disk in drive 2 is inserted | 
 |  * bit 7 if set disk in drive 4 is inserted | 
 |  */ | 
 |  | 
 | /* cmd indexes */ | 
 | #define COMMAND			0 | 
 | #define DRIVE			1 | 
 | #define CONFIG0			1 | 
 | #define SPEC_HUTSRT		1 | 
 | #define TRACK			2 | 
 | #define CONFIG1			2 | 
 | #define SPEC_HLT		2 | 
 | #define HEAD			3 | 
 | #define CONFIG2			3 | 
 | #define SECTOR			4 | 
 | #define SECTOR_SIZE		5 | 
 | #define LAST_TRACK		6 | 
 | #define GAP			7 | 
 | #define DTL			8 | 
 | /* result indexes */ | 
 | #define STATUS_0		0 | 
 | #define STATUS_PCN		1 | 
 | #define STATUS_1		1 | 
 | #define STATUS_2		2 | 
 | #define STATUS_TRACK		3 | 
 | #define STATUS_HEAD		4 | 
 | #define STATUS_SECT		5 | 
 | #define STATUS_SECT_SIZE	6 | 
 |  | 
 |  | 
 | /* Register addresses */ | 
 | #define FDC_BASE	0x3F0 | 
 | #define FDC_SRA		FDC_BASE + 0	/* Status Register A */ | 
 | #define FDC_SRB		FDC_BASE + 1	/* Status Register B */ | 
 | #define FDC_DOR		FDC_BASE + 2	/* Digital Output Register */ | 
 | #define FDC_TDR		FDC_BASE + 3	/* Tape Drive Register */ | 
 | #define FDC_DSR		FDC_BASE + 4	/* Data rate Register */ | 
 | #define FDC_MSR		FDC_BASE + 4	/* Main Status Register */ | 
 | #define FDC_FIFO	FDC_BASE + 5	/* FIFO */ | 
 | #define FDC_DIR		FDC_BASE + 6	/* Digital Input Register */ | 
 | #define FDC_CCR		FDC_BASE + 7	/* Configuration Control */ | 
 | /* Commands */ | 
 | #define FDC_CMD_SENSE_INT	0x08 | 
 | #define FDC_CMD_CONFIGURE	0x13 | 
 | #define FDC_CMD_SPECIFY		0x03 | 
 | #define FDC_CMD_RECALIBRATE	0x07 | 
 | #define FDC_CMD_READ		0x06 | 
 | #define FDC_CMD_READ_TRACK	0x02 | 
 | #define FDC_CMD_READ_ID		0x0A | 
 | #define FDC_CMD_DUMP_REG	0x0E | 
 | #define FDC_CMD_SEEK		0x0F | 
 |  | 
 | #define FDC_CMD_SENSE_INT_LEN	0x01 | 
 | #define FDC_CMD_CONFIGURE_LEN	0x04 | 
 | #define FDC_CMD_SPECIFY_LEN	0x03 | 
 | #define FDC_CMD_RECALIBRATE_LEN	0x02 | 
 | #define FDC_CMD_READ_LEN	0x09 | 
 | #define FDC_CMD_READ_TRACK_LEN	0x09 | 
 | #define FDC_CMD_READ_ID_LEN	0x02 | 
 | #define FDC_CMD_DUMP_REG_LEN	0x01 | 
 | #define FDC_CMD_SEEK_LEN	0x03 | 
 |  | 
 | #define FDC_FIFO_THR		0x0C | 
 | #define FDC_FIFO_DIS		0x00 | 
 | #define FDC_IMPLIED_SEEK	0x01 | 
 | #define FDC_POLL_DIS		0x00 | 
 | #define FDC_PRE_TRK		0x00 | 
 | #define FDC_CONFIGURE		FDC_FIFO_THR | (FDC_POLL_DIS<<4) | (FDC_FIFO_DIS<<5) | (FDC_IMPLIED_SEEK << 6) | 
 | #define FDC_MFM_MODE		0x01 /* MFM enable */ | 
 | #define FDC_SKIP_MODE		0x00 /* skip enable */ | 
 |  | 
 | #define FDC_TIME_OUT 100000 /* time out */ | 
 | #define	FDC_RW_RETRIES		3 /* read write retries */ | 
 | #define FDC_CAL_RETRIES		3 /* calibration and seek retries */ | 
 |  | 
 |  | 
 | /* Disk structure */ | 
 | typedef struct  { | 
 | 	unsigned int size;	/* nr of sectors total */ | 
 | 	unsigned int sect;	/* sectors per track */ | 
 | 	unsigned int head;	/* nr of heads */ | 
 | 	unsigned int track;	/* nr of tracks */ | 
 | 	unsigned int stretch;	/* !=0 means double track steps */ | 
 | 	unsigned char	gap;	/* gap1 size */ | 
 | 	unsigned char	rate;	/* data rate. |= 0x40 for perpendicular */ | 
 | 	unsigned char	spec1;	/* stepping rate, head unload time */ | 
 | 	unsigned char	fmt_gap;/* gap2 size */ | 
 | 	unsigned char hlt;	/* head load time */ | 
 | 	unsigned char sect_code;/* Sector Size code */ | 
 | 	const char	* name;	/* used only for predefined formats */ | 
 | } FD_GEO_STRUCT; | 
 |  | 
 |  | 
 | /* supported Floppy types (currently only one) */ | 
 | const static FD_GEO_STRUCT floppy_type[2] = { | 
 | 	{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,16,2,"H1440" },	/*  7 1.44MB 3.5"   */ | 
 | 	{    0, 0,0, 0,0,0x00,0x00,0x00,0x00, 0,0,NULL    },	/*  end of table    */ | 
 | }; | 
 |  | 
 | static FDC_COMMAND_STRUCT cmd; /* global command struct */ | 
 |  | 
 | /* If the boot drive number is undefined, we assume it's drive 0             */ | 
 | #ifndef CONFIG_SYS_FDC_DRIVE_NUMBER | 
 | #define CONFIG_SYS_FDC_DRIVE_NUMBER 0 | 
 | #endif | 
 |  | 
 | /* Hardware access */ | 
 | #ifndef CONFIG_SYS_ISA_IO_STRIDE | 
 | #define CONFIG_SYS_ISA_IO_STRIDE 1 | 
 | #endif | 
 |  | 
 | #ifndef CONFIG_SYS_ISA_IO_OFFSET | 
 | #define CONFIG_SYS_ISA_IO_OFFSET 0 | 
 | #endif | 
 |  | 
 | /* Supporting Functions */ | 
 | /* reads a Register of the FDC */ | 
 | unsigned char read_fdc_reg(unsigned int addr) | 
 | { | 
 | 	volatile unsigned char *val = | 
 | 		(volatile unsigned char *)(CONFIG_SYS_ISA_IO_BASE_ADDRESS + | 
 | 					   (addr * CONFIG_SYS_ISA_IO_STRIDE) + | 
 | 					   CONFIG_SYS_ISA_IO_OFFSET); | 
 |  | 
 | 	return val [0]; | 
 | } | 
 |  | 
 | /* writes a Register of the FDC */ | 
 | void write_fdc_reg(unsigned int addr, unsigned char val) | 
 | { | 
 | 	volatile unsigned char *tmp = | 
 | 		(volatile unsigned char *)(CONFIG_SYS_ISA_IO_BASE_ADDRESS + | 
 | 					   (addr * CONFIG_SYS_ISA_IO_STRIDE) + | 
 | 					   CONFIG_SYS_ISA_IO_OFFSET); | 
 | 	tmp[0]=val; | 
 | } | 
 |  | 
 | /* waits for an interrupt (polling) */ | 
 | int wait_for_fdc_int(void) | 
 | { | 
 | 	unsigned long timeout; | 
 | 	timeout = FDC_TIME_OUT; | 
 | 	while((read_fdc_reg(FDC_SRA)&0x80)==0) { | 
 | 		timeout--; | 
 | 		udelay(10); | 
 | 		if(timeout==0) /* timeout occured */ | 
 | 			return FALSE; | 
 | 	} | 
 | 	return TRUE; | 
 | } | 
 |  | 
 | /* reads a byte from the FIFO of the FDC and checks direction and RQM bit | 
 |    of the MSR. returns -1 if timeout, or byte if ok */ | 
 | int read_fdc_byte(void) | 
 | { | 
 | 	unsigned long timeout; | 
 | 	timeout = FDC_TIME_OUT; | 
 | 	while((read_fdc_reg(FDC_MSR)&0xC0)!=0xC0) { | 
 | 		/* direction out and ready */ | 
 | 		udelay(10); | 
 | 		timeout--; | 
 | 		if(timeout==0) /* timeout occured */ | 
 | 			return -1; | 
 | 	} | 
 | 	return read_fdc_reg(FDC_FIFO); | 
 | } | 
 |  | 
 | /* if the direction of the FIFO is wrong, this routine is used to | 
 |    empty the FIFO. Should _not_ be used */ | 
 | int fdc_need_more_output(void) | 
 | { | 
 | 	unsigned char c; | 
 | 	while((read_fdc_reg(FDC_MSR)&0xC0)==0xC0)	{ | 
 | 			c=(unsigned char)read_fdc_byte(); | 
 | 			printf("Error: more output: %x\n",c); | 
 | 	} | 
 | 	return TRUE; | 
 | } | 
 |  | 
 |  | 
 | /* writes a byte to the FIFO of the FDC and checks direction and RQM bit | 
 |    of the MSR */ | 
 | int write_fdc_byte(unsigned char val) | 
 | { | 
 | 	unsigned long timeout; | 
 | 	timeout = FDC_TIME_OUT; | 
 | 	while((read_fdc_reg(FDC_MSR)&0xC0)!=0x80) { | 
 | 		/* direction in and ready for byte */ | 
 | 		timeout--; | 
 | 		udelay(10); | 
 | 		fdc_need_more_output(); | 
 | 		if(timeout==0) /* timeout occured */ | 
 | 			return FALSE; | 
 | 	} | 
 | 	write_fdc_reg(FDC_FIFO,val); | 
 | 	return TRUE; | 
 | } | 
 |  | 
 | /* sets up all FDC commands and issues it to the FDC. If | 
 |    the command causes direct results (no Execution Phase) | 
 |    the result is be read as well. */ | 
 |  | 
 | int fdc_issue_cmd(FDC_COMMAND_STRUCT *pCMD,FD_GEO_STRUCT *pFG) | 
 | { | 
 | 	int i; | 
 | 	unsigned long head,track,sect,timeout; | 
 | 	track = pCMD->blnr / (pFG->sect * pFG->head); /* track nr */ | 
 | 	sect =  pCMD->blnr % (pFG->sect * pFG->head); /* remaining blocks */ | 
 | 	head = sect / pFG->sect; /* head nr */ | 
 | 	sect =  sect % pFG->sect; /* remaining blocks */ | 
 | 	sect++; /* sectors are 1 based */ | 
 | 	PRINTF("Cmd 0x%02x Track %ld, Head %ld, Sector %ld, Drive %d (blnr %ld)\n", | 
 | 		pCMD->cmd[0],track,head,sect,pCMD->drive,pCMD->blnr); | 
 |  | 
 | 	if(head|=0) { /* max heads = 2 */ | 
 | 		pCMD->cmd[DRIVE]=pCMD->drive | 0x04; /* head 1 */ | 
 | 		pCMD->cmd[HEAD]=(unsigned char) head; /* head register */ | 
 | 	} | 
 | 	else { | 
 | 		pCMD->cmd[DRIVE]=pCMD->drive; /* head 0 */ | 
 | 		pCMD->cmd[HEAD]=(unsigned char) head; /* head register */ | 
 | 	} | 
 | 	pCMD->cmd[TRACK]=(unsigned char) track; /* track */ | 
 | 	switch (pCMD->cmd[COMMAND]) { | 
 | 		case FDC_CMD_READ: | 
 | 			pCMD->cmd[SECTOR]=(unsigned char) sect; /* sector */ | 
 | 			pCMD->cmd[SECTOR_SIZE]=pFG->sect_code; /* sector size code */ | 
 | 			pCMD->cmd[LAST_TRACK]=pFG->sect; /* End of track */ | 
 | 			pCMD->cmd[GAP]=pFG->gap; /* gap */ | 
 | 			pCMD->cmd[DTL]=0xFF; /* DTL */ | 
 | 			pCMD->cmdlen=FDC_CMD_READ_LEN; | 
 | 			pCMD->cmd[COMMAND]|=(FDC_MFM_MODE<<6); /* set MFM bit */ | 
 | 			pCMD->cmd[COMMAND]|=(FDC_SKIP_MODE<<5); /* set Skip bit */ | 
 | 			pCMD->resultlen=0;  /* result only after execution */ | 
 | 			break; | 
 | 		case FDC_CMD_SEEK: | 
 | 			pCMD->cmdlen=FDC_CMD_SEEK_LEN; | 
 | 			pCMD->resultlen=0;  /* no result */ | 
 | 			break; | 
 | 		case FDC_CMD_CONFIGURE: | 
 | 			pCMD->cmd[CONFIG0]=0; | 
 | 			pCMD->cmd[CONFIG1]=FDC_CONFIGURE; /* FIFO Threshold, Poll, Enable FIFO */ | 
 | 			pCMD->cmd[CONFIG2]=FDC_PRE_TRK;	/* Precompensation Track */ | 
 | 			pCMD->cmdlen=FDC_CMD_CONFIGURE_LEN; | 
 | 			pCMD->resultlen=0;  /* no result */ | 
 | 			break; | 
 | 		case FDC_CMD_SPECIFY: | 
 | 			pCMD->cmd[SPEC_HUTSRT]=pFG->spec1; | 
 | 			pCMD->cmd[SPEC_HLT]=(pFG->hlt)<<1; /* head load time */ | 
 | 			if(pCMD->dma==0) | 
 | 				pCMD->cmd[SPEC_HLT]|=0x1; /* no dma */ | 
 | 			pCMD->cmdlen=FDC_CMD_SPECIFY_LEN; | 
 | 			pCMD->resultlen=0;  /* no result */ | 
 | 			break; | 
 | 		case FDC_CMD_DUMP_REG: | 
 | 			pCMD->cmdlen=FDC_CMD_DUMP_REG_LEN; | 
 | 			pCMD->resultlen=10;  /* 10 byte result */ | 
 | 			break; | 
 | 		case FDC_CMD_READ_ID: | 
 | 			pCMD->cmd[COMMAND]|=(FDC_MFM_MODE<<6); /* set MFM bit */ | 
 | 			pCMD->cmdlen=FDC_CMD_READ_ID_LEN; | 
 | 			pCMD->resultlen=7;  /* 7 byte result */ | 
 | 			break; | 
 | 		case FDC_CMD_RECALIBRATE: | 
 | 			pCMD->cmd[DRIVE]&=0x03; /* don't set the head bit */ | 
 | 			pCMD->cmdlen=FDC_CMD_RECALIBRATE_LEN; | 
 | 			pCMD->resultlen=0;  /* no result */ | 
 | 			break; | 
 | 			break; | 
 | 		case FDC_CMD_SENSE_INT: | 
 | 			pCMD->cmdlen=FDC_CMD_SENSE_INT_LEN; | 
 | 			pCMD->resultlen=2; | 
 | 			break; | 
 | 	} | 
 | 	for(i=0;i<pCMD->cmdlen;i++) { | 
 | 		/* PRINTF("write cmd%d = 0x%02X\n",i,pCMD->cmd[i]); */ | 
 | 		if(write_fdc_byte(pCMD->cmd[i])==FALSE) { | 
 | 			PRINTF("Error: timeout while issue cmd%d\n",i); | 
 | 			return FALSE; | 
 | 		} | 
 | 	} | 
 | 	timeout=FDC_TIME_OUT; | 
 | 	for(i=0;i<pCMD->resultlen;i++) { | 
 | 		while((read_fdc_reg(FDC_MSR)&0xC0)!=0xC0) { | 
 | 			timeout--; | 
 | 			if(timeout==0) { | 
 | 				PRINTF(" timeout while reading result%d MSR=0x%02X\n",i,read_fdc_reg(FDC_MSR)); | 
 | 				return FALSE; | 
 | 			} | 
 | 		} | 
 | 		pCMD->result[i]=(unsigned char)read_fdc_byte(); | 
 | 	} | 
 | 	return TRUE; | 
 | } | 
 |  | 
 | /* selects the drive assigned in the cmd structur and | 
 |    switches on the Motor */ | 
 | void select_fdc_drive(FDC_COMMAND_STRUCT *pCMD) | 
 | { | 
 | 	unsigned char val; | 
 |  | 
 | 	val=(1<<(4+pCMD->drive))|pCMD->drive|0xC; /* set reset, dma gate and motor bits */ | 
 | 	if((read_fdc_reg(FDC_DOR)&val)!=val) { | 
 | 		write_fdc_reg(FDC_DOR,val); | 
 | 		for(val=0;val<255;val++) | 
 | 			udelay(500); /* wait some time to start motor */ | 
 | 	} | 
 | } | 
 |  | 
 | /* switches off the Motor of the specified drive */ | 
 | void stop_fdc_drive(FDC_COMMAND_STRUCT *pCMD) | 
 | { | 
 | 	unsigned char val; | 
 |  | 
 | 	val=(1<<(4+pCMD->drive))|pCMD->drive; /* sets motor bits */ | 
 | 	write_fdc_reg(FDC_DOR,(read_fdc_reg(FDC_DOR)&~val)); | 
 | } | 
 |  | 
 | /* issues a recalibrate command, waits for interrupt and | 
 |  * issues a sense_interrupt */ | 
 | int fdc_recalibrate(FDC_COMMAND_STRUCT *pCMD,FD_GEO_STRUCT *pFG) | 
 | { | 
 | 	pCMD->cmd[COMMAND]=FDC_CMD_RECALIBRATE; | 
 | 	if(fdc_issue_cmd(pCMD,pFG)==FALSE) | 
 | 		return FALSE; | 
 | 	while(wait_for_fdc_int()!=TRUE); | 
 | 	pCMD->cmd[COMMAND]=FDC_CMD_SENSE_INT; | 
 | 	return(fdc_issue_cmd(pCMD,pFG)); | 
 | } | 
 |  | 
 | /* issues a recalibrate command, waits for interrupt and | 
 |  * issues a sense_interrupt */ | 
 | int fdc_seek(FDC_COMMAND_STRUCT *pCMD,FD_GEO_STRUCT *pFG) | 
 | { | 
 | 	pCMD->cmd[COMMAND]=FDC_CMD_SEEK; | 
 | 	if(fdc_issue_cmd(pCMD,pFG)==FALSE) | 
 | 		return FALSE; | 
 | 	while(wait_for_fdc_int()!=TRUE); | 
 | 	pCMD->cmd[COMMAND]=FDC_CMD_SENSE_INT; | 
 | 	return(fdc_issue_cmd(pCMD,pFG)); | 
 | } | 
 |  | 
 | /* terminates current command, by not servicing the FIFO | 
 |  * waits for interrupt and fills in the result bytes */ | 
 | int fdc_terminate(FDC_COMMAND_STRUCT *pCMD) | 
 | { | 
 | 	int i; | 
 | 	for(i=0;i<100;i++) | 
 | 		udelay(500); /* wait 500usec for fifo overrun */ | 
 | 	while((read_fdc_reg(FDC_SRA)&0x80)==0x00); /* wait as long as no int has occured */ | 
 | 	for(i=0;i<7;i++) { | 
 | 		pCMD->result[i]=(unsigned char)read_fdc_byte(); | 
 | 	} | 
 | 	return TRUE; | 
 | } | 
 |  | 
 | /* reads data from FDC, seek commands are issued automatic */ | 
 | int fdc_read_data(unsigned char *buffer, unsigned long blocks,FDC_COMMAND_STRUCT *pCMD, FD_GEO_STRUCT *pFG) | 
 | { | 
 |   /* first seek to start address */ | 
 | 	unsigned long len,readblk,i,timeout,ii,offset; | 
 | 	unsigned char c,retriesrw,retriescal; | 
 | 	unsigned char *bufferw; /* working buffer */ | 
 | 	int sect_size; | 
 | 	int flags; | 
 |  | 
 | 	flags=disable_interrupts(); /* switch off all Interrupts */ | 
 | 	select_fdc_drive(pCMD); /* switch on drive */ | 
 | 	sect_size=0x080<<pFG->sect_code; | 
 | 	retriesrw=0; | 
 | 	retriescal=0; | 
 | 	offset=0; | 
 | 	if(fdc_seek(pCMD,pFG)==FALSE) { | 
 | 		stop_fdc_drive(pCMD); | 
 | 		if (flags) | 
 | 			enable_interrupts(); | 
 | 		return FALSE; | 
 | 	} | 
 | 	if((pCMD->result[STATUS_0]&0x20)!=0x20) { | 
 | 		printf("Seek error Status: %02X\n",pCMD->result[STATUS_0]); | 
 | 		stop_fdc_drive(pCMD); | 
 | 		if (flags) | 
 | 			enable_interrupts(); | 
 | 		return FALSE; | 
 | 	} | 
 | 	/* now determine the next seek point */ | 
 | 	/*	lastblk=pCMD->blnr + blocks; */ | 
 | 	/*	readblk=(pFG->head*pFG->sect)-(pCMD->blnr%(pFG->head*pFG->sect)); */ | 
 | 	readblk=pFG->sect-(pCMD->blnr%pFG->sect); | 
 | 	PRINTF("1st nr of block possible read %ld start %ld\n",readblk,pCMD->blnr); | 
 | 	if(readblk>blocks) /* is end within 1st track */ | 
 | 		readblk=blocks; /* yes, correct it */ | 
 | 	PRINTF("we read %ld blocks start %ld\n",readblk,pCMD->blnr); | 
 | 	bufferw = &buffer[0]; /* setup working buffer */ | 
 | 	do { | 
 | retryrw: | 
 | 		len=sect_size * readblk; | 
 | 		pCMD->cmd[COMMAND]=FDC_CMD_READ; | 
 | 		if(fdc_issue_cmd(pCMD,pFG)==FALSE) { | 
 | 			stop_fdc_drive(pCMD); | 
 | 			if (flags) | 
 | 				enable_interrupts(); | 
 | 			return FALSE; | 
 | 		} | 
 | 		for (i=0;i<len;i++) { | 
 | 			timeout=FDC_TIME_OUT; | 
 | 			do { | 
 | 				c=read_fdc_reg(FDC_MSR); | 
 | 				if((c&0xC0)==0xC0) { | 
 | 					bufferw[i]=read_fdc_reg(FDC_FIFO); | 
 | 					break; | 
 | 				} | 
 | 				if((c&0xC0)==0x80) { /* output */ | 
 | 					PRINTF("Transfer error transfered: at %ld, MSR=%02X\n",i,c); | 
 | 					if(i>6) { | 
 | 						for(ii=0;ii<7;ii++) { | 
 | 							pCMD->result[ii]=bufferw[(i-7+ii)]; | 
 | 						} /* for */ | 
 | 					} | 
 | 					if(retriesrw++>FDC_RW_RETRIES) { | 
 | 						if (retriescal++>FDC_CAL_RETRIES) { | 
 | 							stop_fdc_drive(pCMD); | 
 | 							if (flags) | 
 | 								enable_interrupts(); | 
 | 							return FALSE; | 
 | 						} | 
 | 						else { | 
 | 							PRINTF(" trying to recalibrate Try %d\n",retriescal); | 
 | 							if(fdc_recalibrate(pCMD,pFG)==FALSE) { | 
 | 								stop_fdc_drive(pCMD); | 
 | 								if (flags) | 
 | 									enable_interrupts(); | 
 | 								return FALSE; | 
 | 							} | 
 | 							retriesrw=0; | 
 | 							goto retrycal; | 
 | 						} /* else >FDC_CAL_RETRIES */ | 
 | 					} | 
 | 					else { | 
 | 						PRINTF("Read retry %d\n",retriesrw); | 
 | 						goto retryrw; | 
 | 					} /* else >FDC_RW_RETRIES */ | 
 | 				}/* if output */ | 
 | 				timeout--; | 
 | 			}while(TRUE); | 
 | 		} /* for len */ | 
 | 		/* the last sector of a track or all data has been read, | 
 | 		 * we need to get the results */ | 
 | 		fdc_terminate(pCMD); | 
 | 		offset+=(sect_size*readblk); /* set up buffer pointer */ | 
 | 		bufferw = &buffer[offset]; | 
 | 		pCMD->blnr+=readblk; /* update current block nr */ | 
 | 		blocks-=readblk; /* update blocks */ | 
 | 		if(blocks==0) | 
 | 			break; /* we are finish */ | 
 | 		/* setup new read blocks */ | 
 | 		/*	readblk=pFG->head*pFG->sect; */ | 
 | 		readblk=pFG->sect; | 
 | 		if(readblk>blocks) | 
 | 			readblk=blocks; | 
 | retrycal: | 
 | 		/* a seek is necessary */ | 
 | 		if(fdc_seek(pCMD,pFG)==FALSE) { | 
 | 			stop_fdc_drive(pCMD); | 
 | 			if (flags) | 
 | 				enable_interrupts(); | 
 | 			return FALSE; | 
 | 		} | 
 | 		if((pCMD->result[STATUS_0]&0x20)!=0x20) { | 
 | 			PRINTF("Seek error Status: %02X\n",pCMD->result[STATUS_0]); | 
 | 			stop_fdc_drive(pCMD); | 
 | 			return FALSE; | 
 | 		} | 
 | 	}while(TRUE); /* start over */ | 
 | 	stop_fdc_drive(pCMD); /* switch off drive */ | 
 | 	if (flags) | 
 | 		enable_interrupts(); | 
 | 	return TRUE; | 
 | } | 
 |  | 
 | /* Scan all drives and check if drive is present and disk is inserted */ | 
 | int fdc_check_drive(FDC_COMMAND_STRUCT *pCMD, FD_GEO_STRUCT *pFG) | 
 | { | 
 | 	int i,drives,state; | 
 |   /* OK procedure of data book is satisfied. | 
 | 	 * trying to get some information over the drives */ | 
 | 	state=0; /* no drives, no disks */ | 
 | 	for(drives=0;drives<4;drives++) { | 
 | 		pCMD->drive=drives; | 
 | 		select_fdc_drive(pCMD); | 
 | 		pCMD->blnr=0; /* set to the 1st block */ | 
 | 		if(fdc_recalibrate(pCMD,pFG)==FALSE) | 
 | 			continue; | 
 | 		if((pCMD->result[STATUS_0]&0x10)==0x10) | 
 | 			continue; | 
 | 		/* ok drive connected check for disk */ | 
 | 		state|=(1<<drives); | 
 | 		pCMD->blnr=pFG->size; /* set to the last block */ | 
 | 		if(fdc_seek(pCMD,pFG)==FALSE) | 
 | 			continue; | 
 | 		pCMD->blnr=0; /* set to the 1st block */ | 
 | 		if(fdc_recalibrate(pCMD,pFG)==FALSE) | 
 | 			continue; | 
 | 		pCMD->cmd[COMMAND]=FDC_CMD_READ_ID; | 
 | 		if(fdc_issue_cmd(pCMD,pFG)==FALSE) | 
 | 			continue; | 
 | 		state|=(0x10<<drives); | 
 | 	} | 
 | 	stop_fdc_drive(pCMD); | 
 | 	for(i=0;i<4;i++) { | 
 | 		PRINTF("Floppy Drive %d %sconnected %sDisk inserted %s\n",i, | 
 | 			((state&(1<<i))==(1<<i)) ? "":"not ", | 
 | 			((state&(0x10<<i))==(0x10<<i)) ? "":"no ", | 
 | 			((state&(0x10<<i))==(0x10<<i)) ? pFG->name : ""); | 
 | 	} | 
 | 	pCMD->flags=state; | 
 | 	return TRUE; | 
 | } | 
 |  | 
 |  | 
 | /************************************************************************** | 
 | * int fdc_setup | 
 | * setup the fdc according the datasheet | 
 | * assuming in PS2 Mode | 
 | */ | 
 | int fdc_setup(int drive, FDC_COMMAND_STRUCT *pCMD, FD_GEO_STRUCT *pFG) | 
 | { | 
 | 	int i; | 
 |  | 
 | #ifdef CONFIG_SYS_FDC_HW_INIT | 
 | 	fdc_hw_init (); | 
 | #endif | 
 | 	/* first, we reset the FDC via the DOR */ | 
 | 	write_fdc_reg(FDC_DOR,0x00); | 
 | 	for(i=0; i<255; i++) /* then we wait some time */ | 
 | 		udelay(500); | 
 | 	/* then, we clear the reset in the DOR */ | 
 | 	pCMD->drive=drive; | 
 | 	select_fdc_drive(pCMD); | 
 | 	/* initialize the CCR */ | 
 | 	write_fdc_reg(FDC_CCR,pFG->rate); | 
 | 	/* then initialize the DSR */ | 
 | 	write_fdc_reg(FDC_DSR,pFG->rate); | 
 | 	if(wait_for_fdc_int()==FALSE) { | 
 | 			PRINTF("Time Out after writing CCR\n"); | 
 | 			return FALSE; | 
 | 	} | 
 | 	/* now issue sense Interrupt and status command | 
 | 	 * assuming only one drive present (drive 0) */ | 
 | 	pCMD->dma=0; /* we don't use any dma at all */ | 
 | 	for(i=0;i<4;i++) { | 
 | 		/* issue sense interrupt for all 4 possible drives */ | 
 | 		pCMD->cmd[COMMAND]=FDC_CMD_SENSE_INT; | 
 | 		if(fdc_issue_cmd(pCMD,pFG)==FALSE) { | 
 | 			PRINTF("Sense Interrupt for drive %d failed\n",i); | 
 | 		} | 
 | 	} | 
 | 	/* issue the configure command */ | 
 | 	pCMD->drive=drive; | 
 | 	select_fdc_drive(pCMD); | 
 | 	pCMD->cmd[COMMAND]=FDC_CMD_CONFIGURE; | 
 | 	if(fdc_issue_cmd(pCMD,pFG)==FALSE) { | 
 | 		PRINTF(" configure timeout\n"); | 
 | 		stop_fdc_drive(pCMD); | 
 | 		return FALSE; | 
 | 	} | 
 | 	/* issue specify command */ | 
 | 	pCMD->cmd[COMMAND]=FDC_CMD_SPECIFY; | 
 | 	if(fdc_issue_cmd(pCMD,pFG)==FALSE) { | 
 | 		PRINTF(" specify timeout\n"); | 
 | 		stop_fdc_drive(pCMD); | 
 | 		return FALSE; | 
 |  | 
 | 	} | 
 | 	/* then, we clear the reset in the DOR */ | 
 | 	/* fdc_check_drive(pCMD,pFG);	*/ | 
 | 	/*	write_fdc_reg(FDC_DOR,0x04); */ | 
 |  | 
 | 	return TRUE; | 
 | } | 
 |  | 
 | #if defined(CONFIG_CMD_FDOS) | 
 |  | 
 | /* Low level functions for the Floppy-DOS layer                              */ | 
 |  | 
 | /************************************************************************** | 
 | * int fdc_fdos_init | 
 | * initialize the FDC layer | 
 | * | 
 | */ | 
 | int fdc_fdos_init (int drive) | 
 | { | 
 | 	FD_GEO_STRUCT *pFG = (FD_GEO_STRUCT *)floppy_type; | 
 | 	FDC_COMMAND_STRUCT *pCMD = &cmd; | 
 |  | 
 | 	/* setup FDC and scan for drives  */ | 
 | 	if(fdc_setup(drive,pCMD,pFG)==FALSE) { | 
 | 		printf("\n** Error in setup FDC **\n"); | 
 | 		return FALSE; | 
 | 	} | 
 | 	if(fdc_check_drive(pCMD,pFG)==FALSE) { | 
 | 		printf("\n** Error in check_drives **\n"); | 
 | 		return FALSE; | 
 | 	} | 
 | 	if((pCMD->flags&(1<<drive))==0) { | 
 | 		/* drive not available */ | 
 | 		printf("\n** Drive %d not available **\n",drive); | 
 | 		return FALSE; | 
 | 	} | 
 | 	if((pCMD->flags&(0x10<<drive))==0) { | 
 | 		/* no disk inserted */ | 
 | 		printf("\n** No disk inserted in drive %d **\n",drive); | 
 | 		return FALSE; | 
 | 	} | 
 | 	/* ok, we have a valid source */ | 
 | 	pCMD->drive=drive; | 
 |  | 
 | 	/* read first block */ | 
 | 	pCMD->blnr=0; | 
 | 	return TRUE; | 
 | } | 
 | /************************************************************************** | 
 | * int fdc_fdos_seek | 
 | * parameter is a block number | 
 | */ | 
 | int fdc_fdos_seek (int where) | 
 | { | 
 | 	FD_GEO_STRUCT *pFG = (FD_GEO_STRUCT *)floppy_type; | 
 | 	FDC_COMMAND_STRUCT *pCMD = &cmd; | 
 |  | 
 | 	pCMD -> blnr = where ; | 
 | 	return (fdc_seek (pCMD, pFG)); | 
 | } | 
 | /************************************************************************** | 
 | * int fdc_fdos_read | 
 | *  the length is in block number | 
 | */ | 
 | int fdc_fdos_read (void *buffer, int len) | 
 | { | 
 | 	FD_GEO_STRUCT *pFG = (FD_GEO_STRUCT *)floppy_type; | 
 | 	FDC_COMMAND_STRUCT *pCMD = &cmd; | 
 |  | 
 | 	return (fdc_read_data (buffer, len, pCMD, pFG)); | 
 | } | 
 | #endif | 
 |  | 
 | #if defined(CONFIG_CMD_FDC) | 
 | /**************************************************************************** | 
 |  * main routine do_fdcboot | 
 |  */ | 
 | int do_fdcboot (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | 
 | { | 
 | 	FD_GEO_STRUCT *pFG = (FD_GEO_STRUCT *)floppy_type; | 
 | 	FDC_COMMAND_STRUCT *pCMD = &cmd; | 
 | 	unsigned long addr,imsize; | 
 | 	image_header_t *hdr;  /* used for fdc boot */ | 
 | 	unsigned char boot_drive; | 
 | 	int i,nrofblk; | 
 | #if defined(CONFIG_FIT) | 
 | 	const void *fit_hdr = NULL; | 
 | #endif | 
 |  | 
 | 	switch (argc) { | 
 | 	case 1: | 
 | 		addr = CONFIG_SYS_LOAD_ADDR; | 
 | 		boot_drive=CONFIG_SYS_FDC_DRIVE_NUMBER; | 
 | 		break; | 
 | 	case 2: | 
 | 		addr = simple_strtoul(argv[1], NULL, 16); | 
 | 		boot_drive=CONFIG_SYS_FDC_DRIVE_NUMBER; | 
 | 		break; | 
 | 	case 3: | 
 | 		addr = simple_strtoul(argv[1], NULL, 16); | 
 | 		boot_drive=simple_strtoul(argv[2], NULL, 10); | 
 | 		break; | 
 | 	default: | 
 | 		return cmd_usage(cmdtp); | 
 | 	} | 
 | 	/* setup FDC and scan for drives  */ | 
 | 	if(fdc_setup(boot_drive,pCMD,pFG)==FALSE) { | 
 | 		printf("\n** Error in setup FDC **\n"); | 
 | 		return 1; | 
 | 	} | 
 | 	if(fdc_check_drive(pCMD,pFG)==FALSE) { | 
 | 		printf("\n** Error in check_drives **\n"); | 
 | 		return 1; | 
 | 	} | 
 | 	if((pCMD->flags&(1<<boot_drive))==0) { | 
 | 		/* drive not available */ | 
 | 		printf("\n** Drive %d not availabe **\n",boot_drive); | 
 | 		return 1; | 
 | 	} | 
 | 	if((pCMD->flags&(0x10<<boot_drive))==0) { | 
 | 		/* no disk inserted */ | 
 | 		printf("\n** No disk inserted in drive %d **\n",boot_drive); | 
 | 		return 1; | 
 | 	} | 
 | 	/* ok, we have a valid source */ | 
 | 	pCMD->drive=boot_drive; | 
 | 	/* read first block */ | 
 | 	pCMD->blnr=0; | 
 | 	if(fdc_read_data((unsigned char *)addr,1,pCMD,pFG)==FALSE) { | 
 | 		printf("\nRead error:"); | 
 | 		for(i=0;i<7;i++) | 
 | 			printf("result%d: 0x%02X\n",i,pCMD->result[i]); | 
 | 		return 1; | 
 | 	} | 
 |  | 
 | 	switch (genimg_get_format ((void *)addr)) { | 
 | 	case IMAGE_FORMAT_LEGACY: | 
 | 		hdr = (image_header_t *)addr; | 
 | 		image_print_contents (hdr); | 
 |  | 
 | 		imsize = image_get_image_size (hdr); | 
 | 		break; | 
 | #if defined(CONFIG_FIT) | 
 | 	case IMAGE_FORMAT_FIT: | 
 | 		fit_hdr = (const void *)addr; | 
 | 		puts ("Fit image detected...\n"); | 
 |  | 
 | 		imsize = fit_get_size (fit_hdr); | 
 | 		break; | 
 | #endif | 
 | 	default: | 
 | 		puts ("** Unknown image type\n"); | 
 | 		return 1; | 
 | 	} | 
 |  | 
 | 	nrofblk=imsize/512; | 
 | 	if((imsize%512)>0) | 
 | 		nrofblk++; | 
 | 	printf("Loading %ld Bytes (%d blocks) at 0x%08lx..\n",imsize,nrofblk,addr); | 
 | 	pCMD->blnr=0; | 
 | 	if(fdc_read_data((unsigned char *)addr,nrofblk,pCMD,pFG)==FALSE) { | 
 | 		/* read image block */ | 
 | 		printf("\nRead error:"); | 
 | 		for(i=0;i<7;i++) | 
 | 			printf("result%d: 0x%02X\n",i,pCMD->result[i]); | 
 | 		return 1; | 
 | 	} | 
 | 	printf("OK %ld Bytes loaded.\n",imsize); | 
 |  | 
 | 	flush_cache (addr, imsize); | 
 |  | 
 | #if defined(CONFIG_FIT) | 
 | 	/* This cannot be done earlier, we need complete FIT image in RAM first */ | 
 | 	if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) { | 
 | 		if (!fit_check_format (fit_hdr)) { | 
 | 			puts ("** Bad FIT image format\n"); | 
 | 			return 1; | 
 | 		} | 
 | 		fit_print_contents (fit_hdr); | 
 | 	} | 
 | #endif | 
 |  | 
 | 	/* Loading ok, update default load address */ | 
 | 	load_addr = addr; | 
 |  | 
 | 	return bootm_maybe_autostart(cmdtp, argv[0]); | 
 | } | 
 |  | 
 | U_BOOT_CMD( | 
 | 	fdcboot,	3,	1,	do_fdcboot, | 
 | 	"boot from floppy device", | 
 | 	"loadAddr drive" | 
 | ); | 
 | #endif |