diff options
Diffstat (limited to 'drivers/acorn')
-rw-r--r-- | drivers/acorn/README | 1 | ||||
-rw-r--r-- | drivers/acorn/block/Kconfig | 36 | ||||
-rw-r--r-- | drivers/acorn/block/Makefile | 9 | ||||
-rw-r--r-- | drivers/acorn/block/fd1772.c | 1604 | ||||
-rw-r--r-- | drivers/acorn/block/fd1772dma.S | 100 | ||||
-rw-r--r-- | drivers/acorn/block/mfm.S | 162 | ||||
-rw-r--r-- | drivers/acorn/block/mfmhd.c | 1385 |
7 files changed, 0 insertions, 3297 deletions
diff --git a/drivers/acorn/README b/drivers/acorn/README deleted file mode 100644 index d399c09ca61..00000000000 --- a/drivers/acorn/README +++ /dev/null @@ -1 +0,0 @@ -Drivers for the ACORN "podule" ARM specific bus. diff --git a/drivers/acorn/block/Kconfig b/drivers/acorn/block/Kconfig deleted file mode 100644 index a0ff25ea439..00000000000 --- a/drivers/acorn/block/Kconfig +++ /dev/null @@ -1,36 +0,0 @@ -# -# Block device driver configuration -# - -menu "Acorn-specific block devices" - depends on ARCH_ARC || ARCH_A5K - -config BLK_DEV_FD1772 - tristate "Old Archimedes floppy (1772) support" - depends on ARCH_ARC || ARCH_A5K - help - Support the floppy drive on the Acorn Archimedes (A300, A4x0, A540, - R140 and R260) series of computers; it supports only 720K floppies - at the moment. If you don't have one of these machines just answer - N. - -config BLK_DEV_MFM - tristate "MFM harddisk support" - depends on ARCH_ARC || ARCH_A5K - help - Support the MFM hard drives on the Acorn Archimedes both - on-board the A4x0 motherboards and via the Acorn MFM podules. - Drives up to 64MB are supported. If you haven't got one of these - machines or drives just say N. - -config BLK_DEV_MFM_AUTODETECT - bool "Autodetect hard drive geometry" - depends on BLK_DEV_MFM - help - If you answer Y, the MFM code will attempt to automatically detect - the cylinders/heads/sectors count on your hard drive. WARNING: This - sometimes doesn't work and it also does some dodgy stuff which - potentially might damage your drive. - -endmenu - diff --git a/drivers/acorn/block/Makefile b/drivers/acorn/block/Makefile deleted file mode 100644 index 38a9afe8e03..00000000000 --- a/drivers/acorn/block/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# Makefile for the Acorn block device drivers. -# - -fd1772_mod-objs := fd1772.o fd1772dma.o -mfmhd_mod-objs := mfmhd.o mfm.o - -obj-$(CONFIG_BLK_DEV_FD1772) += fd1772_mod.o -obj-$(CONFIG_BLK_DEV_MFM) += mfmhd_mod.o diff --git a/drivers/acorn/block/fd1772.c b/drivers/acorn/block/fd1772.c deleted file mode 100644 index 423ed08fb6f..00000000000 --- a/drivers/acorn/block/fd1772.c +++ /dev/null @@ -1,1604 +0,0 @@ -/* - * 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 sequence' - * 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/workqueue.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 <linux/bitops.h> - -#include <asm/arch/oldlatches.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> - - -/* 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 - -static struct request_queue *floppy_queue; - -#define MAJOR_NR FLOPPY_MAJOR -#define FLOPPY_DMA 0 -#define DEVICE_NAME "floppy" -#define QUEUE (floppy_queue) -#define CURRENT elv_next_request(floppy_queue) - -/* 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 720 - -static struct gendisk *disks[FD_MAX_UNIT]; - -/* 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 22 - -unsigned 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); - -DECLARE_WORK(fd1772_tq, (void *)fd1772_checkint, NULL); -/* - * 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 necessary 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 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 ); -#endif -static 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 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); -static void do_fd_request(request_queue_t *); - -/************************* End of Prototypes **************************/ - -static DEFINE_TIMER(motor_off_timer, fd_motor_off_timer, 0, 0); - -#ifdef TRACKBUFFER -static DEFINE_TIMER(readtrack_timer, fd_readtrack_check, 0, 0); -#endif - -static DEFINE_TIMER(timeout_timer, fd_times_out, 0, 0); - -static DEFINE_TIMER(fd_timer, check_change, 0, 0); - -/* 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. - */ - -static inline void set_head_settle_flag(void) -{ - HeadSettleFlag = FDC1772CMDADD_E; -} - -static inline int get_head_settle_flag(void) -{ - int tmp = HeadSettleFlag; - HeadSettleFlag = 0; - return (tmp); -} - - - - -/* General Interrupt Handling */ - -static inline void copy_buffer(void *from, void *to) -{ - ulong *p1 = (ulong *) from, *p2 = (ulong *) to; - int cnt; - - for (cnt = 512 / 4; cnt; cnt--) - *p2++ = *p1++; -} - -static void (*FloppyIRQHandler) (int status) = NULL; - -static void floppy_irqconsequencehandler(void) -{ - unsigned char status; - void (*handler) (int); - - fdc1772_fdc_int_done = 0; - - handler = FloppyIRQHandler; - FloppyIRQHandler = NULL; - - if (handler) { - nop(); - status = (unsigned char) fdc1772_comendstatus; - DPRINT(("FDC1772 irq, status = %02x handler = %08lx\n", (unsigned int) status, (unsigned long) handler)); - handler(status); - } else { - DPRINT(("FDC1772 irq, no handler status=%02x\n", fdc1772_comendstatus)); - } - DPRINT(("FDC1772 irq: end of floppy_irq\n")); -} - - -/* Error handling: If some error happened, retry some times, then - * recalibrate, then try again, and fail after MAX_ERRORS. - */ - -static void fd_error(void) -{ - printk("FDC1772: fd_error\n"); - /*panic("fd1772: fd_error"); *//* DAG tmp */ - if (!CURRENT) - return; - CURRENT->errors++; - if (CURRENT->errors >= MAX_ERRORS) { - printk("fd%d: too many errors.\n", SelectedDrive); - end_request(CURRENT, 0); - } else if (CURRENT->errors == RECALIBRATE_ERRORS) { - printk("fd%d: recalibrating\n", SelectedDrive); - if (SelectedDrive != -1) - unit[SelectedDrive].track = -1; - } - redo_fd_request(); -} - - - -#define SET_IRQ_HANDLER(proc) do { FloppyIRQHandler = (proc); } while(0) - - -/* do_fd_action() is the general procedure for a fd request: All - * required parameter settings (drive select, side select, track - * position) are checked and set if needed. For each of these - * parameters and the actual reading or writing exist two functions: - * one that starts the setting (or skips it if possible) and one - * callback for the "done" interrupt. Each done func calls the next - * set function to propagate the request down to fd_rwsec_done(). - */ - -static void do_fd_action(int drive) -{ - struct request *req; - DPRINT(("do_fd_action unit[drive].track=%d\n", unit[drive].track)); - -#ifdef TRACKBUFFER -repeat: - - if (IS_BUFFERED( drive, ReqSide, ReqTrack )) { - req = CURRENT; - if (ReqCmd == READ) { - copy_buffer( SECTOR_BUFFER(ReqSector), ReqData ); - if (++ReqCnt < req->current_nr_sectors) { - /* read next sector */ - setup_req_params( drive ); - goto repeat; - } else { - /* all sectors finished */ - req->nr_sectors -= req->current_nr_sectors; - req->sector += req->current_nr_sectors; - end_request(req, 1); - redo_fd_request(); - return; - } - } else { - /* cmd == WRITE, pay attention to track buffer - * consistency! */ - copy_buffer( ReqData, SECTOR_BUFFER(ReqSector) ); - } - } -#endif - - if (SelectedDrive != drive) { - /*unit[drive].track = -1; DAG */ - fd_select_drive(drive); - }; - - - if (unit[drive].track == -1) - fd_calibrate(); - else if (unit[drive].track != ReqTrack << unit[drive].disktype->stretch) - fd_seek(); - else - fd_rwsec(); -} - - -/* Seek to track 0 if the current track is unknown */ - -static void fd_calibrate(void) -{ - DPRINT(("fd_calibrate\n")); - if (unit[SelectedDrive].track >= 0) { - fd_calibrate_done(0); - return; - } - DPRINT(("fd_calibrate (after track compare)\n")); - SET_IRQ_HANDLER(fd_calibrate_done); - /* we can't verify, since the speed may be incorrect */ - FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_RESTORE | unit[SelectedDrive].steprate); - - NeedSeek = 1; - MotorOn = 1; - START_TIMEOUT(); - /* wait for IRQ */ -} - - -static void fd_calibrate_done(int status) -{ - DPRINT(("fd_calibrate_done()\n")); - STOP_TIMEOUT(); - - /* set the correct speed now */ - if (status & FDC1772STAT_RECNF) { - printk("fd%d: restore failed\n", SelectedDrive); - fd_error(); - } else { - unit[SelectedDrive].track = 0; - fd_seek(); - } -} - - -/* Seek the drive to the requested track. The drive must have been - * calibrated at some point before this. - */ - -static void fd_seek(void) -{ - unsigned long flags; - DPRINT(("fd_seek() to track %d (unit[SelectedDrive].track=%d)\n", ReqTrack, - unit[SelectedDrive].track)); - if (unit[SelectedDrive].track == ReqTrack << - unit[SelectedDrive].disktype->stretch) { - fd_seek_done(0); - return; - } - FDC1772_WRITE(FDC1772REG_DATA, ReqTrack << - unit[SelectedDrive].disktype->stretch); - udelay(25); - save_flags(flags); - clf(); - SET_IRQ_HANDLER(fd_seek_done); - FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK | unit[SelectedDrive].steprate | - /* DAG */ - (MotorOn?FDC1772CMDADD_H:0)); - - restore_flags(flags); - MotorOn = 1; - set_head_settle_flag(); - START_TIMEOUT(); - /* wait for IRQ */ -} - - -static void fd_seek_done(int status) -{ - DPRINT(("fd_seek_done()\n")); - STOP_TIMEOUT(); - - /* set the correct speed */ - if (status & FDC1772STAT_RECNF) { - printk("fd%d: seek error (to track %d)\n", - SelectedDrive, ReqTrack); - /* we don't know exactly which track we are on now! */ - unit[SelectedDrive].track = -1; - fd_error(); - } else { - unit[SelectedDrive].track = ReqTrack << - unit[SelectedDrive].disktype->stretch; - NeedSeek = 0; - fd_rwsec(); - } -} - - -/* This does the actual reading/writing after positioning the head - * over the correct track. - */ - -#ifdef TRACKBUFFER -static int MultReadInProgress = 0; -#endif - - -static void fd_rwsec(void) -{ - unsigned long paddr, flags; - unsigned int rwflag, old_motoron; - unsigned int track; - - DPRINT(("fd_rwsec(), Sec=%d, Access=%c\n", ReqSector, ReqCmd == WRITE ? 'w' : 'r')); - if (ReqCmd == WRITE) { - /*cache_push( (unsigned long)ReqData, 512 ); */ - paddr = (unsigned long) ReqData; - rwflag = 0x100; - } else { - paddr = (unsigned long) PhysDMABuffer; -#ifdef TRACKBUFFER - if (read_track) - paddr = (unsigned long)PhysTrackBuffer; -#endif - rwflag = 0; - } - - DPRINT(("fd_rwsec() before sidesel rwflag=%d sec=%d trk=%d\n", rwflag, - ReqSector, FDC1772_READ(FDC1772REG_TRACK))); - fd_select_side(ReqSide); - - /*DPRINT(("fd_rwsec() before start sector \n")); */ - /* Start sector of this operation */ -#ifdef TRACKBUFFER - FDC1772_WRITE( FDC1772REG_SECTOR, !read_track ? ReqSector : 1 ); -#else - FDC1772_WRITE( FDC1772REG_SECTOR, ReqSector ); -#endif - - /* Cheat for track if stretch != 0 */ - if (unit[SelectedDrive].disktype->stretch) { - track = FDC1772_READ(FDC1772REG_TRACK); - FDC1772_WRITE(FDC1772REG_TRACK, track >> - unit[SelectedDrive].disktype->stretch); - } - udelay(25); - - DPRINT(("fd_rwsec() before setup DMA \n")); - /* Setup DMA - Heavily modified by DAG */ - save_flags(flags); - clf(); - disable_dma(FLOPPY_DMA); - set_dma_mode(FLOPPY_DMA, rwflag ? DMA_MODE_WRITE : DMA_MODE_READ); - set_dma_addr(FLOPPY_DMA, (long) paddr); /* DAG - changed from Atari specific */ -#ifdef TRACKBUFFER - set_dma_count(FLOPPY_DMA,(!read_track ? 1 : unit[SelectedDrive].disktype->spt)*512); -#else - set_dma_count(FLOPPY_DMA, 512); /* Block/sector size - going to have to change */ -#endif - SET_IRQ_HANDLER(fd_rwsec_done); - /* Turn on dma int */ - enable_dma(FLOPPY_DMA); - /* Now give it something to do */ - FDC1772_WRITE(FDC1772REG_CMD, (rwflag ? (FDC1772CMD_WRSEC | FDC1772CMDADD_P) : -#ifdef TRACKBUFFER - (FDC1772CMD_RDSEC | (read_track ? FDC1772CMDADD_M : 0) | - /* Hmm - the idea here is to stop the FDC spinning the disc - up when we know that we already still have it spinning */ - (MotorOn?FDC1772CMDADD_H:0)) -#else - FDC1772CMD_RDSEC -#endif - )); - - restore_flags(flags); - DPRINT(("fd_rwsec() after DMA setup flags=0x%08x\n", flags)); - /*sti(); *//* DAG - Hmm */ - /* Hmm - should do something DAG */ - old_motoron = MotorOn; - MotorOn = 1; - NeedSeek = 1; - - /* wait for interrupt */ - -#ifdef TRACKBUFFER - if (read_track) { - /* - * If reading a whole track, wait about one disk rotation and - * then check if all sectors are read. The FDC will even - * search for the first non-existant sector and need 1 sec to - * recognise that it isn't present :-( - */ - /* 1 rot. + 5 rot.s if motor was off */ - mod_timer(&readtrack_timer, jiffies + HZ/5 + (old_motoron ? 0 : HZ)); - DPRINT(("Setting readtrack_timer to %d @ %d\n", - readtrack_timer.expires,jiffies)); - MultReadInProgress = 1; - } -#endif - - /*DPRINT(("fd_rwsec() before START_TIMEOUT \n")); */ - START_TIMEOUT(); - /*DPRINT(("fd_rwsec() after START_TIMEOUT \n")); */ -} - - -#ifdef TRACKBUFFER - -static void fd_readtrack_check(unsigned long dummy) -{ - unsigned long flags, addr; - extern unsigned char *fdc1772_dataaddr; - - DPRINT(("fd_readtrack_check @ %d\n",jiffies)); - - save_flags(flags); - clf(); - - del_timer( &readtrack_timer ); - - if (!MultReadInProgress) { - /* This prevents a race condition that could arise if the - * interrupt is triggered while the calling of this timer - * callback function takes place. The IRQ function then has - * already cleared 'MultReadInProgress' when control flow - * gets here. - */ - restore_flags(flags); - return; - } - - /* get the current DMA address */ - addr=(unsigned long)fdc1772_dataaddr; /* DAG - ? */ - DPRINT(("fd_readtrack_check: addr=%x PhysTrackBuffer=%x\n",addr,PhysTrackBuffer)); - - if (addr >= (unsigned int)PhysTrackBuffer + unit[SelectedDrive].disktype->spt*512) { - /* already read enough data, force an FDC interrupt to stop - * the read operation - */ - SET_IRQ_HANDLER( NULL ); - restore_flags(flags); - DPRINT(("fd_readtrack_check(): done\n")); - FDC1772_WRITE( FDC1772REG_CMD, FDC1772CMD_FORCI ); - udelay(25); - - /* No error until now -- the FDC would have interrupted - * otherwise! - */ - fd_rwsec_done( 0 ); - } else { - /* not yet finished, wait another tenth rotation */ - restore_flags(flags); - DPRINT(("fd_readtrack_check(): not yet finished\n")); - readtrack_timer.expires = jiffies + HZ/5/10; - add_timer( &readtrack_timer ); - } -} - -#endif - -static void fd_rwsec_done(int status) -{ - unsigned int track; - - DPRINT(("fd_rwsec_done() status=%d @ %d\n", status,jiffies)); - -#ifdef TRACKBUFFER - if (read_track && !MultReadInProgress) - return; - - MultReadInProgress = 0; - - STOP_TIMEOUT(); - - if (read_track) - del_timer( &readtrack_timer ); -#endif - - - /* Correct the track if stretch != 0 */ - if (unit[SelectedDrive].disktype->stretch) { - track = FDC1772_READ(FDC1772REG_TRACK); - FDC1772_WRITE(FDC1772REG_TRACK, track << - unit[SelectedDrive].disktype->stretch); - } - if (ReqCmd == WRITE && (status & FDC1772STAT_WPROT)) { - printk("fd%d: is write protected\n", SelectedDrive); - goto err_end; - } - if ((status & FDC1772STAT_RECNF) -#ifdef TRACKBUFFER - /* RECNF is no error after a multiple read when the FDC - * searched for a non-existant sector! - */ - && !(read_track && - FDC1772_READ(FDC1772REG_SECTOR) > unit[SelectedDrive].disktype->spt) -#endif - ) { - if (Probing) { - if (unit[SelectedDrive].disktype > disk_type) { - /* try another disk type */ - unit[SelectedDrive].disktype--; - set_capacity(disks[SelectedDrive], - unit[SelectedDrive].disktype->blocks); - } else - Probing = 0; - } else { - /* record not found, but not probing. Maybe stretch wrong ? Restart probing */ - if (unit[SelectedDrive].autoprobe) { - unit[SelectedDrive].disktype = disk_type + NUM_DISK_TYPES - 1; - set_capacity(disks[SelectedDrive], - unit[SelectedDrive].disktype->blocks); - Probing = 1; - } - } - if (Probing) { - setup_req_params(SelectedDrive); -#ifdef TRACKBUFFER - BufferDrive = -1; -#endif - do_fd_action(SelectedDrive); - return; - } - printk("fd%d: sector %d not found (side %d, track %d)\n", - SelectedDrive, FDC1772_READ(FDC1772REG_SECTOR), ReqSide, ReqTrack); - goto err_end; - } - if (status & FDC1772STAT_CRC) { - printk("fd%d: CRC error (side %d, track %d, sector %d)\n", - SelectedDrive, ReqSide, ReqTrack, FDC1772_READ(FDC1772REG_SECTOR)); - goto err_end; - } - if (status & FDC1772STAT_LOST) { - printk("fd%d: lost data (side %d, track %d, sector %d)\n", - SelectedDrive, ReqSide, ReqTrack, FDC1772_READ(FDC1772REG_SECTOR)); - goto err_end; - } - Probing = 0; - - if (ReqCmd == READ) { -#ifdef TRACKBUFFER - if (!read_track) { - /*cache_clear (PhysDMABuffer, 512);*/ - copy_buffer (DMABuffer, ReqData); - } else { - /*cache_clear (PhysTrackBuffer, FD1772_MAX_SECTORS * 512);*/ - BufferDrive = SelectedDrive; - BufferSide = ReqSide; - BufferTrack = ReqTrack; - copy_buffer (SECTOR_BUFFER (ReqSector), ReqData); - } -#else - /*cache_clear( PhysDMABuffer, 512 ); */ - copy_buffer(DMABuffer, ReqData); -#endif - } - if (++ReqCnt < CURRENT->current_nr_sectors) { - /* read next sector */ - setup_req_params(SelectedDrive); - do_fd_action(SelectedDrive); - } else { - /* all sectors finished */ - CURRENT->nr_sectors -= CURRENT->current_nr_sectors; - CURRENT->sector += CURRENT->current_nr_sectors; - end_request(CURRENT, 1); - redo_fd_request(); - } - return; - -err_end: -#ifdef TRACKBUFFER - BufferDrive = -1; -#endif - - fd_error(); -} - - -static void fd_times_out(unsigned long dummy) -{ - SET_IRQ_HANDLER(NULL); - /* If the timeout occurred while the readtrack_check timer was - * active, we need to cancel it, else bad things will happen */ - del_timer( &readtrack_timer ); - FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI); - udelay(25); - - printk("floppy timeout\n"); - STOP_TIMEOUT(); /* hmm - should we do this ? */ - fd_error(); -} - - -/* The (noop) seek operation here is needed to make the WP bit in the - * FDC1772 status register accessible for check_change. If the last disk - * operation would have been a RDSEC, this bit would always read as 0 - * no matter what :-( To save time, the seek goes to the track we're - * already on. - */ - -static void finish_fdc(void) -{ - /* DAG - just try without this dummy seek! */ - finish_fdc_done(0); - return; - - if (!NeedSeek) { - finish_fdc_done(0); - } else { - DPRINT(("finish_fdc: dummy seek started\n")); - FDC1772_WRITE(FDC1772REG_DATA, unit[SelectedDrive].track); - SET_IRQ_HANDLER(finish_fdc_done); - FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK); - MotorOn = 1; - START_TIMEOUT(); - /* we must wait for the IRQ here, because the ST-DMA is - * released immediately afterwards and the interrupt may be - * delivered to the wrong driver. - */ - } -} - - -static void finish_fdc_done(int dummy) -{ - unsigned long flags; - - DPRINT(("finish_fdc_done entered\n")); - STOP_TIMEOUT(); - NeedSeek = 0; - - if (timer_pending(&fd_timer) && - time_after(jiffies + 5, fd_timer.expires)) - /* If the check for a disk change is done too early after this - * last seek command, the WP bit still reads wrong :-(( - */ - mod_timer(&fd_timer, jiffies + 5); - else { - /* START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY ); */ - }; - del_timer(&motor_off_timer); - START_MOTOR_OFF_TIMER(FD_MOTOR_OFF_DELAY); - - save_flags(flags); - cli(); - /* stdma_release(); - not sure if I should do something DAG */ - fdc_busy = 0; - wake_up(&fdc_wait); - restore_flags(flags); - - DPRINT(("finish_fdc() finished\n")); -} - - -/* Prevent "aliased" accesses. */ -static int fd_ref[4]; -static int fd_device[4]; - -/* dummy for blk.h */ -static void floppy_off(unsigned int nr) -{ -} - - -/* On the old arcs write protect depends on the particular model - of machine. On the A310, R140, and A440 there is a disc changed - detect, however on the A4x0/1 range there is not. There - is nothing to tell you which machine your on. - At the moment I'm just marking changed always. I've - left the Atari's 'change on write protect change' code in this - part (but nothing sets it). - RiscOS apparently checks the disc serial number etc. to detect changes - - but if it sees a disc change line go high (?) it flips to using - it. Well maybe I'll add that in the future (!?) -*/ -static int check_floppy_change(struct gendisk *disk) -{ - struct archy_floppy_struct *p = disk->private_data; - unsigned int drive = p - unit; - - if (test_bit(drive, &fake_change)) { - /* simulated change (e.g. after formatting) */ - return 1; - } - if (test_bit(drive, &changed_floppies)) { - /* surely changed (the WP signal changed at least once) */ - return 1; - } - if (p->wpstat) { - /* WP is on -> could be changed: to be sure, buffers should be - * invalidated... - */ - return 1; - } - return 1; /* DAG - was 0 */ -} - -static int floppy_revalidate(struct gendisk *disk) -{ - struct archy_floppy_struct *p = disk->private_data; - unsigned int drive = p - unit; - - if (test_bit(drive, &changed_floppies) || test_bit(drive, &fake_change) - || unit[drive].disktype == 0) { -#ifdef TRACKBUFFER - BufferDrive = -1; -#endif - clear_bit(drive, &fake_change); - clear_bit(drive, &changed_floppies); - p->disktype = 0; - } - return 0; -} - -/* This sets up the global variables describing the current request. */ - -static void setup_req_params(int drive) -{ - int block = ReqBlock + ReqCnt; - - ReqTrack = block / unit[drive].disktype->spt; - ReqSector = block - ReqTrack * unit[drive].disktype->spt + 1; - ReqSide = ReqTrack & 1; - ReqTrack >>= 1; - ReqData = ReqBuffer + 512 * ReqCnt; - -#ifdef TRACKBUFFER - read_track = (ReqCmd == READ && CURRENT->errors == 0); -#endif - - DPRINT(("Request params: Si=%d Tr=%d Se=%d Data=%08lx\n", ReqSide, - ReqTrack, ReqSector, (unsigned long) ReqData)); -} - - -static void redo_fd_request(void) -{ - int drive, type; - struct archy_floppy_struct *floppy; - - DPRINT(("redo_fd_request: CURRENT=%p dev=%s CURRENT->sector=%ld\n", - CURRENT, CURRENT ? CURRENT->rq_disk->disk_name : "", - CURRENT ? CURRENT->sector : 0)); - -repeat: - - if (!CURRENT) - goto the_end; - - floppy = CURRENT->rq_disk->private_data; - drive = floppy - unit; - type = fd_device[drive]; - - if (!floppy->connected) { - /* drive not connected */ - printk("Unknown Device: fd%d\n", drive); - end_request(CURRENT, 0); - goto repeat; - } - if (type == 0) { - if (!floppy->disktype) { - Probing = 1; - floppy->disktype = disk_type + NUM_DISK_TYPES - 1; - set_capacity(disks[drive], floppy->disktype->blocks); - floppy->autoprobe = 1; - } - } else { - /* user supplied disk type */ - --type; - if (type >= NUM_DISK_TYPES) { - printk("fd%d: invalid disk format", drive); - end_request(CURRENT, 0); - goto repeat; - } - floppy->disktype = &disk_type[type]; - set_capacity(disks[drive], floppy->disktype->blocks); - floppy->autoprobe = 0; - } - - if (CURRENT->sector + 1 > floppy->disktype->blocks) { - end_request(CURRENT, 0); - goto repeat; - } - /* stop deselect timer */ - del_timer(&motor_off_timer); - - ReqCnt = 0; - ReqCmd = rq_data_dir(CURRENT); - ReqBlock = CURRENT->sector; - ReqBuffer = CURRENT->buffer; - setup_req_params(drive); - do_fd_action(drive); - - return; - -the_end: - finish_fdc(); -} - -static void fd1772_checkint(void) -{ - extern int fdc1772_bytestogo; - - /*printk("fd1772_checkint %d\n",fdc1772_fdc_int_done);*/ - if (fdc1772_fdc_int_done) - floppy_irqconsequencehandler(); - if ((MultReadInProgress) && (fdc1772_bytestogo==0)) fd_readtrack_check(0); - if (fdc_busy) { - schedule_work(&fd1772_tq); - } -} - -static void do_fd_request(request_queue_t* q) -{ - unsigned long flags; - - DPRINT(("do_fd_request for pid %d\n", current->pid)); - if (fdc_busy) return; - save_flags(flags); - cli(); - wait_event(fdc_wait, !fdc_busy); - fdc_busy = 1; - ENABLE_IRQ(); - restore_flags(flags); - - fdc1772_fdc_int_done = 0; - - redo_fd_request(); - - schedule_work(&fd1772_tq); -} - - -static int invalidate_drive(struct block_device *bdev) -{ - struct archy_floppy_struct *p = bdev->bd_disk->private_data; - /* invalidate the buffer track to force a reread */ -#ifdef TRACKBUFFER - BufferDrive = -1; -#endif - - set_bit(p - unit, &fake_change); - return 0; -} - -static int fd_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long param) -{ - struct block_device *bdev = inode->i_bdev; - - switch (cmd) { - case FDFMTEND: - case FDFLUSH: - invalidate_drive(bdev); - check_disk_change(bdev); - case FDFMTBEG: - return 0; - default: - return -EINVAL; - } -} - - -/* Initialize the 'unit' variable for drive 'drive' */ - -static void fd_probe(int drive) -{ - unit[drive].connected = 0; - unit[drive].disktype = NULL; - - if (!fd_test_drive_present(drive)) - return; - - unit[drive].connected = 1; - unit[drive].track = -1; /* If we put the auto detect back in this can go to 0 */ - unit[drive].steprate = FDC1772STEP_6; - MotorOn = 1; /* from probe restore operation! */ -} - - -/* This function tests the physical presence of a floppy drive (not - * whether a disk is inserted). This is done by issuing a restore - * command, waiting max. 2 seconds (that should be enough to move the - * head across the whole disk) and looking at the state of the "TR00" - * signal. This should now be raised if there is a drive connected - * (and there is no hardware failure :-) Otherwise, the drive is - * declared absent. - */ - -static int fd_test_drive_present(int drive) -{ - unsigned long timeout; - unsigned char status; - int ok; - - printk("fd_test_drive_present %d\n", drive); - if (drive > 1) - return (0); - return (1); /* Simple hack for the moment - the autodetect doesn't seem to work on arc */ - fd_select_drive(drive); - - /* disable interrupt temporarily */ - DISABLE_IRQ(); - FDC1772_WRITE(FDC1772REG_TRACK, 0x00); /* was ff00 why? */ - FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_RESTORE | FDC1772CMDADD_H | FDC1772STEP_6); - - /*printk("fd_test_drive_present: Going into timeout loop\n"); */ - for (ok = 0, timeout = jiffies + 2 * HZ + HZ / 2; time_before(jiffies, timeout);) { - /* What does this piece of atariism do? - query for an interrupt? */ - /* if (!(mfp.par_dt_reg & 0x20)) - break; */ - /* Well this is my nearest guess - quit when we get an FDC interrupt */ - if (ioc_readb(IOC_FIQSTAT) & 2) - break; - } - - /*printk("fd_test_drive_present: Coming out of timeout loop\n"); */ - status = FDC1772_READ(FDC1772REG_STATUS); - ok = (status & FDC1772STAT_TR00) != 0; - - /*printk("fd_test_drive_present: ok=%d\n",ok); */ - /* force interrupt to abort restore operation (FDC1772 would try - * about 50 seconds!) */ - FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI); - udelay(500); - status = FDC1772_READ(FDC1772REG_STATUS); - udelay(20); - /*printk("fd_test_drive_present: just before OK code %d\n",ok); */ - - if (ok) { - /* dummy seek command to make WP bit accessible */ - FDC1772_WRITE(FDC1772REG_DATA, 0); - FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK); - printk("fd_test_drive_present: just before wait for int\n"); - /* DAG: Guess means wait for interrupt */ - while (!(ioc_readb(IOC_FIQSTAT) & 2)); - printk("fd_test_drive_present: just after wait for int\n"); - status = FDC1772_READ(FDC1772REG_STATUS); - } - printk("fd_test_drive_present: just before ENABLE_IRQ\n"); - ENABLE_IRQ(); - printk("fd_test_drive_present: about to return\n"); - return (ok); -} - - -/* Look how many and which kind of drives are connected. If there are - * floppies, additionally start the disk-change and motor-off timers. - */ - -static void config_types(void) -{ - int drive, cnt = 0; - - printk("Probing floppy drive(s):\n"); - for (drive = 0; drive < FD_MAX_UNITS; drive++) { - fd_probe(drive); - if (unit[drive].connected) { - printk("fd%d\n", drive); - ++cnt; - } - } - - if (FDC1772_READ(FDC1772REG_STATUS) & FDC1772STAT_BUSY) { - /* If FDC1772 is still busy from probing, give it another FORCI - * command to abort the operation. If this isn't done, the FDC1772 - * will interrupt later and its IRQ line stays low, because - * the status register isn't read. And this will block any - * interrupts on this IRQ line :-( - */ - FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI); - udelay(500); - FDC1772_READ(FDC1772REG_STATUS); - udelay(20); - } - if (cnt > 0) { - START_MOTOR_OFF_TIMER(FD_MOTOR_OFF_DELAY); - if (cnt == 1) - fd_select_drive(0); - /*START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY ); */ - } -} - -/* - * floppy_open check for aliasing (/dev/fd0 can be the same as - * /dev/PS0 etc), and disallows simultaneous access to the same - * drive with different device numbers. - */ - -static int floppy_open(struct inode *inode, struct file *filp) -{ - int drive = iminor(inode) & 3; - int type = iminor(inode) >> 2; - int old_dev = fd_device[drive]; - - if (fd_ref[drive] && old_dev != type) - return -EBUSY; - - if (fd_ref[drive] == -1 || (fd_ref[drive] && filp->f_flags & O_EXCL)) - return -EBUSY; - - if (filp->f_flags & O_EXCL) - fd_ref[drive] = -1; - else - fd_ref[drive]++; - - fd_device[drive] = type; - - if (filp->f_flags & O_NDELAY) - return 0; - - if (filp->f_mode & 3) { - check_disk_change(inode->i_bdev); - if (filp->f_mode & 2) { - if (unit[drive].wpstat) { - floppy_release(inode, filp); - return -EROFS; - } - } - } - return 0; -} - - -static int floppy_release(struct inode *inode, struct file *filp) -{ - int drive = iminor(inode) & 3; - - if (fd_ref[drive] < 0) - fd_ref[drive] = 0; - else if (!fd_ref[drive]--) { - printk("floppy_release with fd_ref == 0"); - fd_ref[drive] = 0; - } - - return 0; -} - -static struct block_device_operations floppy_fops = -{ - .open = floppy_open, - .release = floppy_release, - .ioctl = fd_ioctl, - .media_changed = check_floppy_change, - .revalidate_disk= floppy_revalidate, -}; - -static struct kobject *floppy_find(dev_t dev, int *part, void *data) -{ - int drive = *part & 3; - if ((*part >> 2) > NUM_DISK_TYPES || drive >= FD_MAX_UNITS) - return NULL; - *part = 0; - return get_disk(disks[drive]); -} - -int fd1772_init(void) -{ - static DEFINE_SPINLOCK(lock); - int i, err = -ENOMEM; - - if (!machine_is_archimedes()) - return 0; - - for (i = 0; i < FD_MAX_UNITS; i++) { - disks[i] = alloc_disk(1); - if (!disks[i]) - goto err_disk; - } - - err = register_blkdev(MAJOR_NR, "fd"); - if (err) - goto err_disk; - - err = -EBUSY; - if (request_dma(FLOPPY_DMA, "fd1772")) { - printk("Unable to grab DMA%d for the floppy (1772) driver\n", FLOPPY_DMA); - goto err_blkdev; - }; - - if (request_dma(FIQ_FD1772, "fd1772 end")) { - printk("Unable to grab DMA%d for the floppy (1772) driver\n", FIQ_FD1772); - goto err_dma1; - }; - - /* initialize variables */ - SelectedDrive = -1; -#ifdef TRACKBUFFER - BufferDrive = BufferSide = BufferTrack = -1; - /* Atari uses 512 - I want to eventually cope with 1K sectors */ - DMABuffer = kmalloc((FD1772_MAX_SECTORS+1)*512,GFP_KERNEL); - TrackBuffer = DMABuffer + 512; -#else - /* Allocate memory for the DMAbuffer - on the Atari this takes it - out of some special memory... */ - DMABuffer = kmalloc(2048); /* Copes with pretty large sectors */ -#endif - err = -ENOMEM; - if (!DMAbuffer) - goto err_dma2; - - enable_dma(FIQ_FD1772); /* This inserts a call to our command end routine */ - - floppy_queue = blk_init_queue(do_fd_request, &lock); - if (!floppy_queue) - goto err_queue; - - for (i = 0; i < FD_MAX_UNITS; i++) { - unit[i].track = -1; - disks[i]->major = MAJOR_NR; - disks[i]->first_minor = 0; - disks[i]->fops = &floppy_fops; - sprintf(disks[i]->disk_name, "fd%d", i); - disks[i]->private_data = &unit[i]; - disks[i]->queue = floppy_queue; - set_capacity(disks[i], MAX_DISK_SIZE * 2); - } - blk_register_region(MKDEV(MAJOR_NR, 0), 256, THIS_MODULE, - floppy_find, NULL, NULL); - - for (i = 0; i < FD_MAX_UNITS; i++) - add_disk(disks[i]); - - config_types(); - - return 0; - - err_queue: - kfree(DMAbuffer); - err_dma2: - free_dma(FIQ_FD1772); - - err_dma1: - free_dma(FLOPPY_DMA); - - err_blkdev: - unregister_blkdev(MAJOR_NR, "fd"); - - err_disk: - while (i--) - put_disk(disks[i]); - return err; -} diff --git a/drivers/acorn/block/fd1772dma.S b/drivers/acorn/block/fd1772dma.S deleted file mode 100644 index 7964435443e..00000000000 --- a/drivers/acorn/block/fd1772dma.S +++ /dev/null @@ -1,100 +0,0 @@ -#include <asm/hardware.h> - -@ Code for DMA with the 1772 fdc -.text - - - .global fdc1772_dataaddr -fdc1772_fiqdata: -@ Number of bytes left to DMA - .global fdc1772_bytestogo -fdc1772_bytestogo: - .word 0 -@ Place to put/get data from in DMA - .global fdc1772_dataaddr -fdc1772_dataaddr: - .word 0 - - .global fdc1772_fdc_int_done -fdc1772_fdc_int_done: - .word 0 - .global fdc1772_comendstatus -fdc1772_comendstatus: - .word 0 - -@ We hang this off DMA channel 1 - .global fdc1772_comendhandler -fdc1772_comendhandler: - mov r8,#IOC_BASE - ldrb r9,[r8,#0x34] @ IOC FIQ status - tst r9,#2 - subeqs pc,r14,#4 @ should I leave a space here - orr r9,r8,#0x10000 @ FDC base - adr r8,fdc1772_fdc_int_done - ldrb r10,[r9,#0] @ FDC status - mov r9,#1 @ Got a FIQ flag - stmia r8,{r9,r10} - subs pc,r14,#4 - - - .global fdc1772_dma_read -fdc1772_dma_read: - mov r8,#IOC_BASE - ldrb r9,[r8,#0x34] @ IOC FIQ status - tst r9,#1 - beq fdc1772_dma_read_notours - orr r8,r8,#0x10000 @ FDC base - ldrb r10,[r8,#0xc] @ Read from FDC data reg (also clears interrupt) - ldmia r11,{r8,r9} - subs r8,r8,#1 @ One less byte to go - @ If there was somewhere for this data to go then store it and update pointers - strplb r10,[r9],#1 @ Store the data and increment the pointer - stmplia r11,{r8,r9} @ Update count/pointers - @ Handle any other interrupts if there are any -fdc1772_dma_read_notours: - @ Cant branch because this code has been copied down to the FIQ vector - ldr pc,[pc,#-4] - .word fdc1772_comendhandler - .global fdc1772_dma_read_end -fdc1772_dma_read_end: - - .global fdc1772_dma_write -fdc1772_dma_write: - mov r8,#IOC_BASE - ldrb r9,[r8,#0x34] @ IOC FIQ status - tst r9,#1 - beq fdc1772_dma_write_notours - orr r8,r8,#0x10000 @ FDC base - ldmia r11,{r9,r10} - subs r9,r9,#1 @ One less byte to go - @ If there really is some data then get it, store it and update count - ldrplb r12,[r10],#1 - strplb r12,[r8,#0xc] @ write it to FDC data reg - stmplia r11,{r9,r10} @ Update count and pointer - should clear interrupt - @ Handle any other interrupts -fdc1772_dma_write_notours: - @ Cant branch because this code has been copied down to the FIQ vector - ldr pc,[pc,#-4] - .word fdc1772_comendhandler - - .global fdc1772_dma_write_end -fdc1772_dma_write_end: - - -@ Setup the FIQ R11 to point to the data and store the count, address -@ for this dma -@ R0=count -@ R1=address - .global fdc1772_setupdma -fdc1772_setupdma: - @ The big job is flipping in and out of FIQ mode - adr r2,fdc1772_fiqdata @ This is what we really came here for - stmia r2,{r0,r1} - mov r3, pc - teqp pc,#0x0c000001 @ Disable FIQs, IRQs and switch to FIQ mode - mov r0,r0 @ NOP - mov r11,r2 - teqp r3,#0 @ Normal mode - mov r0,r0 @ NOP - mov pc,r14 - diff --git a/drivers/acorn/block/mfm.S b/drivers/acorn/block/mfm.S deleted file mode 100644 index c90cbd41ce2..00000000000 --- a/drivers/acorn/block/mfm.S +++ /dev/null @@ -1,162 +0,0 @@ -@ Read/Write DMA code for the ST506/MFM hard drive controllers on the A400 Acorn Archimedes -@ motherboard and on ST506 expansion podules. -@ (c) David Alan Gilbert (linux@treblig.org) 1996-1999 - -#include <asm/assembler.h> - -hdc63463_irqdata: -@ Controller base address - .global hdc63463_baseaddress -hdc63463_baseaddress: - .word 0 - - .global hdc63463_irqpolladdress -hdc63463_irqpolladdress: - .word 0 - - .global hdc63463_irqpollmask -hdc63463_irqpollmask: - .word 0 - -@ where to read/write data from the kernel data space - .global hdc63463_dataptr -hdc63463_dataptr: - .word 0 - -@ Number of bytes left to transfer - .global hdc63463_dataleft -hdc63463_dataleft: - .word 0 - -@ ------------------------------------------------------------------------- -@ hdc63463_writedma: DMA from host to controller -@ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask -@ r3=data ptr, r4=data left, r5,r6=temporary - .global hdc63463_writedma -hdc63463_writedma: - stmfd sp!,{r4-r7} - adr r5,hdc63463_irqdata - ldmia r5,{r0,r1,r2,r3,r4} - -writedma_again: - - @ test number of remaining bytes to transfer - cmp r4,#0 - beq writedma_end - bmi writedma_end - - @ Check the hdc is interrupting - ldrb r5,[r1,#0] - tst r5,r2 - beq writedma_end - - @ Transfer a block of upto 256 bytes - cmp r4,#256 - movlt r7,r4 - movge r7,#256 - - @ Check the hdc is still busy and command has not ended and no errors - ldr r5,[r0,#32] @ Status reg - 16 bit - its the top few bits which are status - @ think we should continue DMA until it drops busy - perhaps this was - @ the main problem with corrected errors causing a hang - @tst r5,#0x3c00 @ Test for things which should be off - @bne writedma_end - and r5,r5,#0x8000 @ This is test for things which should be on: Busy - cmp r5,#0x8000 - bne writedma_end - - @ Bytes remaining at end - sub r4,r4,r7 - - @ HDC Write register location - add r0,r0,#32+8 - -writedma_loop: - @ OK - pretty sure we should be doing this - - ldr r5,[r3],#4 @ Get a word to be written - @ get bottom half to be sent first - mov r6,r5,lsl#16 @ Separate the first 2 bytes - orr r2,r6,r6,lsr #16 @ Duplicate them in the bottom half of the word - @ now the top half - mov r6,r5,lsr#16 @ Get 2nd 2 bytes - orr r6,r6,r6,lsl#16 @ Duplicate - @str r6,[r0] @ to hdc - stmia r0,{r2,r6} - subs r7,r7,#4 @ Dec. number of bytes left - bne writedma_loop - - @ If we were too slow we had better go through again - DAG - took out with new interrupt routine - @ sub r0,r0,#32+8 - @ adr r2,hdc63463_irqdata - @ ldr r2,[r2,#8] - @ b writedma_again - -writedma_end: - adr r5,hdc63463_irqdata+12 - stmia r5,{r3,r4} - ldmfd sp!,{r4-r7} - RETINSTR(mov,pc,lr) - -@ ------------------------------------------------------------------------- -@ hdc63463_readdma: DMA from controller to host -@ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask -@ r3=data ptr, r4=data left, r5,r6=temporary - .global hdc63463_readdma -hdc63463_readdma: - stmfd sp!,{r4-r7} - adr r5,hdc63463_irqdata - ldmia r5,{r0,r1,r2,r3,r4} - -readdma_again: - @ test number of remaining bytes to transfer - cmp r4,#0 - beq readdma_end - bmi readdma_end - - @ Check the hdc is interrupting - ldrb r5,[r1,#0] - tst r5,r2 - beq readdma_end - - @ Check the hdc is still busy and command has not ended and no errors - ldr r5,[r0,#32] @ Status reg - 16 bit - its the top few bits which are status - @ think we should continue DMA until it drops busy - perhaps this was - @ the main problem with corrected errors causing a hang - @tst r5,#0x3c00 @ Test for things which should be off - @bne readdma_end - and r5,r5,#0x8000 @ This is test for things which should be on: Busy - cmp r5,#0x8000 - bne readdma_end - - @ Transfer a block of upto 256 bytes - cmp r4,#256 - movlt r7,r4 - movge r7,#256 - - @ Bytes remaining at end - sub r4,r4,r7 - - @ Set a pointer to the data register in the HDC - add r0,r0,#8 -readdma_loop: - @ OK - pretty sure we should be doing this - ldmia r0,{r5,r6} - mov r5,r5,lsl#16 - mov r6,r6,lsl#16 - orr r6,r6,r5,lsr #16 - str r6,[r3],#4 - subs r7,r7,#4 @ Decrement bytes to go - bne readdma_loop - - @ Try reading multiple blocks - if this was fast enough then I do not think - @ this should help - NO taken out DAG - new interrupt handler has - @ non-consecutive memory blocks - @ sub r0,r0,#8 - @ b readdma_again - -readdma_end: - adr r5,hdc63463_irqdata+12 - stmia r5,{r3,r4} - ldmfd sp!,{r4-r7} - RETINSTR(mov,pc,lr) diff --git a/drivers/acorn/block/mfmhd.c b/drivers/acorn/block/mfmhd.c deleted file mode 100644 index d85520f78e6..00000000000 --- a/drivers/acorn/block/mfmhd.c +++ /dev/null @@ -1,1385 +0,0 @@ -/* - * linux/drivers/acorn/block/mfmhd.c - * - * Copyright (C) 1995, 1996 Russell King, Dave Alan Gilbert (gilbertd@cs.man.ac.uk) - * - * MFM hard drive code [experimental] - */ - -/* - * Change list: - * - * 3/2/96:DAG: Started a change list :-) - * Set the hardsect_size pointers up since we are running 256 byte - * sectors - * Added DMA code, put it into the rw_intr - * Moved RCAL out of generic interrupt code - don't want to do it - * while DMA'ing - its now in individual handlers. - * Took interrupt handlers off task queue lists and called - * directly - not sure of implications. - * - * 18/2/96:DAG: Well its reading OK I think, well enough for image file code - * to find the image file; but now I've discovered that I actually - * have to put some code in for image files. - * - * Added stuff for image files; seems to work, but I've not - * got a multisegment image file (I don't think!). - * Put in a hack (yep a real hack) for multiple cylinder reads. - * Not convinced its working. - * - * 5/4/96:DAG: Added asm/hardware.h and use IOC_ macros - * Rewrote dma code in mfm.S (again!) - now takes a word at a time - * from main RAM for speed; still doesn't feel speedy! - * - * 20/4/96:DAG: After rewriting mfm.S a heck of a lot of times and speeding - * things up, I've finally figured out why its so damn slow. - * Linux is only reading a block at a time, and so you never - * get more than 1K per disc revoloution ~=60K/second. - * - * 27/4/96:DAG: On Russell's advice I change ll_rw_blk.c to ask it to - * join adjacent blocks together. Everything falls flat on its - * face. - * Four hours of debugging later; I hadn't realised that - * ll_rw_blk would be so generous as to join blocks whose - * results aren't going into consecutive buffers. - * - * OK; severe rehacking of mfm_rw_interrupt; now end_request's - * as soon as its DMA'd each request. Odd thing is that - * we are sometimes getting interrupts where we are not transferring - * any data; why? Is that what happens when you miss? I doubt - * it; are we too fast? No - its just at command ends. Got 240K/s - * better than before, but RiscOS hits 480K/s - * - * 25/6/96:RMK: Fixed init code to allow the MFM podule to work. Increased the - * number of errors for my Miniscribe drive (8425). - * - * 30/6/96:DAG: Russell suggested that a check drive 0 might turn the LEDs off - * - so in request_done just before it clears Busy it sends a - * check drive 0 - and the LEDs go off!!!! - * - * Added test for mainboard controller. - Removes need for separate - * define. - * - * 13/7/96:DAG: Changed hardware sectore size to 512 in attempt to make - * IM drivers work. - * 21/7/96:DAG: Took out old image file stuff (accessing it now produces an IO - * error.) - * - * 17/8/96:DAG: Ran through indent -kr -i8; evil - all my nice 2 character indents - * gone :-( Hand modified afterwards. - * Took out last remains of the older image map system. - * - * 22/9/96:DAG: Changed mfm.S so it will carry on DMA'ing til; BSY is dropped - * Changed mfm_rw_intr so that it doesn't follow the error - * code until BSY is dropped. Nope - still broke. Problem - * may revolve around when it reads the results for the error - * number? - * - *16/11/96:DAG: Modified for 2.0.18; request_irq changed - * - *17/12/96:RMK: Various cleanups, reorganisation, and the changes for new IO system. - * Improved probe for onboard MFM chip - it was hanging on my A5k. - * Added autodetect CHS code such that we don't rely on the presence - * of an ADFS boot block. Added ioport resource manager calls so - * that we don't clash with already-running hardware (eg. RiscPC Ether - * card slots if someone tries this)! - * - * 17/1/97:RMK: Upgraded to 2.1 kernels. - * - * 4/3/98:RMK: Changed major number to 21. - * - * 27/6/98:RMK: Changed asm/delay.h to linux/delay.h for mdelay(). - */ - -/* - * Possible enhancements: - * Multi-thread the code so that it is possible that while one drive - * is seeking, the other one can be reading data/seeking as well. - * This would be a performance boost with dual drive systems. - */ - -#include <linux/module.h> -#include <linux/fs.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/timer.h> -#include <linux/mm.h> -#include <linux/errno.h> -#include <linux/genhd.h> -#include <linux/major.h> -#include <linux/ioport.h> -#include <linux/delay.h> -#include <linux/blkpg.h> - -#include <asm/system.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/uaccess.h> -#include <asm/dma.h> -#include <asm/hardware.h> -#include <asm/ecard.h> -#include <asm/hardware/ioc.h> - -static void (*do_mfm)(void) = NULL; -static struct request_queue *mfm_queue; -static DEFINE_SPINLOCK(mfm_lock); - -#define MAJOR_NR MFM_ACORN_MAJOR -#define QUEUE (mfm_queue) -#define CURRENT elv_next_request(mfm_queue) - -/* - * Configuration section - * - * This is the maximum number of drives that we accept - */ -#define MFM_MAXDRIVES 2 -/* - * Linux I/O address of onboard MFM controller or 0 to disable this - */ -#define ONBOARD_MFM_ADDRESS ((0x002d0000 >> 2) | 0x80000000) -/* - * Uncomment this to enable debugging in the MFM driver... - */ -#ifndef DEBUG -/*#define DEBUG */ -#endif -/* - * End of configuration - */ - - -/* - * This structure contains all information to do with a particular physical - * device. - */ -struct mfm_info { - unsigned char sectors; - unsigned char heads; - unsigned short cylinders; - unsigned short lowcurrent; - unsigned short precomp; -#define NO_TRACK -1 -#define NEED_1_RECAL -2 -#define NEED_2_RECAL -3 - int cylinder; - struct { - char recal; - char report; - char abort; - } errors; -} mfm_info[MFM_MAXDRIVES]; - -#define MFM_DRV_INFO mfm_info[raw_cmd.dev] - -/* Stuff from the assembly routines */ -extern unsigned int hdc63463_baseaddress; /* Controller base address */ -extern unsigned int hdc63463_irqpolladdress; /* Address to read to test for int */ -extern unsigned int hdc63463_irqpollmask; /* Mask for irq register */ -extern unsigned int hdc63463_dataptr; /* Pointer to kernel data space to DMA */ -extern int hdc63463_dataleft; /* Number of bytes left to transfer */ - - - - -static int lastspecifieddrive; -static unsigned Busy; - -static unsigned int PartFragRead; /* The number of sectors which have been read - during a partial read split over two - cylinders. If 0 it means a partial - read did not occur. */ - -static unsigned int PartFragRead_RestartBlock; /* Where to restart on a split access */ -static unsigned int PartFragRead_SectorsLeft; /* Where to restart on a split access */ - -static int Sectors256LeftInCurrent; /* i.e. 256 byte sectors left in current */ -static int SectorsLeftInRequest; /* i.e. blocks left in the thing mfm_request was called for */ -static int Copy_Sector; /* The 256 byte sector we are currently at - fragments need to know - where to take over */ -static char *Copy_buffer; - - -static void mfm_seek(void); -static void mfm_rerequest(void); -static void mfm_request(void); -static void mfm_specify (void); -static void issue_request(unsigned int block, unsigned int nsect, - struct request *req); - -static unsigned int mfm_addr; /* Controller address */ -static unsigned int mfm_IRQPollLoc; /* Address to read for IRQ information */ -static unsigned int mfm_irqenable; /* Podule IRQ enable location */ -static unsigned char mfm_irq; /* Interrupt number */ -static int mfm_drives = 0; /* drives available */ -static int mfm_status = 0; /* interrupt status */ -static int *errors; - -static struct rawcmd { - unsigned int dev; - unsigned int cylinder; - unsigned int head; - unsigned int sector; - unsigned int cmdtype; - unsigned int cmdcode; - unsigned char cmddata[16]; - unsigned int cmdlen; -} raw_cmd; - -static unsigned char result[16]; - -static struct cont { - void (*interrupt) (void); /* interrupt handler */ - void (*error) (void); /* error handler */ - void (*redo) (void); /* redo handler */ - void (*done) (int st); /* done handler */ -} *cont = NULL; - -#if 0 -static struct tq_struct mfm_tq = {0, 0, (void (*)(void *)) NULL, 0}; -#endif - -int number_mfm_drives = 1; - -/* ------------------------------------------------------------------------------------------ */ -/* - * From the HD63463 data sheet from Hitachi Ltd. - */ - -#define MFM_COMMAND (mfm_addr + 0) -#define MFM_DATAOUT (mfm_addr + 1) -#define MFM_STATUS (mfm_addr + 8) -#define MFM_DATAIN (mfm_addr + 9) - -#define CMD_ABT 0xF0 /* Abort */ -#define CMD_SPC 0xE8 /* Specify */ -#define CMD_TST 0xE0 /* Test */ -#define CMD_RCLB 0xC8 /* Recalibrate */ -#define CMD_SEK 0xC0 /* Seek */ -#define CMD_WFS 0xAB /* Write Format Skew */ -#define CMD_WFM 0xA3 /* Write Format */ -#define CMD_MTB 0x90 /* Memory to buffer */ -#define CMD_CMPD 0x88 /* Compare data */ -#define CMD_WD 0x87 /* Write data */ -#define CMD_RED 0x70 /* Read erroneous data */ -#define CMD_RIS 0x68 /* Read ID skew */ -#define CMD_FID 0x61 /* Find ID */ -#define CMD_RID 0x60 /* Read ID */ -#define CMD_BTM 0x50 /* Buffer to memory */ -#define CMD_CKD 0x48 /* Check data */ -#define CMD_RD 0x40 /* Read data */ -#define CMD_OPBW 0x38 /* Open buffer write */ -#define CMD_OPBR 0x30 /* Open buffer read */ -#define CMD_CKV 0x28 /* Check drive */ -#define CMD_CKE 0x20 /* Check ECC */ -#define CMD_POD 0x18 /* Polling disable */ -#define CMD_POL 0x10 /* Polling enable */ -#define CMD_RCAL 0x08 /* Recall */ - -#define STAT_BSY 0x8000 /* Busy */ -#define STAT_CPR 0x4000 /* Command Parameter Rejection */ -#define STAT_CED 0x2000 /* Command end */ -#define STAT_SED 0x1000 /* Seek end */ -#define STAT_DER 0x0800 /* Drive error */ -#define STAT_ABN 0x0400 /* Abnormal end */ -#define STAT_POL 0x0200 /* Polling */ - -/* ------------------------------------------------------------------------------------------ */ -#ifdef DEBUG -static void console_printf(const char *fmt,...) -{ - static char buffer[2048]; /* Arbitary! */ - extern void console_print(const char *); - unsigned long flags; - va_list ap; - - local_irq_save(flags); - - va_start(ap, fmt); - vsprintf(buffer, fmt, ap); - console_print(buffer); - va_end(fmt); - - local_irq_restore(flags); -}; /* console_printf */ - -#define DBG(x...) console_printf(x) -#else -#define DBG(x...) -#endif - -static void print_status(void) -{ - char *error; - static char *errors[] = { - "no error", - "command aborted", - "invalid command", - "parameter error", - "not initialised", - "rejected TEST", - "no useld", - "write fault", - "not ready", - "no scp", - "in seek", - "invalid NCA", - "invalid step rate", - "seek error", - "over run", - "invalid PHA", - "data field EEC error", - "data field CRC error", - "error corrected", - "data field fatal error", - "no data am", - "not hit", - "ID field CRC error", - "time over", - "no ID am", - "not writable" - }; - if (result[1] < 0x65) - error = errors[result[1] >> 2]; - else - error = "unknown"; - printk("("); - if (mfm_status & STAT_BSY) printk("BSY "); - if (mfm_status & STAT_CPR) printk("CPR "); - if (mfm_status & STAT_CED) printk("CED "); - if (mfm_status & STAT_SED) printk("SED "); - if (mfm_status & STAT_DER) printk("DER "); - if (mfm_status & STAT_ABN) printk("ABN "); - if (mfm_status & STAT_POL) printk("POL "); - printk(") SSB = %X (%s)\n", result[1], error); - -} - -/* ------------------------------------------------------------------------------------- */ - -static void issue_command(int command, unsigned char *cmdb, int len) -{ - int status; -#ifdef DEBUG - int i; - console_printf("issue_command: %02X: ", command); - for (i = 0; i < len; i++) - console_printf("%02X ", cmdb[i]); - console_printf("\n"); -#endif - - do { - status = inw(MFM_STATUS); - } while (status & (STAT_BSY | STAT_POL)); - DBG("issue_command: status after pol/bsy loop: %02X:\n ", status >> 8); - - if (status & (STAT_CPR | STAT_CED | STAT_SED | STAT_DER | STAT_ABN)) { - outw(CMD_RCAL, MFM_COMMAND); - while (inw(MFM_STATUS) & STAT_BSY); - } - status = inw(MFM_STATUS); - DBG("issue_command: status before parameter issue: %02X:\n ", status >> 8); - - while (len > 0) { - outw(cmdb[1] | (cmdb[0] << 8), MFM_DATAOUT); - len -= 2; - cmdb += 2; - } - status = inw(MFM_STATUS); - DBG("issue_command: status before command issue: %02X:\n ", status >> 8); - - outw(command, MFM_COMMAND); - status = inw(MFM_STATUS); - DBG("issue_command: status immediately after command issue: %02X:\n ", status >> 8); -} - -static void wait_for_completion(void) -{ - while ((mfm_status = inw(MFM_STATUS)) & STAT_BSY); -} - -static void wait_for_command_end(void) -{ - int i; - - while (!((mfm_status = inw(MFM_STATUS)) & STAT_CED)); - - for (i = 0; i < 16;) { - int in; - in = inw(MFM_DATAIN); - result[i++] = in >> 8; - result[i++] = in; - } - outw (CMD_RCAL, MFM_COMMAND); -} - -/* ------------------------------------------------------------------------------------- */ - -static void mfm_rw_intr(void) -{ - int old_status; /* Holds status on entry, we read to see if the command just finished */ -#ifdef DEBUG - console_printf("mfm_rw_intr...dataleft=%d\n", hdc63463_dataleft); - print_status(); -#endif - - /* Now don't handle the error until BSY drops */ - if ((mfm_status & (STAT_DER | STAT_ABN)) && ((mfm_status&STAT_BSY)==0)) { - /* Something has gone wrong - let's try that again */ - outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ - if (cont) { - DBG("mfm_rw_intr: DER/ABN err\n"); - cont->error(); - cont->redo(); - }; - return; - }; - - /* OK so what ever happened it's not an error, now I reckon we are left between - a choice of command end or some data which is ready to be collected */ - /* I think we have to transfer data while the interrupt line is on and its - not any other type of interrupt */ - if (rq_data_dir(CURRENT) == WRITE) { - extern void hdc63463_writedma(void); - if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) { - printk("mfm_rw_intr: Apparent DMA write request when no more to DMA\n"); - if (cont) { - cont->error(); - cont->redo(); - }; - return; - }; - hdc63463_writedma(); - } else { - extern void hdc63463_readdma(void); - if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) { - printk("mfm_rw_intr: Apparent DMA read request when no more to DMA\n"); - if (cont) { - cont->error(); - cont->redo(); - }; - return; - }; - DBG("Going to try read dma..............status=0x%x, buffer=%p\n", mfm_status, hdc63463_dataptr); - hdc63463_readdma(); - }; /* Read */ - - if (hdc63463_dataptr != ((unsigned int) Copy_buffer + 256)) { - /* If we didn't actually manage to get any data on this interrupt - but why? We got the interrupt */ - /* Ah - well looking at the status its just when we get command end; so no problem */ - /*console_printf("mfm: dataptr mismatch. dataptr=0x%08x Copy_buffer+256=0x%08p\n", - hdc63463_dataptr,Copy_buffer+256); - print_status(); */ - } else { - Sectors256LeftInCurrent--; - Copy_buffer += 256; - Copy_Sector++; - - /* We have come to the end of this request */ - if (!Sectors256LeftInCurrent) { - DBG("mfm: end_request for CURRENT=0x%p CURRENT(sector=%d current_nr_sectors=%d nr_sectors=%d)\n", - CURRENT, CURRENT->sector, CURRENT->current_nr_sectors, CURRENT->nr_sectors); - - CURRENT->nr_sectors -= CURRENT->current_nr_sectors; - CURRENT->sector += CURRENT->current_nr_sectors; - SectorsLeftInRequest -= CURRENT->current_nr_sectors; - - end_request(CURRENT, 1); - if (SectorsLeftInRequest) { - hdc63463_dataptr = (unsigned int) CURRENT->buffer; - Copy_buffer = CURRENT->buffer; - Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2; - errors = &(CURRENT->errors); - /* These should match the present calculations of the next logical sector - on the device - Copy_Sector=CURRENT->sector*2; */ - - if (Copy_Sector != CURRENT->sector * 2) -#ifdef DEBUG - /*console_printf*/printk("mfm: Copy_Sector mismatch. Copy_Sector=%d CURRENT->sector*2=%d\n", - Copy_Sector, CURRENT->sector * 2); -#else - printk("mfm: Copy_Sector mismatch! Eek!\n"); -#endif - }; /* CURRENT */ - }; /* Sectors256LeftInCurrent */ - }; - - old_status = mfm_status; - mfm_status = inw(MFM_STATUS); - if (mfm_status & (STAT_DER | STAT_ABN)) { - /* Something has gone wrong - let's try that again */ - if (cont) { - DBG("mfm_rw_intr: DER/ABN error\n"); - cont->error(); - cont->redo(); - }; - return; - }; - - /* If this code wasn't entered due to command_end but there is - now a command end we must read the command results out. If it was - entered like this then mfm_interrupt_handler would have done the - job. */ - if ((!((old_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) && - ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) { - int len = 0; - while (len < 16) { - int in; - in = inw(MFM_DATAIN); - result[len++] = in >> 8; - result[len++] = in; - }; - }; /* Result read */ - - /*console_printf ("mfm_rw_intr nearexit [%02X]\n", __raw_readb(mfm_IRQPollLoc)); */ - - /* If end of command move on */ - if (mfm_status & (STAT_CED)) { - outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ - /* End of command - trigger the next command */ - if (cont) { - cont->done(1); - } - DBG("mfm_rw_intr: returned from cont->done\n"); - } else { - /* Its going to generate another interrupt */ - do_mfm = mfm_rw_intr; - }; -} - -static void mfm_setup_rw(void) -{ - DBG("setting up for rw...\n"); - - do_mfm = mfm_rw_intr; - issue_command(raw_cmd.cmdcode, raw_cmd.cmddata, raw_cmd.cmdlen); -} - -static void mfm_recal_intr(void) -{ -#ifdef DEBUG - console_printf("recal intr - status = "); - print_status(); -#endif - outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ - if (mfm_status & (STAT_DER | STAT_ABN)) { - printk("recal failed\n"); - MFM_DRV_INFO.cylinder = NEED_2_RECAL; - if (cont) { - cont->error(); - cont->redo(); - } - return; - } - /* Thats seek end - we are finished */ - if (mfm_status & STAT_SED) { - issue_command(CMD_POD, NULL, 0); - MFM_DRV_INFO.cylinder = 0; - mfm_seek(); - return; - } - /* Command end without seek end (see data sheet p.20) for parallel seek - - we have to send a POL command to wait for the seek */ - if (mfm_status & STAT_CED) { - do_mfm = mfm_recal_intr; - issue_command(CMD_POL, NULL, 0); - return; - } - printk("recal: unknown status\n"); -} - -static void mfm_seek_intr(void) -{ -#ifdef DEBUG - console_printf("seek intr - status = "); - print_status(); -#endif - outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ - if (mfm_status & (STAT_DER | STAT_ABN)) { - printk("seek failed\n"); - MFM_DRV_INFO.cylinder = NEED_2_RECAL; - if (cont) { - cont->error(); - cont->redo(); - } - return; - } - if (mfm_status & STAT_SED) { - issue_command(CMD_POD, NULL, 0); - MFM_DRV_INFO.cylinder = raw_cmd.cylinder; - mfm_seek(); - return; - } - if (mfm_status & STAT_CED) { - do_mfm = mfm_seek_intr; - issue_command(CMD_POL, NULL, 0); - return; - } - printk("seek: unknown status\n"); -} - -/* IDEA2 seems to work better - its what RiscOS sets my - * disc to - on its SECOND call to specify! - */ -#define IDEA2 -#ifndef IDEA2 -#define SPEC_SL 0x16 -#define SPEC_SH 0xa9 /* Step pulse high=21, Record Length=001 (256 bytes) */ -#else -#define SPEC_SL 0x00 /* OM2 - SL - step pulse low */ -#define SPEC_SH 0x21 /* Step pulse high=4, Record Length=001 (256 bytes) */ -#endif - -static void mfm_setupspecify (int drive, unsigned char *cmdb) -{ - cmdb[0] = 0x1f; /* OM0 - !SECT,!MOD,!DIF,PADP,ECD,CRCP,CRCI,ACOR */ - cmdb[1] = 0xc3; /* OM1 - DTM,BRST,!CEDM,!SEDM,!DERM,0,AMEX,PSK */ - cmdb[2] = SPEC_SL; /* OM2 - SL - step pulse low */ - cmdb[3] = (number_mfm_drives == 1) ? 0x02 : 0x06; /* 1 or 2 drives */ - cmdb[4] = 0xfc | ((mfm_info[drive].cylinders - 1) >> 8);/* RW time over/high part of number of cylinders */ - cmdb[5] = mfm_info[drive].cylinders - 1; /* low part of number of cylinders */ - cmdb[6] = mfm_info[drive].heads - 1; /* Number of heads */ - cmdb[7] = mfm_info[drive].sectors - 1; /* Number of sectors */ - cmdb[8] = SPEC_SH; - cmdb[9] = 0x0a; /* gap length 1 */ - cmdb[10] = 0x0d; /* gap length 2 */ - cmdb[11] = 0x0c; /* gap length 3 */ - cmdb[12] = (mfm_info[drive].precomp - 1) >> 8; /* pre comp cylinder */ - cmdb[13] = mfm_info[drive].precomp - 1; - cmdb[14] = (mfm_info[drive].lowcurrent - 1) >> 8; /* Low current cylinder */ - cmdb[15] = mfm_info[drive].lowcurrent - 1; -} - -static void mfm_specify (void) -{ - unsigned char cmdb[16]; - - DBG("specify...dev=%d lastspecified=%d\n", raw_cmd.dev, lastspecifieddrive); - mfm_setupspecify (raw_cmd.dev, cmdb); - - issue_command (CMD_SPC, cmdb, 16); - /* Ensure that we will do another specify if we move to the other drive */ - lastspecifieddrive = raw_cmd.dev; - wait_for_completion(); -} - -static void mfm_seek(void) -{ - unsigned char cmdb[4]; - - DBG("seeking...\n"); - if (MFM_DRV_INFO.cylinder < 0) { - do_mfm = mfm_recal_intr; - DBG("mfm_seek: about to call specify\n"); - mfm_specify (); /* DAG added this */ - - cmdb[0] = raw_cmd.dev + 1; - cmdb[1] = 0; - - issue_command(CMD_RCLB, cmdb, 2); - return; - } - if (MFM_DRV_INFO.cylinder != raw_cmd.cylinder) { - cmdb[0] = raw_cmd.dev + 1; - cmdb[1] = 0; /* raw_cmd.head; DAG: My data sheet says this should be 0 */ - cmdb[2] = raw_cmd.cylinder >> 8; - cmdb[3] = raw_cmd.cylinder; - - do_mfm = mfm_seek_intr; - issue_command(CMD_SEK, cmdb, 4); - } else - mfm_setup_rw(); -} - -static void mfm_initialise(void) -{ - DBG("init...\n"); - mfm_seek(); -} - -static void request_done(int uptodate) -{ - DBG("mfm:request_done\n"); - if (uptodate) { - unsigned char block[2] = {0, 0}; - - /* Apparently worked - let's check bytes left to DMA */ - if (hdc63463_dataleft != (PartFragRead_SectorsLeft * 256)) { - printk("mfm: request_done - dataleft=%d - should be %d - Eek!\n", hdc63463_dataleft, PartFragRead_SectorsLeft * 256); - end_request(CURRENT, 0); - Busy = 0; - }; - /* Potentially this means that we've done; but we might be doing - a partial access, (over two cylinders) or we may have a number - of fragments in an image file. First let's deal with partial accesss - */ - if (PartFragRead) { - /* Yep - a partial access */ - - /* and issue the remainder */ - issue_request(PartFragRead_RestartBlock, PartFragRead_SectorsLeft, CURRENT); - return; - } - - /* ah well - perhaps there is another fragment to go */ - - /* Increment pointers/counts to start of next fragment */ - if (SectorsLeftInRequest > 0) printk("mfm: SectorsLeftInRequest>0 - Eek! Shouldn't happen!\n"); - - /* No - its the end of the line */ - /* end_request's should have happened at the end of sector DMAs */ - /* Turns Drive LEDs off - may slow it down? */ - if (!elv_next_request(QUEUE)) - issue_command(CMD_CKV, block, 2); - - Busy = 0; - DBG("request_done: About to mfm_request\n"); - /* Next one please */ - mfm_request(); /* Moved from mfm_rw_intr */ - DBG("request_done: returned from mfm_request\n"); - } else { - printk("mfm:request_done: update=0\n"); - end_request(CURRENT, 0); - Busy = 0; - } -} - -static void error_handler(void) -{ - printk("error detected... status = "); - print_status(); - (*errors)++; - if (*errors > MFM_DRV_INFO.errors.abort) - cont->done(0); - if (*errors > MFM_DRV_INFO.errors.recal) - MFM_DRV_INFO.cylinder = NEED_2_RECAL; -} - -static void rw_interrupt(void) -{ - printk("rw_interrupt\n"); -} - -static struct cont rw_cont = -{ - rw_interrupt, - error_handler, - mfm_rerequest, - request_done -}; - -/* - * Actually gets round to issuing the request - note everything at this - * point is in 256 byte sectors not Linux 512 byte blocks - */ -static void issue_request(unsigned int block, unsigned int nsect, - struct request *req) -{ - struct gendisk *disk = req->rq_disk; - struct mfm_info *p = disk->private_data; - int track, start_head, start_sector; - int sectors_to_next_cyl; - dev = p - mfm_info; - - track = block / p->sectors; - start_sector = block % p->sectors; - start_head = track % p->heads; - - /* First get the number of whole tracks which are free before the next - track */ - sectors_to_next_cyl = (p->heads - (start_head + 1)) * p->sectors; - /* Then add in the number of sectors left on this track */ - sectors_to_next_cyl += (p->sectors - start_sector); - - DBG("issue_request: mfm_info[dev].sectors=%d track=%d\n", p->sectors, track); - - raw_cmd.dev = dev; - raw_cmd.sector = start_sector; - raw_cmd.head = start_head; - raw_cmd.cylinder = track / p->heads; - raw_cmd.cmdtype = CURRENT->cmd; - raw_cmd.cmdcode = rq_data_dir(CURRENT) == WRITE ? CMD_WD : CMD_RD; - raw_cmd.cmddata[0] = dev + 1; /* DAG: +1 to get US */ - raw_cmd.cmddata[1] = raw_cmd.head; - raw_cmd.cmddata[2] = raw_cmd.cylinder >> 8; - raw_cmd.cmddata[3] = raw_cmd.cylinder; - raw_cmd.cmddata[4] = raw_cmd.head; - raw_cmd.cmddata[5] = raw_cmd.sector; - - /* Was == and worked - how the heck??? */ - if (lastspecifieddrive != raw_cmd.dev) - mfm_specify (); - - if (nsect <= sectors_to_next_cyl) { - raw_cmd.cmddata[6] = nsect >> 8; - raw_cmd.cmddata[7] = nsect; - PartFragRead = 0; /* All in one */ - PartFragRead_SectorsLeft = 0; /* Must set this - used in DMA calcs */ - } else { - raw_cmd.cmddata[6] = sectors_to_next_cyl >> 8; - raw_cmd.cmddata[7] = sectors_to_next_cyl; - PartFragRead = sectors_to_next_cyl; /* only do this many this time */ - PartFragRead_RestartBlock = block + sectors_to_next_cyl; /* Where to restart from */ - PartFragRead_SectorsLeft = nsect - sectors_to_next_cyl; - } - raw_cmd.cmdlen = 8; - - /* Setup DMA pointers */ - hdc63463_dataptr = (unsigned int) Copy_buffer; - hdc63463_dataleft = nsect * 256; /* Better way? */ - - DBG("mfm%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx (%p)\n", - raw_cmd.dev + 'a', rq_data_dir(CURRENT) == READ ? "read" : "writ", - raw_cmd.cylinder, - raw_cmd.head, - raw_cmd.sector, nsect, (unsigned long) Copy_buffer, CURRENT); - - cont = &rw_cont; - errors = &(CURRENT->errors); -#if 0 - mfm_tq.routine = (void (*)(void *)) mfm_initialise; - queue_task(&mfm_tq, &tq_immediate); - mark_bh(IMMEDIATE_BH); -#else - mfm_initialise(); -#endif -} /* issue_request */ - -/* - * Called when an error has just happened - need to trick mfm_request - * into thinking we weren't busy - * - * Turn off ints - mfm_request expects them this way - */ -static void mfm_rerequest(void) -{ - DBG("mfm_rerequest\n"); - cli(); - Busy = 0; - mfm_request(); -} - -static struct gendisk *mfm_gendisk[2]; - -static void mfm_request(void) -{ - DBG("mfm_request CURRENT=%p Busy=%d\n", CURRENT, Busy); - - /* If we are still processing then return; we will get called again */ - if (Busy) { - /* Again seems to be common in 1.3.45 */ - /*DBG*/printk("mfm_request: Exiting due to busy\n"); - return; - } - Busy = 1; - - while (1) { - unsigned int block, nsect; - struct gendisk *disk; - - DBG("mfm_request: loop start\n"); - sti(); - - DBG("mfm_request: before !CURRENT\n"); - - if (!CURRENT) { - printk("mfm_request: Exiting due to empty queue (pre)\n"); - do_mfm = NULL; - Busy = 0; - return; - } - - DBG("mfm_request: before arg extraction\n"); - - disk = CURRENT->rq_disk; - block = CURRENT->sector; - nsect = CURRENT->nr_sectors; - if (block >= get_capacity(disk) || - block+nsect > get_capacity(disk)) { - printk("%s: bad access: block=%d, count=%d, nr_sects=%ld\n", - disk->disk_name, block, nsect, get_capacity(disk)); - printk("mfm: continue 1\n"); - end_request(CURRENT, 0); - Busy = 0; - continue; - } - - /* DAG: Linux doesn't cope with this - even though it has an array telling - it the hardware block size - silly */ - block <<= 1; /* Now in 256 byte sectors */ - nsect <<= 1; /* Ditto */ - - SectorsLeftInRequest = nsect >> 1; - Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2; - Copy_buffer = CURRENT->buffer; - Copy_Sector = CURRENT->sector << 1; - - DBG("mfm_request: block after offset=%d\n", block); - - issue_request(block, nsect, CURRENT); - - break; - } - DBG("mfm_request: Dropping out bottom\n"); -} - -static void do_mfm_request(request_queue_t *q) -{ - DBG("do_mfm_request: about to mfm_request\n"); - mfm_request(); -} - -static void mfm_interrupt_handler(int unused, void *dev_id) -{ - void (*handler) (void) = do_mfm; - - do_mfm = NULL; - - DBG("mfm_interrupt_handler (handler=0x%p)\n", handler); - - mfm_status = inw(MFM_STATUS); - - /* If CPR (Command Parameter Reject) and not busy it means that the command - has some return message to give us */ - if ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR) { - int len = 0; - while (len < 16) { - int in; - in = inw(MFM_DATAIN); - result[len++] = in >> 8; - result[len++] = in; - } - } - if (handler) { - handler(); - return; - } - outw (CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ - printk ("mfm: unexpected interrupt - status = "); - print_status (); - while (1); -} - - - - - -/* - * Tell the user about the drive if we decided it exists. - */ -static void mfm_geometry(int drive) -{ - struct mfm_info *p = mfm_info + drive; - struct gendisk *disk = mfm_gendisk[drive]; - disk->private_data = p; - if (p->cylinders) - printk ("%s: %dMB CHS=%d/%d/%d LCC=%d RECOMP=%d\n", - disk->disk_name, - p->cylinders * p->heads * p->sectors / 4096, - p->cylinders, p->heads, p->sectors, - p->lowcurrent, p->precomp); - set_capacity(disk, p->cylinders * p->heads * p->sectors / 2); -} - -#ifdef CONFIG_BLK_DEV_MFM_AUTODETECT -/* - * Attempt to detect a drive and find its geometry. The drive has already been - * specified... - * - * We first recalibrate the disk, then try to probe sectors, heads and then - * cylinders. NOTE! the cylinder probe may break drives. The xd disk driver - * does something along these lines, so I assume that most drives are up to - * this mistreatment... - */ -static int mfm_detectdrive (int drive) -{ - unsigned int mingeo[3], maxgeo[3]; - unsigned int attribute, need_recal = 1; - unsigned char cmdb[8]; - - memset (mingeo, 0, sizeof (mingeo)); - maxgeo[0] = mfm_info[drive].sectors; - maxgeo[1] = mfm_info[drive].heads; - maxgeo[2] = mfm_info[drive].cylinders; - - cmdb[0] = drive + 1; - cmdb[6] = 0; - cmdb[7] = 1; - for (attribute = 0; attribute < 3; attribute++) { - while (mingeo[attribute] != maxgeo[attribute]) { - unsigned int variable; - - variable = (maxgeo[attribute] + mingeo[attribute]) >> 1; - cmdb[1] = cmdb[2] = cmdb[3] = cmdb[4] = cmdb[5] = 0; - - if (need_recal) { - int tries = 5; - - do { - issue_command (CMD_RCLB, cmdb, 2); - wait_for_completion (); - wait_for_command_end (); - if (result[1] == 0x20) - break; - } while (result[1] && --tries); - if (result[1]) { - outw (CMD_RCAL, MFM_COMMAND); - return 0; - } - need_recal = 0; - } - - switch (attribute) { - case 0: - cmdb[5] = variable; - issue_command (CMD_CMPD, cmdb, 8); - break; - case 1: - cmdb[1] = variable; - cmdb[4] = variable; - issue_command (CMD_CMPD, cmdb, 8); - break; - case 2: - cmdb[2] = variable >> 8; - cmdb[3] = variable; - issue_command (CMD_SEK, cmdb, 4); - break; - } - wait_for_completion (); - wait_for_command_end (); - - switch (result[1]) { - case 0x00: - case 0x50: - mingeo[attribute] = variable + 1; - break; - - case 0x20: - outw (CMD_RCAL, MFM_COMMAND); - return 0; - - case 0x24: - need_recal = 1; - default: - maxgeo[attribute] = variable; - break; - } - } - } - mfm_info[drive].cylinders = mingeo[2]; - mfm_info[drive].lowcurrent = mingeo[2]; - mfm_info[drive].precomp = mingeo[2] / 2; - mfm_info[drive].heads = mingeo[1]; - mfm_info[drive].sectors = mingeo[0]; - outw (CMD_RCAL, MFM_COMMAND); - return 1; -} -#endif - -/* - * Initialise all drive information for this controller. - */ -static int mfm_initdrives(void) -{ - int drive; - - if (number_mfm_drives > MFM_MAXDRIVES) { - number_mfm_drives = MFM_MAXDRIVES; - printk("No. of ADFS MFM drives is greater than MFM_MAXDRIVES - you can't have that many!\n"); - } - - for (drive = 0; drive < number_mfm_drives; drive++) { - mfm_info[drive].lowcurrent = 1; - mfm_info[drive].precomp = 1; - mfm_info[drive].cylinder = -1; - mfm_info[drive].errors.recal = 0; - mfm_info[drive].errors.report = 0; - mfm_info[drive].errors.abort = 4; - -#ifdef CONFIG_BLK_DEV_MFM_AUTODETECT - mfm_info[drive].cylinders = 1024; - mfm_info[drive].heads = 8; - mfm_info[drive].sectors = 64; - { - unsigned char cmdb[16]; - - mfm_setupspecify (drive, cmdb); - cmdb[1] &= ~0x81; - issue_command (CMD_SPC, cmdb, 16); - wait_for_completion (); - if (!mfm_detectdrive (drive)) { - mfm_info[drive].cylinders = 0; - mfm_info[drive].heads = 0; - mfm_info[drive].sectors = 0; - } - cmdb[0] = cmdb[1] = 0; - issue_command (CMD_CKV, cmdb, 2); - } -#else - mfm_info[drive].cylinders = 1; /* its going to have to figure it out from the partition info */ - mfm_info[drive].heads = 4; - mfm_info[drive].sectors = 32; -#endif - } - return number_mfm_drives; -} - - - -/* - * The 'front' end of the mfm driver follows... - */ - -static int mfm_getgeo(struct block_device *bdev, struct hd_geometry *geo) -{ - struct mfm_info *p = bdev->bd_disk->private_data; - - geo->heads = p->heads; - geo->sectors = p->sectors; - geo->cylinders = p->cylinders; - return 0; -} - -/* - * This is to handle various kernel command line parameters - * specific to this driver. - */ -void mfm_setup(char *str, int *ints) -{ - return; -} - -/* - * Set the CHS from the ADFS boot block if it is present. This is not ideal - * since if there are any non-ADFS partitions on the disk, this won't work! - * Hence, I want to get rid of this... - */ -void xd_set_geometry(struct block_device *bdev, unsigned char secsptrack, - unsigned char heads, unsigned int secsize) -{ - struct mfm_info *p = bdev->bd_disk->private_data; - int drive = p - mfm_info; - unsigned long disksize = bdev->bd_inode->i_size; - - if (p->cylinders == 1) { - p->sectors = secsptrack; - p->heads = heads; - p->cylinders = discsize / (secsptrack * heads * secsize); - - if ((heads < 1) || (p->cylinders > 1024)) { - printk("%s: Insane disc shape! Setting to 512/4/32\n", - bdev->bd_disk->disk_name); - - /* These values are fairly arbitary, but are there so that if your - * lucky you can pick apart your disc to find out what is going on - - * I reckon these figures won't hurt MOST drives - */ - p->sectors = 32; - p->heads = 4; - p->cylinders = 512; - } - if (raw_cmd.dev == drive) - mfm_specify (); - mfm_geometry (drive); - } -} - -static struct block_device_operations mfm_fops = -{ - .owner = THIS_MODULE, - .getgeo = mfm_getgeo, -}; - -/* - * See if there is a controller at the address presently at mfm_addr - * - * We check to see if the controller is busy - if it is, we abort it first, - * and check that the chip is no longer busy after at least 180 clock cycles. - * We then issue a command and check that the BSY or CPR bits are set. - */ -static int mfm_probecontroller (unsigned int mfm_addr) -{ - if (inw (MFM_STATUS) & STAT_BSY) { - outw (CMD_ABT, MFM_COMMAND); - udelay (50); - if (inw (MFM_STATUS) & STAT_BSY) - return 0; - } - - if (inw (MFM_STATUS) & STAT_CED) - outw (CMD_RCAL, MFM_COMMAND); - - outw (CMD_SEK, MFM_COMMAND); - - if (inw (MFM_STATUS) & (STAT_BSY | STAT_CPR)) { - unsigned int count = 2000; - while (inw (MFM_STATUS) & STAT_BSY) { - udelay (500); - if (!--count) - return 0; - } - - outw (CMD_RCAL, MFM_COMMAND); - } - return 1; -} - -static int mfm_do_init(unsigned char irqmask) -{ - int i, ret; - - printk("mfm: found at address %08X, interrupt %d\n", mfm_addr, mfm_irq); - - ret = -EBUSY; - if (!request_region (mfm_addr, 10, "mfm")) - goto out1; - - ret = register_blkdev(MAJOR_NR, "mfm"); - if (ret) - goto out2; - - /* Stuff for the assembler routines to get to */ - hdc63463_baseaddress = ioaddr(mfm_addr); - hdc63463_irqpolladdress = mfm_IRQPollLoc; - hdc63463_irqpollmask = irqmask; - - mfm_queue = blk_init_queue(do_mfm_request, &mfm_lock); - if (!mfm_queue) - goto out2a; - - Busy = 0; - lastspecifieddrive = -1; - - mfm_drives = mfm_initdrives(); - if (!mfm_drives) { - ret = -ENODEV; - goto out3; - } - - for (i = 0; i < mfm_drives; i++) { - struct gendisk *disk = alloc_disk(64); - if (!disk) - goto Enomem; - disk->major = MAJOR_NR; - disk->first_minor = i << 6; - disk->fops = &mfm_fops; - sprintf(disk->disk_name, "mfm%c", 'a'+i); - mfm_gendisk[i] = disk; - } - - printk("mfm: detected %d hard drive%s\n", mfm_drives, - mfm_drives == 1 ? "" : "s"); - ret = request_irq(mfm_irq, mfm_interrupt_handler, IRQF_DISABLED, "MFM harddisk", NULL); - if (ret) { - printk("mfm: unable to get IRQ%d\n", mfm_irq); - goto out4; - } - - if (mfm_irqenable) - outw(0x80, mfm_irqenable); /* Required to enable IRQs from MFM podule */ - - for (i = 0; i < mfm_drives; i++) { - mfm_geometry(i); - mfm_gendisk[i]->queue = mfm_queue; - add_disk(mfm_gendisk[i]); - } - return 0; - -out4: - for (i = 0; i < mfm_drives; i++) - put_disk(mfm_gendisk[i]); -out3: - blk_cleanup_queue(mfm_queue); -out2a: - unregister_blkdev(MAJOR_NR, "mfm"); -out2: - release_region(mfm_addr, 10); -out1: - return ret; -Enomem: - while (i--) - put_disk(mfm_gendisk[i]); - goto out3; -} - -static void mfm_do_exit(void) -{ - int i; - - free_irq(mfm_irq, NULL); - for (i = 0; i < mfm_drives; i++) { - del_gendisk(mfm_gendisk[i]); - put_disk(mfm_gendisk[i]); - } - blk_cleanup_queue(mfm_queue); - unregister_blkdev(MAJOR_NR, "mfm"); - if (mfm_addr) - release_region(mfm_addr, 10); -} - -static int __devinit mfm_probe(struct expansion_card *ec, struct ecard_id *id) -{ - if (mfm_addr) - return -EBUSY; - - mfm_addr = ecard_address(ec, ECARD_IOC, ECARD_MEDIUM) + 0x800; - mfm_IRQPollLoc = ioaddr(mfm_addr + 0x400); - mfm_irqenable = mfm_IRQPollLoc; - mfm_irq = ec->irq; - - return mfm_do_init(0x08); -} - -static void __devexit mfm_remove(struct expansion_card *ec) -{ - outw (0, mfm_irqenable); /* Required to enable IRQs from MFM podule */ - mfm_do_exit(); -} - -static const struct ecard_id mfm_cids[] = { - { MANU_ACORN, PROD_ACORN_MFM }, - { 0xffff, 0xffff }, -}; - -static struct ecard_driver mfm_driver = { - .probe = mfm_probe, - .remove = __devexit(mfm_remove), - .id_table = mfm_cids, - .drv = { - .name = "mfm", - }, -}; - -/* - * Look for a MFM controller - first check the motherboard, then the podules - * The podules have an extra interrupt enable that needs to be played with - * - * The HDC is accessed at MEDIUM IOC speeds. - */ -static int __init mfm_init (void) -{ - unsigned char irqmask; - - if (mfm_probecontroller(ONBOARD_MFM_ADDRESS)) { - mfm_addr = ONBOARD_MFM_ADDRESS; - mfm_IRQPollLoc = IOC_IRQSTATB; - mfm_irqenable = 0; - mfm_irq = IRQ_HARDDISK; - return mfm_do_init(0x08); /* IL3 pin */ - } else { - return ecard_register_driver(&mfm_driver); - } -} - -static void __exit mfm_exit(void) -{ - if (mfm_addr == ONBOARD_MFM_ADDRESS) - mfm_do_exit(); - else - ecard_unregister_driver(&mfm_driver); -} - -module_init(mfm_init) -module_exit(mfm_exit) -MODULE_LICENSE("GPL"); |