|  | /* | 
|  | * (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 | 
|  |  | 
|  |  | 
|  | #ifdef CONFIG_AMIGAONEG3SE | 
|  | unsigned char INT6_Status; | 
|  |  | 
|  | void fdc_interrupt(void) | 
|  | { | 
|  | INT6_Status = 0x80; | 
|  | } | 
|  |  | 
|  | /* waits for an interrupt (polling) */ | 
|  | int wait_for_fdc_int(void) | 
|  | { | 
|  | unsigned long timeout; | 
|  | timeout = FDC_TIME_OUT; | 
|  | while(((volatile)INT6_Status & 0x80) == 0) { | 
|  | timeout--; | 
|  | udelay(10); | 
|  | if(timeout == 0) /* timeout occured */ | 
|  | return FALSE; | 
|  | } | 
|  | INT6_Status = 0; | 
|  | return TRUE; | 
|  | } | 
|  | #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; | 
|  | } | 
|  |  | 
|  | #ifndef CONFIG_AMIGAONEG3SE | 
|  | /* 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; | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  | /* 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)); | 
|  | } | 
|  |  | 
|  | #ifndef CONFIG_AMIGAONEG3SE | 
|  | /* 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; | 
|  | } | 
|  | #endif | 
|  | #ifdef CONFIG_AMIGAONEG3SE | 
|  | int fdc_terminate(FDC_COMMAND_STRUCT *pCMD) | 
|  | { | 
|  | int i; | 
|  | for(i=0;i<100;i++) | 
|  | udelay(500); /* wait 500usec for fifo overrun */ | 
|  | while((INT6_Status&0x80)==0x00); /* wait as long as no int has occured */ | 
|  | for(i=0;i<7;i++) { | 
|  | pCMD->result[i]=(unsigned char)read_fdc_byte(); | 
|  | } | 
|  | INT6_Status = 0; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  | #ifdef CONFIG_AMIGAONEG3SE | 
|  | #define disable_interrupts() 0 | 
|  | #define enable_interrupts() (void)0 | 
|  | #endif | 
|  |  | 
|  | /* 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,lastblk,readblk,i,timeout,ii,offset; | 
|  | unsigned char pcn,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); | 
|  | 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); | 
|  | enable_interrupts(); | 
|  | return FALSE; | 
|  | } | 
|  | pcn=pCMD->result[STATUS_PCN]; /* current track */ | 
|  | /* 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); | 
|  | 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); | 
|  | enable_interrupts(); | 
|  | return FALSE; | 
|  | } | 
|  | else { | 
|  | PRINTF(" trying to recalibrate Try %d\n",retriescal); | 
|  | if(fdc_recalibrate(pCMD,pFG)==FALSE) { | 
|  | stop_fdc_drive(pCMD); | 
|  | 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); | 
|  | 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; | 
|  | } | 
|  | pcn=pCMD->result[STATUS_PCN]; /* current track */ | 
|  | }while(TRUE); /* start over */ | 
|  | stop_fdc_drive(pCMD); /* switch off drive */ | 
|  | enable_interrupts(); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_AMIGAONEG3SE | 
|  | #undef disable_interrupts() | 
|  | #undef enable_interrupts() | 
|  | #endif | 
|  |  | 
|  | /* 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_AMIGAONEG3SE | 
|  | irq_install_handler(6, (interrupt_handler_t *)fdc_interrupt, NULL); | 
|  | i8259_unmask_irq(6); | 
|  | #endif | 
|  |  | 
|  | #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 *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; | 
|  | char *ep; | 
|  | int rcode = 0; | 
|  | #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: | 
|  | cmd_usage(cmdtp); | 
|  | return 1; | 
|  | } | 
|  | /* 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; | 
|  |  | 
|  | /* Check if we should attempt an auto-start */ | 
|  | if (((ep = getenv("autostart")) != NULL) && (strcmp(ep,"yes") == 0)) { | 
|  | char *local_args[2]; | 
|  | extern int do_bootm (cmd_tbl_t *, int, int, char *[]); | 
|  |  | 
|  | local_args[0] = argv[0]; | 
|  | local_args[1] = NULL; | 
|  |  | 
|  | printf ("Automatic boot of image at addr 0x%08lX ...\n", addr); | 
|  |  | 
|  | do_bootm (cmdtp, 0, 1, local_args); | 
|  | rcode ++; | 
|  | } | 
|  | return rcode; | 
|  | } | 
|  |  | 
|  | U_BOOT_CMD( | 
|  | fdcboot,	3,	1,	do_fdcboot, | 
|  | "boot from floppy device", | 
|  | "loadAddr drive" | 
|  | ); | 
|  | #endif |