xref: /openbmc/linux/drivers/block/amiflop.c (revision 47225db5)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  linux/amiga/amiflop.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *  Copyright (C) 1993  Greg Harp
51da177e4SLinus Torvalds  *  Portions of this driver are based on code contributed by Brad Pepers
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  revised 28.5.95 by Joerg Dorchain
81da177e4SLinus Torvalds  *  - now no bugs(?) any more for both HD & DD
91da177e4SLinus Torvalds  *  - added support for 40 Track 5.25" drives, 80-track hopefully behaves
101da177e4SLinus Torvalds  *    like 3.5" dd (no way to test - are there any 5.25" drives out there
111da177e4SLinus Torvalds  *    that work on an A4000?)
121da177e4SLinus Torvalds  *  - wrote formatting routine (maybe dirty, but works)
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *  june/july 1995 added ms-dos support by Joerg Dorchain
151da177e4SLinus Torvalds  *  (portions based on messydos.device and various contributors)
161da177e4SLinus Torvalds  *  - currently only 9 and 18 sector disks
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  *  - fixed a bug with the internal trackbuffer when using multiple
191da177e4SLinus Torvalds  *    disks the same time
201da177e4SLinus Torvalds  *  - made formatting a bit safer
211da177e4SLinus Torvalds  *  - added command line and machine based default for "silent" df0
221da177e4SLinus Torvalds  *
231da177e4SLinus Torvalds  *  december 1995 adapted for 1.2.13pl4 by Joerg Dorchain
241da177e4SLinus Torvalds  *  - works but I think it's inefficient. (look in redo_fd_request)
251da177e4SLinus Torvalds  *    But the changes were very efficient. (only three and a half lines)
261da177e4SLinus Torvalds  *
271da177e4SLinus Torvalds  *  january 1996 added special ioctl for tracking down read/write problems
281da177e4SLinus Torvalds  *  - usage ioctl(d, RAW_TRACK, ptr); the raw track buffer (MFM-encoded data
291da177e4SLinus Torvalds  *    is copied to area. (area should be large enough since no checking is
301da177e4SLinus Torvalds  *    done - 30K is currently sufficient). return the actual size of the
311da177e4SLinus Torvalds  *    trackbuffer
321da177e4SLinus Torvalds  *  - replaced udelays() by a timer (CIAA timer B) for the waits
331da177e4SLinus Torvalds  *    needed for the disk mechanic.
341da177e4SLinus Torvalds  *
351da177e4SLinus Torvalds  *  february 1996 fixed error recovery and multiple disk access
361da177e4SLinus Torvalds  *  - both got broken the first time I tampered with the driver :-(
371da177e4SLinus Torvalds  *  - still not safe, but better than before
381da177e4SLinus Torvalds  *
391da177e4SLinus Torvalds  *  revised Marts 3rd, 1996 by Jes Sorensen for use in the 1.3.28 kernel.
401da177e4SLinus Torvalds  *  - Minor changes to accept the kdev_t.
411da177e4SLinus Torvalds  *  - Replaced some more udelays with ms_delays. Udelay is just a loop,
421da177e4SLinus Torvalds  *    and so the delay will be different depending on the given
431da177e4SLinus Torvalds  *    processor :-(
441da177e4SLinus Torvalds  *  - The driver could use a major cleanup because of the new
451da177e4SLinus Torvalds  *    major/minor handling that came with kdev_t. It seems to work for
461da177e4SLinus Torvalds  *    the time being, but I can't guarantee that it will stay like
471da177e4SLinus Torvalds  *    that when we start using 16 (24?) bit minors.
481da177e4SLinus Torvalds  *
491da177e4SLinus Torvalds  * restructured jan 1997 by Joerg Dorchain
501da177e4SLinus Torvalds  * - Fixed Bug accessing multiple disks
511da177e4SLinus Torvalds  * - some code cleanup
521da177e4SLinus Torvalds  * - added trackbuffer for each drive to speed things up
531da177e4SLinus Torvalds  * - fixed some race conditions (who finds the next may send it to me ;-)
541da177e4SLinus Torvalds  */
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds #include <linux/module.h>
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds #include <linux/fd.h>
591da177e4SLinus Torvalds #include <linux/hdreg.h>
601da177e4SLinus Torvalds #include <linux/delay.h>
611da177e4SLinus Torvalds #include <linux/init.h>
621da177e4SLinus Torvalds #include <linux/amifdreg.h>
631da177e4SLinus Torvalds #include <linux/amifd.h>
641da177e4SLinus Torvalds #include <linux/buffer_head.h>
651da177e4SLinus Torvalds #include <linux/blkdev.h>
661da177e4SLinus Torvalds #include <linux/elevator.h>
67b5dc7840SRoman Zippel #include <linux/interrupt.h>
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds #include <asm/setup.h>
701da177e4SLinus Torvalds #include <asm/uaccess.h>
711da177e4SLinus Torvalds #include <asm/amigahw.h>
721da177e4SLinus Torvalds #include <asm/amigaints.h>
731da177e4SLinus Torvalds #include <asm/irq.h>
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds #undef DEBUG /* print _LOTS_ of infos */
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds #define RAW_IOCTL
781da177e4SLinus Torvalds #ifdef RAW_IOCTL
791da177e4SLinus Torvalds #define IOCTL_RAW_TRACK 0x5254524B  /* 'RTRK' */
801da177e4SLinus Torvalds #endif
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds /*
831da177e4SLinus Torvalds  *  Defines
841da177e4SLinus Torvalds  */
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds /*
871da177e4SLinus Torvalds  *  Error codes
881da177e4SLinus Torvalds  */
891da177e4SLinus Torvalds #define FD_OK		0	/* operation succeeded */
901da177e4SLinus Torvalds #define FD_ERROR	-1	/* general error (seek, read, write, etc) */
911da177e4SLinus Torvalds #define FD_NOUNIT	1	/* unit does not exist */
921da177e4SLinus Torvalds #define FD_UNITBUSY	2	/* unit already active */
931da177e4SLinus Torvalds #define FD_NOTACTIVE	3	/* unit is not active */
941da177e4SLinus Torvalds #define FD_NOTREADY	4	/* unit is not ready (motor not on/no disk) */
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds #define MFM_NOSYNC	1
971da177e4SLinus Torvalds #define MFM_HEADER	2
981da177e4SLinus Torvalds #define MFM_DATA	3
991da177e4SLinus Torvalds #define MFM_TRACK	4
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds /*
1021da177e4SLinus Torvalds  *  Floppy ID values
1031da177e4SLinus Torvalds  */
1041da177e4SLinus Torvalds #define FD_NODRIVE	0x00000000  /* response when no unit is present */
1051da177e4SLinus Torvalds #define FD_DD_3 	0xffffffff  /* double-density 3.5" (880K) drive */
1061da177e4SLinus Torvalds #define FD_HD_3 	0x55555555  /* high-density 3.5" (1760K) drive */
1071da177e4SLinus Torvalds #define FD_DD_5 	0xaaaaaaaa  /* double-density 5.25" (440K) drive */
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds static unsigned long int fd_def_df0 = FD_DD_3;     /* default for df0 if it doesn't identify */
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds module_param(fd_def_df0, ulong, 0);
1121da177e4SLinus Torvalds MODULE_LICENSE("GPL");
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds static struct request_queue *floppy_queue;
1151da177e4SLinus Torvalds #define QUEUE (floppy_queue)
1161da177e4SLinus Torvalds #define CURRENT elv_next_request(floppy_queue)
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds /*
1191da177e4SLinus Torvalds  *  Macros
1201da177e4SLinus Torvalds  */
1211da177e4SLinus Torvalds #define MOTOR_ON	(ciab.prb &= ~DSKMOTOR)
1221da177e4SLinus Torvalds #define MOTOR_OFF	(ciab.prb |= DSKMOTOR)
1231da177e4SLinus Torvalds #define SELECT(mask)    (ciab.prb &= ~mask)
1241da177e4SLinus Torvalds #define DESELECT(mask)  (ciab.prb |= mask)
1251da177e4SLinus Torvalds #define SELMASK(drive)  (1 << (3 + (drive & 3)))
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds static struct fd_drive_type drive_types[] = {
1281da177e4SLinus Torvalds /*  code	name	   tr he   rdsz   wrsz sm pc1 pc2 sd  st st*/
1291da177e4SLinus Torvalds /*  warning: times are now in milliseconds (ms)                    */
1301da177e4SLinus Torvalds { FD_DD_3,	"DD 3.5",  80, 2, 14716, 13630, 1, 80,161, 3, 18, 1},
1311da177e4SLinus Torvalds { FD_HD_3,	"HD 3.5",  80, 2, 28344, 27258, 2, 80,161, 3, 18, 1},
1321da177e4SLinus Torvalds { FD_DD_5,	"DD 5.25", 40, 2, 14716, 13630, 1, 40, 81, 6, 30, 2},
1331da177e4SLinus Torvalds { FD_NODRIVE, "No Drive", 0, 0,     0,     0, 0,  0,  0,  0,  0, 0}
1341da177e4SLinus Torvalds };
135945f390fSTobias Klauser static int num_dr_types = ARRAY_SIZE(drive_types);
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds static int amiga_read(int), dos_read(int);
1381da177e4SLinus Torvalds static void amiga_write(int), dos_write(int);
1391da177e4SLinus Torvalds static struct fd_data_type data_types[] = {
1401da177e4SLinus Torvalds 	{ "Amiga", 11 , amiga_read, amiga_write},
1411da177e4SLinus Torvalds 	{ "MS-Dos", 9, dos_read, dos_write}
1421da177e4SLinus Torvalds };
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds /* current info on each unit */
1451da177e4SLinus Torvalds static struct amiga_floppy_struct unit[FD_MAX_UNITS];
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds static struct timer_list flush_track_timer[FD_MAX_UNITS];
1481da177e4SLinus Torvalds static struct timer_list post_write_timer;
1491da177e4SLinus Torvalds static struct timer_list motor_on_timer;
1501da177e4SLinus Torvalds static struct timer_list motor_off_timer[FD_MAX_UNITS];
1511da177e4SLinus Torvalds static int on_attempts;
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds /* Synchronization of FDC access */
1541da177e4SLinus Torvalds /* request loop (trackbuffer) */
1551da177e4SLinus Torvalds static volatile int fdc_busy = -1;
1561da177e4SLinus Torvalds static volatile int fdc_nested;
1571da177e4SLinus Torvalds static DECLARE_WAIT_QUEUE_HEAD(fdc_wait);
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds static DECLARE_WAIT_QUEUE_HEAD(motor_wait);
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds static volatile int selected = -1;	/* currently selected drive */
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds static int writepending;
1641da177e4SLinus Torvalds static int writefromint;
1651da177e4SLinus Torvalds static char *raw_buf;
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds static DEFINE_SPINLOCK(amiflop_lock);
1681da177e4SLinus Torvalds 
1691da177e4SLinus Torvalds #define RAW_BUF_SIZE 30000  /* size of raw disk data */
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds /*
1721da177e4SLinus Torvalds  * These are global variables, as that's the easiest way to give
1731da177e4SLinus Torvalds  * information to interrupts. They are the data used for the current
1741da177e4SLinus Torvalds  * request.
1751da177e4SLinus Torvalds  */
1761da177e4SLinus Torvalds static volatile char block_flag;
1771da177e4SLinus Torvalds static DECLARE_WAIT_QUEUE_HEAD(wait_fd_block);
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds /* MS-Dos MFM Coding tables (should go quick and easy) */
1801da177e4SLinus Torvalds static unsigned char mfmencode[16]={
1811da177e4SLinus Torvalds 	0x2a, 0x29, 0x24, 0x25, 0x12, 0x11, 0x14, 0x15,
1821da177e4SLinus Torvalds 	0x4a, 0x49, 0x44, 0x45, 0x52, 0x51, 0x54, 0x55
1831da177e4SLinus Torvalds };
1841da177e4SLinus Torvalds static unsigned char mfmdecode[128];
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds /* floppy internal millisecond timer stuff */
1871da177e4SLinus Torvalds static volatile int ms_busy = -1;
1881da177e4SLinus Torvalds static DECLARE_WAIT_QUEUE_HEAD(ms_wait);
1891da177e4SLinus Torvalds #define MS_TICKS ((amiga_eclock+50)/1000)
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds /*
1921da177e4SLinus Torvalds  * Note that MAX_ERRORS=X doesn't imply that we retry every bad read
1931da177e4SLinus Torvalds  * max X times - some types of errors increase the errorcount by 2 or
1941da177e4SLinus Torvalds  * even 3, so we might actually retry only X/2 times before giving up.
1951da177e4SLinus Torvalds  */
1961da177e4SLinus Torvalds #define MAX_ERRORS 12
1971da177e4SLinus Torvalds 
198b4290a23SAl Viro #define custom amiga_custom
199b4290a23SAl Viro 
2001da177e4SLinus Torvalds /* Prevent "aliased" accesses. */
2011da177e4SLinus Torvalds static int fd_ref[4] = { 0,0,0,0 };
2021da177e4SLinus Torvalds static int fd_device[4] = { 0, 0, 0, 0 };
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds /*
2051da177e4SLinus Torvalds  * Here come the actual hardware access and helper functions.
2061da177e4SLinus Torvalds  * They are not reentrant and single threaded because all drives
2071da177e4SLinus Torvalds  * share the same hardware and the same trackbuffer.
2081da177e4SLinus Torvalds  */
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds /* Milliseconds timer */
2111da177e4SLinus Torvalds 
2127d12e780SDavid Howells static irqreturn_t ms_isr(int irq, void *dummy)
2131da177e4SLinus Torvalds {
2141da177e4SLinus Torvalds 	ms_busy = -1;
2151da177e4SLinus Torvalds 	wake_up(&ms_wait);
2161da177e4SLinus Torvalds 	return IRQ_HANDLED;
2171da177e4SLinus Torvalds }
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds /* all waits are queued up
2201da177e4SLinus Torvalds    A more generic routine would do a schedule a la timer.device */
2211da177e4SLinus Torvalds static void ms_delay(int ms)
2221da177e4SLinus Torvalds {
2231da177e4SLinus Torvalds 	unsigned long flags;
2241da177e4SLinus Torvalds 	int ticks;
2251da177e4SLinus Torvalds 	if (ms > 0) {
2261da177e4SLinus Torvalds 		local_irq_save(flags);
2271da177e4SLinus Torvalds 		while (ms_busy == 0)
2281da177e4SLinus Torvalds 			sleep_on(&ms_wait);
2291da177e4SLinus Torvalds 		ms_busy = 0;
2301da177e4SLinus Torvalds 		local_irq_restore(flags);
2311da177e4SLinus Torvalds 		ticks = MS_TICKS*ms-1;
2321da177e4SLinus Torvalds 		ciaa.tblo=ticks%256;
2331da177e4SLinus Torvalds 		ciaa.tbhi=ticks/256;
2341da177e4SLinus Torvalds 		ciaa.crb=0x19; /*count eclock, force load, one-shoot, start */
2351da177e4SLinus Torvalds 		sleep_on(&ms_wait);
2361da177e4SLinus Torvalds 	}
2371da177e4SLinus Torvalds }
2381da177e4SLinus Torvalds 
2391da177e4SLinus Torvalds /* Hardware semaphore */
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds /* returns true when we would get the semaphore */
2421da177e4SLinus Torvalds static inline int try_fdc(int drive)
2431da177e4SLinus Torvalds {
2441da177e4SLinus Torvalds 	drive &= 3;
2451da177e4SLinus Torvalds 	return ((fdc_busy < 0) || (fdc_busy == drive));
2461da177e4SLinus Torvalds }
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds static void get_fdc(int drive)
2491da177e4SLinus Torvalds {
2501da177e4SLinus Torvalds 	unsigned long flags;
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds 	drive &= 3;
2531da177e4SLinus Torvalds #ifdef DEBUG
2541da177e4SLinus Torvalds 	printk("get_fdc: drive %d  fdc_busy %d  fdc_nested %d\n",drive,fdc_busy,fdc_nested);
2551da177e4SLinus Torvalds #endif
2561da177e4SLinus Torvalds 	local_irq_save(flags);
2571da177e4SLinus Torvalds 	while (!try_fdc(drive))
2581da177e4SLinus Torvalds 		sleep_on(&fdc_wait);
2591da177e4SLinus Torvalds 	fdc_busy = drive;
2601da177e4SLinus Torvalds 	fdc_nested++;
2611da177e4SLinus Torvalds 	local_irq_restore(flags);
2621da177e4SLinus Torvalds }
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds static inline void rel_fdc(void)
2651da177e4SLinus Torvalds {
2661da177e4SLinus Torvalds #ifdef DEBUG
2671da177e4SLinus Torvalds 	if (fdc_nested == 0)
2681da177e4SLinus Torvalds 		printk("fd: unmatched rel_fdc\n");
2691da177e4SLinus Torvalds 	printk("rel_fdc: fdc_busy %d fdc_nested %d\n",fdc_busy,fdc_nested);
2701da177e4SLinus Torvalds #endif
2711da177e4SLinus Torvalds 	fdc_nested--;
2721da177e4SLinus Torvalds 	if (fdc_nested == 0) {
2731da177e4SLinus Torvalds 		fdc_busy = -1;
2741da177e4SLinus Torvalds 		wake_up(&fdc_wait);
2751da177e4SLinus Torvalds 	}
2761da177e4SLinus Torvalds }
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds static void fd_select (int drive)
2791da177e4SLinus Torvalds {
2801da177e4SLinus Torvalds 	unsigned char prb = ~0;
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds 	drive&=3;
2831da177e4SLinus Torvalds #ifdef DEBUG
2841da177e4SLinus Torvalds 	printk("selecting %d\n",drive);
2851da177e4SLinus Torvalds #endif
2861da177e4SLinus Torvalds 	if (drive == selected)
2871da177e4SLinus Torvalds 		return;
2881da177e4SLinus Torvalds 	get_fdc(drive);
2891da177e4SLinus Torvalds 	selected = drive;
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds 	if (unit[drive].track % 2 != 0)
2921da177e4SLinus Torvalds 		prb &= ~DSKSIDE;
2931da177e4SLinus Torvalds 	if (unit[drive].motor == 1)
2941da177e4SLinus Torvalds 		prb &= ~DSKMOTOR;
2951da177e4SLinus Torvalds 	ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
2961da177e4SLinus Torvalds 	ciab.prb = prb;
2971da177e4SLinus Torvalds 	prb &= ~SELMASK(drive);
2981da177e4SLinus Torvalds 	ciab.prb = prb;
2991da177e4SLinus Torvalds 	rel_fdc();
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds static void fd_deselect (int drive)
3031da177e4SLinus Torvalds {
3041da177e4SLinus Torvalds 	unsigned char prb;
3051da177e4SLinus Torvalds 	unsigned long flags;
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 	drive&=3;
3081da177e4SLinus Torvalds #ifdef DEBUG
3091da177e4SLinus Torvalds 	printk("deselecting %d\n",drive);
3101da177e4SLinus Torvalds #endif
3111da177e4SLinus Torvalds 	if (drive != selected) {
3121da177e4SLinus Torvalds 		printk(KERN_WARNING "Deselecting drive %d while %d was selected!\n",drive,selected);
3131da177e4SLinus Torvalds 		return;
3141da177e4SLinus Torvalds 	}
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds 	get_fdc(drive);
3171da177e4SLinus Torvalds 	local_irq_save(flags);
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds 	selected = -1;
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 	prb = ciab.prb;
3221da177e4SLinus Torvalds 	prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
3231da177e4SLinus Torvalds 	ciab.prb = prb;
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 	local_irq_restore (flags);
3261da177e4SLinus Torvalds 	rel_fdc();
3271da177e4SLinus Torvalds 
3281da177e4SLinus Torvalds }
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds static void motor_on_callback(unsigned long nr)
3311da177e4SLinus Torvalds {
3321da177e4SLinus Torvalds 	if (!(ciaa.pra & DSKRDY) || --on_attempts == 0) {
3331da177e4SLinus Torvalds 		wake_up (&motor_wait);
3341da177e4SLinus Torvalds 	} else {
3351da177e4SLinus Torvalds 		motor_on_timer.expires = jiffies + HZ/10;
3361da177e4SLinus Torvalds 		add_timer(&motor_on_timer);
3371da177e4SLinus Torvalds 	}
3381da177e4SLinus Torvalds }
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds static int fd_motor_on(int nr)
3411da177e4SLinus Torvalds {
3421da177e4SLinus Torvalds 	nr &= 3;
3431da177e4SLinus Torvalds 
3441da177e4SLinus Torvalds 	del_timer(motor_off_timer + nr);
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds 	if (!unit[nr].motor) {
3471da177e4SLinus Torvalds 		unit[nr].motor = 1;
3481da177e4SLinus Torvalds 		fd_select(nr);
3491da177e4SLinus Torvalds 
3501da177e4SLinus Torvalds 		motor_on_timer.data = nr;
3511da177e4SLinus Torvalds 		mod_timer(&motor_on_timer, jiffies + HZ/2);
3521da177e4SLinus Torvalds 
3531da177e4SLinus Torvalds 		on_attempts = 10;
3541da177e4SLinus Torvalds 		sleep_on (&motor_wait);
3551da177e4SLinus Torvalds 		fd_deselect(nr);
3561da177e4SLinus Torvalds 	}
3571da177e4SLinus Torvalds 
3581da177e4SLinus Torvalds 	if (on_attempts == 0) {
3591da177e4SLinus Torvalds 		on_attempts = -1;
3601da177e4SLinus Torvalds #if 0
3611da177e4SLinus Torvalds 		printk (KERN_ERR "motor_on failed, turning motor off\n");
3621da177e4SLinus Torvalds 		fd_motor_off (nr);
3631da177e4SLinus Torvalds 		return 0;
3641da177e4SLinus Torvalds #else
3651da177e4SLinus Torvalds 		printk (KERN_WARNING "DSKRDY not set after 1.5 seconds - assuming drive is spinning notwithstanding\n");
3661da177e4SLinus Torvalds #endif
3671da177e4SLinus Torvalds 	}
3681da177e4SLinus Torvalds 
3691da177e4SLinus Torvalds 	return 1;
3701da177e4SLinus Torvalds }
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds static void fd_motor_off(unsigned long drive)
3731da177e4SLinus Torvalds {
3741da177e4SLinus Torvalds 	long calledfromint;
3751da177e4SLinus Torvalds #ifdef MODULE
3761da177e4SLinus Torvalds 	long decusecount;
3771da177e4SLinus Torvalds 
3781da177e4SLinus Torvalds 	decusecount = drive & 0x40000000;
3791da177e4SLinus Torvalds #endif
3801da177e4SLinus Torvalds 	calledfromint = drive & 0x80000000;
3811da177e4SLinus Torvalds 	drive&=3;
3821da177e4SLinus Torvalds 	if (calledfromint && !try_fdc(drive)) {
3831da177e4SLinus Torvalds 		/* We would be blocked in an interrupt, so try again later */
3841da177e4SLinus Torvalds 		motor_off_timer[drive].expires = jiffies + 1;
3851da177e4SLinus Torvalds 		add_timer(motor_off_timer + drive);
3861da177e4SLinus Torvalds 		return;
3871da177e4SLinus Torvalds 	}
3881da177e4SLinus Torvalds 	unit[drive].motor = 0;
3891da177e4SLinus Torvalds 	fd_select(drive);
3901da177e4SLinus Torvalds 	udelay (1);
3911da177e4SLinus Torvalds 	fd_deselect(drive);
3921da177e4SLinus Torvalds }
3931da177e4SLinus Torvalds 
3941da177e4SLinus Torvalds static void floppy_off (unsigned int nr)
3951da177e4SLinus Torvalds {
3961da177e4SLinus Torvalds 	int drive;
3971da177e4SLinus Torvalds 
3981da177e4SLinus Torvalds 	drive = nr & 3;
3991da177e4SLinus Torvalds 	/* called this way it is always from interrupt */
4001da177e4SLinus Torvalds 	motor_off_timer[drive].data = nr | 0x80000000;
4011da177e4SLinus Torvalds 	mod_timer(motor_off_timer + drive, jiffies + 3*HZ);
4021da177e4SLinus Torvalds }
4031da177e4SLinus Torvalds 
4041da177e4SLinus Torvalds static int fd_calibrate(int drive)
4051da177e4SLinus Torvalds {
4061da177e4SLinus Torvalds 	unsigned char prb;
4071da177e4SLinus Torvalds 	int n;
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds 	drive &= 3;
4101da177e4SLinus Torvalds 	get_fdc(drive);
4111da177e4SLinus Torvalds 	if (!fd_motor_on (drive))
4121da177e4SLinus Torvalds 		return 0;
4131da177e4SLinus Torvalds 	fd_select (drive);
4141da177e4SLinus Torvalds 	prb = ciab.prb;
4151da177e4SLinus Torvalds 	prb |= DSKSIDE;
4161da177e4SLinus Torvalds 	prb &= ~DSKDIREC;
4171da177e4SLinus Torvalds 	ciab.prb = prb;
4181da177e4SLinus Torvalds 	for (n = unit[drive].type->tracks/2; n != 0; --n) {
4191da177e4SLinus Torvalds 		if (ciaa.pra & DSKTRACK0)
4201da177e4SLinus Torvalds 			break;
4211da177e4SLinus Torvalds 		prb &= ~DSKSTEP;
4221da177e4SLinus Torvalds 		ciab.prb = prb;
4231da177e4SLinus Torvalds 		prb |= DSKSTEP;
4241da177e4SLinus Torvalds 		udelay (2);
4251da177e4SLinus Torvalds 		ciab.prb = prb;
4261da177e4SLinus Torvalds 		ms_delay(unit[drive].type->step_delay);
4271da177e4SLinus Torvalds 	}
4281da177e4SLinus Torvalds 	ms_delay (unit[drive].type->settle_time);
4291da177e4SLinus Torvalds 	prb |= DSKDIREC;
4301da177e4SLinus Torvalds 	n = unit[drive].type->tracks + 20;
4311da177e4SLinus Torvalds 	for (;;) {
4321da177e4SLinus Torvalds 		prb &= ~DSKSTEP;
4331da177e4SLinus Torvalds 		ciab.prb = prb;
4341da177e4SLinus Torvalds 		prb |= DSKSTEP;
4351da177e4SLinus Torvalds 		udelay (2);
4361da177e4SLinus Torvalds 		ciab.prb = prb;
4371da177e4SLinus Torvalds 		ms_delay(unit[drive].type->step_delay + 1);
4381da177e4SLinus Torvalds 		if ((ciaa.pra & DSKTRACK0) == 0)
4391da177e4SLinus Torvalds 			break;
4401da177e4SLinus Torvalds 		if (--n == 0) {
4411da177e4SLinus Torvalds 			printk (KERN_ERR "fd%d: calibrate failed, turning motor off\n", drive);
4421da177e4SLinus Torvalds 			fd_motor_off (drive);
4431da177e4SLinus Torvalds 			unit[drive].track = -1;
4441da177e4SLinus Torvalds 			rel_fdc();
4451da177e4SLinus Torvalds 			return 0;
4461da177e4SLinus Torvalds 		}
4471da177e4SLinus Torvalds 	}
4481da177e4SLinus Torvalds 	unit[drive].track = 0;
4491da177e4SLinus Torvalds 	ms_delay(unit[drive].type->settle_time);
4501da177e4SLinus Torvalds 
4511da177e4SLinus Torvalds 	rel_fdc();
4521da177e4SLinus Torvalds 	fd_deselect(drive);
4531da177e4SLinus Torvalds 	return 1;
4541da177e4SLinus Torvalds }
4551da177e4SLinus Torvalds 
4561da177e4SLinus Torvalds static int fd_seek(int drive, int track)
4571da177e4SLinus Torvalds {
4581da177e4SLinus Torvalds 	unsigned char prb;
4591da177e4SLinus Torvalds 	int cnt;
4601da177e4SLinus Torvalds 
4611da177e4SLinus Torvalds #ifdef DEBUG
4621da177e4SLinus Torvalds 	printk("seeking drive %d to track %d\n",drive,track);
4631da177e4SLinus Torvalds #endif
4641da177e4SLinus Torvalds 	drive &= 3;
4651da177e4SLinus Torvalds 	get_fdc(drive);
4661da177e4SLinus Torvalds 	if (unit[drive].track == track) {
4671da177e4SLinus Torvalds 		rel_fdc();
4681da177e4SLinus Torvalds 		return 1;
4691da177e4SLinus Torvalds 	}
4701da177e4SLinus Torvalds 	if (!fd_motor_on(drive)) {
4711da177e4SLinus Torvalds 		rel_fdc();
4721da177e4SLinus Torvalds 		return 0;
4731da177e4SLinus Torvalds 	}
4741da177e4SLinus Torvalds 	if (unit[drive].track < 0 && !fd_calibrate(drive)) {
4751da177e4SLinus Torvalds 		rel_fdc();
4761da177e4SLinus Torvalds 		return 0;
4771da177e4SLinus Torvalds 	}
4781da177e4SLinus Torvalds 
4791da177e4SLinus Torvalds 	fd_select (drive);
4801da177e4SLinus Torvalds 	cnt = unit[drive].track/2 - track/2;
4811da177e4SLinus Torvalds 	prb = ciab.prb;
4821da177e4SLinus Torvalds 	prb |= DSKSIDE | DSKDIREC;
4831da177e4SLinus Torvalds 	if (track % 2 != 0)
4841da177e4SLinus Torvalds 		prb &= ~DSKSIDE;
4851da177e4SLinus Torvalds 	if (cnt < 0) {
4861da177e4SLinus Torvalds 		cnt = - cnt;
4871da177e4SLinus Torvalds 		prb &= ~DSKDIREC;
4881da177e4SLinus Torvalds 	}
4891da177e4SLinus Torvalds 	ciab.prb = prb;
4901da177e4SLinus Torvalds 	if (track % 2 != unit[drive].track % 2)
4911da177e4SLinus Torvalds 		ms_delay (unit[drive].type->side_time);
4921da177e4SLinus Torvalds 	unit[drive].track = track;
4931da177e4SLinus Torvalds 	if (cnt == 0) {
4941da177e4SLinus Torvalds 		rel_fdc();
4951da177e4SLinus Torvalds 		fd_deselect(drive);
4961da177e4SLinus Torvalds 		return 1;
4971da177e4SLinus Torvalds 	}
4981da177e4SLinus Torvalds 	do {
4991da177e4SLinus Torvalds 		prb &= ~DSKSTEP;
5001da177e4SLinus Torvalds 		ciab.prb = prb;
5011da177e4SLinus Torvalds 		prb |= DSKSTEP;
5021da177e4SLinus Torvalds 		udelay (1);
5031da177e4SLinus Torvalds 		ciab.prb = prb;
5041da177e4SLinus Torvalds 		ms_delay (unit[drive].type->step_delay);
5051da177e4SLinus Torvalds 	} while (--cnt != 0);
5061da177e4SLinus Torvalds 	ms_delay (unit[drive].type->settle_time);
5071da177e4SLinus Torvalds 
5081da177e4SLinus Torvalds 	rel_fdc();
5091da177e4SLinus Torvalds 	fd_deselect(drive);
5101da177e4SLinus Torvalds 	return 1;
5111da177e4SLinus Torvalds }
5121da177e4SLinus Torvalds 
5131da177e4SLinus Torvalds static unsigned long fd_get_drive_id(int drive)
5141da177e4SLinus Torvalds {
5151da177e4SLinus Torvalds 	int i;
5161da177e4SLinus Torvalds 	ulong id = 0;
5171da177e4SLinus Torvalds 
5181da177e4SLinus Torvalds   	drive&=3;
5191da177e4SLinus Torvalds   	get_fdc(drive);
5201da177e4SLinus Torvalds 	/* set up for ID */
5211da177e4SLinus Torvalds 	MOTOR_ON;
5221da177e4SLinus Torvalds 	udelay(2);
5231da177e4SLinus Torvalds 	SELECT(SELMASK(drive));
5241da177e4SLinus Torvalds 	udelay(2);
5251da177e4SLinus Torvalds 	DESELECT(SELMASK(drive));
5261da177e4SLinus Torvalds 	udelay(2);
5271da177e4SLinus Torvalds 	MOTOR_OFF;
5281da177e4SLinus Torvalds 	udelay(2);
5291da177e4SLinus Torvalds 	SELECT(SELMASK(drive));
5301da177e4SLinus Torvalds 	udelay(2);
5311da177e4SLinus Torvalds 	DESELECT(SELMASK(drive));
5321da177e4SLinus Torvalds 	udelay(2);
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds 	/* loop and read disk ID */
5351da177e4SLinus Torvalds 	for (i=0; i<32; i++) {
5361da177e4SLinus Torvalds 		SELECT(SELMASK(drive));
5371da177e4SLinus Torvalds 		udelay(2);
5381da177e4SLinus Torvalds 
5391da177e4SLinus Torvalds 		/* read and store value of DSKRDY */
5401da177e4SLinus Torvalds 		id <<= 1;
5411da177e4SLinus Torvalds 		id |= (ciaa.pra & DSKRDY) ? 0 : 1;	/* cia regs are low-active! */
5421da177e4SLinus Torvalds 
5431da177e4SLinus Torvalds 		DESELECT(SELMASK(drive));
5441da177e4SLinus Torvalds 	}
5451da177e4SLinus Torvalds 
5461da177e4SLinus Torvalds 	rel_fdc();
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds         /*
5491da177e4SLinus Torvalds          * RB: At least A500/A2000's df0: don't identify themselves.
5501da177e4SLinus Torvalds          * As every (real) Amiga has at least a 3.5" DD drive as df0:
5511da177e4SLinus Torvalds          * we default to that if df0: doesn't identify as a certain
5521da177e4SLinus Torvalds          * type.
5531da177e4SLinus Torvalds          */
5541da177e4SLinus Torvalds         if(drive == 0 && id == FD_NODRIVE)
5551da177e4SLinus Torvalds 	{
5561da177e4SLinus Torvalds                 id = fd_def_df0;
5571da177e4SLinus Torvalds                 printk(KERN_NOTICE "fd: drive 0 didn't identify, setting default %08lx\n", (ulong)fd_def_df0);
5581da177e4SLinus Torvalds 	}
5591da177e4SLinus Torvalds 	/* return the ID value */
5601da177e4SLinus Torvalds 	return (id);
5611da177e4SLinus Torvalds }
5621da177e4SLinus Torvalds 
5637d12e780SDavid Howells static irqreturn_t fd_block_done(int irq, void *dummy)
5641da177e4SLinus Torvalds {
5651da177e4SLinus Torvalds 	if (block_flag)
5661da177e4SLinus Torvalds 		custom.dsklen = 0x4000;
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds 	if (block_flag == 2) { /* writing */
5691da177e4SLinus Torvalds 		writepending = 2;
5701da177e4SLinus Torvalds 		post_write_timer.expires = jiffies + 1; /* at least 2 ms */
5711da177e4SLinus Torvalds 		post_write_timer.data = selected;
5721da177e4SLinus Torvalds 		add_timer(&post_write_timer);
5731da177e4SLinus Torvalds 	}
5741da177e4SLinus Torvalds 	else {                /* reading */
5751da177e4SLinus Torvalds 		block_flag = 0;
5761da177e4SLinus Torvalds 		wake_up (&wait_fd_block);
5771da177e4SLinus Torvalds 	}
5781da177e4SLinus Torvalds 	return IRQ_HANDLED;
5791da177e4SLinus Torvalds }
5801da177e4SLinus Torvalds 
5811da177e4SLinus Torvalds static void raw_read(int drive)
5821da177e4SLinus Torvalds {
5831da177e4SLinus Torvalds 	drive&=3;
5841da177e4SLinus Torvalds 	get_fdc(drive);
5851da177e4SLinus Torvalds 	while (block_flag)
5861da177e4SLinus Torvalds 		sleep_on(&wait_fd_block);
5871da177e4SLinus Torvalds 	fd_select(drive);
5881da177e4SLinus Torvalds 	/* setup adkcon bits correctly */
5891da177e4SLinus Torvalds 	custom.adkcon = ADK_MSBSYNC;
5901da177e4SLinus Torvalds 	custom.adkcon = ADK_SETCLR|ADK_WORDSYNC|ADK_FAST;
5911da177e4SLinus Torvalds 
5921da177e4SLinus Torvalds 	custom.dsksync = MFM_SYNC;
5931da177e4SLinus Torvalds 
5941da177e4SLinus Torvalds 	custom.dsklen = 0;
5951da177e4SLinus Torvalds 	custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)raw_buf);
5961da177e4SLinus Torvalds 	custom.dsklen = unit[drive].type->read_size/sizeof(short) | DSKLEN_DMAEN;
5971da177e4SLinus Torvalds 	custom.dsklen = unit[drive].type->read_size/sizeof(short) | DSKLEN_DMAEN;
5981da177e4SLinus Torvalds 
5991da177e4SLinus Torvalds 	block_flag = 1;
6001da177e4SLinus Torvalds 
6011da177e4SLinus Torvalds 	while (block_flag)
6021da177e4SLinus Torvalds 		sleep_on (&wait_fd_block);
6031da177e4SLinus Torvalds 
6041da177e4SLinus Torvalds 	custom.dsklen = 0;
6051da177e4SLinus Torvalds 	fd_deselect(drive);
6061da177e4SLinus Torvalds 	rel_fdc();
6071da177e4SLinus Torvalds }
6081da177e4SLinus Torvalds 
6091da177e4SLinus Torvalds static int raw_write(int drive)
6101da177e4SLinus Torvalds {
6111da177e4SLinus Torvalds 	ushort adk;
6121da177e4SLinus Torvalds 
6131da177e4SLinus Torvalds 	drive&=3;
6141da177e4SLinus Torvalds 	get_fdc(drive); /* corresponds to rel_fdc() in post_write() */
6151da177e4SLinus Torvalds 	if ((ciaa.pra & DSKPROT) == 0) {
6161da177e4SLinus Torvalds 		rel_fdc();
6171da177e4SLinus Torvalds 		return 0;
6181da177e4SLinus Torvalds 	}
6191da177e4SLinus Torvalds 	while (block_flag)
6201da177e4SLinus Torvalds 		sleep_on(&wait_fd_block);
6211da177e4SLinus Torvalds 	fd_select(drive);
6221da177e4SLinus Torvalds 	/* clear adkcon bits */
6231da177e4SLinus Torvalds 	custom.adkcon = ADK_PRECOMP1|ADK_PRECOMP0|ADK_WORDSYNC|ADK_MSBSYNC;
6241da177e4SLinus Torvalds 	/* set appropriate adkcon bits */
6251da177e4SLinus Torvalds 	adk = ADK_SETCLR|ADK_FAST;
6261da177e4SLinus Torvalds 	if ((ulong)unit[drive].track >= unit[drive].type->precomp2)
6271da177e4SLinus Torvalds 		adk |= ADK_PRECOMP1;
6281da177e4SLinus Torvalds 	else if ((ulong)unit[drive].track >= unit[drive].type->precomp1)
6291da177e4SLinus Torvalds 		adk |= ADK_PRECOMP0;
6301da177e4SLinus Torvalds 	custom.adkcon = adk;
6311da177e4SLinus Torvalds 
6321da177e4SLinus Torvalds 	custom.dsklen = DSKLEN_WRITE;
6331da177e4SLinus Torvalds 	custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)raw_buf);
6341da177e4SLinus Torvalds 	custom.dsklen = unit[drive].type->write_size/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE;
6351da177e4SLinus Torvalds 	custom.dsklen = unit[drive].type->write_size/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE;
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds 	block_flag = 2;
6381da177e4SLinus Torvalds 	return 1;
6391da177e4SLinus Torvalds }
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds /*
6421da177e4SLinus Torvalds  * to be called at least 2ms after the write has finished but before any
6431da177e4SLinus Torvalds  * other access to the hardware.
6441da177e4SLinus Torvalds  */
6451da177e4SLinus Torvalds static void post_write (unsigned long drive)
6461da177e4SLinus Torvalds {
6471da177e4SLinus Torvalds #ifdef DEBUG
6481da177e4SLinus Torvalds 	printk("post_write for drive %ld\n",drive);
6491da177e4SLinus Torvalds #endif
6501da177e4SLinus Torvalds 	drive &= 3;
6511da177e4SLinus Torvalds 	custom.dsklen = 0;
6521da177e4SLinus Torvalds 	block_flag = 0;
6531da177e4SLinus Torvalds 	writepending = 0;
6541da177e4SLinus Torvalds 	writefromint = 0;
6551da177e4SLinus Torvalds 	unit[drive].dirty = 0;
6561da177e4SLinus Torvalds 	wake_up(&wait_fd_block);
6571da177e4SLinus Torvalds 	fd_deselect(drive);
6581da177e4SLinus Torvalds 	rel_fdc(); /* corresponds to get_fdc() in raw_write */
6591da177e4SLinus Torvalds }
6601da177e4SLinus Torvalds 
6611da177e4SLinus Torvalds 
6621da177e4SLinus Torvalds /*
6631da177e4SLinus Torvalds  * The following functions are to convert the block contents into raw data
6641da177e4SLinus Torvalds  * written to disk and vice versa.
6651da177e4SLinus Torvalds  * (Add other formats here ;-))
6661da177e4SLinus Torvalds  */
6671da177e4SLinus Torvalds 
6681da177e4SLinus Torvalds static unsigned long scan_sync(unsigned long raw, unsigned long end)
6691da177e4SLinus Torvalds {
6701da177e4SLinus Torvalds 	ushort *ptr = (ushort *)raw, *endp = (ushort *)end;
6711da177e4SLinus Torvalds 
6721da177e4SLinus Torvalds 	while (ptr < endp && *ptr++ != 0x4489)
6731da177e4SLinus Torvalds 		;
6741da177e4SLinus Torvalds 	if (ptr < endp) {
6751da177e4SLinus Torvalds 		while (*ptr == 0x4489 && ptr < endp)
6761da177e4SLinus Torvalds 			ptr++;
6771da177e4SLinus Torvalds 		return (ulong)ptr;
6781da177e4SLinus Torvalds 	}
6791da177e4SLinus Torvalds 	return 0;
6801da177e4SLinus Torvalds }
6811da177e4SLinus Torvalds 
6821da177e4SLinus Torvalds static inline unsigned long checksum(unsigned long *addr, int len)
6831da177e4SLinus Torvalds {
6841da177e4SLinus Torvalds 	unsigned long csum = 0;
6851da177e4SLinus Torvalds 
6861da177e4SLinus Torvalds 	len /= sizeof(*addr);
6871da177e4SLinus Torvalds 	while (len-- > 0)
6881da177e4SLinus Torvalds 		csum ^= *addr++;
6891da177e4SLinus Torvalds 	csum = ((csum>>1) & 0x55555555)  ^  (csum & 0x55555555);
6901da177e4SLinus Torvalds 
6911da177e4SLinus Torvalds 	return csum;
6921da177e4SLinus Torvalds }
6931da177e4SLinus Torvalds 
6941da177e4SLinus Torvalds static unsigned long decode (unsigned long *data, unsigned long *raw,
6951da177e4SLinus Torvalds 			     int len)
6961da177e4SLinus Torvalds {
6971da177e4SLinus Torvalds 	ulong *odd, *even;
6981da177e4SLinus Torvalds 
6991da177e4SLinus Torvalds 	/* convert length from bytes to longwords */
7001da177e4SLinus Torvalds 	len >>= 2;
7011da177e4SLinus Torvalds 	odd = raw;
7021da177e4SLinus Torvalds 	even = odd + len;
7031da177e4SLinus Torvalds 
7041da177e4SLinus Torvalds 	/* prepare return pointer */
7051da177e4SLinus Torvalds 	raw += len * 2;
7061da177e4SLinus Torvalds 
7071da177e4SLinus Torvalds 	do {
7081da177e4SLinus Torvalds 		*data++ = ((*odd++ & 0x55555555) << 1) | (*even++ & 0x55555555);
7091da177e4SLinus Torvalds 	} while (--len != 0);
7101da177e4SLinus Torvalds 
7111da177e4SLinus Torvalds 	return (ulong)raw;
7121da177e4SLinus Torvalds }
7131da177e4SLinus Torvalds 
7141da177e4SLinus Torvalds struct header {
7151da177e4SLinus Torvalds 	unsigned char magic;
7161da177e4SLinus Torvalds 	unsigned char track;
7171da177e4SLinus Torvalds 	unsigned char sect;
7181da177e4SLinus Torvalds 	unsigned char ord;
7191da177e4SLinus Torvalds 	unsigned char labels[16];
7201da177e4SLinus Torvalds 	unsigned long hdrchk;
7211da177e4SLinus Torvalds 	unsigned long datachk;
7221da177e4SLinus Torvalds };
7231da177e4SLinus Torvalds 
7241da177e4SLinus Torvalds static int amiga_read(int drive)
7251da177e4SLinus Torvalds {
7261da177e4SLinus Torvalds 	unsigned long raw;
7271da177e4SLinus Torvalds 	unsigned long end;
7281da177e4SLinus Torvalds 	int scnt;
7291da177e4SLinus Torvalds 	unsigned long csum;
7301da177e4SLinus Torvalds 	struct header hdr;
7311da177e4SLinus Torvalds 
7321da177e4SLinus Torvalds 	drive&=3;
7331da177e4SLinus Torvalds 	raw = (long) raw_buf;
7341da177e4SLinus Torvalds 	end = raw + unit[drive].type->read_size;
7351da177e4SLinus Torvalds 
7361da177e4SLinus Torvalds 	for (scnt = 0;scnt < unit[drive].dtype->sects * unit[drive].type->sect_mult; scnt++) {
7371da177e4SLinus Torvalds 		if (!(raw = scan_sync(raw, end))) {
7381da177e4SLinus Torvalds 			printk (KERN_INFO "can't find sync for sector %d\n", scnt);
7391da177e4SLinus Torvalds 			return MFM_NOSYNC;
7401da177e4SLinus Torvalds 		}
7411da177e4SLinus Torvalds 
7421da177e4SLinus Torvalds 		raw = decode ((ulong *)&hdr.magic, (ulong *)raw, 4);
7431da177e4SLinus Torvalds 		raw = decode ((ulong *)&hdr.labels, (ulong *)raw, 16);
7441da177e4SLinus Torvalds 		raw = decode ((ulong *)&hdr.hdrchk, (ulong *)raw, 4);
7451da177e4SLinus Torvalds 		raw = decode ((ulong *)&hdr.datachk, (ulong *)raw, 4);
7461da177e4SLinus Torvalds 		csum = checksum((ulong *)&hdr,
7471da177e4SLinus Torvalds 				(char *)&hdr.hdrchk-(char *)&hdr);
7481da177e4SLinus Torvalds 
7491da177e4SLinus Torvalds #ifdef DEBUG
7501da177e4SLinus Torvalds 		printk ("(%x,%d,%d,%d) (%lx,%lx,%lx,%lx) %lx %lx\n",
7511da177e4SLinus Torvalds 			hdr.magic, hdr.track, hdr.sect, hdr.ord,
7521da177e4SLinus Torvalds 			*(ulong *)&hdr.labels[0], *(ulong *)&hdr.labels[4],
7531da177e4SLinus Torvalds 			*(ulong *)&hdr.labels[8], *(ulong *)&hdr.labels[12],
7541da177e4SLinus Torvalds 			hdr.hdrchk, hdr.datachk);
7551da177e4SLinus Torvalds #endif
7561da177e4SLinus Torvalds 
7571da177e4SLinus Torvalds 		if (hdr.hdrchk != csum) {
7581da177e4SLinus Torvalds 			printk(KERN_INFO "MFM_HEADER: %08lx,%08lx\n", hdr.hdrchk, csum);
7591da177e4SLinus Torvalds 			return MFM_HEADER;
7601da177e4SLinus Torvalds 		}
7611da177e4SLinus Torvalds 
7621da177e4SLinus Torvalds 		/* verify track */
7631da177e4SLinus Torvalds 		if (hdr.track != unit[drive].track) {
7641da177e4SLinus Torvalds 			printk(KERN_INFO "MFM_TRACK: %d, %d\n", hdr.track, unit[drive].track);
7651da177e4SLinus Torvalds 			return MFM_TRACK;
7661da177e4SLinus Torvalds 		}
7671da177e4SLinus Torvalds 
7681da177e4SLinus Torvalds 		raw = decode ((ulong *)(unit[drive].trackbuf + hdr.sect*512),
7691da177e4SLinus Torvalds 			      (ulong *)raw, 512);
7701da177e4SLinus Torvalds 		csum = checksum((ulong *)(unit[drive].trackbuf + hdr.sect*512), 512);
7711da177e4SLinus Torvalds 
7721da177e4SLinus Torvalds 		if (hdr.datachk != csum) {
7731da177e4SLinus Torvalds 			printk(KERN_INFO "MFM_DATA: (%x:%d:%d:%d) sc=%d %lx, %lx\n",
7741da177e4SLinus Torvalds 			       hdr.magic, hdr.track, hdr.sect, hdr.ord, scnt,
7751da177e4SLinus Torvalds 			       hdr.datachk, csum);
7761da177e4SLinus Torvalds 			printk (KERN_INFO "data=(%lx,%lx,%lx,%lx)\n",
7771da177e4SLinus Torvalds 				((ulong *)(unit[drive].trackbuf+hdr.sect*512))[0],
7781da177e4SLinus Torvalds 				((ulong *)(unit[drive].trackbuf+hdr.sect*512))[1],
7791da177e4SLinus Torvalds 				((ulong *)(unit[drive].trackbuf+hdr.sect*512))[2],
7801da177e4SLinus Torvalds 				((ulong *)(unit[drive].trackbuf+hdr.sect*512))[3]);
7811da177e4SLinus Torvalds 			return MFM_DATA;
7821da177e4SLinus Torvalds 		}
7831da177e4SLinus Torvalds 	}
7841da177e4SLinus Torvalds 
7851da177e4SLinus Torvalds 	return 0;
7861da177e4SLinus Torvalds }
7871da177e4SLinus Torvalds 
7881da177e4SLinus Torvalds static void encode(unsigned long data, unsigned long *dest)
7891da177e4SLinus Torvalds {
7901da177e4SLinus Torvalds 	unsigned long data2;
7911da177e4SLinus Torvalds 
7921da177e4SLinus Torvalds 	data &= 0x55555555;
7931da177e4SLinus Torvalds 	data2 = data ^ 0x55555555;
7941da177e4SLinus Torvalds 	data |= ((data2 >> 1) | 0x80000000) & (data2 << 1);
7951da177e4SLinus Torvalds 
7961da177e4SLinus Torvalds 	if (*(dest - 1) & 0x00000001)
7971da177e4SLinus Torvalds 		data &= 0x7FFFFFFF;
7981da177e4SLinus Torvalds 
7991da177e4SLinus Torvalds 	*dest = data;
8001da177e4SLinus Torvalds }
8011da177e4SLinus Torvalds 
8021da177e4SLinus Torvalds static void encode_block(unsigned long *dest, unsigned long *src, int len)
8031da177e4SLinus Torvalds {
8041da177e4SLinus Torvalds 	int cnt, to_cnt = 0;
8051da177e4SLinus Torvalds 	unsigned long data;
8061da177e4SLinus Torvalds 
8071da177e4SLinus Torvalds 	/* odd bits */
8081da177e4SLinus Torvalds 	for (cnt = 0; cnt < len / 4; cnt++) {
8091da177e4SLinus Torvalds 		data = src[cnt] >> 1;
8101da177e4SLinus Torvalds 		encode(data, dest + to_cnt++);
8111da177e4SLinus Torvalds 	}
8121da177e4SLinus Torvalds 
8131da177e4SLinus Torvalds 	/* even bits */
8141da177e4SLinus Torvalds 	for (cnt = 0; cnt < len / 4; cnt++) {
8151da177e4SLinus Torvalds 		data = src[cnt];
8161da177e4SLinus Torvalds 		encode(data, dest + to_cnt++);
8171da177e4SLinus Torvalds 	}
8181da177e4SLinus Torvalds }
8191da177e4SLinus Torvalds 
8201da177e4SLinus Torvalds static unsigned long *putsec(int disk, unsigned long *raw, int cnt)
8211da177e4SLinus Torvalds {
8221da177e4SLinus Torvalds 	struct header hdr;
8231da177e4SLinus Torvalds 	int i;
8241da177e4SLinus Torvalds 
8251da177e4SLinus Torvalds 	disk&=3;
8261da177e4SLinus Torvalds 	*raw = (raw[-1]&1) ? 0x2AAAAAAA : 0xAAAAAAAA;
8271da177e4SLinus Torvalds 	raw++;
8281da177e4SLinus Torvalds 	*raw++ = 0x44894489;
8291da177e4SLinus Torvalds 
8301da177e4SLinus Torvalds 	hdr.magic = 0xFF;
8311da177e4SLinus Torvalds 	hdr.track = unit[disk].track;
8321da177e4SLinus Torvalds 	hdr.sect = cnt;
8331da177e4SLinus Torvalds 	hdr.ord = unit[disk].dtype->sects * unit[disk].type->sect_mult - cnt;
8341da177e4SLinus Torvalds 	for (i = 0; i < 16; i++)
8351da177e4SLinus Torvalds 		hdr.labels[i] = 0;
8361da177e4SLinus Torvalds 	hdr.hdrchk = checksum((ulong *)&hdr,
8371da177e4SLinus Torvalds 			      (char *)&hdr.hdrchk-(char *)&hdr);
8381da177e4SLinus Torvalds 	hdr.datachk = checksum((ulong *)(unit[disk].trackbuf+cnt*512), 512);
8391da177e4SLinus Torvalds 
8401da177e4SLinus Torvalds 	encode_block(raw, (ulong *)&hdr.magic, 4);
8411da177e4SLinus Torvalds 	raw += 2;
8421da177e4SLinus Torvalds 	encode_block(raw, (ulong *)&hdr.labels, 16);
8431da177e4SLinus Torvalds 	raw += 8;
8441da177e4SLinus Torvalds 	encode_block(raw, (ulong *)&hdr.hdrchk, 4);
8451da177e4SLinus Torvalds 	raw += 2;
8461da177e4SLinus Torvalds 	encode_block(raw, (ulong *)&hdr.datachk, 4);
8471da177e4SLinus Torvalds 	raw += 2;
8481da177e4SLinus Torvalds 	encode_block(raw, (ulong *)(unit[disk].trackbuf+cnt*512), 512);
8491da177e4SLinus Torvalds 	raw += 256;
8501da177e4SLinus Torvalds 
8511da177e4SLinus Torvalds 	return raw;
8521da177e4SLinus Torvalds }
8531da177e4SLinus Torvalds 
8541da177e4SLinus Torvalds static void amiga_write(int disk)
8551da177e4SLinus Torvalds {
8561da177e4SLinus Torvalds 	unsigned int cnt;
8571da177e4SLinus Torvalds 	unsigned long *ptr = (unsigned long *)raw_buf;
8581da177e4SLinus Torvalds 
8591da177e4SLinus Torvalds 	disk&=3;
8601da177e4SLinus Torvalds 	/* gap space */
8611da177e4SLinus Torvalds 	for (cnt = 0; cnt < 415 * unit[disk].type->sect_mult; cnt++)
8621da177e4SLinus Torvalds 		*ptr++ = 0xaaaaaaaa;
8631da177e4SLinus Torvalds 
8641da177e4SLinus Torvalds 	/* sectors */
8651da177e4SLinus Torvalds 	for (cnt = 0; cnt < unit[disk].dtype->sects * unit[disk].type->sect_mult; cnt++)
8661da177e4SLinus Torvalds 		ptr = putsec (disk, ptr, cnt);
8671da177e4SLinus Torvalds 	*(ushort *)ptr = (ptr[-1]&1) ? 0x2AA8 : 0xAAA8;
8681da177e4SLinus Torvalds }
8691da177e4SLinus Torvalds 
8701da177e4SLinus Torvalds 
8711da177e4SLinus Torvalds struct dos_header {
8721da177e4SLinus Torvalds 	unsigned char track,   /* 0-80 */
8731da177e4SLinus Torvalds 		side,    /* 0-1 */
8741da177e4SLinus Torvalds 		sec,     /* 0-...*/
8751da177e4SLinus Torvalds 		len_desc;/* 2 */
8761da177e4SLinus Torvalds 	unsigned short crc;     /* on 68000 we got an alignment problem,
8771da177e4SLinus Torvalds 				   but this compiler solves it  by adding silently
8781da177e4SLinus Torvalds 				   adding a pad byte so data won't fit
8791da177e4SLinus Torvalds 				   and this took about 3h to discover.... */
8801da177e4SLinus Torvalds 	unsigned char gap1[22];     /* for longword-alignedness (0x4e) */
8811da177e4SLinus Torvalds };
8821da177e4SLinus Torvalds 
8831da177e4SLinus Torvalds /* crc routines are borrowed from the messydos-handler  */
8841da177e4SLinus Torvalds 
8851da177e4SLinus Torvalds /* excerpt from the messydos-device
8861da177e4SLinus Torvalds ; The CRC is computed not only over the actual data, but including
8871da177e4SLinus Torvalds ; the SYNC mark (3 * $a1) and the 'ID/DATA - Address Mark' ($fe/$fb).
8881da177e4SLinus Torvalds ; As we don't read or encode these fields into our buffers, we have to
8891da177e4SLinus Torvalds ; preload the registers containing the CRC with the values they would have
8901da177e4SLinus Torvalds ; after stepping over these fields.
8911da177e4SLinus Torvalds ;
8921da177e4SLinus Torvalds ; How CRCs "really" work:
8931da177e4SLinus Torvalds ;
8941da177e4SLinus Torvalds ; First, you should regard a bitstring as a series of coefficients of
8951da177e4SLinus Torvalds ; polynomials. We calculate with these polynomials in modulo-2
8961da177e4SLinus Torvalds ; arithmetic, in which both add and subtract are done the same as
8971da177e4SLinus Torvalds ; exclusive-or. Now, we modify our data (a very long polynomial) in
8981da177e4SLinus Torvalds ; such a way that it becomes divisible by the CCITT-standard 16-bit
8991da177e4SLinus Torvalds ;		 16   12   5
9001da177e4SLinus Torvalds ; polynomial:	x  + x	+ x + 1, represented by $11021. The easiest
9011da177e4SLinus Torvalds ; way to do this would be to multiply (using proper arithmetic) our
9021da177e4SLinus Torvalds ; datablock with $11021. So we have:
9031da177e4SLinus Torvalds ;   data * $11021		 =
9041da177e4SLinus Torvalds ;   data * ($10000 + $1021)      =
9051da177e4SLinus Torvalds ;   data * $10000 + data * $1021
9061da177e4SLinus Torvalds ; The left part of this is simple: Just add two 0 bytes. But then
9071da177e4SLinus Torvalds ; the right part (data $1021) remains difficult and even could have
9081da177e4SLinus Torvalds ; a carry into the left part. The solution is to use a modified
9091da177e4SLinus Torvalds ; multiplication, which has a result that is not correct, but with
9101da177e4SLinus Torvalds ; a difference of any multiple of $11021. We then only need to keep
9111da177e4SLinus Torvalds ; the 16 least significant bits of the result.
9121da177e4SLinus Torvalds ;
9131da177e4SLinus Torvalds ; The following algorithm does this for us:
9141da177e4SLinus Torvalds ;
9151da177e4SLinus Torvalds ;   unsigned char *data, c, crclo, crchi;
9161da177e4SLinus Torvalds ;   while (not done) {
9171da177e4SLinus Torvalds ;	c = *data++ + crchi;
9181da177e4SLinus Torvalds ;	crchi = (@ c) >> 8 + crclo;
9191da177e4SLinus Torvalds ;	crclo = @ c;
9201da177e4SLinus Torvalds ;   }
9211da177e4SLinus Torvalds ;
9221da177e4SLinus Torvalds ; Remember, + is done with EOR, the @ operator is in two tables (high
9231da177e4SLinus Torvalds ; and low byte separately), which is calculated as
9241da177e4SLinus Torvalds ;
9251da177e4SLinus Torvalds ;      $1021 * (c & $F0)
9261da177e4SLinus Torvalds ;  xor $1021 * (c & $0F)
9271da177e4SLinus Torvalds ;  xor $1021 * (c >> 4)         (* is regular multiplication)
9281da177e4SLinus Torvalds ;
9291da177e4SLinus Torvalds ;
9301da177e4SLinus Torvalds ; Anyway, the end result is the same as the remainder of the division of
9311da177e4SLinus Torvalds ; the data by $11021. I am afraid I need to study theory a bit more...
9321da177e4SLinus Torvalds 
9331da177e4SLinus Torvalds 
9341da177e4SLinus Torvalds my only works was to code this from manx to C....
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds */
9371da177e4SLinus Torvalds 
9381da177e4SLinus Torvalds static ushort dos_crc(void * data_a3, int data_d0, int data_d1, int data_d3)
9391da177e4SLinus Torvalds {
9401da177e4SLinus Torvalds 	static unsigned char CRCTable1[] = {
9411da177e4SLinus Torvalds 		0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x81,0x91,0xa1,0xb1,0xc1,0xd1,0xe1,0xf1,
9421da177e4SLinus Torvalds 		0x12,0x02,0x32,0x22,0x52,0x42,0x72,0x62,0x93,0x83,0xb3,0xa3,0xd3,0xc3,0xf3,0xe3,
9431da177e4SLinus Torvalds 		0x24,0x34,0x04,0x14,0x64,0x74,0x44,0x54,0xa5,0xb5,0x85,0x95,0xe5,0xf5,0xc5,0xd5,
9441da177e4SLinus Torvalds 		0x36,0x26,0x16,0x06,0x76,0x66,0x56,0x46,0xb7,0xa7,0x97,0x87,0xf7,0xe7,0xd7,0xc7,
9451da177e4SLinus Torvalds 		0x48,0x58,0x68,0x78,0x08,0x18,0x28,0x38,0xc9,0xd9,0xe9,0xf9,0x89,0x99,0xa9,0xb9,
9461da177e4SLinus Torvalds 		0x5a,0x4a,0x7a,0x6a,0x1a,0x0a,0x3a,0x2a,0xdb,0xcb,0xfb,0xeb,0x9b,0x8b,0xbb,0xab,
9471da177e4SLinus Torvalds 		0x6c,0x7c,0x4c,0x5c,0x2c,0x3c,0x0c,0x1c,0xed,0xfd,0xcd,0xdd,0xad,0xbd,0x8d,0x9d,
9481da177e4SLinus Torvalds 		0x7e,0x6e,0x5e,0x4e,0x3e,0x2e,0x1e,0x0e,0xff,0xef,0xdf,0xcf,0xbf,0xaf,0x9f,0x8f,
9491da177e4SLinus Torvalds 		0x91,0x81,0xb1,0xa1,0xd1,0xc1,0xf1,0xe1,0x10,0x00,0x30,0x20,0x50,0x40,0x70,0x60,
9501da177e4SLinus Torvalds 		0x83,0x93,0xa3,0xb3,0xc3,0xd3,0xe3,0xf3,0x02,0x12,0x22,0x32,0x42,0x52,0x62,0x72,
9511da177e4SLinus Torvalds 		0xb5,0xa5,0x95,0x85,0xf5,0xe5,0xd5,0xc5,0x34,0x24,0x14,0x04,0x74,0x64,0x54,0x44,
9521da177e4SLinus Torvalds 		0xa7,0xb7,0x87,0x97,0xe7,0xf7,0xc7,0xd7,0x26,0x36,0x06,0x16,0x66,0x76,0x46,0x56,
9531da177e4SLinus Torvalds 		0xd9,0xc9,0xf9,0xe9,0x99,0x89,0xb9,0xa9,0x58,0x48,0x78,0x68,0x18,0x08,0x38,0x28,
9541da177e4SLinus Torvalds 		0xcb,0xdb,0xeb,0xfb,0x8b,0x9b,0xab,0xbb,0x4a,0x5a,0x6a,0x7a,0x0a,0x1a,0x2a,0x3a,
9551da177e4SLinus Torvalds 		0xfd,0xed,0xdd,0xcd,0xbd,0xad,0x9d,0x8d,0x7c,0x6c,0x5c,0x4c,0x3c,0x2c,0x1c,0x0c,
9561da177e4SLinus Torvalds 		0xef,0xff,0xcf,0xdf,0xaf,0xbf,0x8f,0x9f,0x6e,0x7e,0x4e,0x5e,0x2e,0x3e,0x0e,0x1e
9571da177e4SLinus Torvalds 	};
9581da177e4SLinus Torvalds 
9591da177e4SLinus Torvalds 	static unsigned char CRCTable2[] = {
9601da177e4SLinus Torvalds 		0x00,0x21,0x42,0x63,0x84,0xa5,0xc6,0xe7,0x08,0x29,0x4a,0x6b,0x8c,0xad,0xce,0xef,
9611da177e4SLinus Torvalds 		0x31,0x10,0x73,0x52,0xb5,0x94,0xf7,0xd6,0x39,0x18,0x7b,0x5a,0xbd,0x9c,0xff,0xde,
9621da177e4SLinus Torvalds 		0x62,0x43,0x20,0x01,0xe6,0xc7,0xa4,0x85,0x6a,0x4b,0x28,0x09,0xee,0xcf,0xac,0x8d,
9631da177e4SLinus Torvalds 		0x53,0x72,0x11,0x30,0xd7,0xf6,0x95,0xb4,0x5b,0x7a,0x19,0x38,0xdf,0xfe,0x9d,0xbc,
9641da177e4SLinus Torvalds 		0xc4,0xe5,0x86,0xa7,0x40,0x61,0x02,0x23,0xcc,0xed,0x8e,0xaf,0x48,0x69,0x0a,0x2b,
9651da177e4SLinus Torvalds 		0xf5,0xd4,0xb7,0x96,0x71,0x50,0x33,0x12,0xfd,0xdc,0xbf,0x9e,0x79,0x58,0x3b,0x1a,
9661da177e4SLinus Torvalds 		0xa6,0x87,0xe4,0xc5,0x22,0x03,0x60,0x41,0xae,0x8f,0xec,0xcd,0x2a,0x0b,0x68,0x49,
9671da177e4SLinus Torvalds 		0x97,0xb6,0xd5,0xf4,0x13,0x32,0x51,0x70,0x9f,0xbe,0xdd,0xfc,0x1b,0x3a,0x59,0x78,
9681da177e4SLinus Torvalds 		0x88,0xa9,0xca,0xeb,0x0c,0x2d,0x4e,0x6f,0x80,0xa1,0xc2,0xe3,0x04,0x25,0x46,0x67,
9691da177e4SLinus Torvalds 		0xb9,0x98,0xfb,0xda,0x3d,0x1c,0x7f,0x5e,0xb1,0x90,0xf3,0xd2,0x35,0x14,0x77,0x56,
9701da177e4SLinus Torvalds 		0xea,0xcb,0xa8,0x89,0x6e,0x4f,0x2c,0x0d,0xe2,0xc3,0xa0,0x81,0x66,0x47,0x24,0x05,
9711da177e4SLinus Torvalds 		0xdb,0xfa,0x99,0xb8,0x5f,0x7e,0x1d,0x3c,0xd3,0xf2,0x91,0xb0,0x57,0x76,0x15,0x34,
9721da177e4SLinus Torvalds 		0x4c,0x6d,0x0e,0x2f,0xc8,0xe9,0x8a,0xab,0x44,0x65,0x06,0x27,0xc0,0xe1,0x82,0xa3,
9731da177e4SLinus Torvalds 		0x7d,0x5c,0x3f,0x1e,0xf9,0xd8,0xbb,0x9a,0x75,0x54,0x37,0x16,0xf1,0xd0,0xb3,0x92,
9741da177e4SLinus Torvalds 		0x2e,0x0f,0x6c,0x4d,0xaa,0x8b,0xe8,0xc9,0x26,0x07,0x64,0x45,0xa2,0x83,0xe0,0xc1,
9751da177e4SLinus Torvalds 		0x1f,0x3e,0x5d,0x7c,0x9b,0xba,0xd9,0xf8,0x17,0x36,0x55,0x74,0x93,0xb2,0xd1,0xf0
9761da177e4SLinus Torvalds 	};
9771da177e4SLinus Torvalds 
9781da177e4SLinus Torvalds /* look at the asm-code - what looks in C a bit strange is almost as good as handmade */
9791da177e4SLinus Torvalds 	register int i;
9801da177e4SLinus Torvalds 	register unsigned char *CRCT1, *CRCT2, *data, c, crch, crcl;
9811da177e4SLinus Torvalds 
9821da177e4SLinus Torvalds 	CRCT1=CRCTable1;
9831da177e4SLinus Torvalds 	CRCT2=CRCTable2;
9841da177e4SLinus Torvalds 	data=data_a3;
9851da177e4SLinus Torvalds 	crcl=data_d1;
9861da177e4SLinus Torvalds 	crch=data_d0;
9871da177e4SLinus Torvalds 	for (i=data_d3; i>=0; i--) {
9881da177e4SLinus Torvalds 		c = (*data++) ^ crch;
9891da177e4SLinus Torvalds 		crch = CRCT1[c] ^ crcl;
9901da177e4SLinus Torvalds 		crcl = CRCT2[c];
9911da177e4SLinus Torvalds 	}
9921da177e4SLinus Torvalds 	return (crch<<8)|crcl;
9931da177e4SLinus Torvalds }
9941da177e4SLinus Torvalds 
9951da177e4SLinus Torvalds static inline ushort dos_hdr_crc (struct dos_header *hdr)
9961da177e4SLinus Torvalds {
9971da177e4SLinus Torvalds 	return dos_crc(&(hdr->track), 0xb2, 0x30, 3); /* precomputed magic */
9981da177e4SLinus Torvalds }
9991da177e4SLinus Torvalds 
10001da177e4SLinus Torvalds static inline ushort dos_data_crc(unsigned char *data)
10011da177e4SLinus Torvalds {
10021da177e4SLinus Torvalds 	return dos_crc(data, 0xe2, 0x95 ,511); /* precomputed magic */
10031da177e4SLinus Torvalds }
10041da177e4SLinus Torvalds 
10051da177e4SLinus Torvalds static inline unsigned char dos_decode_byte(ushort word)
10061da177e4SLinus Torvalds {
10071da177e4SLinus Torvalds 	register ushort w2;
10081da177e4SLinus Torvalds 	register unsigned char byte;
10091da177e4SLinus Torvalds 	register unsigned char *dec = mfmdecode;
10101da177e4SLinus Torvalds 
10111da177e4SLinus Torvalds 	w2=word;
10121da177e4SLinus Torvalds 	w2>>=8;
10131da177e4SLinus Torvalds 	w2&=127;
10141da177e4SLinus Torvalds 	byte = dec[w2];
10151da177e4SLinus Torvalds 	byte <<= 4;
10161da177e4SLinus Torvalds 	w2 = word & 127;
10171da177e4SLinus Torvalds 	byte |= dec[w2];
10181da177e4SLinus Torvalds 	return byte;
10191da177e4SLinus Torvalds }
10201da177e4SLinus Torvalds 
10211da177e4SLinus Torvalds static unsigned long dos_decode(unsigned char *data, unsigned short *raw, int len)
10221da177e4SLinus Torvalds {
10231da177e4SLinus Torvalds 	int i;
10241da177e4SLinus Torvalds 
10251da177e4SLinus Torvalds 	for (i = 0; i < len; i++)
10261da177e4SLinus Torvalds 		*data++=dos_decode_byte(*raw++);
10271da177e4SLinus Torvalds 	return ((ulong)raw);
10281da177e4SLinus Torvalds }
10291da177e4SLinus Torvalds 
10301da177e4SLinus Torvalds #ifdef DEBUG
10311da177e4SLinus Torvalds static void dbg(unsigned long ptr)
10321da177e4SLinus Torvalds {
10331da177e4SLinus Torvalds 	printk("raw data @%08lx: %08lx, %08lx ,%08lx, %08lx\n", ptr,
10341da177e4SLinus Torvalds 	       ((ulong *)ptr)[0], ((ulong *)ptr)[1],
10351da177e4SLinus Torvalds 	       ((ulong *)ptr)[2], ((ulong *)ptr)[3]);
10361da177e4SLinus Torvalds }
10371da177e4SLinus Torvalds #endif
10381da177e4SLinus Torvalds 
10391da177e4SLinus Torvalds static int dos_read(int drive)
10401da177e4SLinus Torvalds {
10411da177e4SLinus Torvalds 	unsigned long end;
10421da177e4SLinus Torvalds 	unsigned long raw;
10431da177e4SLinus Torvalds 	int scnt;
10441da177e4SLinus Torvalds 	unsigned short crc,data_crc[2];
10451da177e4SLinus Torvalds 	struct dos_header hdr;
10461da177e4SLinus Torvalds 
10471da177e4SLinus Torvalds 	drive&=3;
10481da177e4SLinus Torvalds 	raw = (long) raw_buf;
10491da177e4SLinus Torvalds 	end = raw + unit[drive].type->read_size;
10501da177e4SLinus Torvalds 
10511da177e4SLinus Torvalds 	for (scnt=0; scnt < unit[drive].dtype->sects * unit[drive].type->sect_mult; scnt++) {
10521da177e4SLinus Torvalds 		do { /* search for the right sync of each sec-hdr */
10531da177e4SLinus Torvalds 			if (!(raw = scan_sync (raw, end))) {
10541da177e4SLinus Torvalds 				printk(KERN_INFO "dos_read: no hdr sync on "
10551da177e4SLinus Torvalds 				       "track %d, unit %d for sector %d\n",
10561da177e4SLinus Torvalds 				       unit[drive].track,drive,scnt);
10571da177e4SLinus Torvalds 				return MFM_NOSYNC;
10581da177e4SLinus Torvalds 			}
10591da177e4SLinus Torvalds #ifdef DEBUG
10601da177e4SLinus Torvalds 			dbg(raw);
10611da177e4SLinus Torvalds #endif
10621da177e4SLinus Torvalds 		} while (*((ushort *)raw)!=0x5554); /* loop usually only once done */
10631da177e4SLinus Torvalds 		raw+=2; /* skip over headermark */
10641da177e4SLinus Torvalds 		raw = dos_decode((unsigned char *)&hdr,(ushort *) raw,8);
10651da177e4SLinus Torvalds 		crc = dos_hdr_crc(&hdr);
10661da177e4SLinus Torvalds 
10671da177e4SLinus Torvalds #ifdef DEBUG
10681da177e4SLinus Torvalds 		printk("(%3d,%d,%2d,%d) %x\n", hdr.track, hdr.side,
10691da177e4SLinus Torvalds 		       hdr.sec, hdr.len_desc, hdr.crc);
10701da177e4SLinus Torvalds #endif
10711da177e4SLinus Torvalds 
10721da177e4SLinus Torvalds 		if (crc != hdr.crc) {
10731da177e4SLinus Torvalds 			printk(KERN_INFO "dos_read: MFM_HEADER %04x,%04x\n",
10741da177e4SLinus Torvalds 			       hdr.crc, crc);
10751da177e4SLinus Torvalds 			return MFM_HEADER;
10761da177e4SLinus Torvalds 		}
10771da177e4SLinus Torvalds 		if (hdr.track != unit[drive].track/unit[drive].type->heads) {
10781da177e4SLinus Torvalds 			printk(KERN_INFO "dos_read: MFM_TRACK %d, %d\n",
10791da177e4SLinus Torvalds 			       hdr.track,
10801da177e4SLinus Torvalds 			       unit[drive].track/unit[drive].type->heads);
10811da177e4SLinus Torvalds 			return MFM_TRACK;
10821da177e4SLinus Torvalds 		}
10831da177e4SLinus Torvalds 
10841da177e4SLinus Torvalds 		if (hdr.side != unit[drive].track%unit[drive].type->heads) {
10851da177e4SLinus Torvalds 			printk(KERN_INFO "dos_read: MFM_SIDE %d, %d\n",
10861da177e4SLinus Torvalds 			       hdr.side,
10871da177e4SLinus Torvalds 			       unit[drive].track%unit[drive].type->heads);
10881da177e4SLinus Torvalds 			return MFM_TRACK;
10891da177e4SLinus Torvalds 		}
10901da177e4SLinus Torvalds 
10911da177e4SLinus Torvalds 		if (hdr.len_desc != 2) {
10921da177e4SLinus Torvalds 			printk(KERN_INFO "dos_read: unknown sector len "
10931da177e4SLinus Torvalds 			       "descriptor %d\n", hdr.len_desc);
10941da177e4SLinus Torvalds 			return MFM_DATA;
10951da177e4SLinus Torvalds 		}
10961da177e4SLinus Torvalds #ifdef DEBUG
10971da177e4SLinus Torvalds 		printk("hdr accepted\n");
10981da177e4SLinus Torvalds #endif
10991da177e4SLinus Torvalds 		if (!(raw = scan_sync (raw, end))) {
11001da177e4SLinus Torvalds 			printk(KERN_INFO "dos_read: no data sync on track "
11011da177e4SLinus Torvalds 			       "%d, unit %d for sector%d, disk sector %d\n",
11021da177e4SLinus Torvalds 			       unit[drive].track, drive, scnt, hdr.sec);
11031da177e4SLinus Torvalds 			return MFM_NOSYNC;
11041da177e4SLinus Torvalds 		}
11051da177e4SLinus Torvalds #ifdef DEBUG
11061da177e4SLinus Torvalds 		dbg(raw);
11071da177e4SLinus Torvalds #endif
11081da177e4SLinus Torvalds 
11091da177e4SLinus Torvalds 		if (*((ushort *)raw)!=0x5545) {
11101da177e4SLinus Torvalds 			printk(KERN_INFO "dos_read: no data mark after "
11111da177e4SLinus Torvalds 			       "sync (%d,%d,%d,%d) sc=%d\n",
11121da177e4SLinus Torvalds 			       hdr.track,hdr.side,hdr.sec,hdr.len_desc,scnt);
11131da177e4SLinus Torvalds 			return MFM_NOSYNC;
11141da177e4SLinus Torvalds 		}
11151da177e4SLinus Torvalds 
11161da177e4SLinus Torvalds 		raw+=2;  /* skip data mark (included in checksum) */
11171da177e4SLinus Torvalds 		raw = dos_decode((unsigned char *)(unit[drive].trackbuf + (hdr.sec - 1) * 512), (ushort *) raw, 512);
11181da177e4SLinus Torvalds 		raw = dos_decode((unsigned char  *)data_crc,(ushort *) raw,4);
11191da177e4SLinus Torvalds 		crc = dos_data_crc(unit[drive].trackbuf + (hdr.sec - 1) * 512);
11201da177e4SLinus Torvalds 
11211da177e4SLinus Torvalds 		if (crc != data_crc[0]) {
11221da177e4SLinus Torvalds 			printk(KERN_INFO "dos_read: MFM_DATA (%d,%d,%d,%d) "
11231da177e4SLinus Torvalds 			       "sc=%d, %x %x\n", hdr.track, hdr.side,
11241da177e4SLinus Torvalds 			       hdr.sec, hdr.len_desc, scnt,data_crc[0], crc);
11251da177e4SLinus Torvalds 			printk(KERN_INFO "data=(%lx,%lx,%lx,%lx,...)\n",
11261da177e4SLinus Torvalds 			       ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[0],
11271da177e4SLinus Torvalds 			       ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[1],
11281da177e4SLinus Torvalds 			       ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[2],
11291da177e4SLinus Torvalds 			       ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[3]);
11301da177e4SLinus Torvalds 			return MFM_DATA;
11311da177e4SLinus Torvalds 		}
11321da177e4SLinus Torvalds 	}
11331da177e4SLinus Torvalds 	return 0;
11341da177e4SLinus Torvalds }
11351da177e4SLinus Torvalds 
11361da177e4SLinus Torvalds static inline ushort dos_encode_byte(unsigned char byte)
11371da177e4SLinus Torvalds {
11381da177e4SLinus Torvalds 	register unsigned char *enc, b2, b1;
11391da177e4SLinus Torvalds 	register ushort word;
11401da177e4SLinus Torvalds 
11411da177e4SLinus Torvalds 	enc=mfmencode;
11421da177e4SLinus Torvalds 	b1=byte;
11431da177e4SLinus Torvalds 	b2=b1>>4;
11441da177e4SLinus Torvalds 	b1&=15;
11451da177e4SLinus Torvalds 	word=enc[b2] <<8 | enc [b1];
11461da177e4SLinus Torvalds 	return (word|((word&(256|64)) ? 0: 128));
11471da177e4SLinus Torvalds }
11481da177e4SLinus Torvalds 
11491da177e4SLinus Torvalds static void dos_encode_block(ushort *dest, unsigned char *src, int len)
11501da177e4SLinus Torvalds {
11511da177e4SLinus Torvalds 	int i;
11521da177e4SLinus Torvalds 
11531da177e4SLinus Torvalds 	for (i = 0; i < len; i++) {
11541da177e4SLinus Torvalds 		*dest=dos_encode_byte(*src++);
11551da177e4SLinus Torvalds 		*dest|=((dest[-1]&1)||(*dest&0x4000))? 0: 0x8000;
11561da177e4SLinus Torvalds 		dest++;
11571da177e4SLinus Torvalds 	}
11581da177e4SLinus Torvalds }
11591da177e4SLinus Torvalds 
11601da177e4SLinus Torvalds static unsigned long *ms_putsec(int drive, unsigned long *raw, int cnt)
11611da177e4SLinus Torvalds {
11621da177e4SLinus Torvalds 	static struct dos_header hdr={0,0,0,2,0,
11631da177e4SLinus Torvalds 	  {78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78}};
11641da177e4SLinus Torvalds 	int i;
11651da177e4SLinus Torvalds 	static ushort crc[2]={0,0x4e4e};
11661da177e4SLinus Torvalds 
11671da177e4SLinus Torvalds 	drive&=3;
11681da177e4SLinus Torvalds /* id gap 1 */
11691da177e4SLinus Torvalds /* the MFM word before is always 9254 */
11701da177e4SLinus Torvalds 	for(i=0;i<6;i++)
11711da177e4SLinus Torvalds 		*raw++=0xaaaaaaaa;
11721da177e4SLinus Torvalds /* 3 sync + 1 headermark */
11731da177e4SLinus Torvalds 	*raw++=0x44894489;
11741da177e4SLinus Torvalds 	*raw++=0x44895554;
11751da177e4SLinus Torvalds 
11761da177e4SLinus Torvalds /* fill in the variable parts of the header */
11771da177e4SLinus Torvalds 	hdr.track=unit[drive].track/unit[drive].type->heads;
11781da177e4SLinus Torvalds 	hdr.side=unit[drive].track%unit[drive].type->heads;
11791da177e4SLinus Torvalds 	hdr.sec=cnt+1;
11801da177e4SLinus Torvalds 	hdr.crc=dos_hdr_crc(&hdr);
11811da177e4SLinus Torvalds 
11821da177e4SLinus Torvalds /* header (without "magic") and id gap 2*/
11831da177e4SLinus Torvalds 	dos_encode_block((ushort *)raw,(unsigned char *) &hdr.track,28);
11841da177e4SLinus Torvalds 	raw+=14;
11851da177e4SLinus Torvalds 
11861da177e4SLinus Torvalds /*id gap 3 */
11871da177e4SLinus Torvalds 	for(i=0;i<6;i++)
11881da177e4SLinus Torvalds 		*raw++=0xaaaaaaaa;
11891da177e4SLinus Torvalds 
11901da177e4SLinus Torvalds /* 3 syncs and 1 datamark */
11911da177e4SLinus Torvalds 	*raw++=0x44894489;
11921da177e4SLinus Torvalds 	*raw++=0x44895545;
11931da177e4SLinus Torvalds 
11941da177e4SLinus Torvalds /* data */
11951da177e4SLinus Torvalds 	dos_encode_block((ushort *)raw,
11961da177e4SLinus Torvalds 			 (unsigned char *)unit[drive].trackbuf+cnt*512,512);
11971da177e4SLinus Torvalds 	raw+=256;
11981da177e4SLinus Torvalds 
11991da177e4SLinus Torvalds /*data crc + jd's special gap (long words :-/) */
12001da177e4SLinus Torvalds 	crc[0]=dos_data_crc(unit[drive].trackbuf+cnt*512);
12011da177e4SLinus Torvalds 	dos_encode_block((ushort *) raw,(unsigned char *)crc,4);
12021da177e4SLinus Torvalds 	raw+=2;
12031da177e4SLinus Torvalds 
12041da177e4SLinus Torvalds /* data gap */
12051da177e4SLinus Torvalds 	for(i=0;i<38;i++)
12061da177e4SLinus Torvalds 		*raw++=0x92549254;
12071da177e4SLinus Torvalds 
12081da177e4SLinus Torvalds 	return raw; /* wrote 652 MFM words */
12091da177e4SLinus Torvalds }
12101da177e4SLinus Torvalds 
12111da177e4SLinus Torvalds static void dos_write(int disk)
12121da177e4SLinus Torvalds {
12131da177e4SLinus Torvalds 	int cnt;
12141da177e4SLinus Torvalds 	unsigned long raw = (unsigned long) raw_buf;
12151da177e4SLinus Torvalds 	unsigned long *ptr=(unsigned long *)raw;
12161da177e4SLinus Torvalds 
12171da177e4SLinus Torvalds 	disk&=3;
12181da177e4SLinus Torvalds /* really gap4 + indexgap , but we write it first and round it up */
12191da177e4SLinus Torvalds 	for (cnt=0;cnt<425;cnt++)
12201da177e4SLinus Torvalds 		*ptr++=0x92549254;
12211da177e4SLinus Torvalds 
12221da177e4SLinus Torvalds /* the following is just guessed */
12231da177e4SLinus Torvalds 	if (unit[disk].type->sect_mult==2)  /* check for HD-Disks */
12241da177e4SLinus Torvalds 		for(cnt=0;cnt<473;cnt++)
12251da177e4SLinus Torvalds 			*ptr++=0x92549254;
12261da177e4SLinus Torvalds 
12271da177e4SLinus Torvalds /* now the index marks...*/
12281da177e4SLinus Torvalds 	for (cnt=0;cnt<20;cnt++)
12291da177e4SLinus Torvalds 		*ptr++=0x92549254;
12301da177e4SLinus Torvalds 	for (cnt=0;cnt<6;cnt++)
12311da177e4SLinus Torvalds 		*ptr++=0xaaaaaaaa;
12321da177e4SLinus Torvalds 	*ptr++=0x52245224;
12331da177e4SLinus Torvalds 	*ptr++=0x52245552;
12341da177e4SLinus Torvalds 	for (cnt=0;cnt<20;cnt++)
12351da177e4SLinus Torvalds 		*ptr++=0x92549254;
12361da177e4SLinus Torvalds 
12371da177e4SLinus Torvalds /* sectors */
12381da177e4SLinus Torvalds 	for(cnt = 0; cnt < unit[disk].dtype->sects * unit[disk].type->sect_mult; cnt++)
12391da177e4SLinus Torvalds 		ptr=ms_putsec(disk,ptr,cnt);
12401da177e4SLinus Torvalds 
12411da177e4SLinus Torvalds 	*(ushort *)ptr = 0xaaa8; /* MFM word before is always 0x9254 */
12421da177e4SLinus Torvalds }
12431da177e4SLinus Torvalds 
12441da177e4SLinus Torvalds /*
12451da177e4SLinus Torvalds  * Here comes the high level stuff (i.e. the filesystem interface)
12461da177e4SLinus Torvalds  * and helper functions.
12471da177e4SLinus Torvalds  * Normally this should be the only part that has to be adapted to
12481da177e4SLinus Torvalds  * different kernel versions.
12491da177e4SLinus Torvalds  */
12501da177e4SLinus Torvalds 
12511da177e4SLinus Torvalds /* FIXME: this assumes the drive is still spinning -
12521da177e4SLinus Torvalds  * which is only true if we complete writing a track within three seconds
12531da177e4SLinus Torvalds  */
12541da177e4SLinus Torvalds static void flush_track_callback(unsigned long nr)
12551da177e4SLinus Torvalds {
12561da177e4SLinus Torvalds 	nr&=3;
12571da177e4SLinus Torvalds 	writefromint = 1;
12581da177e4SLinus Torvalds 	if (!try_fdc(nr)) {
12591da177e4SLinus Torvalds 		/* we might block in an interrupt, so try again later */
12601da177e4SLinus Torvalds 		flush_track_timer[nr].expires = jiffies + 1;
12611da177e4SLinus Torvalds 		add_timer(flush_track_timer + nr);
12621da177e4SLinus Torvalds 		return;
12631da177e4SLinus Torvalds 	}
12641da177e4SLinus Torvalds 	get_fdc(nr);
12651da177e4SLinus Torvalds 	(*unit[nr].dtype->write_fkt)(nr);
12661da177e4SLinus Torvalds 	if (!raw_write(nr)) {
12671da177e4SLinus Torvalds 		printk (KERN_NOTICE "floppy disk write protected\n");
12681da177e4SLinus Torvalds 		writefromint = 0;
12691da177e4SLinus Torvalds 		writepending = 0;
12701da177e4SLinus Torvalds 	}
12711da177e4SLinus Torvalds 	rel_fdc();
12721da177e4SLinus Torvalds }
12731da177e4SLinus Torvalds 
12741da177e4SLinus Torvalds static int non_int_flush_track (unsigned long nr)
12751da177e4SLinus Torvalds {
12761da177e4SLinus Torvalds 	unsigned long flags;
12771da177e4SLinus Torvalds 
12781da177e4SLinus Torvalds 	nr&=3;
12791da177e4SLinus Torvalds 	writefromint = 0;
12801da177e4SLinus Torvalds 	del_timer(&post_write_timer);
12811da177e4SLinus Torvalds 	get_fdc(nr);
12821da177e4SLinus Torvalds 	if (!fd_motor_on(nr)) {
12831da177e4SLinus Torvalds 		writepending = 0;
12841da177e4SLinus Torvalds 		rel_fdc();
12851da177e4SLinus Torvalds 		return 0;
12861da177e4SLinus Torvalds 	}
12871da177e4SLinus Torvalds 	local_irq_save(flags);
12881da177e4SLinus Torvalds 	if (writepending != 2) {
12891da177e4SLinus Torvalds 		local_irq_restore(flags);
12901da177e4SLinus Torvalds 		(*unit[nr].dtype->write_fkt)(nr);
12911da177e4SLinus Torvalds 		if (!raw_write(nr)) {
12921da177e4SLinus Torvalds 			printk (KERN_NOTICE "floppy disk write protected "
12931da177e4SLinus Torvalds 				"in write!\n");
12941da177e4SLinus Torvalds 			writepending = 0;
12951da177e4SLinus Torvalds 			return 0;
12961da177e4SLinus Torvalds 		}
12971da177e4SLinus Torvalds 		while (block_flag == 2)
12981da177e4SLinus Torvalds 			sleep_on (&wait_fd_block);
12991da177e4SLinus Torvalds 	}
13001da177e4SLinus Torvalds 	else {
13011da177e4SLinus Torvalds 		local_irq_restore(flags);
13021da177e4SLinus Torvalds 		ms_delay(2); /* 2 ms post_write delay */
13031da177e4SLinus Torvalds 		post_write(nr);
13041da177e4SLinus Torvalds 	}
13051da177e4SLinus Torvalds 	rel_fdc();
13061da177e4SLinus Torvalds 	return 1;
13071da177e4SLinus Torvalds }
13081da177e4SLinus Torvalds 
13091da177e4SLinus Torvalds static int get_track(int drive, int track)
13101da177e4SLinus Torvalds {
13111da177e4SLinus Torvalds 	int error, errcnt;
13121da177e4SLinus Torvalds 
13131da177e4SLinus Torvalds 	drive&=3;
13141da177e4SLinus Torvalds 	if (unit[drive].track == track)
13151da177e4SLinus Torvalds 		return 0;
13161da177e4SLinus Torvalds 	get_fdc(drive);
13171da177e4SLinus Torvalds 	if (!fd_motor_on(drive)) {
13181da177e4SLinus Torvalds 		rel_fdc();
13191da177e4SLinus Torvalds 		return -1;
13201da177e4SLinus Torvalds 	}
13211da177e4SLinus Torvalds 
13221da177e4SLinus Torvalds 	if (unit[drive].dirty == 1) {
13231da177e4SLinus Torvalds 		del_timer (flush_track_timer + drive);
13241da177e4SLinus Torvalds 		non_int_flush_track (drive);
13251da177e4SLinus Torvalds 	}
13261da177e4SLinus Torvalds 	errcnt = 0;
13271da177e4SLinus Torvalds 	while (errcnt < MAX_ERRORS) {
13281da177e4SLinus Torvalds 		if (!fd_seek(drive, track))
13291da177e4SLinus Torvalds 			return -1;
13301da177e4SLinus Torvalds 		raw_read(drive);
13311da177e4SLinus Torvalds 		error = (*unit[drive].dtype->read_fkt)(drive);
13321da177e4SLinus Torvalds 		if (error == 0) {
13331da177e4SLinus Torvalds 			rel_fdc();
13341da177e4SLinus Torvalds 			return 0;
13351da177e4SLinus Torvalds 		}
13361da177e4SLinus Torvalds 		/* Read Error Handling: recalibrate and try again */
13371da177e4SLinus Torvalds 		unit[drive].track = -1;
13381da177e4SLinus Torvalds 		errcnt++;
13391da177e4SLinus Torvalds 	}
13401da177e4SLinus Torvalds 	rel_fdc();
13411da177e4SLinus Torvalds 	return -1;
13421da177e4SLinus Torvalds }
13431da177e4SLinus Torvalds 
13441da177e4SLinus Torvalds static void redo_fd_request(void)
13451da177e4SLinus Torvalds {
13461da177e4SLinus Torvalds 	unsigned int cnt, block, track, sector;
13471da177e4SLinus Torvalds 	int drive;
13481da177e4SLinus Torvalds 	struct amiga_floppy_struct *floppy;
13491da177e4SLinus Torvalds 	char *data;
13501da177e4SLinus Torvalds 	unsigned long flags;
13511da177e4SLinus Torvalds 
13521da177e4SLinus Torvalds  repeat:
13531da177e4SLinus Torvalds 	if (!CURRENT) {
13541da177e4SLinus Torvalds 		/* Nothing left to do */
13551da177e4SLinus Torvalds 		return;
13561da177e4SLinus Torvalds 	}
13571da177e4SLinus Torvalds 
13581da177e4SLinus Torvalds 	floppy = CURRENT->rq_disk->private_data;
13591da177e4SLinus Torvalds 	drive = floppy - unit;
13601da177e4SLinus Torvalds 
13611da177e4SLinus Torvalds 	/* Here someone could investigate to be more efficient */
13621da177e4SLinus Torvalds 	for (cnt = 0; cnt < CURRENT->current_nr_sectors; cnt++) {
13631da177e4SLinus Torvalds #ifdef DEBUG
13641da177e4SLinus Torvalds 		printk("fd: sector %ld + %d requested for %s\n",
13651da177e4SLinus Torvalds 		       CURRENT->sector,cnt,
1366e654bc43SBoaz Harrosh 		       (rq_data_dir(CURRENT) == READ) ? "read" : "write");
13671da177e4SLinus Torvalds #endif
13681da177e4SLinus Torvalds 		block = CURRENT->sector + cnt;
13691da177e4SLinus Torvalds 		if ((int)block > floppy->blocks) {
13701da177e4SLinus Torvalds 			end_request(CURRENT, 0);
13711da177e4SLinus Torvalds 			goto repeat;
13721da177e4SLinus Torvalds 		}
13731da177e4SLinus Torvalds 
13741da177e4SLinus Torvalds 		track = block / (floppy->dtype->sects * floppy->type->sect_mult);
13751da177e4SLinus Torvalds 		sector = block % (floppy->dtype->sects * floppy->type->sect_mult);
13761da177e4SLinus Torvalds 		data = CURRENT->buffer + 512 * cnt;
13771da177e4SLinus Torvalds #ifdef DEBUG
13781da177e4SLinus Torvalds 		printk("access to track %d, sector %d, with buffer at "
13791da177e4SLinus Torvalds 		       "0x%08lx\n", track, sector, data);
13801da177e4SLinus Torvalds #endif
13811da177e4SLinus Torvalds 
13821da177e4SLinus Torvalds 		if ((rq_data_dir(CURRENT) != READ) && (rq_data_dir(CURRENT) != WRITE)) {
13831da177e4SLinus Torvalds 			printk(KERN_WARNING "do_fd_request: unknown command\n");
13841da177e4SLinus Torvalds 			end_request(CURRENT, 0);
13851da177e4SLinus Torvalds 			goto repeat;
13861da177e4SLinus Torvalds 		}
13871da177e4SLinus Torvalds 		if (get_track(drive, track) == -1) {
13881da177e4SLinus Torvalds 			end_request(CURRENT, 0);
13891da177e4SLinus Torvalds 			goto repeat;
13901da177e4SLinus Torvalds 		}
13911da177e4SLinus Torvalds 
13921da177e4SLinus Torvalds 		switch (rq_data_dir(CURRENT)) {
13931da177e4SLinus Torvalds 		case READ:
13941da177e4SLinus Torvalds 			memcpy(data, floppy->trackbuf + sector * 512, 512);
13951da177e4SLinus Torvalds 			break;
13961da177e4SLinus Torvalds 
13971da177e4SLinus Torvalds 		case WRITE:
13981da177e4SLinus Torvalds 			memcpy(floppy->trackbuf + sector * 512, data, 512);
13991da177e4SLinus Torvalds 
14001da177e4SLinus Torvalds 			/* keep the drive spinning while writes are scheduled */
14011da177e4SLinus Torvalds 			if (!fd_motor_on(drive)) {
14021da177e4SLinus Torvalds 				end_request(CURRENT, 0);
14031da177e4SLinus Torvalds 				goto repeat;
14041da177e4SLinus Torvalds 			}
14051da177e4SLinus Torvalds 			/*
14061da177e4SLinus Torvalds 			 * setup a callback to write the track buffer
14071da177e4SLinus Torvalds 			 * after a short (1 tick) delay.
14081da177e4SLinus Torvalds 			 */
14091da177e4SLinus Torvalds 			local_irq_save(flags);
14101da177e4SLinus Torvalds 
14111da177e4SLinus Torvalds 			floppy->dirty = 1;
14121da177e4SLinus Torvalds 		        /* reset the timer */
14131da177e4SLinus Torvalds 			mod_timer (flush_track_timer + drive, jiffies + 1);
14141da177e4SLinus Torvalds 			local_irq_restore(flags);
14151da177e4SLinus Torvalds 			break;
14161da177e4SLinus Torvalds 		}
14171da177e4SLinus Torvalds 	}
14181da177e4SLinus Torvalds 	CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
14191da177e4SLinus Torvalds 	CURRENT->sector += CURRENT->current_nr_sectors;
14201da177e4SLinus Torvalds 
14211da177e4SLinus Torvalds 	end_request(CURRENT, 1);
14221da177e4SLinus Torvalds 	goto repeat;
14231da177e4SLinus Torvalds }
14241da177e4SLinus Torvalds 
1425165125e1SJens Axboe static void do_fd_request(struct request_queue * q)
14261da177e4SLinus Torvalds {
14271da177e4SLinus Torvalds 	redo_fd_request();
14281da177e4SLinus Torvalds }
14291da177e4SLinus Torvalds 
1430a885c8c4SChristoph Hellwig static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1431a885c8c4SChristoph Hellwig {
1432a885c8c4SChristoph Hellwig 	int drive = MINOR(bdev->bd_dev) & 3;
1433a885c8c4SChristoph Hellwig 
1434a885c8c4SChristoph Hellwig 	geo->heads = unit[drive].type->heads;
1435a885c8c4SChristoph Hellwig 	geo->sectors = unit[drive].dtype->sects * unit[drive].type->sect_mult;
1436a885c8c4SChristoph Hellwig 	geo->cylinders = unit[drive].type->tracks;
1437a885c8c4SChristoph Hellwig 	return 0;
1438a885c8c4SChristoph Hellwig }
1439a885c8c4SChristoph Hellwig 
144047225db5SAl Viro static int fd_ioctl(struct block_device *bdev, fmode_t mode,
14411da177e4SLinus Torvalds 		    unsigned int cmd, unsigned long param)
14421da177e4SLinus Torvalds {
144347225db5SAl Viro 	struct amiga_floppy_struct *p = bdev->bd_disk->private_data;
144447225db5SAl Viro 	int drive = p - unit;
14451da177e4SLinus Torvalds 	static struct floppy_struct getprm;
14468a423e54SAl Viro 	void __user *argp = (void __user *)param;
14471da177e4SLinus Torvalds 
14481da177e4SLinus Torvalds 	switch(cmd){
14491da177e4SLinus Torvalds 	case FDFMTBEG:
14501da177e4SLinus Torvalds 		get_fdc(drive);
14511da177e4SLinus Torvalds 		if (fd_ref[drive] > 1) {
14521da177e4SLinus Torvalds 			rel_fdc();
14531da177e4SLinus Torvalds 			return -EBUSY;
14541da177e4SLinus Torvalds 		}
145547225db5SAl Viro 		fsync_bdev(bdev);
14561da177e4SLinus Torvalds 		if (fd_motor_on(drive) == 0) {
14571da177e4SLinus Torvalds 			rel_fdc();
14581da177e4SLinus Torvalds 			return -ENODEV;
14591da177e4SLinus Torvalds 		}
14601da177e4SLinus Torvalds 		if (fd_calibrate(drive) == 0) {
14611da177e4SLinus Torvalds 			rel_fdc();
14621da177e4SLinus Torvalds 			return -ENXIO;
14631da177e4SLinus Torvalds 		}
14641da177e4SLinus Torvalds 		floppy_off(drive);
14651da177e4SLinus Torvalds 		rel_fdc();
14661da177e4SLinus Torvalds 		break;
14671da177e4SLinus Torvalds 	case FDFMTTRK:
146847225db5SAl Viro 		if (param < p->type->tracks * p->type->heads)
14691da177e4SLinus Torvalds 		{
14701da177e4SLinus Torvalds 			get_fdc(drive);
14711da177e4SLinus Torvalds 			if (fd_seek(drive,param) != 0){
147247225db5SAl Viro 				memset(p->trackbuf, FD_FILL_BYTE,
147347225db5SAl Viro 				       p->dtype->sects * p->type->sect_mult * 512);
14741da177e4SLinus Torvalds 				non_int_flush_track(drive);
14751da177e4SLinus Torvalds 			}
14761da177e4SLinus Torvalds 			floppy_off(drive);
14771da177e4SLinus Torvalds 			rel_fdc();
14781da177e4SLinus Torvalds 		}
14791da177e4SLinus Torvalds 		else
14801da177e4SLinus Torvalds 			return -EINVAL;
14811da177e4SLinus Torvalds 		break;
14821da177e4SLinus Torvalds 	case FDFMTEND:
14831da177e4SLinus Torvalds 		floppy_off(drive);
148447225db5SAl Viro 		invalidate_bdev(bdev);
14851da177e4SLinus Torvalds 		break;
14861da177e4SLinus Torvalds 	case FDGETPRM:
14871da177e4SLinus Torvalds 		memset((void *)&getprm, 0, sizeof (getprm));
148847225db5SAl Viro 		getprm.track=p->type->tracks;
148947225db5SAl Viro 		getprm.head=p->type->heads;
149047225db5SAl Viro 		getprm.sect=p->dtype->sects * p->type->sect_mult;
149147225db5SAl Viro 		getprm.size=p->blocks;
14928a423e54SAl Viro 		if (copy_to_user(argp, &getprm, sizeof(struct floppy_struct)))
14931da177e4SLinus Torvalds 			return -EFAULT;
14941da177e4SLinus Torvalds 		break;
14951da177e4SLinus Torvalds 	case FDSETPRM:
14961da177e4SLinus Torvalds 	case FDDEFPRM:
14971da177e4SLinus Torvalds 		return -EINVAL;
14981da177e4SLinus Torvalds 	case FDFLUSH: /* unconditionally, even if not needed */
14991da177e4SLinus Torvalds 		del_timer (flush_track_timer + drive);
15001da177e4SLinus Torvalds 		non_int_flush_track(drive);
15011da177e4SLinus Torvalds 		break;
15021da177e4SLinus Torvalds #ifdef RAW_IOCTL
15031da177e4SLinus Torvalds 	case IOCTL_RAW_TRACK:
150447225db5SAl Viro 		if (copy_to_user(argp, raw_buf, p->type->read_size))
15051da177e4SLinus Torvalds 			return -EFAULT;
15061da177e4SLinus Torvalds 		else
150747225db5SAl Viro 			return p->type->read_size;
15081da177e4SLinus Torvalds #endif
15091da177e4SLinus Torvalds 	default:
15101da177e4SLinus Torvalds 		printk(KERN_DEBUG "fd_ioctl: unknown cmd %d for drive %d.",
15111da177e4SLinus Torvalds 		       cmd, drive);
15121da177e4SLinus Torvalds 		return -ENOSYS;
15131da177e4SLinus Torvalds 	}
15141da177e4SLinus Torvalds 	return 0;
15151da177e4SLinus Torvalds }
15161da177e4SLinus Torvalds 
15171da177e4SLinus Torvalds static void fd_probe(int dev)
15181da177e4SLinus Torvalds {
15191da177e4SLinus Torvalds 	unsigned long code;
15201da177e4SLinus Torvalds 	int type;
15211da177e4SLinus Torvalds 	int drive;
15221da177e4SLinus Torvalds 
15231da177e4SLinus Torvalds 	drive = dev & 3;
15241da177e4SLinus Torvalds 	code = fd_get_drive_id(drive);
15251da177e4SLinus Torvalds 
15261da177e4SLinus Torvalds 	/* get drive type */
15271da177e4SLinus Torvalds 	for (type = 0; type < num_dr_types; type++)
15281da177e4SLinus Torvalds 		if (drive_types[type].code == code)
15291da177e4SLinus Torvalds 			break;
15301da177e4SLinus Torvalds 
15311da177e4SLinus Torvalds 	if (type >= num_dr_types) {
15321da177e4SLinus Torvalds 		printk(KERN_WARNING "fd_probe: unsupported drive type "
15331da177e4SLinus Torvalds 		       "%08lx found\n", code);
15341da177e4SLinus Torvalds 		unit[drive].type = &drive_types[num_dr_types-1]; /* FD_NODRIVE */
15351da177e4SLinus Torvalds 		return;
15361da177e4SLinus Torvalds 	}
15371da177e4SLinus Torvalds 
15381da177e4SLinus Torvalds 	unit[drive].type = drive_types + type;
15391da177e4SLinus Torvalds 	unit[drive].track = -1;
15401da177e4SLinus Torvalds 
15411da177e4SLinus Torvalds 	unit[drive].disk = -1;
15421da177e4SLinus Torvalds 	unit[drive].motor = 0;
15431da177e4SLinus Torvalds 	unit[drive].busy = 0;
15441da177e4SLinus Torvalds 	unit[drive].status = -1;
15451da177e4SLinus Torvalds }
15461da177e4SLinus Torvalds 
15471da177e4SLinus Torvalds /*
15481da177e4SLinus Torvalds  * floppy_open check for aliasing (/dev/fd0 can be the same as
15491da177e4SLinus Torvalds  * /dev/PS0 etc), and disallows simultaneous access to the same
15501da177e4SLinus Torvalds  * drive with different device numbers.
15511da177e4SLinus Torvalds  */
155247225db5SAl Viro static int floppy_open(struct block_device *bdev, fmode_t mode)
15531da177e4SLinus Torvalds {
155447225db5SAl Viro 	int drive = MINOR(bdev->bd_dev) & 3;
155547225db5SAl Viro 	int system =  (MINOR(bdev->bd_dev) & 4) >> 2;
15561da177e4SLinus Torvalds 	int old_dev;
15571da177e4SLinus Torvalds 	unsigned long flags;
15581da177e4SLinus Torvalds 
15591da177e4SLinus Torvalds 	old_dev = fd_device[drive];
15601da177e4SLinus Torvalds 
15611da177e4SLinus Torvalds 	if (fd_ref[drive] && old_dev != system)
15621da177e4SLinus Torvalds 		return -EBUSY;
15631da177e4SLinus Torvalds 
156447225db5SAl Viro 	if (mode & (FMODE_READ|FMODE_WRITE)) {
156547225db5SAl Viro 		check_disk_change(bdev);
156647225db5SAl Viro 		if (mode & FMODE_WRITE) {
15671da177e4SLinus Torvalds 			int wrprot;
15681da177e4SLinus Torvalds 
15691da177e4SLinus Torvalds 			get_fdc(drive);
15701da177e4SLinus Torvalds 			fd_select (drive);
15711da177e4SLinus Torvalds 			wrprot = !(ciaa.pra & DSKPROT);
15721da177e4SLinus Torvalds 			fd_deselect (drive);
15731da177e4SLinus Torvalds 			rel_fdc();
15741da177e4SLinus Torvalds 
15751da177e4SLinus Torvalds 			if (wrprot)
15761da177e4SLinus Torvalds 				return -EROFS;
15771da177e4SLinus Torvalds 		}
15781da177e4SLinus Torvalds 	}
15791da177e4SLinus Torvalds 
15801da177e4SLinus Torvalds 	local_irq_save(flags);
15811da177e4SLinus Torvalds 	fd_ref[drive]++;
15821da177e4SLinus Torvalds 	fd_device[drive] = system;
15831da177e4SLinus Torvalds 	local_irq_restore(flags);
15841da177e4SLinus Torvalds 
15851da177e4SLinus Torvalds 	unit[drive].dtype=&data_types[system];
15861da177e4SLinus Torvalds 	unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks*
15871da177e4SLinus Torvalds 		data_types[system].sects*unit[drive].type->sect_mult;
15881da177e4SLinus Torvalds 	set_capacity(unit[drive].gendisk, unit[drive].blocks);
15891da177e4SLinus Torvalds 
15901da177e4SLinus Torvalds 	printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive,
15911da177e4SLinus Torvalds 	       unit[drive].type->name, data_types[system].name);
15921da177e4SLinus Torvalds 
15931da177e4SLinus Torvalds 	return 0;
15941da177e4SLinus Torvalds }
15951da177e4SLinus Torvalds 
159647225db5SAl Viro static int floppy_release(struct gendisk *disk, fmode_t mode)
15971da177e4SLinus Torvalds {
159847225db5SAl Viro 	struct amiga_floppy_struct *p = disk->private_data;
159947225db5SAl Viro 	int drive = p - unit;
16001da177e4SLinus Torvalds 
16011da177e4SLinus Torvalds 	if (unit[drive].dirty == 1) {
16021da177e4SLinus Torvalds 		del_timer (flush_track_timer + drive);
16031da177e4SLinus Torvalds 		non_int_flush_track (drive);
16041da177e4SLinus Torvalds 	}
16051da177e4SLinus Torvalds 
16061da177e4SLinus Torvalds 	if (!fd_ref[drive]--) {
16071da177e4SLinus Torvalds 		printk(KERN_CRIT "floppy_release with fd_ref == 0");
16081da177e4SLinus Torvalds 		fd_ref[drive] = 0;
16091da177e4SLinus Torvalds 	}
16101da177e4SLinus Torvalds #ifdef MODULE
16111da177e4SLinus Torvalds /* the mod_use counter is handled this way */
16121da177e4SLinus Torvalds 	floppy_off (drive | 0x40000000);
16131da177e4SLinus Torvalds #endif
16141da177e4SLinus Torvalds 	return 0;
16151da177e4SLinus Torvalds }
16161da177e4SLinus Torvalds 
16171da177e4SLinus Torvalds /*
16181da177e4SLinus Torvalds  * floppy-change is never called from an interrupt, so we can relax a bit
16191da177e4SLinus Torvalds  * here, sleep etc. Note that floppy-on tries to set current_DOR to point
16201da177e4SLinus Torvalds  * to the desired drive, but it will probably not survive the sleep if
16211da177e4SLinus Torvalds  * several floppies are used at the same time: thus the loop.
16221da177e4SLinus Torvalds  */
16231da177e4SLinus Torvalds static int amiga_floppy_change(struct gendisk *disk)
16241da177e4SLinus Torvalds {
16251da177e4SLinus Torvalds 	struct amiga_floppy_struct *p = disk->private_data;
16261da177e4SLinus Torvalds 	int drive = p - unit;
16271da177e4SLinus Torvalds 	int changed;
16281da177e4SLinus Torvalds 	static int first_time = 1;
16291da177e4SLinus Torvalds 
16301da177e4SLinus Torvalds 	if (first_time)
16311da177e4SLinus Torvalds 		changed = first_time--;
16321da177e4SLinus Torvalds 	else {
16331da177e4SLinus Torvalds 		get_fdc(drive);
16341da177e4SLinus Torvalds 		fd_select (drive);
16351da177e4SLinus Torvalds 		changed = !(ciaa.pra & DSKCHANGE);
16361da177e4SLinus Torvalds 		fd_deselect (drive);
16371da177e4SLinus Torvalds 		rel_fdc();
16381da177e4SLinus Torvalds 	}
16391da177e4SLinus Torvalds 
16401da177e4SLinus Torvalds 	if (changed) {
16411da177e4SLinus Torvalds 		fd_probe(drive);
16421da177e4SLinus Torvalds 		p->track = -1;
16431da177e4SLinus Torvalds 		p->dirty = 0;
16441da177e4SLinus Torvalds 		writepending = 0; /* if this was true before, too bad! */
16451da177e4SLinus Torvalds 		writefromint = 0;
16461da177e4SLinus Torvalds 		return 1;
16471da177e4SLinus Torvalds 	}
16481da177e4SLinus Torvalds 	return 0;
16491da177e4SLinus Torvalds }
16501da177e4SLinus Torvalds 
16511da177e4SLinus Torvalds static struct block_device_operations floppy_fops = {
16521da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
165347225db5SAl Viro 	.open		= floppy_open,
165447225db5SAl Viro 	.release	= floppy_release,
165547225db5SAl Viro 	.locked_ioctl	= fd_ioctl,
1656a885c8c4SChristoph Hellwig 	.getgeo		= fd_getgeo,
16571da177e4SLinus Torvalds 	.media_changed	= amiga_floppy_change,
16581da177e4SLinus Torvalds };
16591da177e4SLinus Torvalds 
16601da177e4SLinus Torvalds static int __init fd_probe_drives(void)
16611da177e4SLinus Torvalds {
16621da177e4SLinus Torvalds 	int drive,drives,nomem;
16631da177e4SLinus Torvalds 
16641da177e4SLinus Torvalds 	printk(KERN_INFO "FD: probing units\n" KERN_INFO "found ");
16651da177e4SLinus Torvalds 	drives=0;
16661da177e4SLinus Torvalds 	nomem=0;
16671da177e4SLinus Torvalds 	for(drive=0;drive<FD_MAX_UNITS;drive++) {
16681da177e4SLinus Torvalds 		struct gendisk *disk;
16691da177e4SLinus Torvalds 		fd_probe(drive);
16701da177e4SLinus Torvalds 		if (unit[drive].type->code == FD_NODRIVE)
16711da177e4SLinus Torvalds 			continue;
16721da177e4SLinus Torvalds 		disk = alloc_disk(1);
16731da177e4SLinus Torvalds 		if (!disk) {
16741da177e4SLinus Torvalds 			unit[drive].type->code = FD_NODRIVE;
16751da177e4SLinus Torvalds 			continue;
16761da177e4SLinus Torvalds 		}
16771da177e4SLinus Torvalds 		unit[drive].gendisk = disk;
16781da177e4SLinus Torvalds 		drives++;
16791da177e4SLinus Torvalds 		if ((unit[drive].trackbuf = kmalloc(FLOPPY_MAX_SECTORS * 512, GFP_KERNEL)) == NULL) {
16801da177e4SLinus Torvalds 			printk("no mem for ");
16811da177e4SLinus Torvalds 			unit[drive].type = &drive_types[num_dr_types - 1]; /* FD_NODRIVE */
16821da177e4SLinus Torvalds 			drives--;
16831da177e4SLinus Torvalds 			nomem = 1;
16841da177e4SLinus Torvalds 		}
16851da177e4SLinus Torvalds 		printk("fd%d ",drive);
16861da177e4SLinus Torvalds 		disk->major = FLOPPY_MAJOR;
16871da177e4SLinus Torvalds 		disk->first_minor = drive;
16881da177e4SLinus Torvalds 		disk->fops = &floppy_fops;
16891da177e4SLinus Torvalds 		sprintf(disk->disk_name, "fd%d", drive);
16901da177e4SLinus Torvalds 		disk->private_data = &unit[drive];
16911da177e4SLinus Torvalds 		disk->queue = floppy_queue;
16921da177e4SLinus Torvalds 		set_capacity(disk, 880*2);
16931da177e4SLinus Torvalds 		add_disk(disk);
16941da177e4SLinus Torvalds 	}
16951da177e4SLinus Torvalds 	if ((drives > 0) || (nomem == 0)) {
16961da177e4SLinus Torvalds 		if (drives == 0)
16971da177e4SLinus Torvalds 			printk("no drives");
16981da177e4SLinus Torvalds 		printk("\n");
16991da177e4SLinus Torvalds 		return drives;
17001da177e4SLinus Torvalds 	}
17011da177e4SLinus Torvalds 	printk("\n");
17021da177e4SLinus Torvalds 	return -ENOMEM;
17031da177e4SLinus Torvalds }
17041da177e4SLinus Torvalds 
17051da177e4SLinus Torvalds static struct kobject *floppy_find(dev_t dev, int *part, void *data)
17061da177e4SLinus Torvalds {
17071da177e4SLinus Torvalds 	int drive = *part & 3;
17081da177e4SLinus Torvalds 	if (unit[drive].type->code == FD_NODRIVE)
17091da177e4SLinus Torvalds 		return NULL;
17101da177e4SLinus Torvalds 	*part = 0;
17111da177e4SLinus Torvalds 	return get_disk(unit[drive].gendisk);
17121da177e4SLinus Torvalds }
17131da177e4SLinus Torvalds 
17142db5f59cSAl Viro static int __init amiga_floppy_init(void)
17151da177e4SLinus Torvalds {
17161da177e4SLinus Torvalds 	int i, ret;
17171da177e4SLinus Torvalds 
17182db5f59cSAl Viro 	if (!MACH_IS_AMIGA)
1719fd5b462fSGeert Uytterhoeven 		return -ENODEV;
17202db5f59cSAl Viro 
17211da177e4SLinus Torvalds 	if (!AMIGAHW_PRESENT(AMI_FLOPPY))
1722fd5b462fSGeert Uytterhoeven 		return -ENODEV;
17231da177e4SLinus Torvalds 
17241da177e4SLinus Torvalds 	if (register_blkdev(FLOPPY_MAJOR,"fd"))
17251da177e4SLinus Torvalds 		return -EBUSY;
17261da177e4SLinus Torvalds 
17271da177e4SLinus Torvalds 	/*
17281da177e4SLinus Torvalds 	 *  We request DSKPTR, DSKLEN and DSKDATA only, because the other
17291da177e4SLinus Torvalds 	 *  floppy registers are too spreaded over the custom register space
17301da177e4SLinus Torvalds 	 */
17311da177e4SLinus Torvalds 	ret = -EBUSY;
17321da177e4SLinus Torvalds 	if (!request_mem_region(CUSTOM_PHYSADDR+0x20, 8, "amiflop [Paula]")) {
17331da177e4SLinus Torvalds 		printk("fd: cannot get floppy registers\n");
17341da177e4SLinus Torvalds 		goto out_blkdev;
17351da177e4SLinus Torvalds 	}
17361da177e4SLinus Torvalds 
17371da177e4SLinus Torvalds 	ret = -ENOMEM;
17381da177e4SLinus Torvalds 	if ((raw_buf = (char *)amiga_chip_alloc (RAW_BUF_SIZE, "Floppy")) ==
17391da177e4SLinus Torvalds 	    NULL) {
17401da177e4SLinus Torvalds 		printk("fd: cannot get chip mem buffer\n");
17411da177e4SLinus Torvalds 		goto out_memregion;
17421da177e4SLinus Torvalds 	}
17431da177e4SLinus Torvalds 
17441da177e4SLinus Torvalds 	ret = -EBUSY;
17451da177e4SLinus Torvalds 	if (request_irq(IRQ_AMIGA_DSKBLK, fd_block_done, 0, "floppy_dma", NULL)) {
17461da177e4SLinus Torvalds 		printk("fd: cannot get irq for dma\n");
17471da177e4SLinus Torvalds 		goto out_irq;
17481da177e4SLinus Torvalds 	}
17491da177e4SLinus Torvalds 
17501da177e4SLinus Torvalds 	if (request_irq(IRQ_AMIGA_CIAA_TB, ms_isr, 0, "floppy_timer", NULL)) {
17511da177e4SLinus Torvalds 		printk("fd: cannot get irq for timer\n");
17521da177e4SLinus Torvalds 		goto out_irq2;
17531da177e4SLinus Torvalds 	}
17541da177e4SLinus Torvalds 
17551da177e4SLinus Torvalds 	ret = -ENOMEM;
17561da177e4SLinus Torvalds 	floppy_queue = blk_init_queue(do_fd_request, &amiflop_lock);
17571da177e4SLinus Torvalds 	if (!floppy_queue)
17581da177e4SLinus Torvalds 		goto out_queue;
17591da177e4SLinus Torvalds 
1760fd5b462fSGeert Uytterhoeven 	ret = -ENODEV;
17611da177e4SLinus Torvalds 	if (fd_probe_drives() < 1) /* No usable drives */
17621da177e4SLinus Torvalds 		goto out_probe;
17631da177e4SLinus Torvalds 
17641da177e4SLinus Torvalds 	blk_register_region(MKDEV(FLOPPY_MAJOR, 0), 256, THIS_MODULE,
17651da177e4SLinus Torvalds 				floppy_find, NULL, NULL);
17661da177e4SLinus Torvalds 
17671da177e4SLinus Torvalds 	/* initialize variables */
17681da177e4SLinus Torvalds 	init_timer(&motor_on_timer);
17691da177e4SLinus Torvalds 	motor_on_timer.expires = 0;
17701da177e4SLinus Torvalds 	motor_on_timer.data = 0;
17711da177e4SLinus Torvalds 	motor_on_timer.function = motor_on_callback;
17721da177e4SLinus Torvalds 	for (i = 0; i < FD_MAX_UNITS; i++) {
17731da177e4SLinus Torvalds 		init_timer(&motor_off_timer[i]);
17741da177e4SLinus Torvalds 		motor_off_timer[i].expires = 0;
17751da177e4SLinus Torvalds 		motor_off_timer[i].data = i|0x80000000;
17761da177e4SLinus Torvalds 		motor_off_timer[i].function = fd_motor_off;
17771da177e4SLinus Torvalds 		init_timer(&flush_track_timer[i]);
17781da177e4SLinus Torvalds 		flush_track_timer[i].expires = 0;
17791da177e4SLinus Torvalds 		flush_track_timer[i].data = i;
17801da177e4SLinus Torvalds 		flush_track_timer[i].function = flush_track_callback;
17811da177e4SLinus Torvalds 
17821da177e4SLinus Torvalds 		unit[i].track = -1;
17831da177e4SLinus Torvalds 	}
17841da177e4SLinus Torvalds 
17851da177e4SLinus Torvalds 	init_timer(&post_write_timer);
17861da177e4SLinus Torvalds 	post_write_timer.expires = 0;
17871da177e4SLinus Torvalds 	post_write_timer.data = 0;
17881da177e4SLinus Torvalds 	post_write_timer.function = post_write;
17891da177e4SLinus Torvalds 
17901da177e4SLinus Torvalds 	for (i = 0; i < 128; i++)
17911da177e4SLinus Torvalds 		mfmdecode[i]=255;
17921da177e4SLinus Torvalds 	for (i = 0; i < 16; i++)
17931da177e4SLinus Torvalds 		mfmdecode[mfmencode[i]]=i;
17941da177e4SLinus Torvalds 
17951da177e4SLinus Torvalds 	/* make sure that disk DMA is enabled */
17961da177e4SLinus Torvalds 	custom.dmacon = DMAF_SETCLR | DMAF_DISK;
17971da177e4SLinus Torvalds 
17981da177e4SLinus Torvalds 	/* init ms timer */
17991da177e4SLinus Torvalds 	ciaa.crb = 8; /* one-shot, stop */
18001da177e4SLinus Torvalds 	return 0;
18011da177e4SLinus Torvalds 
18021da177e4SLinus Torvalds out_probe:
18031da177e4SLinus Torvalds 	blk_cleanup_queue(floppy_queue);
18041da177e4SLinus Torvalds out_queue:
18051da177e4SLinus Torvalds 	free_irq(IRQ_AMIGA_CIAA_TB, NULL);
18061da177e4SLinus Torvalds out_irq2:
18071da177e4SLinus Torvalds 	free_irq(IRQ_AMIGA_DSKBLK, NULL);
18081da177e4SLinus Torvalds out_irq:
18091da177e4SLinus Torvalds 	amiga_chip_free(raw_buf);
18101da177e4SLinus Torvalds out_memregion:
18111da177e4SLinus Torvalds 	release_mem_region(CUSTOM_PHYSADDR+0x20, 8);
18121da177e4SLinus Torvalds out_blkdev:
18131da177e4SLinus Torvalds 	unregister_blkdev(FLOPPY_MAJOR,"fd");
18141da177e4SLinus Torvalds 	return ret;
18151da177e4SLinus Torvalds }
18161da177e4SLinus Torvalds 
18172db5f59cSAl Viro module_init(amiga_floppy_init);
18181da177e4SLinus Torvalds #ifdef MODULE
18191da177e4SLinus Torvalds 
18201da177e4SLinus Torvalds #if 0 /* not safe to unload */
18211da177e4SLinus Torvalds void cleanup_module(void)
18221da177e4SLinus Torvalds {
18231da177e4SLinus Torvalds 	int i;
18241da177e4SLinus Torvalds 
18251da177e4SLinus Torvalds 	for( i = 0; i < FD_MAX_UNITS; i++) {
18261da177e4SLinus Torvalds 		if (unit[i].type->code != FD_NODRIVE) {
18271da177e4SLinus Torvalds 			del_gendisk(unit[i].gendisk);
18281da177e4SLinus Torvalds 			put_disk(unit[i].gendisk);
18291da177e4SLinus Torvalds 			kfree(unit[i].trackbuf);
18301da177e4SLinus Torvalds 		}
18311da177e4SLinus Torvalds 	}
18321da177e4SLinus Torvalds 	blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
18331da177e4SLinus Torvalds 	free_irq(IRQ_AMIGA_CIAA_TB, NULL);
18341da177e4SLinus Torvalds 	free_irq(IRQ_AMIGA_DSKBLK, NULL);
18351da177e4SLinus Torvalds 	custom.dmacon = DMAF_DISK; /* disable DMA */
18361da177e4SLinus Torvalds 	amiga_chip_free(raw_buf);
18371da177e4SLinus Torvalds 	blk_cleanup_queue(floppy_queue);
18381da177e4SLinus Torvalds 	release_mem_region(CUSTOM_PHYSADDR+0x20, 8);
18391da177e4SLinus Torvalds 	unregister_blkdev(FLOPPY_MAJOR, "fd");
18401da177e4SLinus Torvalds }
18411da177e4SLinus Torvalds #endif
184263907435SAl Viro 
184363907435SAl Viro #else
184463907435SAl Viro static int __init amiga_floppy_setup (char *str)
184563907435SAl Viro {
184663907435SAl Viro 	int n;
184763907435SAl Viro 	if (!MACH_IS_AMIGA)
184863907435SAl Viro 		return 0;
184963907435SAl Viro 	if (!get_option(&str, &n))
185063907435SAl Viro 		return 0;
185163907435SAl Viro 	printk (KERN_INFO "amiflop: Setting default df0 to %x\n", n);
185263907435SAl Viro 	fd_def_df0 = n;
18539b41046cSOGAWA Hirofumi 	return 1;
185463907435SAl Viro }
185563907435SAl Viro 
185663907435SAl Viro __setup("floppy=", amiga_floppy_setup);
18571da177e4SLinus Torvalds #endif
1858