📄 fd1772.c
字号:
/* * linux/kernel/arch/arm/drivers/block/fd1772.c * Based on ataflop.c in the m68k Linux * Copyright (C) 1993 Greg Harp * Atari Support by Bjoern Brauel, Roman Hodek * Archimedes Support by Dave Gilbert (linux@treblig.org) * * Big cleanup Sep 11..14 1994 Roman Hodek: * - Driver now works interrupt driven * - Support for two drives; should work, but I cannot test that :-( * - Reading is done in whole tracks and buffered to speed up things * - Disk change detection and drive deselecting after motor-off * similar to TOS * - Autodetection of disk format (DD/HD); untested yet, because I * don't have an HD drive :-( * * Fixes Nov 13 1994 Martin Schaller: * - Autodetection works now * - Support for 5 1/4" disks * - Removed drive type (unknown on atari) * - Do seeks with 8 Mhz * * Changes by Andreas Schwab: * - After errors in multiple read mode try again reading single sectors * (Feb 1995): * - Clean up error handling * - Set blk_size for proper size checking * - Initialize track register when testing presence of floppy * - Implement some ioctl's * * Changes by Torsten Lang: * - When probing the floppies we should add the FDC1772CMDADD_H flag since * the FDC1772 will otherwise wait forever when no disk is inserted... * * Things left to do: * - Formatting * - Maybe a better strategy for disk change detection (does anyone * know one?) * - There are some strange problems left: The strangest one is * that, at least on my TT (4+4MB), the first 2 Bytes of the last * page of the TT-Ram (!) change their contents (some bits get * set) while a floppy DMA is going on. But there are no accesses * to these memory locations from the kernel... (I tested that by * making the page read-only). I cannot explain what's going on... * - Sometimes the drive-change-detection stops to work. The * function is still called, but the WP bit always reads as 0... * Maybe a problem with the status reg mode or a timing problem. * Note 10/12/94: The change detection now seems to work reliably. * There is no proof, but I've seen no hang for a long time... * * ARCHIMEDES changes: (gilbertd@cs.man.ac.uk) * 26/12/95 - Changed all names starting with FDC to FDC1772 * Removed all references to clock speed of FDC - we're stuck with 8MHz * Modified disk_type structure to remove HD formats * * 7/ 1/96 - Wrote FIQ code, removed most remaining atariisms * * 13/ 1/96 - Well I think its read a single sector; but there is a problem * fd_rwsec_done which is called in FIQ mode starts another transfer * off (in fd_rwsec) while still in FIQ mode. Because its still in * FIQ mode it can't service the DMA and loses data. So need to * heavily restructure. * 14/ 1/96 - Found that the definitions of the register numbers of the * FDC were multiplied by 2 in the header for the 16bit words * of the atari so half the writes were going in the wrong place. * Also realised that the FIQ entry didn't make any attempt to * preserve registers or return correctly; now in assembler. * * 11/ 2/96 - Hmm - doesn't work on real machine. Auto detect doesn't * and hacking that past seems to wait forever - check motor * being turned on. * * 17/ 2/96 - still having problems - forcing track to -1 when selecting * new drives seems to allow it to read first few sectors * but then we get solid hangs at apparently random places * which change depending what is happening. * * 9/ 3/96 - Fiddled a lot of stuff around to move to kernel 1.3.35 * A lot of fiddling in DMA stuff. Having problems with it * constnatly thinking its timeing out. Ah - its timeout * was set to (6*HZ) rather than jiffies+(6*HZ). Now giving * duff data! * * 5/ 4/96 - Made it use the new IOC_ macros rather than *ioc * Hmm - giving unexpected FIQ and then timeouts * 18/ 8/96 - Ran through indent -kr -i8 * Some changes to disc change detect; don't know how well it * works. * 24/ 8/96 - Put all the track buffering code back in from the atari * code - I wonder if it will still work... No :-) * Still works if I turn off track buffering. * 25/ 8/96 - Changed the timer expires that I'd added back to be * jiffies + ....; and it all sprang to life! Got 2.8K/sec * off a cp -r of a 679K disc (showed 94% cpu usage!) * (PC gets 14.3K/sec - 0% CPU!) Hmm - hard drive corrupt! * Also perhaps that compile was with cache off. * changed cli in fd_readtrack_check to cliIF * changed vmallocs to kmalloc (whats the difference!!) * Removed the busy wait loop in do_fd_request and replaced * by a routine on tq_immediate; only 11% cpu on a dd off the * raw disc - but the speed is the same. * 1/ 9/96 - Idea (failed!) - set the 'disable spin-up seqeunce' * when we read the track if we know the motor is on; didn't * help - perhaps we have to do it in stepping as well. * Nope. Still doesn't help. * Hmm - what seems to be happening is that fd_readtrack_check * is never getting called. Its job is to terminate the read * just after we think we should have got the data; otherwise * the fdc takes 1 second to timeout; which is what's happening * Now I can see 'readtrack_timer' being set (which should do the * call); but it never seems to be called - hmm! * OK - I've moved the check to my tq_immediate code - * and it WORKS! 13.95K/second at 19% CPU. * I wish I knew why that timer didn't work..... * * 16/11/96 - Fiddled and frigged for 2.0.18 * * DAG 30/01/99 - Started frobbing for 2.2.1 * DAG 20/06/99 - A little more frobbing: * Included include/asm/uaccess.h for get_user/put_user * * DAG 1/09/00 - Dusted off for 2.4.0-test7 * MAX_SECTORS was name clashing so it is now FD1772_... * Minor parameter, name layouts for 2.4.x differences */#include <linux/sched.h>#include <linux/fs.h>#include <linux/fcntl.h>#include <linux/slab.h>#include <linux/kernel.h>#include <linux/interrupt.h>#include <linux/timer.h>#include <linux/tqueue.h>#include <linux/fd.h>#include <linux/fd1772.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/delay.h>#include <linux/mm.h>#include <asm/arch/oldlatches.h>#include <asm/bitops.h>#include <asm/dma.h>#include <asm/hardware.h>#include <asm/hardware/ioc.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/mach-types.h>#include <asm/pgtable.h>#include <asm/system.h>#include <asm/uaccess.h>#define MAJOR_NR FLOPPY_MAJOR#define FLOPPY_DMA 0#include <linux/blk.h>/* Note: FD_MAX_UNITS could be redefined to 2 for the Atari (with * little additional rework in this file). But I'm not yet sure if * some other code depends on the number of floppies... (It is defined * in a public header!) */#if 0#undef FD_MAX_UNITS#define FD_MAX_UNITS 2#endif/* Ditto worries for Arc - DAG */#define FD_MAX_UNITS 4#define TRACKBUFFER 0/*#define DEBUG*/#ifdef DEBUG#define DPRINT(a) printk a#else#define DPRINT(a)#endif/* Disk types: DD */static struct archy_disk_type { const char *name; unsigned spt; /* sectors per track */ unsigned blocks; /* total number of blocks */ unsigned stretch; /* track doubling ? */} disk_type[] = { { "d360", 9, 720, 0 }, /* 360kB diskette */ { "D360", 9, 720, 1 }, /* 360kb in 720kb drive */ { "D720", 9, 1440, 0 }, /* 720kb diskette (DD) */ /*{ "D820", 10,1640, 0}, *//* DD disk with 82 tracks/10 sectors - DAG - can't see how type detect can distinguish this from 720K until it reads block 4 by which time its too late! */};#define NUM_DISK_TYPES (sizeof(disk_type)/sizeof(*disk_type))/* * Maximum disk size (in kilobytes). This default is used whenever the * current disk size is unknown. */#define MAX_DISK_SIZE 720static int floppy_sizes[256];static int floppy_blocksizes[256];/* current info on each unit */static struct archy_floppy_struct { int connected; /* !=0 : drive is connected */ int autoprobe; /* !=0 : do autoprobe */ struct archy_disk_type *disktype; /* current type of disk */ int track; /* current head position or -1 * if unknown */ unsigned int steprate; /* steprate setting */ unsigned int wpstat; /* current state of WP signal * (for disk change detection) */} unit[FD_MAX_UNITS];/* DAG: On Arc we spin on a flag being cleared by fdc1772_comendhandler which is an assembler routine */extern void fdc1772_comendhandler(void); /* Actually doens't have these parameters - see fd1772.S */extern volatile int fdc1772_comendstatus;extern volatile int fdc1772_fdc_int_done;#define FDC1772BASE ((0x210000>>2)|0x80000000)#define FDC1772_READ(reg) inb(FDC1772BASE+(reg/2))/* DAG: You wouldn't be silly to ask why FDC1772_WRITE is a function rather than the #def below - well simple - the #def won't compile - and I don't understand why (__outwc not defined) *//* NOTE: Reg is 0,2,4,6 as opposed to 0,1,2,3 or 0,4,8,12 to keep compatibility with the ST version of fd1772.h *//*#define FDC1772_WRITE(reg,val) outw(val,(reg+FDC1772BASE)); */void FDC1772_WRITE(int reg, unsigned char val){ if (reg == FDC1772REG_CMD) { DPRINT(("FDC1772_WRITE new command 0x%x @ %d\n", val,jiffies)); if (fdc1772_fdc_int_done) { DPRINT(("FDC1772_WRITE: Hmm fdc1772_fdc_int_done true - resetting\n")); fdc1772_fdc_int_done = 0; }; }; outb(val, (reg / 2) + FDC1772BASE);};#define FD1772_MAX_SECTORS 22unsigned char *DMABuffer; /* buffer for writes *//*static unsigned long PhysDMABuffer; *//* physical address *//* DAG: On Arc we just go straight for the DMA buffer */#define PhysDMABuffer DMABuffer#ifdef TRACKBUFFER unsigned char *TrackBuffer; /* buffer for reads */#define PhysTrackBuffer TrackBuffer /* physical address */static int BufferDrive, BufferSide, BufferTrack;static int read_track; /* non-zero if we are reading whole tracks */ #define SECTOR_BUFFER(sec) (TrackBuffer + ((sec)-1)*512)#define IS_BUFFERED(drive,side,track) \ (BufferDrive == (drive) && BufferSide == (side) && BufferTrack == (track))#endif/* * These are global variables, as that's the easiest way to give * information to interrupts. They are the data used for the current * request. */static int SelectedDrive = 0;static int ReqCmd, ReqBlock;static int ReqSide, ReqTrack, ReqSector, ReqCnt;static int HeadSettleFlag = 0;static unsigned char *ReqData, *ReqBuffer;static int MotorOn = 0, MotorOffTrys;/* Synchronization of FDC1772 access. */static volatile int fdc_busy = 0;static DECLARE_WAIT_QUEUE_HEAD(fdc_wait);/* long req'd for set_bit --RR */static unsigned long changed_floppies = 0xff, fake_change = 0;#define CHECK_CHANGE_DELAY HZ/2/* DAG - increased to 30*HZ - not sure if this is the correct thing to do */#define FD_MOTOR_OFF_DELAY (10*HZ)#define FD_MOTOR_OFF_MAXTRY (10*20)#define FLOPPY_TIMEOUT (6*HZ)#define RECALIBRATE_ERRORS 4 /* After this many errors the drive * will be recalibrated. */#define MAX_ERRORS 8 /* After this many errors the driver * will give up. */#define START_MOTOR_OFF_TIMER(delay) \ do { \ motor_off_timer.expires = jiffies + (delay); \ add_timer( &motor_off_timer ); \ MotorOffTrys = 0; \ } while(0)#define START_CHECK_CHANGE_TIMER(delay) \ do { \ mod_timer(&fd_timer, jiffies + (delay)); \ } while(0)#define START_TIMEOUT() \ do { \ mod_timer(&timeout_timer, jiffies+FLOPPY_TIMEOUT); \ } while(0)#define STOP_TIMEOUT() \ do { \ del_timer( &timeout_timer ); \ } while(0)#define ENABLE_IRQ() enable_irq(FIQ_FD1772+64);#define DISABLE_IRQ() disable_irq(FIQ_FD1772+64);static void fd1772_checkint(void);struct tq_struct fd1772_tq = { 0,0, (void *)fd1772_checkint, 0 };/* * The driver is trying to determine the correct media format * while Probing is set. fd_rwsec_done() clears it after a * successful access. */static int Probing = 0;/* This flag is set when a dummy seek is necesary to make the WP * status bit accessible. */static int NeedSeek = 0;/***************************** Prototypes *****************************/static void fd_select_side(int side);static void fd_select_drive(int drive);static void fd_deselect(void);static void fd_motor_off_timer(unsigned long dummy);static void check_change(unsigned long dummy);static __inline__ void set_head_settle_flag(void);static __inline__ int get_head_settle_flag(void);static void floppy_irqconsequencehandler(void);static void fd_error(void);static void do_fd_action(int drive);static void fd_calibrate(void);static void fd_calibrate_done(int status);static void fd_seek(void);static void fd_seek_done(int status);static void fd_rwsec(void);#ifdef TRACKBUFFER static void fd_readtrack_check( unsigned long dummy ); #endifstatic void fd_rwsec_done(int status);static void fd_times_out(unsigned long dummy);static void finish_fdc(void);static void finish_fdc_done(int dummy);static void floppy_off(unsigned int nr);static __inline__ void copy_buffer(void *from, void *to);static void setup_req_params(int drive);static void redo_fd_request(void);static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param);static void fd_probe(int drive);static int fd_test_drive_present(int drive);static void config_types(void);static int floppy_open(struct inode *inode, struct file *filp);static int floppy_release(struct inode *inode, struct file *filp);/************************* End of Prototypes **************************/static struct timer_list motor_off_timer = { function: fd_motor_off_timer,};#ifdef TRACKBUFFERstatic struct timer_list readtrack_timer = { function: fd_readtrack_check,};#endifstatic struct timer_list timeout_timer = { function: fd_times_out,};static struct timer_list fd_timer = { function: check_change,};/* DAG: Haven't got a clue what this is? */int stdma_islocked(void){ return 0;};/* Select the side to use. */static void fd_select_side(int side){ oldlatch_aupdate(LATCHA_SIDESEL, side ? 0 : LATCHA_SIDESEL);}/* Select a drive, update the FDC1772's track register */static void fd_select_drive(int drive){#ifdef DEBUG printk("fd_select_drive:%d\n", drive);#endif /* Hmm - nowhere do we seem to turn the motor on - I'm going to do it here! */ oldlatch_aupdate(LATCHA_MOTOR | LATCHA_INUSE, 0); if (drive == SelectedDrive) return; oldlatch_aupdate(LATCHA_FDSELALL, 0xf - (1 << drive)); /* restore track register to saved value */ FDC1772_WRITE(FDC1772REG_TRACK, unit[drive].track); udelay(25); SelectedDrive = drive;}/* Deselect both drives. */static void fd_deselect(void){ unsigned long flags; DPRINT(("fd_deselect\n")); oldlatch_aupdate(LATCHA_FDSELALL | LATCHA_MOTOR | LATCHA_INUSE, 0xf | LATCHA_MOTOR | LATCHA_INUSE); SelectedDrive = -1;}/* This timer function deselects the drives when the FDC1772 switched the * motor off. The deselection cannot happen earlier because the FDC1772 * counts the index signals, which arrive only if one drive is selected. */static void fd_motor_off_timer(unsigned long dummy){ unsigned long flags; unsigned char status; int delay; del_timer(&motor_off_timer); if (SelectedDrive < 0) /* no drive selected, needn't deselect anyone */ return; save_flags(flags); cli(); if (fdc_busy) /* was stdma_islocked */ goto retry; status = FDC1772_READ(FDC1772REG_STATUS); if (!(status & 0x80)) { /* * motor already turned off by FDC1772 -> deselect drives * In actual fact its this deselection which turns the motor * off on the Arc, since the motor control is actually on * Latch A */ DPRINT(("fdc1772: deselecting in fd_motor_off_timer\n")); fd_deselect(); MotorOn = 0; restore_flags(flags); return; } /* not yet off, try again */retry: restore_flags(flags); /* Test again later; if tested too often, it seems there is no disk * in the drive and the FDC1772 will leave the motor on forever (or, * at least until a disk is inserted). So we'll test only twice * per second from then on... */ delay = (MotorOffTrys < FD_MOTOR_OFF_MAXTRY) ? (++MotorOffTrys, HZ / 20) : HZ / 2; START_MOTOR_OFF_TIMER(delay);}/* This function is repeatedly called to detect disk changes (as good * as possible) and keep track of the current state of the write protection. */static void check_change(unsigned long dummy){ static int drive = 0; unsigned long flags; int stat; if (fdc_busy) return; /* Don't start poking about if the fdc is busy */ return; /* let's just forget it for the mo DAG */ if (++drive > 1 || !unit[drive].connected) drive = 0; save_flags(flags); cli(); if (!stdma_islocked()) { stat = !!(FDC1772_READ(FDC1772REG_STATUS) & FDC1772STAT_WPROT); /* The idea here is that if the write protect line has changed then the disc must have changed */ if (stat != unit[drive].wpstat) { DPRINT(("wpstat[%d] = %d\n", drive, stat)); unit[drive].wpstat = stat; set_bit(drive, &changed_floppies); } } restore_flags(flags); START_CHECK_CHANGE_TIMER(CHECK_CHANGE_DELAY);}/* Handling of the Head Settling Flag: This flag should be set after each * seek operation, because we don't use seeks with verify. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -