109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/amiga/amiflop.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 1993 Greg Harp
61da177e4SLinus Torvalds * Portions of this driver are based on code contributed by Brad Pepers
71da177e4SLinus Torvalds *
81da177e4SLinus Torvalds * revised 28.5.95 by Joerg Dorchain
91da177e4SLinus Torvalds * - now no bugs(?) any more for both HD & DD
101da177e4SLinus Torvalds * - added support for 40 Track 5.25" drives, 80-track hopefully behaves
111da177e4SLinus Torvalds * like 3.5" dd (no way to test - are there any 5.25" drives out there
121da177e4SLinus Torvalds * that work on an A4000?)
131da177e4SLinus Torvalds * - wrote formatting routine (maybe dirty, but works)
141da177e4SLinus Torvalds *
151da177e4SLinus Torvalds * june/july 1995 added ms-dos support by Joerg Dorchain
161da177e4SLinus Torvalds * (portions based on messydos.device and various contributors)
171da177e4SLinus Torvalds * - currently only 9 and 18 sector disks
181da177e4SLinus Torvalds *
191da177e4SLinus Torvalds * - fixed a bug with the internal trackbuffer when using multiple
201da177e4SLinus Torvalds * disks the same time
211da177e4SLinus Torvalds * - made formatting a bit safer
221da177e4SLinus Torvalds * - added command line and machine based default for "silent" df0
231da177e4SLinus Torvalds *
241da177e4SLinus Torvalds * december 1995 adapted for 1.2.13pl4 by Joerg Dorchain
251da177e4SLinus Torvalds * - works but I think it's inefficient. (look in redo_fd_request)
261da177e4SLinus Torvalds * But the changes were very efficient. (only three and a half lines)
271da177e4SLinus Torvalds *
281da177e4SLinus Torvalds * january 1996 added special ioctl for tracking down read/write problems
291da177e4SLinus Torvalds * - usage ioctl(d, RAW_TRACK, ptr); the raw track buffer (MFM-encoded data
301da177e4SLinus Torvalds * is copied to area. (area should be large enough since no checking is
311da177e4SLinus Torvalds * done - 30K is currently sufficient). return the actual size of the
321da177e4SLinus Torvalds * trackbuffer
331da177e4SLinus Torvalds * - replaced udelays() by a timer (CIAA timer B) for the waits
341da177e4SLinus Torvalds * needed for the disk mechanic.
351da177e4SLinus Torvalds *
361da177e4SLinus Torvalds * february 1996 fixed error recovery and multiple disk access
371da177e4SLinus Torvalds * - both got broken the first time I tampered with the driver :-(
381da177e4SLinus Torvalds * - still not safe, but better than before
391da177e4SLinus Torvalds *
401da177e4SLinus Torvalds * revised Marts 3rd, 1996 by Jes Sorensen for use in the 1.3.28 kernel.
411da177e4SLinus Torvalds * - Minor changes to accept the kdev_t.
421da177e4SLinus Torvalds * - Replaced some more udelays with ms_delays. Udelay is just a loop,
431da177e4SLinus Torvalds * and so the delay will be different depending on the given
441da177e4SLinus Torvalds * processor :-(
451da177e4SLinus Torvalds * - The driver could use a major cleanup because of the new
461da177e4SLinus Torvalds * major/minor handling that came with kdev_t. It seems to work for
471da177e4SLinus Torvalds * the time being, but I can't guarantee that it will stay like
481da177e4SLinus Torvalds * that when we start using 16 (24?) bit minors.
491da177e4SLinus Torvalds *
501da177e4SLinus Torvalds * restructured jan 1997 by Joerg Dorchain
511da177e4SLinus Torvalds * - Fixed Bug accessing multiple disks
521da177e4SLinus Torvalds * - some code cleanup
531da177e4SLinus Torvalds * - added trackbuffer for each drive to speed things up
541da177e4SLinus Torvalds * - fixed some race conditions (who finds the next may send it to me ;-)
551da177e4SLinus Torvalds */
561da177e4SLinus Torvalds
571da177e4SLinus Torvalds #include <linux/module.h>
585a0e3ad6STejun Heo #include <linux/slab.h>
591da177e4SLinus Torvalds
601da177e4SLinus Torvalds #include <linux/fd.h>
611da177e4SLinus Torvalds #include <linux/hdreg.h>
621da177e4SLinus Torvalds #include <linux/delay.h>
631da177e4SLinus Torvalds #include <linux/init.h>
64b81e0c23SChristoph Hellwig #include <linux/major.h>
652a48fc0aSArnd Bergmann #include <linux/mutex.h>
66ff01bb48SAl Viro #include <linux/fs.h>
67c87228f1SOmar Sandoval #include <linux/blk-mq.h>
68b5dc7840SRoman Zippel #include <linux/interrupt.h>
6992183b34SGeert Uytterhoeven #include <linux/platform_device.h>
701da177e4SLinus Torvalds
711da177e4SLinus Torvalds #include <asm/setup.h>
727c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
731da177e4SLinus Torvalds #include <asm/amigahw.h>
741da177e4SLinus Torvalds #include <asm/amigaints.h>
751da177e4SLinus Torvalds #include <asm/irq.h>
761da177e4SLinus Torvalds
771da177e4SLinus Torvalds #undef DEBUG /* print _LOTS_ of infos */
781da177e4SLinus Torvalds
791da177e4SLinus Torvalds #define RAW_IOCTL
801da177e4SLinus Torvalds #ifdef RAW_IOCTL
811da177e4SLinus Torvalds #define IOCTL_RAW_TRACK 0x5254524B /* 'RTRK' */
821da177e4SLinus Torvalds #endif
831da177e4SLinus Torvalds
841da177e4SLinus Torvalds /*
851da177e4SLinus Torvalds * Defines
861da177e4SLinus Torvalds */
871da177e4SLinus Torvalds
881da177e4SLinus Torvalds /*
89c87228f1SOmar Sandoval * CIAAPRA bits (read only)
90c87228f1SOmar Sandoval */
91c87228f1SOmar Sandoval
92c87228f1SOmar Sandoval #define DSKRDY (0x1<<5) /* disk ready when low */
93c87228f1SOmar Sandoval #define DSKTRACK0 (0x1<<4) /* head at track zero when low */
94c87228f1SOmar Sandoval #define DSKPROT (0x1<<3) /* disk protected when low */
95c87228f1SOmar Sandoval #define DSKCHANGE (0x1<<2) /* low when disk removed */
96c87228f1SOmar Sandoval
97c87228f1SOmar Sandoval /*
98c87228f1SOmar Sandoval * CIAAPRB bits (read/write)
99c87228f1SOmar Sandoval */
100c87228f1SOmar Sandoval
101c87228f1SOmar Sandoval #define DSKMOTOR (0x1<<7) /* motor on when low */
102c87228f1SOmar Sandoval #define DSKSEL3 (0x1<<6) /* select drive 3 when low */
103c87228f1SOmar Sandoval #define DSKSEL2 (0x1<<5) /* select drive 2 when low */
104c87228f1SOmar Sandoval #define DSKSEL1 (0x1<<4) /* select drive 1 when low */
105c87228f1SOmar Sandoval #define DSKSEL0 (0x1<<3) /* select drive 0 when low */
106c87228f1SOmar Sandoval #define DSKSIDE (0x1<<2) /* side selection: 0 = upper, 1 = lower */
107c87228f1SOmar Sandoval #define DSKDIREC (0x1<<1) /* step direction: 0=in, 1=out (to trk 0) */
108c87228f1SOmar Sandoval #define DSKSTEP (0x1) /* pulse low to step head 1 track */
109c87228f1SOmar Sandoval
110c87228f1SOmar Sandoval /*
111c87228f1SOmar Sandoval * DSKBYTR bits (read only)
112c87228f1SOmar Sandoval */
113c87228f1SOmar Sandoval
114c87228f1SOmar Sandoval #define DSKBYT (1<<15) /* register contains valid byte when set */
115c87228f1SOmar Sandoval #define DMAON (1<<14) /* disk DMA enabled */
116c87228f1SOmar Sandoval #define DISKWRITE (1<<13) /* disk write bit in DSKLEN enabled */
117c87228f1SOmar Sandoval #define WORDEQUAL (1<<12) /* DSKSYNC register match when true */
118c87228f1SOmar Sandoval /* bits 7-0 are data */
119c87228f1SOmar Sandoval
120c87228f1SOmar Sandoval /*
121c87228f1SOmar Sandoval * ADKCON/ADKCONR bits
122c87228f1SOmar Sandoval */
123c87228f1SOmar Sandoval
124c87228f1SOmar Sandoval #ifndef SETCLR
125c87228f1SOmar Sandoval #define ADK_SETCLR (1<<15) /* control bit */
126c87228f1SOmar Sandoval #endif
127c87228f1SOmar Sandoval #define ADK_PRECOMP1 (1<<14) /* precompensation selection */
128c87228f1SOmar Sandoval #define ADK_PRECOMP0 (1<<13) /* 00=none, 01=140ns, 10=280ns, 11=500ns */
129c87228f1SOmar Sandoval #define ADK_MFMPREC (1<<12) /* 0=GCR precomp., 1=MFM precomp. */
130c87228f1SOmar Sandoval #define ADK_WORDSYNC (1<<10) /* enable DSKSYNC auto DMA */
131c87228f1SOmar Sandoval #define ADK_MSBSYNC (1<<9) /* when 1, enable sync on MSbit (for GCR) */
132c87228f1SOmar Sandoval #define ADK_FAST (1<<8) /* bit cell: 0=2us (GCR), 1=1us (MFM) */
133c87228f1SOmar Sandoval
134c87228f1SOmar Sandoval /*
135c87228f1SOmar Sandoval * DSKLEN bits
136c87228f1SOmar Sandoval */
137c87228f1SOmar Sandoval
138c87228f1SOmar Sandoval #define DSKLEN_DMAEN (1<<15)
139c87228f1SOmar Sandoval #define DSKLEN_WRITE (1<<14)
140c87228f1SOmar Sandoval
141c87228f1SOmar Sandoval /*
142c87228f1SOmar Sandoval * INTENA/INTREQ bits
143c87228f1SOmar Sandoval */
144c87228f1SOmar Sandoval
145c87228f1SOmar Sandoval #define DSKINDEX (0x1<<4) /* DSKINDEX bit */
146c87228f1SOmar Sandoval
147c87228f1SOmar Sandoval /*
148c87228f1SOmar Sandoval * Misc
149c87228f1SOmar Sandoval */
150c87228f1SOmar Sandoval
151c87228f1SOmar Sandoval #define MFM_SYNC 0x4489 /* standard MFM sync value */
152c87228f1SOmar Sandoval
153c87228f1SOmar Sandoval /* Values for FD_COMMAND */
154c87228f1SOmar Sandoval #define FD_RECALIBRATE 0x07 /* move to track 0 */
155c87228f1SOmar Sandoval #define FD_SEEK 0x0F /* seek track */
156c87228f1SOmar Sandoval #define FD_READ 0xE6 /* read with MT, MFM, SKip deleted */
157c87228f1SOmar Sandoval #define FD_WRITE 0xC5 /* write with MT, MFM */
158c87228f1SOmar Sandoval #define FD_SENSEI 0x08 /* Sense Interrupt Status */
159c87228f1SOmar Sandoval #define FD_SPECIFY 0x03 /* specify HUT etc */
160c87228f1SOmar Sandoval #define FD_FORMAT 0x4D /* format one track */
161c87228f1SOmar Sandoval #define FD_VERSION 0x10 /* get version code */
162c87228f1SOmar Sandoval #define FD_CONFIGURE 0x13 /* configure FIFO operation */
163c87228f1SOmar Sandoval #define FD_PERPENDICULAR 0x12 /* perpendicular r/w mode */
164c87228f1SOmar Sandoval
165c87228f1SOmar Sandoval #define FD_MAX_UNITS 4 /* Max. Number of drives */
166c87228f1SOmar Sandoval #define FLOPPY_MAX_SECTORS 22 /* Max. Number of sectors per track */
167c87228f1SOmar Sandoval
168c87228f1SOmar Sandoval struct fd_data_type {
169c87228f1SOmar Sandoval char *name; /* description of data type */
170c87228f1SOmar Sandoval int sects; /* sectors per track */
171c87228f1SOmar Sandoval int (*read_fkt)(int); /* read whole track */
172c87228f1SOmar Sandoval void (*write_fkt)(int); /* write whole track */
173c87228f1SOmar Sandoval };
174c87228f1SOmar Sandoval
175c87228f1SOmar Sandoval struct fd_drive_type {
176c87228f1SOmar Sandoval unsigned long code; /* code returned from drive */
177c87228f1SOmar Sandoval char *name; /* description of drive */
178c87228f1SOmar Sandoval unsigned int tracks; /* number of tracks */
179c87228f1SOmar Sandoval unsigned int heads; /* number of heads */
180c87228f1SOmar Sandoval unsigned int read_size; /* raw read size for one track */
181c87228f1SOmar Sandoval unsigned int write_size; /* raw write size for one track */
182c87228f1SOmar Sandoval unsigned int sect_mult; /* sectors and gap multiplier (HD = 2) */
183c87228f1SOmar Sandoval unsigned int precomp1; /* start track for precomp 1 */
184c87228f1SOmar Sandoval unsigned int precomp2; /* start track for precomp 2 */
185c87228f1SOmar Sandoval unsigned int step_delay; /* time (in ms) for delay after step */
186c87228f1SOmar Sandoval unsigned int settle_time; /* time to settle after dir change */
187c87228f1SOmar Sandoval unsigned int side_time; /* time needed to change sides */
188c87228f1SOmar Sandoval };
189c87228f1SOmar Sandoval
190c87228f1SOmar Sandoval struct amiga_floppy_struct {
191c87228f1SOmar Sandoval struct fd_drive_type *type; /* type of floppy for this unit */
192c87228f1SOmar Sandoval struct fd_data_type *dtype; /* type of floppy for this unit */
193c87228f1SOmar Sandoval int track; /* current track (-1 == unknown) */
194c87228f1SOmar Sandoval unsigned char *trackbuf; /* current track (kmaloc()'d */
195c87228f1SOmar Sandoval
196c87228f1SOmar Sandoval int blocks; /* total # blocks on disk */
197c87228f1SOmar Sandoval
198c87228f1SOmar Sandoval int changed; /* true when not known */
199c87228f1SOmar Sandoval int disk; /* disk in drive (-1 == unknown) */
200c87228f1SOmar Sandoval int motor; /* true when motor is at speed */
201c87228f1SOmar Sandoval int busy; /* true when drive is active */
202c87228f1SOmar Sandoval int dirty; /* true when trackbuf is not on disk */
203c87228f1SOmar Sandoval int status; /* current error code for unit */
2040033a9b4SChristoph Hellwig struct gendisk *gendisk[2];
20521b07f35SOmar Sandoval struct blk_mq_tag_set tag_set;
206c87228f1SOmar Sandoval };
207c87228f1SOmar Sandoval
208c87228f1SOmar Sandoval /*
2091da177e4SLinus Torvalds * Error codes
2101da177e4SLinus Torvalds */
2111da177e4SLinus Torvalds #define FD_OK 0 /* operation succeeded */
2121da177e4SLinus Torvalds #define FD_ERROR -1 /* general error (seek, read, write, etc) */
2131da177e4SLinus Torvalds #define FD_NOUNIT 1 /* unit does not exist */
2141da177e4SLinus Torvalds #define FD_UNITBUSY 2 /* unit already active */
2151da177e4SLinus Torvalds #define FD_NOTACTIVE 3 /* unit is not active */
2161da177e4SLinus Torvalds #define FD_NOTREADY 4 /* unit is not ready (motor not on/no disk) */
2171da177e4SLinus Torvalds
2181da177e4SLinus Torvalds #define MFM_NOSYNC 1
2191da177e4SLinus Torvalds #define MFM_HEADER 2
2201da177e4SLinus Torvalds #define MFM_DATA 3
2211da177e4SLinus Torvalds #define MFM_TRACK 4
2221da177e4SLinus Torvalds
2231da177e4SLinus Torvalds /*
2241da177e4SLinus Torvalds * Floppy ID values
2251da177e4SLinus Torvalds */
2261da177e4SLinus Torvalds #define FD_NODRIVE 0x00000000 /* response when no unit is present */
2271da177e4SLinus Torvalds #define FD_DD_3 0xffffffff /* double-density 3.5" (880K) drive */
2281da177e4SLinus Torvalds #define FD_HD_3 0x55555555 /* high-density 3.5" (1760K) drive */
2291da177e4SLinus Torvalds #define FD_DD_5 0xaaaaaaaa /* double-density 5.25" (440K) drive */
2301da177e4SLinus Torvalds
2312a48fc0aSArnd Bergmann static DEFINE_MUTEX(amiflop_mutex);
2321da177e4SLinus Torvalds static unsigned long int fd_def_df0 = FD_DD_3; /* default for df0 if it doesn't identify */
2331da177e4SLinus Torvalds
2341da177e4SLinus Torvalds module_param(fd_def_df0, ulong, 0);
2351da177e4SLinus Torvalds MODULE_LICENSE("GPL");
2361da177e4SLinus Torvalds
2371da177e4SLinus Torvalds /*
2381da177e4SLinus Torvalds * Macros
2391da177e4SLinus Torvalds */
2401da177e4SLinus Torvalds #define MOTOR_ON (ciab.prb &= ~DSKMOTOR)
2411da177e4SLinus Torvalds #define MOTOR_OFF (ciab.prb |= DSKMOTOR)
2421da177e4SLinus Torvalds #define SELECT(mask) (ciab.prb &= ~mask)
2431da177e4SLinus Torvalds #define DESELECT(mask) (ciab.prb |= mask)
2441da177e4SLinus Torvalds #define SELMASK(drive) (1 << (3 + (drive & 3)))
2451da177e4SLinus Torvalds
2461da177e4SLinus Torvalds static struct fd_drive_type drive_types[] = {
2471da177e4SLinus Torvalds /* code name tr he rdsz wrsz sm pc1 pc2 sd st st*/
2481da177e4SLinus Torvalds /* warning: times are now in milliseconds (ms) */
2491da177e4SLinus Torvalds { FD_DD_3, "DD 3.5", 80, 2, 14716, 13630, 1, 80,161, 3, 18, 1},
2501da177e4SLinus Torvalds { FD_HD_3, "HD 3.5", 80, 2, 28344, 27258, 2, 80,161, 3, 18, 1},
2511da177e4SLinus Torvalds { FD_DD_5, "DD 5.25", 40, 2, 14716, 13630, 1, 40, 81, 6, 30, 2},
2521da177e4SLinus Torvalds { FD_NODRIVE, "No Drive", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
2531da177e4SLinus Torvalds };
254945f390fSTobias Klauser static int num_dr_types = ARRAY_SIZE(drive_types);
2551da177e4SLinus Torvalds
2561da177e4SLinus Torvalds static int amiga_read(int), dos_read(int);
2571da177e4SLinus Torvalds static void amiga_write(int), dos_write(int);
2581da177e4SLinus Torvalds static struct fd_data_type data_types[] = {
2591da177e4SLinus Torvalds { "Amiga", 11 , amiga_read, amiga_write},
2601da177e4SLinus Torvalds { "MS-Dos", 9, dos_read, dos_write}
2611da177e4SLinus Torvalds };
2621da177e4SLinus Torvalds
2631da177e4SLinus Torvalds /* current info on each unit */
2641da177e4SLinus Torvalds static struct amiga_floppy_struct unit[FD_MAX_UNITS];
2651da177e4SLinus Torvalds
2661da177e4SLinus Torvalds static struct timer_list flush_track_timer[FD_MAX_UNITS];
2671da177e4SLinus Torvalds static struct timer_list post_write_timer;
268cbb9d178SKees Cook static unsigned long post_write_timer_drive;
2691da177e4SLinus Torvalds static struct timer_list motor_on_timer;
2701da177e4SLinus Torvalds static struct timer_list motor_off_timer[FD_MAX_UNITS];
2711da177e4SLinus Torvalds static int on_attempts;
2721da177e4SLinus Torvalds
2731da177e4SLinus Torvalds /* Synchronization of FDC access */
2741da177e4SLinus Torvalds /* request loop (trackbuffer) */
2751da177e4SLinus Torvalds static volatile int fdc_busy = -1;
2761da177e4SLinus Torvalds static volatile int fdc_nested;
2771da177e4SLinus Torvalds static DECLARE_WAIT_QUEUE_HEAD(fdc_wait);
2781da177e4SLinus Torvalds
2796d0be946SAndreas Bombe static DECLARE_COMPLETION(motor_on_completion);
2801da177e4SLinus Torvalds
2811da177e4SLinus Torvalds static volatile int selected = -1; /* currently selected drive */
2821da177e4SLinus Torvalds
2831da177e4SLinus Torvalds static int writepending;
2841da177e4SLinus Torvalds static int writefromint;
2851da177e4SLinus Torvalds static char *raw_buf;
2861da177e4SLinus Torvalds
2871da177e4SLinus Torvalds static DEFINE_SPINLOCK(amiflop_lock);
2881da177e4SLinus Torvalds
2891da177e4SLinus Torvalds #define RAW_BUF_SIZE 30000 /* size of raw disk data */
2901da177e4SLinus Torvalds
2911da177e4SLinus Torvalds /*
2921da177e4SLinus Torvalds * These are global variables, as that's the easiest way to give
2931da177e4SLinus Torvalds * information to interrupts. They are the data used for the current
2941da177e4SLinus Torvalds * request.
2951da177e4SLinus Torvalds */
2961da177e4SLinus Torvalds static volatile char block_flag;
2971da177e4SLinus Torvalds static DECLARE_WAIT_QUEUE_HEAD(wait_fd_block);
2981da177e4SLinus Torvalds
2991da177e4SLinus Torvalds /* MS-Dos MFM Coding tables (should go quick and easy) */
3001da177e4SLinus Torvalds static unsigned char mfmencode[16]={
3011da177e4SLinus Torvalds 0x2a, 0x29, 0x24, 0x25, 0x12, 0x11, 0x14, 0x15,
3021da177e4SLinus Torvalds 0x4a, 0x49, 0x44, 0x45, 0x52, 0x51, 0x54, 0x55
3031da177e4SLinus Torvalds };
3041da177e4SLinus Torvalds static unsigned char mfmdecode[128];
3051da177e4SLinus Torvalds
3061da177e4SLinus Torvalds /* floppy internal millisecond timer stuff */
3076d0be946SAndreas Bombe static DECLARE_COMPLETION(ms_wait_completion);
3081da177e4SLinus Torvalds #define MS_TICKS ((amiga_eclock+50)/1000)
3091da177e4SLinus Torvalds
3101da177e4SLinus Torvalds /*
3111da177e4SLinus Torvalds * Note that MAX_ERRORS=X doesn't imply that we retry every bad read
3121da177e4SLinus Torvalds * max X times - some types of errors increase the errorcount by 2 or
3131da177e4SLinus Torvalds * even 3, so we might actually retry only X/2 times before giving up.
3141da177e4SLinus Torvalds */
3151da177e4SLinus Torvalds #define MAX_ERRORS 12
3161da177e4SLinus Torvalds
317b4290a23SAl Viro #define custom amiga_custom
318b4290a23SAl Viro
3191da177e4SLinus Torvalds /* Prevent "aliased" accesses. */
3201da177e4SLinus Torvalds static int fd_ref[4] = { 0,0,0,0 };
3211da177e4SLinus Torvalds static int fd_device[4] = { 0, 0, 0, 0 };
3221da177e4SLinus Torvalds
3231da177e4SLinus Torvalds /*
3241da177e4SLinus Torvalds * Here come the actual hardware access and helper functions.
3251da177e4SLinus Torvalds * They are not reentrant and single threaded because all drives
3261da177e4SLinus Torvalds * share the same hardware and the same trackbuffer.
3271da177e4SLinus Torvalds */
3281da177e4SLinus Torvalds
3291da177e4SLinus Torvalds /* Milliseconds timer */
3301da177e4SLinus Torvalds
ms_isr(int irq,void * dummy)3317d12e780SDavid Howells static irqreturn_t ms_isr(int irq, void *dummy)
3321da177e4SLinus Torvalds {
3336d0be946SAndreas Bombe complete(&ms_wait_completion);
3341da177e4SLinus Torvalds return IRQ_HANDLED;
3351da177e4SLinus Torvalds }
3361da177e4SLinus Torvalds
3371da177e4SLinus Torvalds /* all waits are queued up
3381da177e4SLinus Torvalds A more generic routine would do a schedule a la timer.device */
ms_delay(int ms)3391da177e4SLinus Torvalds static void ms_delay(int ms)
3401da177e4SLinus Torvalds {
3411da177e4SLinus Torvalds int ticks;
3426d0be946SAndreas Bombe static DEFINE_MUTEX(mutex);
3436d0be946SAndreas Bombe
3441da177e4SLinus Torvalds if (ms > 0) {
3456d0be946SAndreas Bombe mutex_lock(&mutex);
3461da177e4SLinus Torvalds ticks = MS_TICKS*ms-1;
3471da177e4SLinus Torvalds ciaa.tblo=ticks%256;
3481da177e4SLinus Torvalds ciaa.tbhi=ticks/256;
3491da177e4SLinus Torvalds ciaa.crb=0x19; /*count eclock, force load, one-shoot, start */
3506d0be946SAndreas Bombe wait_for_completion(&ms_wait_completion);
3516d0be946SAndreas Bombe mutex_unlock(&mutex);
3521da177e4SLinus Torvalds }
3531da177e4SLinus Torvalds }
3541da177e4SLinus Torvalds
3551da177e4SLinus Torvalds /* Hardware semaphore */
3561da177e4SLinus Torvalds
3571da177e4SLinus Torvalds /* returns true when we would get the semaphore */
try_fdc(int drive)3581da177e4SLinus Torvalds static inline int try_fdc(int drive)
3591da177e4SLinus Torvalds {
3601da177e4SLinus Torvalds drive &= 3;
3611da177e4SLinus Torvalds return ((fdc_busy < 0) || (fdc_busy == drive));
3621da177e4SLinus Torvalds }
3631da177e4SLinus Torvalds
get_fdc(int drive)3641da177e4SLinus Torvalds static void get_fdc(int drive)
3651da177e4SLinus Torvalds {
3661da177e4SLinus Torvalds unsigned long flags;
3671da177e4SLinus Torvalds
3681da177e4SLinus Torvalds drive &= 3;
3691da177e4SLinus Torvalds #ifdef DEBUG
3701da177e4SLinus Torvalds printk("get_fdc: drive %d fdc_busy %d fdc_nested %d\n",drive,fdc_busy,fdc_nested);
3711da177e4SLinus Torvalds #endif
3721da177e4SLinus Torvalds local_irq_save(flags);
3736d0be946SAndreas Bombe wait_event(fdc_wait, try_fdc(drive));
3741da177e4SLinus Torvalds fdc_busy = drive;
3751da177e4SLinus Torvalds fdc_nested++;
3761da177e4SLinus Torvalds local_irq_restore(flags);
3771da177e4SLinus Torvalds }
3781da177e4SLinus Torvalds
rel_fdc(void)3791da177e4SLinus Torvalds static inline void rel_fdc(void)
3801da177e4SLinus Torvalds {
3811da177e4SLinus Torvalds #ifdef DEBUG
3821da177e4SLinus Torvalds if (fdc_nested == 0)
3831da177e4SLinus Torvalds printk("fd: unmatched rel_fdc\n");
3841da177e4SLinus Torvalds printk("rel_fdc: fdc_busy %d fdc_nested %d\n",fdc_busy,fdc_nested);
3851da177e4SLinus Torvalds #endif
3861da177e4SLinus Torvalds fdc_nested--;
3871da177e4SLinus Torvalds if (fdc_nested == 0) {
3881da177e4SLinus Torvalds fdc_busy = -1;
3891da177e4SLinus Torvalds wake_up(&fdc_wait);
3901da177e4SLinus Torvalds }
3911da177e4SLinus Torvalds }
3921da177e4SLinus Torvalds
fd_select(int drive)3931da177e4SLinus Torvalds static void fd_select (int drive)
3941da177e4SLinus Torvalds {
3951da177e4SLinus Torvalds unsigned char prb = ~0;
3961da177e4SLinus Torvalds
3971da177e4SLinus Torvalds drive&=3;
3981da177e4SLinus Torvalds #ifdef DEBUG
3991da177e4SLinus Torvalds printk("selecting %d\n",drive);
4001da177e4SLinus Torvalds #endif
4011da177e4SLinus Torvalds if (drive == selected)
4021da177e4SLinus Torvalds return;
4031da177e4SLinus Torvalds get_fdc(drive);
4041da177e4SLinus Torvalds selected = drive;
4051da177e4SLinus Torvalds
4061da177e4SLinus Torvalds if (unit[drive].track % 2 != 0)
4071da177e4SLinus Torvalds prb &= ~DSKSIDE;
4081da177e4SLinus Torvalds if (unit[drive].motor == 1)
4091da177e4SLinus Torvalds prb &= ~DSKMOTOR;
4101da177e4SLinus Torvalds ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
4111da177e4SLinus Torvalds ciab.prb = prb;
4121da177e4SLinus Torvalds prb &= ~SELMASK(drive);
4131da177e4SLinus Torvalds ciab.prb = prb;
4141da177e4SLinus Torvalds rel_fdc();
4151da177e4SLinus Torvalds }
4161da177e4SLinus Torvalds
fd_deselect(int drive)4171da177e4SLinus Torvalds static void fd_deselect (int drive)
4181da177e4SLinus Torvalds {
4191da177e4SLinus Torvalds unsigned char prb;
4201da177e4SLinus Torvalds unsigned long flags;
4211da177e4SLinus Torvalds
4221da177e4SLinus Torvalds drive&=3;
4231da177e4SLinus Torvalds #ifdef DEBUG
4241da177e4SLinus Torvalds printk("deselecting %d\n",drive);
4251da177e4SLinus Torvalds #endif
4261da177e4SLinus Torvalds if (drive != selected) {
4271da177e4SLinus Torvalds printk(KERN_WARNING "Deselecting drive %d while %d was selected!\n",drive,selected);
4281da177e4SLinus Torvalds return;
4291da177e4SLinus Torvalds }
4301da177e4SLinus Torvalds
4311da177e4SLinus Torvalds get_fdc(drive);
4321da177e4SLinus Torvalds local_irq_save(flags);
4331da177e4SLinus Torvalds
4341da177e4SLinus Torvalds selected = -1;
4351da177e4SLinus Torvalds
4361da177e4SLinus Torvalds prb = ciab.prb;
4371da177e4SLinus Torvalds prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
4381da177e4SLinus Torvalds ciab.prb = prb;
4391da177e4SLinus Torvalds
4401da177e4SLinus Torvalds local_irq_restore (flags);
4411da177e4SLinus Torvalds rel_fdc();
4421da177e4SLinus Torvalds
4431da177e4SLinus Torvalds }
4441da177e4SLinus Torvalds
motor_on_callback(struct timer_list * unused)445cbb9d178SKees Cook static void motor_on_callback(struct timer_list *unused)
4461da177e4SLinus Torvalds {
4471da177e4SLinus Torvalds if (!(ciaa.pra & DSKRDY) || --on_attempts == 0) {
4486d0be946SAndreas Bombe complete_all(&motor_on_completion);
4491da177e4SLinus Torvalds } else {
4501da177e4SLinus Torvalds motor_on_timer.expires = jiffies + HZ/10;
4511da177e4SLinus Torvalds add_timer(&motor_on_timer);
4521da177e4SLinus Torvalds }
4531da177e4SLinus Torvalds }
4541da177e4SLinus Torvalds
fd_motor_on(int nr)4551da177e4SLinus Torvalds static int fd_motor_on(int nr)
4561da177e4SLinus Torvalds {
4571da177e4SLinus Torvalds nr &= 3;
4581da177e4SLinus Torvalds
4591da177e4SLinus Torvalds del_timer(motor_off_timer + nr);
4601da177e4SLinus Torvalds
4611da177e4SLinus Torvalds if (!unit[nr].motor) {
4621da177e4SLinus Torvalds unit[nr].motor = 1;
4631da177e4SLinus Torvalds fd_select(nr);
4641da177e4SLinus Torvalds
46516735d02SWolfram Sang reinit_completion(&motor_on_completion);
4661da177e4SLinus Torvalds mod_timer(&motor_on_timer, jiffies + HZ/2);
4671da177e4SLinus Torvalds
4681da177e4SLinus Torvalds on_attempts = 10;
4696d0be946SAndreas Bombe wait_for_completion(&motor_on_completion);
4701da177e4SLinus Torvalds fd_deselect(nr);
4711da177e4SLinus Torvalds }
4721da177e4SLinus Torvalds
4731da177e4SLinus Torvalds if (on_attempts == 0) {
4741da177e4SLinus Torvalds on_attempts = -1;
4751da177e4SLinus Torvalds #if 0
4761da177e4SLinus Torvalds printk (KERN_ERR "motor_on failed, turning motor off\n");
477cbb9d178SKees Cook fd_motor_off (motor_off_timer + nr);
4781da177e4SLinus Torvalds return 0;
4791da177e4SLinus Torvalds #else
4801da177e4SLinus Torvalds printk (KERN_WARNING "DSKRDY not set after 1.5 seconds - assuming drive is spinning notwithstanding\n");
4811da177e4SLinus Torvalds #endif
4821da177e4SLinus Torvalds }
4831da177e4SLinus Torvalds
4841da177e4SLinus Torvalds return 1;
4851da177e4SLinus Torvalds }
4861da177e4SLinus Torvalds
fd_motor_off(struct timer_list * timer)487cbb9d178SKees Cook static void fd_motor_off(struct timer_list *timer)
4881da177e4SLinus Torvalds {
489cbb9d178SKees Cook unsigned long drive = ((unsigned long)timer -
490cbb9d178SKees Cook (unsigned long)&motor_off_timer[0]) /
491cbb9d178SKees Cook sizeof(motor_off_timer[0]);
4921da177e4SLinus Torvalds
4931da177e4SLinus Torvalds drive&=3;
494cbb9d178SKees Cook if (!try_fdc(drive)) {
4951da177e4SLinus Torvalds /* We would be blocked in an interrupt, so try again later */
496cbb9d178SKees Cook timer->expires = jiffies + 1;
497cbb9d178SKees Cook add_timer(timer);
4981da177e4SLinus Torvalds return;
4991da177e4SLinus Torvalds }
5001da177e4SLinus Torvalds unit[drive].motor = 0;
5011da177e4SLinus Torvalds fd_select(drive);
5021da177e4SLinus Torvalds udelay (1);
5031da177e4SLinus Torvalds fd_deselect(drive);
5041da177e4SLinus Torvalds }
5051da177e4SLinus Torvalds
floppy_off(unsigned int nr)5061da177e4SLinus Torvalds static void floppy_off (unsigned int nr)
5071da177e4SLinus Torvalds {
5081da177e4SLinus Torvalds int drive;
5091da177e4SLinus Torvalds
5101da177e4SLinus Torvalds drive = nr & 3;
5111da177e4SLinus Torvalds mod_timer(motor_off_timer + drive, jiffies + 3*HZ);
5121da177e4SLinus Torvalds }
5131da177e4SLinus Torvalds
fd_calibrate(int drive)5141da177e4SLinus Torvalds static int fd_calibrate(int drive)
5151da177e4SLinus Torvalds {
5161da177e4SLinus Torvalds unsigned char prb;
5171da177e4SLinus Torvalds int n;
5181da177e4SLinus Torvalds
5191da177e4SLinus Torvalds drive &= 3;
5201da177e4SLinus Torvalds get_fdc(drive);
5211da177e4SLinus Torvalds if (!fd_motor_on (drive))
5221da177e4SLinus Torvalds return 0;
5231da177e4SLinus Torvalds fd_select (drive);
5241da177e4SLinus Torvalds prb = ciab.prb;
5251da177e4SLinus Torvalds prb |= DSKSIDE;
5261da177e4SLinus Torvalds prb &= ~DSKDIREC;
5271da177e4SLinus Torvalds ciab.prb = prb;
5281da177e4SLinus Torvalds for (n = unit[drive].type->tracks/2; n != 0; --n) {
5291da177e4SLinus Torvalds if (ciaa.pra & DSKTRACK0)
5301da177e4SLinus Torvalds break;
5311da177e4SLinus Torvalds prb &= ~DSKSTEP;
5321da177e4SLinus Torvalds ciab.prb = prb;
5331da177e4SLinus Torvalds prb |= DSKSTEP;
5341da177e4SLinus Torvalds udelay (2);
5351da177e4SLinus Torvalds ciab.prb = prb;
5361da177e4SLinus Torvalds ms_delay(unit[drive].type->step_delay);
5371da177e4SLinus Torvalds }
5381da177e4SLinus Torvalds ms_delay (unit[drive].type->settle_time);
5391da177e4SLinus Torvalds prb |= DSKDIREC;
5401da177e4SLinus Torvalds n = unit[drive].type->tracks + 20;
5411da177e4SLinus Torvalds for (;;) {
5421da177e4SLinus Torvalds prb &= ~DSKSTEP;
5431da177e4SLinus Torvalds ciab.prb = prb;
5441da177e4SLinus Torvalds prb |= DSKSTEP;
5451da177e4SLinus Torvalds udelay (2);
5461da177e4SLinus Torvalds ciab.prb = prb;
5471da177e4SLinus Torvalds ms_delay(unit[drive].type->step_delay + 1);
5481da177e4SLinus Torvalds if ((ciaa.pra & DSKTRACK0) == 0)
5491da177e4SLinus Torvalds break;
5501da177e4SLinus Torvalds if (--n == 0) {
5511da177e4SLinus Torvalds printk (KERN_ERR "fd%d: calibrate failed, turning motor off\n", drive);
552cbb9d178SKees Cook fd_motor_off (motor_off_timer + drive);
5531da177e4SLinus Torvalds unit[drive].track = -1;
5541da177e4SLinus Torvalds rel_fdc();
5551da177e4SLinus Torvalds return 0;
5561da177e4SLinus Torvalds }
5571da177e4SLinus Torvalds }
5581da177e4SLinus Torvalds unit[drive].track = 0;
5591da177e4SLinus Torvalds ms_delay(unit[drive].type->settle_time);
5601da177e4SLinus Torvalds
5611da177e4SLinus Torvalds rel_fdc();
5621da177e4SLinus Torvalds fd_deselect(drive);
5631da177e4SLinus Torvalds return 1;
5641da177e4SLinus Torvalds }
5651da177e4SLinus Torvalds
fd_seek(int drive,int track)5661da177e4SLinus Torvalds static int fd_seek(int drive, int track)
5671da177e4SLinus Torvalds {
5681da177e4SLinus Torvalds unsigned char prb;
5691da177e4SLinus Torvalds int cnt;
5701da177e4SLinus Torvalds
5711da177e4SLinus Torvalds #ifdef DEBUG
5721da177e4SLinus Torvalds printk("seeking drive %d to track %d\n",drive,track);
5731da177e4SLinus Torvalds #endif
5741da177e4SLinus Torvalds drive &= 3;
5751da177e4SLinus Torvalds get_fdc(drive);
5761da177e4SLinus Torvalds if (unit[drive].track == track) {
5771da177e4SLinus Torvalds rel_fdc();
5781da177e4SLinus Torvalds return 1;
5791da177e4SLinus Torvalds }
5801da177e4SLinus Torvalds if (!fd_motor_on(drive)) {
5811da177e4SLinus Torvalds rel_fdc();
5821da177e4SLinus Torvalds return 0;
5831da177e4SLinus Torvalds }
5841da177e4SLinus Torvalds if (unit[drive].track < 0 && !fd_calibrate(drive)) {
5851da177e4SLinus Torvalds rel_fdc();
5861da177e4SLinus Torvalds return 0;
5871da177e4SLinus Torvalds }
5881da177e4SLinus Torvalds
5891da177e4SLinus Torvalds fd_select (drive);
5901da177e4SLinus Torvalds cnt = unit[drive].track/2 - track/2;
5911da177e4SLinus Torvalds prb = ciab.prb;
5921da177e4SLinus Torvalds prb |= DSKSIDE | DSKDIREC;
5931da177e4SLinus Torvalds if (track % 2 != 0)
5941da177e4SLinus Torvalds prb &= ~DSKSIDE;
5951da177e4SLinus Torvalds if (cnt < 0) {
5961da177e4SLinus Torvalds cnt = - cnt;
5971da177e4SLinus Torvalds prb &= ~DSKDIREC;
5981da177e4SLinus Torvalds }
5991da177e4SLinus Torvalds ciab.prb = prb;
6001da177e4SLinus Torvalds if (track % 2 != unit[drive].track % 2)
6011da177e4SLinus Torvalds ms_delay (unit[drive].type->side_time);
6021da177e4SLinus Torvalds unit[drive].track = track;
6031da177e4SLinus Torvalds if (cnt == 0) {
6041da177e4SLinus Torvalds rel_fdc();
6051da177e4SLinus Torvalds fd_deselect(drive);
6061da177e4SLinus Torvalds return 1;
6071da177e4SLinus Torvalds }
6081da177e4SLinus Torvalds do {
6091da177e4SLinus Torvalds prb &= ~DSKSTEP;
6101da177e4SLinus Torvalds ciab.prb = prb;
6111da177e4SLinus Torvalds prb |= DSKSTEP;
6121da177e4SLinus Torvalds udelay (1);
6131da177e4SLinus Torvalds ciab.prb = prb;
6141da177e4SLinus Torvalds ms_delay (unit[drive].type->step_delay);
6151da177e4SLinus Torvalds } while (--cnt != 0);
6161da177e4SLinus Torvalds ms_delay (unit[drive].type->settle_time);
6171da177e4SLinus Torvalds
6181da177e4SLinus Torvalds rel_fdc();
6191da177e4SLinus Torvalds fd_deselect(drive);
6201da177e4SLinus Torvalds return 1;
6211da177e4SLinus Torvalds }
6221da177e4SLinus Torvalds
fd_get_drive_id(int drive)6231da177e4SLinus Torvalds static unsigned long fd_get_drive_id(int drive)
6241da177e4SLinus Torvalds {
6251da177e4SLinus Torvalds int i;
6261da177e4SLinus Torvalds ulong id = 0;
6271da177e4SLinus Torvalds
6281da177e4SLinus Torvalds drive&=3;
6291da177e4SLinus Torvalds get_fdc(drive);
6301da177e4SLinus Torvalds /* set up for ID */
6311da177e4SLinus Torvalds MOTOR_ON;
6321da177e4SLinus Torvalds udelay(2);
6331da177e4SLinus Torvalds SELECT(SELMASK(drive));
6341da177e4SLinus Torvalds udelay(2);
6351da177e4SLinus Torvalds DESELECT(SELMASK(drive));
6361da177e4SLinus Torvalds udelay(2);
6371da177e4SLinus Torvalds MOTOR_OFF;
6381da177e4SLinus Torvalds udelay(2);
6391da177e4SLinus Torvalds SELECT(SELMASK(drive));
6401da177e4SLinus Torvalds udelay(2);
6411da177e4SLinus Torvalds DESELECT(SELMASK(drive));
6421da177e4SLinus Torvalds udelay(2);
6431da177e4SLinus Torvalds
6441da177e4SLinus Torvalds /* loop and read disk ID */
6451da177e4SLinus Torvalds for (i=0; i<32; i++) {
6461da177e4SLinus Torvalds SELECT(SELMASK(drive));
6471da177e4SLinus Torvalds udelay(2);
6481da177e4SLinus Torvalds
6491da177e4SLinus Torvalds /* read and store value of DSKRDY */
6501da177e4SLinus Torvalds id <<= 1;
6511da177e4SLinus Torvalds id |= (ciaa.pra & DSKRDY) ? 0 : 1; /* cia regs are low-active! */
6521da177e4SLinus Torvalds
6531da177e4SLinus Torvalds DESELECT(SELMASK(drive));
6541da177e4SLinus Torvalds }
6551da177e4SLinus Torvalds
6561da177e4SLinus Torvalds rel_fdc();
6571da177e4SLinus Torvalds
6581da177e4SLinus Torvalds /*
6591da177e4SLinus Torvalds * RB: At least A500/A2000's df0: don't identify themselves.
6601da177e4SLinus Torvalds * As every (real) Amiga has at least a 3.5" DD drive as df0:
6611da177e4SLinus Torvalds * we default to that if df0: doesn't identify as a certain
6621da177e4SLinus Torvalds * type.
6631da177e4SLinus Torvalds */
6641da177e4SLinus Torvalds if(drive == 0 && id == FD_NODRIVE)
6651da177e4SLinus Torvalds {
6661da177e4SLinus Torvalds id = fd_def_df0;
6671da177e4SLinus Torvalds printk(KERN_NOTICE "fd: drive 0 didn't identify, setting default %08lx\n", (ulong)fd_def_df0);
6681da177e4SLinus Torvalds }
6691da177e4SLinus Torvalds /* return the ID value */
6701da177e4SLinus Torvalds return (id);
6711da177e4SLinus Torvalds }
6721da177e4SLinus Torvalds
fd_block_done(int irq,void * dummy)6737d12e780SDavid Howells static irqreturn_t fd_block_done(int irq, void *dummy)
6741da177e4SLinus Torvalds {
6751da177e4SLinus Torvalds if (block_flag)
6761da177e4SLinus Torvalds custom.dsklen = 0x4000;
6771da177e4SLinus Torvalds
6781da177e4SLinus Torvalds if (block_flag == 2) { /* writing */
6791da177e4SLinus Torvalds writepending = 2;
6801da177e4SLinus Torvalds post_write_timer.expires = jiffies + 1; /* at least 2 ms */
681cbb9d178SKees Cook post_write_timer_drive = selected;
6821da177e4SLinus Torvalds add_timer(&post_write_timer);
6831da177e4SLinus Torvalds }
6841da177e4SLinus Torvalds else { /* reading */
6851da177e4SLinus Torvalds block_flag = 0;
6861da177e4SLinus Torvalds wake_up (&wait_fd_block);
6871da177e4SLinus Torvalds }
6881da177e4SLinus Torvalds return IRQ_HANDLED;
6891da177e4SLinus Torvalds }
6901da177e4SLinus Torvalds
raw_read(int drive)6911da177e4SLinus Torvalds static void raw_read(int drive)
6921da177e4SLinus Torvalds {
6931da177e4SLinus Torvalds drive&=3;
6941da177e4SLinus Torvalds get_fdc(drive);
6956d0be946SAndreas Bombe wait_event(wait_fd_block, !block_flag);
6961da177e4SLinus Torvalds fd_select(drive);
6971da177e4SLinus Torvalds /* setup adkcon bits correctly */
6981da177e4SLinus Torvalds custom.adkcon = ADK_MSBSYNC;
6991da177e4SLinus Torvalds custom.adkcon = ADK_SETCLR|ADK_WORDSYNC|ADK_FAST;
7001da177e4SLinus Torvalds
7011da177e4SLinus Torvalds custom.dsksync = MFM_SYNC;
7021da177e4SLinus Torvalds
7031da177e4SLinus Torvalds custom.dsklen = 0;
7041da177e4SLinus Torvalds custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)raw_buf);
7051da177e4SLinus Torvalds custom.dsklen = unit[drive].type->read_size/sizeof(short) | DSKLEN_DMAEN;
7061da177e4SLinus Torvalds custom.dsklen = unit[drive].type->read_size/sizeof(short) | DSKLEN_DMAEN;
7071da177e4SLinus Torvalds
7081da177e4SLinus Torvalds block_flag = 1;
7091da177e4SLinus Torvalds
7106d0be946SAndreas Bombe wait_event(wait_fd_block, !block_flag);
7111da177e4SLinus Torvalds
7121da177e4SLinus Torvalds custom.dsklen = 0;
7131da177e4SLinus Torvalds fd_deselect(drive);
7141da177e4SLinus Torvalds rel_fdc();
7151da177e4SLinus Torvalds }
7161da177e4SLinus Torvalds
raw_write(int drive)7171da177e4SLinus Torvalds static int raw_write(int drive)
7181da177e4SLinus Torvalds {
7191da177e4SLinus Torvalds ushort adk;
7201da177e4SLinus Torvalds
7211da177e4SLinus Torvalds drive&=3;
7221da177e4SLinus Torvalds get_fdc(drive); /* corresponds to rel_fdc() in post_write() */
7231da177e4SLinus Torvalds if ((ciaa.pra & DSKPROT) == 0) {
7241da177e4SLinus Torvalds rel_fdc();
7251da177e4SLinus Torvalds return 0;
7261da177e4SLinus Torvalds }
7276d0be946SAndreas Bombe wait_event(wait_fd_block, !block_flag);
7281da177e4SLinus Torvalds fd_select(drive);
7291da177e4SLinus Torvalds /* clear adkcon bits */
7301da177e4SLinus Torvalds custom.adkcon = ADK_PRECOMP1|ADK_PRECOMP0|ADK_WORDSYNC|ADK_MSBSYNC;
7311da177e4SLinus Torvalds /* set appropriate adkcon bits */
7321da177e4SLinus Torvalds adk = ADK_SETCLR|ADK_FAST;
7331da177e4SLinus Torvalds if ((ulong)unit[drive].track >= unit[drive].type->precomp2)
7341da177e4SLinus Torvalds adk |= ADK_PRECOMP1;
7351da177e4SLinus Torvalds else if ((ulong)unit[drive].track >= unit[drive].type->precomp1)
7361da177e4SLinus Torvalds adk |= ADK_PRECOMP0;
7371da177e4SLinus Torvalds custom.adkcon = adk;
7381da177e4SLinus Torvalds
7391da177e4SLinus Torvalds custom.dsklen = DSKLEN_WRITE;
7401da177e4SLinus Torvalds custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)raw_buf);
7411da177e4SLinus Torvalds custom.dsklen = unit[drive].type->write_size/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE;
7421da177e4SLinus Torvalds custom.dsklen = unit[drive].type->write_size/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE;
7431da177e4SLinus Torvalds
7441da177e4SLinus Torvalds block_flag = 2;
7451da177e4SLinus Torvalds return 1;
7461da177e4SLinus Torvalds }
7471da177e4SLinus Torvalds
7481da177e4SLinus Torvalds /*
7491da177e4SLinus Torvalds * to be called at least 2ms after the write has finished but before any
7501da177e4SLinus Torvalds * other access to the hardware.
7511da177e4SLinus Torvalds */
post_write(unsigned long drive)7521da177e4SLinus Torvalds static void post_write (unsigned long drive)
7531da177e4SLinus Torvalds {
7541da177e4SLinus Torvalds #ifdef DEBUG
7551da177e4SLinus Torvalds printk("post_write for drive %ld\n",drive);
7561da177e4SLinus Torvalds #endif
7571da177e4SLinus Torvalds drive &= 3;
7581da177e4SLinus Torvalds custom.dsklen = 0;
7591da177e4SLinus Torvalds block_flag = 0;
7601da177e4SLinus Torvalds writepending = 0;
7611da177e4SLinus Torvalds writefromint = 0;
7621da177e4SLinus Torvalds unit[drive].dirty = 0;
7631da177e4SLinus Torvalds wake_up(&wait_fd_block);
7641da177e4SLinus Torvalds fd_deselect(drive);
7651da177e4SLinus Torvalds rel_fdc(); /* corresponds to get_fdc() in raw_write */
7661da177e4SLinus Torvalds }
7671da177e4SLinus Torvalds
post_write_callback(struct timer_list * timer)768cbb9d178SKees Cook static void post_write_callback(struct timer_list *timer)
769cbb9d178SKees Cook {
770cbb9d178SKees Cook post_write(post_write_timer_drive);
771cbb9d178SKees Cook }
7721da177e4SLinus Torvalds
7731da177e4SLinus Torvalds /*
7741da177e4SLinus Torvalds * The following functions are to convert the block contents into raw data
7751da177e4SLinus Torvalds * written to disk and vice versa.
7761da177e4SLinus Torvalds * (Add other formats here ;-))
7771da177e4SLinus Torvalds */
7781da177e4SLinus Torvalds
scan_sync(unsigned long raw,unsigned long end)7791da177e4SLinus Torvalds static unsigned long scan_sync(unsigned long raw, unsigned long end)
7801da177e4SLinus Torvalds {
7811da177e4SLinus Torvalds ushort *ptr = (ushort *)raw, *endp = (ushort *)end;
7821da177e4SLinus Torvalds
7831da177e4SLinus Torvalds while (ptr < endp && *ptr++ != 0x4489)
7841da177e4SLinus Torvalds ;
7851da177e4SLinus Torvalds if (ptr < endp) {
7861da177e4SLinus Torvalds while (*ptr == 0x4489 && ptr < endp)
7871da177e4SLinus Torvalds ptr++;
7881da177e4SLinus Torvalds return (ulong)ptr;
7891da177e4SLinus Torvalds }
7901da177e4SLinus Torvalds return 0;
7911da177e4SLinus Torvalds }
7921da177e4SLinus Torvalds
checksum(unsigned long * addr,int len)7931da177e4SLinus Torvalds static inline unsigned long checksum(unsigned long *addr, int len)
7941da177e4SLinus Torvalds {
7951da177e4SLinus Torvalds unsigned long csum = 0;
7961da177e4SLinus Torvalds
7971da177e4SLinus Torvalds len /= sizeof(*addr);
7981da177e4SLinus Torvalds while (len-- > 0)
7991da177e4SLinus Torvalds csum ^= *addr++;
8001da177e4SLinus Torvalds csum = ((csum>>1) & 0x55555555) ^ (csum & 0x55555555);
8011da177e4SLinus Torvalds
8021da177e4SLinus Torvalds return csum;
8031da177e4SLinus Torvalds }
8041da177e4SLinus Torvalds
decode(unsigned long * data,unsigned long * raw,int len)8051da177e4SLinus Torvalds static unsigned long decode (unsigned long *data, unsigned long *raw,
8061da177e4SLinus Torvalds int len)
8071da177e4SLinus Torvalds {
8081da177e4SLinus Torvalds ulong *odd, *even;
8091da177e4SLinus Torvalds
8101da177e4SLinus Torvalds /* convert length from bytes to longwords */
8111da177e4SLinus Torvalds len >>= 2;
8121da177e4SLinus Torvalds odd = raw;
8131da177e4SLinus Torvalds even = odd + len;
8141da177e4SLinus Torvalds
8151da177e4SLinus Torvalds /* prepare return pointer */
8161da177e4SLinus Torvalds raw += len * 2;
8171da177e4SLinus Torvalds
8181da177e4SLinus Torvalds do {
8191da177e4SLinus Torvalds *data++ = ((*odd++ & 0x55555555) << 1) | (*even++ & 0x55555555);
8201da177e4SLinus Torvalds } while (--len != 0);
8211da177e4SLinus Torvalds
8221da177e4SLinus Torvalds return (ulong)raw;
8231da177e4SLinus Torvalds }
8241da177e4SLinus Torvalds
8251da177e4SLinus Torvalds struct header {
8261da177e4SLinus Torvalds unsigned char magic;
8271da177e4SLinus Torvalds unsigned char track;
8281da177e4SLinus Torvalds unsigned char sect;
8291da177e4SLinus Torvalds unsigned char ord;
8301da177e4SLinus Torvalds unsigned char labels[16];
8311da177e4SLinus Torvalds unsigned long hdrchk;
8321da177e4SLinus Torvalds unsigned long datachk;
8331da177e4SLinus Torvalds };
8341da177e4SLinus Torvalds
amiga_read(int drive)8351da177e4SLinus Torvalds static int amiga_read(int drive)
8361da177e4SLinus Torvalds {
8371da177e4SLinus Torvalds unsigned long raw;
8381da177e4SLinus Torvalds unsigned long end;
8391da177e4SLinus Torvalds int scnt;
8401da177e4SLinus Torvalds unsigned long csum;
8411da177e4SLinus Torvalds struct header hdr;
8421da177e4SLinus Torvalds
8431da177e4SLinus Torvalds drive&=3;
8441da177e4SLinus Torvalds raw = (long) raw_buf;
8451da177e4SLinus Torvalds end = raw + unit[drive].type->read_size;
8461da177e4SLinus Torvalds
8471da177e4SLinus Torvalds for (scnt = 0;scnt < unit[drive].dtype->sects * unit[drive].type->sect_mult; scnt++) {
8481da177e4SLinus Torvalds if (!(raw = scan_sync(raw, end))) {
8491da177e4SLinus Torvalds printk (KERN_INFO "can't find sync for sector %d\n", scnt);
8501da177e4SLinus Torvalds return MFM_NOSYNC;
8511da177e4SLinus Torvalds }
8521da177e4SLinus Torvalds
8531da177e4SLinus Torvalds raw = decode ((ulong *)&hdr.magic, (ulong *)raw, 4);
8541da177e4SLinus Torvalds raw = decode ((ulong *)&hdr.labels, (ulong *)raw, 16);
8551da177e4SLinus Torvalds raw = decode ((ulong *)&hdr.hdrchk, (ulong *)raw, 4);
8561da177e4SLinus Torvalds raw = decode ((ulong *)&hdr.datachk, (ulong *)raw, 4);
8571da177e4SLinus Torvalds csum = checksum((ulong *)&hdr,
8581da177e4SLinus Torvalds (char *)&hdr.hdrchk-(char *)&hdr);
8591da177e4SLinus Torvalds
8601da177e4SLinus Torvalds #ifdef DEBUG
8611da177e4SLinus Torvalds printk ("(%x,%d,%d,%d) (%lx,%lx,%lx,%lx) %lx %lx\n",
8621da177e4SLinus Torvalds hdr.magic, hdr.track, hdr.sect, hdr.ord,
8631da177e4SLinus Torvalds *(ulong *)&hdr.labels[0], *(ulong *)&hdr.labels[4],
8641da177e4SLinus Torvalds *(ulong *)&hdr.labels[8], *(ulong *)&hdr.labels[12],
8651da177e4SLinus Torvalds hdr.hdrchk, hdr.datachk);
8661da177e4SLinus Torvalds #endif
8671da177e4SLinus Torvalds
8681da177e4SLinus Torvalds if (hdr.hdrchk != csum) {
8691da177e4SLinus Torvalds printk(KERN_INFO "MFM_HEADER: %08lx,%08lx\n", hdr.hdrchk, csum);
8701da177e4SLinus Torvalds return MFM_HEADER;
8711da177e4SLinus Torvalds }
8721da177e4SLinus Torvalds
8731da177e4SLinus Torvalds /* verify track */
8741da177e4SLinus Torvalds if (hdr.track != unit[drive].track) {
8751da177e4SLinus Torvalds printk(KERN_INFO "MFM_TRACK: %d, %d\n", hdr.track, unit[drive].track);
8761da177e4SLinus Torvalds return MFM_TRACK;
8771da177e4SLinus Torvalds }
8781da177e4SLinus Torvalds
8791da177e4SLinus Torvalds raw = decode ((ulong *)(unit[drive].trackbuf + hdr.sect*512),
8801da177e4SLinus Torvalds (ulong *)raw, 512);
8811da177e4SLinus Torvalds csum = checksum((ulong *)(unit[drive].trackbuf + hdr.sect*512), 512);
8821da177e4SLinus Torvalds
8831da177e4SLinus Torvalds if (hdr.datachk != csum) {
8841da177e4SLinus Torvalds printk(KERN_INFO "MFM_DATA: (%x:%d:%d:%d) sc=%d %lx, %lx\n",
8851da177e4SLinus Torvalds hdr.magic, hdr.track, hdr.sect, hdr.ord, scnt,
8861da177e4SLinus Torvalds hdr.datachk, csum);
8871da177e4SLinus Torvalds printk (KERN_INFO "data=(%lx,%lx,%lx,%lx)\n",
8881da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[0],
8891da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[1],
8901da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[2],
8911da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[3]);
8921da177e4SLinus Torvalds return MFM_DATA;
8931da177e4SLinus Torvalds }
8941da177e4SLinus Torvalds }
8951da177e4SLinus Torvalds
8961da177e4SLinus Torvalds return 0;
8971da177e4SLinus Torvalds }
8981da177e4SLinus Torvalds
encode(unsigned long data,unsigned long * dest)8991da177e4SLinus Torvalds static void encode(unsigned long data, unsigned long *dest)
9001da177e4SLinus Torvalds {
9011da177e4SLinus Torvalds unsigned long data2;
9021da177e4SLinus Torvalds
9031da177e4SLinus Torvalds data &= 0x55555555;
9041da177e4SLinus Torvalds data2 = data ^ 0x55555555;
9051da177e4SLinus Torvalds data |= ((data2 >> 1) | 0x80000000) & (data2 << 1);
9061da177e4SLinus Torvalds
9071da177e4SLinus Torvalds if (*(dest - 1) & 0x00000001)
9081da177e4SLinus Torvalds data &= 0x7FFFFFFF;
9091da177e4SLinus Torvalds
9101da177e4SLinus Torvalds *dest = data;
9111da177e4SLinus Torvalds }
9121da177e4SLinus Torvalds
encode_block(unsigned long * dest,unsigned long * src,int len)9131da177e4SLinus Torvalds static void encode_block(unsigned long *dest, unsigned long *src, int len)
9141da177e4SLinus Torvalds {
9151da177e4SLinus Torvalds int cnt, to_cnt = 0;
9161da177e4SLinus Torvalds unsigned long data;
9171da177e4SLinus Torvalds
9181da177e4SLinus Torvalds /* odd bits */
9191da177e4SLinus Torvalds for (cnt = 0; cnt < len / 4; cnt++) {
9201da177e4SLinus Torvalds data = src[cnt] >> 1;
9211da177e4SLinus Torvalds encode(data, dest + to_cnt++);
9221da177e4SLinus Torvalds }
9231da177e4SLinus Torvalds
9241da177e4SLinus Torvalds /* even bits */
9251da177e4SLinus Torvalds for (cnt = 0; cnt < len / 4; cnt++) {
9261da177e4SLinus Torvalds data = src[cnt];
9271da177e4SLinus Torvalds encode(data, dest + to_cnt++);
9281da177e4SLinus Torvalds }
9291da177e4SLinus Torvalds }
9301da177e4SLinus Torvalds
putsec(int disk,unsigned long * raw,int cnt)9311da177e4SLinus Torvalds static unsigned long *putsec(int disk, unsigned long *raw, int cnt)
9321da177e4SLinus Torvalds {
9331da177e4SLinus Torvalds struct header hdr;
9341da177e4SLinus Torvalds int i;
9351da177e4SLinus Torvalds
9361da177e4SLinus Torvalds disk&=3;
9371da177e4SLinus Torvalds *raw = (raw[-1]&1) ? 0x2AAAAAAA : 0xAAAAAAAA;
9381da177e4SLinus Torvalds raw++;
9391da177e4SLinus Torvalds *raw++ = 0x44894489;
9401da177e4SLinus Torvalds
9411da177e4SLinus Torvalds hdr.magic = 0xFF;
9421da177e4SLinus Torvalds hdr.track = unit[disk].track;
9431da177e4SLinus Torvalds hdr.sect = cnt;
9441da177e4SLinus Torvalds hdr.ord = unit[disk].dtype->sects * unit[disk].type->sect_mult - cnt;
9451da177e4SLinus Torvalds for (i = 0; i < 16; i++)
9461da177e4SLinus Torvalds hdr.labels[i] = 0;
9471da177e4SLinus Torvalds hdr.hdrchk = checksum((ulong *)&hdr,
9481da177e4SLinus Torvalds (char *)&hdr.hdrchk-(char *)&hdr);
9491da177e4SLinus Torvalds hdr.datachk = checksum((ulong *)(unit[disk].trackbuf+cnt*512), 512);
9501da177e4SLinus Torvalds
9511da177e4SLinus Torvalds encode_block(raw, (ulong *)&hdr.magic, 4);
9521da177e4SLinus Torvalds raw += 2;
9531da177e4SLinus Torvalds encode_block(raw, (ulong *)&hdr.labels, 16);
9541da177e4SLinus Torvalds raw += 8;
9551da177e4SLinus Torvalds encode_block(raw, (ulong *)&hdr.hdrchk, 4);
9561da177e4SLinus Torvalds raw += 2;
9571da177e4SLinus Torvalds encode_block(raw, (ulong *)&hdr.datachk, 4);
9581da177e4SLinus Torvalds raw += 2;
9591da177e4SLinus Torvalds encode_block(raw, (ulong *)(unit[disk].trackbuf+cnt*512), 512);
9601da177e4SLinus Torvalds raw += 256;
9611da177e4SLinus Torvalds
9621da177e4SLinus Torvalds return raw;
9631da177e4SLinus Torvalds }
9641da177e4SLinus Torvalds
amiga_write(int disk)9651da177e4SLinus Torvalds static void amiga_write(int disk)
9661da177e4SLinus Torvalds {
9671da177e4SLinus Torvalds unsigned int cnt;
9681da177e4SLinus Torvalds unsigned long *ptr = (unsigned long *)raw_buf;
9691da177e4SLinus Torvalds
9701da177e4SLinus Torvalds disk&=3;
9711da177e4SLinus Torvalds /* gap space */
9721da177e4SLinus Torvalds for (cnt = 0; cnt < 415 * unit[disk].type->sect_mult; cnt++)
9731da177e4SLinus Torvalds *ptr++ = 0xaaaaaaaa;
9741da177e4SLinus Torvalds
9751da177e4SLinus Torvalds /* sectors */
9761da177e4SLinus Torvalds for (cnt = 0; cnt < unit[disk].dtype->sects * unit[disk].type->sect_mult; cnt++)
9771da177e4SLinus Torvalds ptr = putsec (disk, ptr, cnt);
9781da177e4SLinus Torvalds *(ushort *)ptr = (ptr[-1]&1) ? 0x2AA8 : 0xAAA8;
9791da177e4SLinus Torvalds }
9801da177e4SLinus Torvalds
9811da177e4SLinus Torvalds
9821da177e4SLinus Torvalds struct dos_header {
9831da177e4SLinus Torvalds unsigned char track, /* 0-80 */
9841da177e4SLinus Torvalds side, /* 0-1 */
9851da177e4SLinus Torvalds sec, /* 0-...*/
9861da177e4SLinus Torvalds len_desc;/* 2 */
9871da177e4SLinus Torvalds unsigned short crc; /* on 68000 we got an alignment problem,
9881da177e4SLinus Torvalds but this compiler solves it by adding silently
9891da177e4SLinus Torvalds adding a pad byte so data won't fit
9901da177e4SLinus Torvalds and this took about 3h to discover.... */
9911da177e4SLinus Torvalds unsigned char gap1[22]; /* for longword-alignedness (0x4e) */
9921da177e4SLinus Torvalds };
9931da177e4SLinus Torvalds
9941da177e4SLinus Torvalds /* crc routines are borrowed from the messydos-handler */
9951da177e4SLinus Torvalds
9961da177e4SLinus Torvalds /* excerpt from the messydos-device
9971da177e4SLinus Torvalds ; The CRC is computed not only over the actual data, but including
9981da177e4SLinus Torvalds ; the SYNC mark (3 * $a1) and the 'ID/DATA - Address Mark' ($fe/$fb).
9991da177e4SLinus Torvalds ; As we don't read or encode these fields into our buffers, we have to
10001da177e4SLinus Torvalds ; preload the registers containing the CRC with the values they would have
10011da177e4SLinus Torvalds ; after stepping over these fields.
10021da177e4SLinus Torvalds ;
10031da177e4SLinus Torvalds ; How CRCs "really" work:
10041da177e4SLinus Torvalds ;
10051da177e4SLinus Torvalds ; First, you should regard a bitstring as a series of coefficients of
10061da177e4SLinus Torvalds ; polynomials. We calculate with these polynomials in modulo-2
10071da177e4SLinus Torvalds ; arithmetic, in which both add and subtract are done the same as
10081da177e4SLinus Torvalds ; exclusive-or. Now, we modify our data (a very long polynomial) in
10091da177e4SLinus Torvalds ; such a way that it becomes divisible by the CCITT-standard 16-bit
10101da177e4SLinus Torvalds ; 16 12 5
10111da177e4SLinus Torvalds ; polynomial: x + x + x + 1, represented by $11021. The easiest
10121da177e4SLinus Torvalds ; way to do this would be to multiply (using proper arithmetic) our
10131da177e4SLinus Torvalds ; datablock with $11021. So we have:
10141da177e4SLinus Torvalds ; data * $11021 =
10151da177e4SLinus Torvalds ; data * ($10000 + $1021) =
10161da177e4SLinus Torvalds ; data * $10000 + data * $1021
10171da177e4SLinus Torvalds ; The left part of this is simple: Just add two 0 bytes. But then
10181da177e4SLinus Torvalds ; the right part (data $1021) remains difficult and even could have
10191da177e4SLinus Torvalds ; a carry into the left part. The solution is to use a modified
10201da177e4SLinus Torvalds ; multiplication, which has a result that is not correct, but with
10211da177e4SLinus Torvalds ; a difference of any multiple of $11021. We then only need to keep
10221da177e4SLinus Torvalds ; the 16 least significant bits of the result.
10231da177e4SLinus Torvalds ;
10241da177e4SLinus Torvalds ; The following algorithm does this for us:
10251da177e4SLinus Torvalds ;
10261da177e4SLinus Torvalds ; unsigned char *data, c, crclo, crchi;
10271da177e4SLinus Torvalds ; while (not done) {
10281da177e4SLinus Torvalds ; c = *data++ + crchi;
10291da177e4SLinus Torvalds ; crchi = (@ c) >> 8 + crclo;
10301da177e4SLinus Torvalds ; crclo = @ c;
10311da177e4SLinus Torvalds ; }
10321da177e4SLinus Torvalds ;
10331da177e4SLinus Torvalds ; Remember, + is done with EOR, the @ operator is in two tables (high
10341da177e4SLinus Torvalds ; and low byte separately), which is calculated as
10351da177e4SLinus Torvalds ;
10361da177e4SLinus Torvalds ; $1021 * (c & $F0)
10371da177e4SLinus Torvalds ; xor $1021 * (c & $0F)
10381da177e4SLinus Torvalds ; xor $1021 * (c >> 4) (* is regular multiplication)
10391da177e4SLinus Torvalds ;
10401da177e4SLinus Torvalds ;
10411da177e4SLinus Torvalds ; Anyway, the end result is the same as the remainder of the division of
10421da177e4SLinus Torvalds ; the data by $11021. I am afraid I need to study theory a bit more...
10431da177e4SLinus Torvalds
10441da177e4SLinus Torvalds
10451da177e4SLinus Torvalds my only works was to code this from manx to C....
10461da177e4SLinus Torvalds
10471da177e4SLinus Torvalds */
10481da177e4SLinus Torvalds
dos_crc(void * data_a3,int data_d0,int data_d1,int data_d3)10491da177e4SLinus Torvalds static ushort dos_crc(void * data_a3, int data_d0, int data_d1, int data_d3)
10501da177e4SLinus Torvalds {
10511da177e4SLinus Torvalds static unsigned char CRCTable1[] = {
10521da177e4SLinus Torvalds 0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x81,0x91,0xa1,0xb1,0xc1,0xd1,0xe1,0xf1,
10531da177e4SLinus Torvalds 0x12,0x02,0x32,0x22,0x52,0x42,0x72,0x62,0x93,0x83,0xb3,0xa3,0xd3,0xc3,0xf3,0xe3,
10541da177e4SLinus Torvalds 0x24,0x34,0x04,0x14,0x64,0x74,0x44,0x54,0xa5,0xb5,0x85,0x95,0xe5,0xf5,0xc5,0xd5,
10551da177e4SLinus Torvalds 0x36,0x26,0x16,0x06,0x76,0x66,0x56,0x46,0xb7,0xa7,0x97,0x87,0xf7,0xe7,0xd7,0xc7,
10561da177e4SLinus Torvalds 0x48,0x58,0x68,0x78,0x08,0x18,0x28,0x38,0xc9,0xd9,0xe9,0xf9,0x89,0x99,0xa9,0xb9,
10571da177e4SLinus Torvalds 0x5a,0x4a,0x7a,0x6a,0x1a,0x0a,0x3a,0x2a,0xdb,0xcb,0xfb,0xeb,0x9b,0x8b,0xbb,0xab,
10581da177e4SLinus Torvalds 0x6c,0x7c,0x4c,0x5c,0x2c,0x3c,0x0c,0x1c,0xed,0xfd,0xcd,0xdd,0xad,0xbd,0x8d,0x9d,
10591da177e4SLinus Torvalds 0x7e,0x6e,0x5e,0x4e,0x3e,0x2e,0x1e,0x0e,0xff,0xef,0xdf,0xcf,0xbf,0xaf,0x9f,0x8f,
10601da177e4SLinus Torvalds 0x91,0x81,0xb1,0xa1,0xd1,0xc1,0xf1,0xe1,0x10,0x00,0x30,0x20,0x50,0x40,0x70,0x60,
10611da177e4SLinus Torvalds 0x83,0x93,0xa3,0xb3,0xc3,0xd3,0xe3,0xf3,0x02,0x12,0x22,0x32,0x42,0x52,0x62,0x72,
10621da177e4SLinus Torvalds 0xb5,0xa5,0x95,0x85,0xf5,0xe5,0xd5,0xc5,0x34,0x24,0x14,0x04,0x74,0x64,0x54,0x44,
10631da177e4SLinus Torvalds 0xa7,0xb7,0x87,0x97,0xe7,0xf7,0xc7,0xd7,0x26,0x36,0x06,0x16,0x66,0x76,0x46,0x56,
10641da177e4SLinus Torvalds 0xd9,0xc9,0xf9,0xe9,0x99,0x89,0xb9,0xa9,0x58,0x48,0x78,0x68,0x18,0x08,0x38,0x28,
10651da177e4SLinus Torvalds 0xcb,0xdb,0xeb,0xfb,0x8b,0x9b,0xab,0xbb,0x4a,0x5a,0x6a,0x7a,0x0a,0x1a,0x2a,0x3a,
10661da177e4SLinus Torvalds 0xfd,0xed,0xdd,0xcd,0xbd,0xad,0x9d,0x8d,0x7c,0x6c,0x5c,0x4c,0x3c,0x2c,0x1c,0x0c,
10671da177e4SLinus Torvalds 0xef,0xff,0xcf,0xdf,0xaf,0xbf,0x8f,0x9f,0x6e,0x7e,0x4e,0x5e,0x2e,0x3e,0x0e,0x1e
10681da177e4SLinus Torvalds };
10691da177e4SLinus Torvalds
10701da177e4SLinus Torvalds static unsigned char CRCTable2[] = {
10711da177e4SLinus Torvalds 0x00,0x21,0x42,0x63,0x84,0xa5,0xc6,0xe7,0x08,0x29,0x4a,0x6b,0x8c,0xad,0xce,0xef,
10721da177e4SLinus Torvalds 0x31,0x10,0x73,0x52,0xb5,0x94,0xf7,0xd6,0x39,0x18,0x7b,0x5a,0xbd,0x9c,0xff,0xde,
10731da177e4SLinus Torvalds 0x62,0x43,0x20,0x01,0xe6,0xc7,0xa4,0x85,0x6a,0x4b,0x28,0x09,0xee,0xcf,0xac,0x8d,
10741da177e4SLinus Torvalds 0x53,0x72,0x11,0x30,0xd7,0xf6,0x95,0xb4,0x5b,0x7a,0x19,0x38,0xdf,0xfe,0x9d,0xbc,
10751da177e4SLinus Torvalds 0xc4,0xe5,0x86,0xa7,0x40,0x61,0x02,0x23,0xcc,0xed,0x8e,0xaf,0x48,0x69,0x0a,0x2b,
10761da177e4SLinus Torvalds 0xf5,0xd4,0xb7,0x96,0x71,0x50,0x33,0x12,0xfd,0xdc,0xbf,0x9e,0x79,0x58,0x3b,0x1a,
10771da177e4SLinus Torvalds 0xa6,0x87,0xe4,0xc5,0x22,0x03,0x60,0x41,0xae,0x8f,0xec,0xcd,0x2a,0x0b,0x68,0x49,
10781da177e4SLinus Torvalds 0x97,0xb6,0xd5,0xf4,0x13,0x32,0x51,0x70,0x9f,0xbe,0xdd,0xfc,0x1b,0x3a,0x59,0x78,
10791da177e4SLinus Torvalds 0x88,0xa9,0xca,0xeb,0x0c,0x2d,0x4e,0x6f,0x80,0xa1,0xc2,0xe3,0x04,0x25,0x46,0x67,
10801da177e4SLinus Torvalds 0xb9,0x98,0xfb,0xda,0x3d,0x1c,0x7f,0x5e,0xb1,0x90,0xf3,0xd2,0x35,0x14,0x77,0x56,
10811da177e4SLinus Torvalds 0xea,0xcb,0xa8,0x89,0x6e,0x4f,0x2c,0x0d,0xe2,0xc3,0xa0,0x81,0x66,0x47,0x24,0x05,
10821da177e4SLinus Torvalds 0xdb,0xfa,0x99,0xb8,0x5f,0x7e,0x1d,0x3c,0xd3,0xf2,0x91,0xb0,0x57,0x76,0x15,0x34,
10831da177e4SLinus Torvalds 0x4c,0x6d,0x0e,0x2f,0xc8,0xe9,0x8a,0xab,0x44,0x65,0x06,0x27,0xc0,0xe1,0x82,0xa3,
10841da177e4SLinus Torvalds 0x7d,0x5c,0x3f,0x1e,0xf9,0xd8,0xbb,0x9a,0x75,0x54,0x37,0x16,0xf1,0xd0,0xb3,0x92,
10851da177e4SLinus Torvalds 0x2e,0x0f,0x6c,0x4d,0xaa,0x8b,0xe8,0xc9,0x26,0x07,0x64,0x45,0xa2,0x83,0xe0,0xc1,
10861da177e4SLinus Torvalds 0x1f,0x3e,0x5d,0x7c,0x9b,0xba,0xd9,0xf8,0x17,0x36,0x55,0x74,0x93,0xb2,0xd1,0xf0
10871da177e4SLinus Torvalds };
10881da177e4SLinus Torvalds
10891da177e4SLinus Torvalds /* look at the asm-code - what looks in C a bit strange is almost as good as handmade */
10901da177e4SLinus Torvalds register int i;
10911da177e4SLinus Torvalds register unsigned char *CRCT1, *CRCT2, *data, c, crch, crcl;
10921da177e4SLinus Torvalds
10931da177e4SLinus Torvalds CRCT1=CRCTable1;
10941da177e4SLinus Torvalds CRCT2=CRCTable2;
10951da177e4SLinus Torvalds data=data_a3;
10961da177e4SLinus Torvalds crcl=data_d1;
10971da177e4SLinus Torvalds crch=data_d0;
10981da177e4SLinus Torvalds for (i=data_d3; i>=0; i--) {
10991da177e4SLinus Torvalds c = (*data++) ^ crch;
11001da177e4SLinus Torvalds crch = CRCT1[c] ^ crcl;
11011da177e4SLinus Torvalds crcl = CRCT2[c];
11021da177e4SLinus Torvalds }
11031da177e4SLinus Torvalds return (crch<<8)|crcl;
11041da177e4SLinus Torvalds }
11051da177e4SLinus Torvalds
dos_hdr_crc(struct dos_header * hdr)11061da177e4SLinus Torvalds static inline ushort dos_hdr_crc (struct dos_header *hdr)
11071da177e4SLinus Torvalds {
11081da177e4SLinus Torvalds return dos_crc(&(hdr->track), 0xb2, 0x30, 3); /* precomputed magic */
11091da177e4SLinus Torvalds }
11101da177e4SLinus Torvalds
dos_data_crc(unsigned char * data)11111da177e4SLinus Torvalds static inline ushort dos_data_crc(unsigned char *data)
11121da177e4SLinus Torvalds {
11131da177e4SLinus Torvalds return dos_crc(data, 0xe2, 0x95 ,511); /* precomputed magic */
11141da177e4SLinus Torvalds }
11151da177e4SLinus Torvalds
dos_decode_byte(ushort word)11161da177e4SLinus Torvalds static inline unsigned char dos_decode_byte(ushort word)
11171da177e4SLinus Torvalds {
11181da177e4SLinus Torvalds register ushort w2;
11191da177e4SLinus Torvalds register unsigned char byte;
11201da177e4SLinus Torvalds register unsigned char *dec = mfmdecode;
11211da177e4SLinus Torvalds
11221da177e4SLinus Torvalds w2=word;
11231da177e4SLinus Torvalds w2>>=8;
11241da177e4SLinus Torvalds w2&=127;
11251da177e4SLinus Torvalds byte = dec[w2];
11261da177e4SLinus Torvalds byte <<= 4;
11271da177e4SLinus Torvalds w2 = word & 127;
11281da177e4SLinus Torvalds byte |= dec[w2];
11291da177e4SLinus Torvalds return byte;
11301da177e4SLinus Torvalds }
11311da177e4SLinus Torvalds
dos_decode(unsigned char * data,unsigned short * raw,int len)11321da177e4SLinus Torvalds static unsigned long dos_decode(unsigned char *data, unsigned short *raw, int len)
11331da177e4SLinus Torvalds {
11341da177e4SLinus Torvalds int i;
11351da177e4SLinus Torvalds
11361da177e4SLinus Torvalds for (i = 0; i < len; i++)
11371da177e4SLinus Torvalds *data++=dos_decode_byte(*raw++);
11381da177e4SLinus Torvalds return ((ulong)raw);
11391da177e4SLinus Torvalds }
11401da177e4SLinus Torvalds
11411da177e4SLinus Torvalds #ifdef DEBUG
dbg(unsigned long ptr)11421da177e4SLinus Torvalds static void dbg(unsigned long ptr)
11431da177e4SLinus Torvalds {
11441da177e4SLinus Torvalds printk("raw data @%08lx: %08lx, %08lx ,%08lx, %08lx\n", ptr,
11451da177e4SLinus Torvalds ((ulong *)ptr)[0], ((ulong *)ptr)[1],
11461da177e4SLinus Torvalds ((ulong *)ptr)[2], ((ulong *)ptr)[3]);
11471da177e4SLinus Torvalds }
11481da177e4SLinus Torvalds #endif
11491da177e4SLinus Torvalds
dos_read(int drive)11501da177e4SLinus Torvalds static int dos_read(int drive)
11511da177e4SLinus Torvalds {
11521da177e4SLinus Torvalds unsigned long end;
11531da177e4SLinus Torvalds unsigned long raw;
11541da177e4SLinus Torvalds int scnt;
11551da177e4SLinus Torvalds unsigned short crc,data_crc[2];
11561da177e4SLinus Torvalds struct dos_header hdr;
11571da177e4SLinus Torvalds
11581da177e4SLinus Torvalds drive&=3;
11591da177e4SLinus Torvalds raw = (long) raw_buf;
11601da177e4SLinus Torvalds end = raw + unit[drive].type->read_size;
11611da177e4SLinus Torvalds
11621da177e4SLinus Torvalds for (scnt=0; scnt < unit[drive].dtype->sects * unit[drive].type->sect_mult; scnt++) {
11631da177e4SLinus Torvalds do { /* search for the right sync of each sec-hdr */
11641da177e4SLinus Torvalds if (!(raw = scan_sync (raw, end))) {
11651da177e4SLinus Torvalds printk(KERN_INFO "dos_read: no hdr sync on "
11661da177e4SLinus Torvalds "track %d, unit %d for sector %d\n",
11671da177e4SLinus Torvalds unit[drive].track,drive,scnt);
11681da177e4SLinus Torvalds return MFM_NOSYNC;
11691da177e4SLinus Torvalds }
11701da177e4SLinus Torvalds #ifdef DEBUG
11711da177e4SLinus Torvalds dbg(raw);
11721da177e4SLinus Torvalds #endif
11731da177e4SLinus Torvalds } while (*((ushort *)raw)!=0x5554); /* loop usually only once done */
11741da177e4SLinus Torvalds raw+=2; /* skip over headermark */
11751da177e4SLinus Torvalds raw = dos_decode((unsigned char *)&hdr,(ushort *) raw,8);
11761da177e4SLinus Torvalds crc = dos_hdr_crc(&hdr);
11771da177e4SLinus Torvalds
11781da177e4SLinus Torvalds #ifdef DEBUG
11791da177e4SLinus Torvalds printk("(%3d,%d,%2d,%d) %x\n", hdr.track, hdr.side,
11801da177e4SLinus Torvalds hdr.sec, hdr.len_desc, hdr.crc);
11811da177e4SLinus Torvalds #endif
11821da177e4SLinus Torvalds
11831da177e4SLinus Torvalds if (crc != hdr.crc) {
11841da177e4SLinus Torvalds printk(KERN_INFO "dos_read: MFM_HEADER %04x,%04x\n",
11851da177e4SLinus Torvalds hdr.crc, crc);
11861da177e4SLinus Torvalds return MFM_HEADER;
11871da177e4SLinus Torvalds }
11881da177e4SLinus Torvalds if (hdr.track != unit[drive].track/unit[drive].type->heads) {
11891da177e4SLinus Torvalds printk(KERN_INFO "dos_read: MFM_TRACK %d, %d\n",
11901da177e4SLinus Torvalds hdr.track,
11911da177e4SLinus Torvalds unit[drive].track/unit[drive].type->heads);
11921da177e4SLinus Torvalds return MFM_TRACK;
11931da177e4SLinus Torvalds }
11941da177e4SLinus Torvalds
11951da177e4SLinus Torvalds if (hdr.side != unit[drive].track%unit[drive].type->heads) {
11961da177e4SLinus Torvalds printk(KERN_INFO "dos_read: MFM_SIDE %d, %d\n",
11971da177e4SLinus Torvalds hdr.side,
11981da177e4SLinus Torvalds unit[drive].track%unit[drive].type->heads);
11991da177e4SLinus Torvalds return MFM_TRACK;
12001da177e4SLinus Torvalds }
12011da177e4SLinus Torvalds
12021da177e4SLinus Torvalds if (hdr.len_desc != 2) {
12031da177e4SLinus Torvalds printk(KERN_INFO "dos_read: unknown sector len "
12041da177e4SLinus Torvalds "descriptor %d\n", hdr.len_desc);
12051da177e4SLinus Torvalds return MFM_DATA;
12061da177e4SLinus Torvalds }
12071da177e4SLinus Torvalds #ifdef DEBUG
12081da177e4SLinus Torvalds printk("hdr accepted\n");
12091da177e4SLinus Torvalds #endif
12101da177e4SLinus Torvalds if (!(raw = scan_sync (raw, end))) {
12111da177e4SLinus Torvalds printk(KERN_INFO "dos_read: no data sync on track "
12121da177e4SLinus Torvalds "%d, unit %d for sector%d, disk sector %d\n",
12131da177e4SLinus Torvalds unit[drive].track, drive, scnt, hdr.sec);
12141da177e4SLinus Torvalds return MFM_NOSYNC;
12151da177e4SLinus Torvalds }
12161da177e4SLinus Torvalds #ifdef DEBUG
12171da177e4SLinus Torvalds dbg(raw);
12181da177e4SLinus Torvalds #endif
12191da177e4SLinus Torvalds
12201da177e4SLinus Torvalds if (*((ushort *)raw)!=0x5545) {
12211da177e4SLinus Torvalds printk(KERN_INFO "dos_read: no data mark after "
12221da177e4SLinus Torvalds "sync (%d,%d,%d,%d) sc=%d\n",
12231da177e4SLinus Torvalds hdr.track,hdr.side,hdr.sec,hdr.len_desc,scnt);
12241da177e4SLinus Torvalds return MFM_NOSYNC;
12251da177e4SLinus Torvalds }
12261da177e4SLinus Torvalds
12271da177e4SLinus Torvalds raw+=2; /* skip data mark (included in checksum) */
12281da177e4SLinus Torvalds raw = dos_decode((unsigned char *)(unit[drive].trackbuf + (hdr.sec - 1) * 512), (ushort *) raw, 512);
12291da177e4SLinus Torvalds raw = dos_decode((unsigned char *)data_crc,(ushort *) raw,4);
12301da177e4SLinus Torvalds crc = dos_data_crc(unit[drive].trackbuf + (hdr.sec - 1) * 512);
12311da177e4SLinus Torvalds
12321da177e4SLinus Torvalds if (crc != data_crc[0]) {
12331da177e4SLinus Torvalds printk(KERN_INFO "dos_read: MFM_DATA (%d,%d,%d,%d) "
12341da177e4SLinus Torvalds "sc=%d, %x %x\n", hdr.track, hdr.side,
12351da177e4SLinus Torvalds hdr.sec, hdr.len_desc, scnt,data_crc[0], crc);
12361da177e4SLinus Torvalds printk(KERN_INFO "data=(%lx,%lx,%lx,%lx,...)\n",
12371da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[0],
12381da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[1],
12391da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[2],
12401da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[3]);
12411da177e4SLinus Torvalds return MFM_DATA;
12421da177e4SLinus Torvalds }
12431da177e4SLinus Torvalds }
12441da177e4SLinus Torvalds return 0;
12451da177e4SLinus Torvalds }
12461da177e4SLinus Torvalds
dos_encode_byte(unsigned char byte)12471da177e4SLinus Torvalds static inline ushort dos_encode_byte(unsigned char byte)
12481da177e4SLinus Torvalds {
12491da177e4SLinus Torvalds register unsigned char *enc, b2, b1;
12501da177e4SLinus Torvalds register ushort word;
12511da177e4SLinus Torvalds
12521da177e4SLinus Torvalds enc=mfmencode;
12531da177e4SLinus Torvalds b1=byte;
12541da177e4SLinus Torvalds b2=b1>>4;
12551da177e4SLinus Torvalds b1&=15;
12561da177e4SLinus Torvalds word=enc[b2] <<8 | enc [b1];
12571da177e4SLinus Torvalds return (word|((word&(256|64)) ? 0: 128));
12581da177e4SLinus Torvalds }
12591da177e4SLinus Torvalds
dos_encode_block(ushort * dest,unsigned char * src,int len)12601da177e4SLinus Torvalds static void dos_encode_block(ushort *dest, unsigned char *src, int len)
12611da177e4SLinus Torvalds {
12621da177e4SLinus Torvalds int i;
12631da177e4SLinus Torvalds
12641da177e4SLinus Torvalds for (i = 0; i < len; i++) {
12651da177e4SLinus Torvalds *dest=dos_encode_byte(*src++);
12661da177e4SLinus Torvalds *dest|=((dest[-1]&1)||(*dest&0x4000))? 0: 0x8000;
12671da177e4SLinus Torvalds dest++;
12681da177e4SLinus Torvalds }
12691da177e4SLinus Torvalds }
12701da177e4SLinus Torvalds
ms_putsec(int drive,unsigned long * raw,int cnt)12711da177e4SLinus Torvalds static unsigned long *ms_putsec(int drive, unsigned long *raw, int cnt)
12721da177e4SLinus Torvalds {
12731da177e4SLinus Torvalds static struct dos_header hdr={0,0,0,2,0,
12741da177e4SLinus Torvalds {78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78}};
12751da177e4SLinus Torvalds int i;
12761da177e4SLinus Torvalds static ushort crc[2]={0,0x4e4e};
12771da177e4SLinus Torvalds
12781da177e4SLinus Torvalds drive&=3;
12791da177e4SLinus Torvalds /* id gap 1 */
12801da177e4SLinus Torvalds /* the MFM word before is always 9254 */
12811da177e4SLinus Torvalds for(i=0;i<6;i++)
12821da177e4SLinus Torvalds *raw++=0xaaaaaaaa;
12831da177e4SLinus Torvalds /* 3 sync + 1 headermark */
12841da177e4SLinus Torvalds *raw++=0x44894489;
12851da177e4SLinus Torvalds *raw++=0x44895554;
12861da177e4SLinus Torvalds
12871da177e4SLinus Torvalds /* fill in the variable parts of the header */
12881da177e4SLinus Torvalds hdr.track=unit[drive].track/unit[drive].type->heads;
12891da177e4SLinus Torvalds hdr.side=unit[drive].track%unit[drive].type->heads;
12901da177e4SLinus Torvalds hdr.sec=cnt+1;
12911da177e4SLinus Torvalds hdr.crc=dos_hdr_crc(&hdr);
12921da177e4SLinus Torvalds
12931da177e4SLinus Torvalds /* header (without "magic") and id gap 2*/
12941da177e4SLinus Torvalds dos_encode_block((ushort *)raw,(unsigned char *) &hdr.track,28);
12951da177e4SLinus Torvalds raw+=14;
12961da177e4SLinus Torvalds
12971da177e4SLinus Torvalds /*id gap 3 */
12981da177e4SLinus Torvalds for(i=0;i<6;i++)
12991da177e4SLinus Torvalds *raw++=0xaaaaaaaa;
13001da177e4SLinus Torvalds
13011da177e4SLinus Torvalds /* 3 syncs and 1 datamark */
13021da177e4SLinus Torvalds *raw++=0x44894489;
13031da177e4SLinus Torvalds *raw++=0x44895545;
13041da177e4SLinus Torvalds
13051da177e4SLinus Torvalds /* data */
13061da177e4SLinus Torvalds dos_encode_block((ushort *)raw,
13071da177e4SLinus Torvalds (unsigned char *)unit[drive].trackbuf+cnt*512,512);
13081da177e4SLinus Torvalds raw+=256;
13091da177e4SLinus Torvalds
13101da177e4SLinus Torvalds /*data crc + jd's special gap (long words :-/) */
13111da177e4SLinus Torvalds crc[0]=dos_data_crc(unit[drive].trackbuf+cnt*512);
13121da177e4SLinus Torvalds dos_encode_block((ushort *) raw,(unsigned char *)crc,4);
13131da177e4SLinus Torvalds raw+=2;
13141da177e4SLinus Torvalds
13151da177e4SLinus Torvalds /* data gap */
13161da177e4SLinus Torvalds for(i=0;i<38;i++)
13171da177e4SLinus Torvalds *raw++=0x92549254;
13181da177e4SLinus Torvalds
13191da177e4SLinus Torvalds return raw; /* wrote 652 MFM words */
13201da177e4SLinus Torvalds }
13211da177e4SLinus Torvalds
dos_write(int disk)13221da177e4SLinus Torvalds static void dos_write(int disk)
13231da177e4SLinus Torvalds {
13241da177e4SLinus Torvalds int cnt;
13251da177e4SLinus Torvalds unsigned long raw = (unsigned long) raw_buf;
13261da177e4SLinus Torvalds unsigned long *ptr=(unsigned long *)raw;
13271da177e4SLinus Torvalds
13281da177e4SLinus Torvalds disk&=3;
13291da177e4SLinus Torvalds /* really gap4 + indexgap , but we write it first and round it up */
13301da177e4SLinus Torvalds for (cnt=0;cnt<425;cnt++)
13311da177e4SLinus Torvalds *ptr++=0x92549254;
13321da177e4SLinus Torvalds
13331da177e4SLinus Torvalds /* the following is just guessed */
13341da177e4SLinus Torvalds if (unit[disk].type->sect_mult==2) /* check for HD-Disks */
13351da177e4SLinus Torvalds for(cnt=0;cnt<473;cnt++)
13361da177e4SLinus Torvalds *ptr++=0x92549254;
13371da177e4SLinus Torvalds
13381da177e4SLinus Torvalds /* now the index marks...*/
13391da177e4SLinus Torvalds for (cnt=0;cnt<20;cnt++)
13401da177e4SLinus Torvalds *ptr++=0x92549254;
13411da177e4SLinus Torvalds for (cnt=0;cnt<6;cnt++)
13421da177e4SLinus Torvalds *ptr++=0xaaaaaaaa;
13431da177e4SLinus Torvalds *ptr++=0x52245224;
13441da177e4SLinus Torvalds *ptr++=0x52245552;
13451da177e4SLinus Torvalds for (cnt=0;cnt<20;cnt++)
13461da177e4SLinus Torvalds *ptr++=0x92549254;
13471da177e4SLinus Torvalds
13481da177e4SLinus Torvalds /* sectors */
13491da177e4SLinus Torvalds for(cnt = 0; cnt < unit[disk].dtype->sects * unit[disk].type->sect_mult; cnt++)
13501da177e4SLinus Torvalds ptr=ms_putsec(disk,ptr,cnt);
13511da177e4SLinus Torvalds
13521da177e4SLinus Torvalds *(ushort *)ptr = 0xaaa8; /* MFM word before is always 0x9254 */
13531da177e4SLinus Torvalds }
13541da177e4SLinus Torvalds
13551da177e4SLinus Torvalds /*
13561da177e4SLinus Torvalds * Here comes the high level stuff (i.e. the filesystem interface)
13571da177e4SLinus Torvalds * and helper functions.
13581da177e4SLinus Torvalds * Normally this should be the only part that has to be adapted to
13591da177e4SLinus Torvalds * different kernel versions.
13601da177e4SLinus Torvalds */
13611da177e4SLinus Torvalds
13621da177e4SLinus Torvalds /* FIXME: this assumes the drive is still spinning -
13631da177e4SLinus Torvalds * which is only true if we complete writing a track within three seconds
13641da177e4SLinus Torvalds */
flush_track_callback(struct timer_list * timer)1365cbb9d178SKees Cook static void flush_track_callback(struct timer_list *timer)
13661da177e4SLinus Torvalds {
1367cbb9d178SKees Cook unsigned long nr = ((unsigned long)timer -
1368cbb9d178SKees Cook (unsigned long)&flush_track_timer[0]) /
1369cbb9d178SKees Cook sizeof(flush_track_timer[0]);
1370cbb9d178SKees Cook
13711da177e4SLinus Torvalds nr&=3;
13721da177e4SLinus Torvalds writefromint = 1;
13731da177e4SLinus Torvalds if (!try_fdc(nr)) {
13741da177e4SLinus Torvalds /* we might block in an interrupt, so try again later */
13751da177e4SLinus Torvalds flush_track_timer[nr].expires = jiffies + 1;
13761da177e4SLinus Torvalds add_timer(flush_track_timer + nr);
13771da177e4SLinus Torvalds return;
13781da177e4SLinus Torvalds }
13791da177e4SLinus Torvalds get_fdc(nr);
13801da177e4SLinus Torvalds (*unit[nr].dtype->write_fkt)(nr);
13811da177e4SLinus Torvalds if (!raw_write(nr)) {
13821da177e4SLinus Torvalds printk (KERN_NOTICE "floppy disk write protected\n");
13831da177e4SLinus Torvalds writefromint = 0;
13841da177e4SLinus Torvalds writepending = 0;
13851da177e4SLinus Torvalds }
13861da177e4SLinus Torvalds rel_fdc();
13871da177e4SLinus Torvalds }
13881da177e4SLinus Torvalds
non_int_flush_track(unsigned long nr)13891da177e4SLinus Torvalds static int non_int_flush_track (unsigned long nr)
13901da177e4SLinus Torvalds {
13911da177e4SLinus Torvalds unsigned long flags;
13921da177e4SLinus Torvalds
13931da177e4SLinus Torvalds nr&=3;
13941da177e4SLinus Torvalds writefromint = 0;
13951da177e4SLinus Torvalds del_timer(&post_write_timer);
13961da177e4SLinus Torvalds get_fdc(nr);
13971da177e4SLinus Torvalds if (!fd_motor_on(nr)) {
13981da177e4SLinus Torvalds writepending = 0;
13991da177e4SLinus Torvalds rel_fdc();
14001da177e4SLinus Torvalds return 0;
14011da177e4SLinus Torvalds }
14021da177e4SLinus Torvalds local_irq_save(flags);
14031da177e4SLinus Torvalds if (writepending != 2) {
14041da177e4SLinus Torvalds local_irq_restore(flags);
14051da177e4SLinus Torvalds (*unit[nr].dtype->write_fkt)(nr);
14061da177e4SLinus Torvalds if (!raw_write(nr)) {
14071da177e4SLinus Torvalds printk (KERN_NOTICE "floppy disk write protected "
14081da177e4SLinus Torvalds "in write!\n");
14091da177e4SLinus Torvalds writepending = 0;
14101da177e4SLinus Torvalds return 0;
14111da177e4SLinus Torvalds }
14126d0be946SAndreas Bombe wait_event(wait_fd_block, block_flag != 2);
14131da177e4SLinus Torvalds }
14141da177e4SLinus Torvalds else {
14151da177e4SLinus Torvalds local_irq_restore(flags);
14161da177e4SLinus Torvalds ms_delay(2); /* 2 ms post_write delay */
14171da177e4SLinus Torvalds post_write(nr);
14181da177e4SLinus Torvalds }
14191da177e4SLinus Torvalds rel_fdc();
14201da177e4SLinus Torvalds return 1;
14211da177e4SLinus Torvalds }
14221da177e4SLinus Torvalds
get_track(int drive,int track)14231da177e4SLinus Torvalds static int get_track(int drive, int track)
14241da177e4SLinus Torvalds {
14251da177e4SLinus Torvalds int error, errcnt;
14261da177e4SLinus Torvalds
14271da177e4SLinus Torvalds drive&=3;
14281da177e4SLinus Torvalds if (unit[drive].track == track)
14291da177e4SLinus Torvalds return 0;
14301da177e4SLinus Torvalds get_fdc(drive);
14311da177e4SLinus Torvalds if (!fd_motor_on(drive)) {
14321da177e4SLinus Torvalds rel_fdc();
14331da177e4SLinus Torvalds return -1;
14341da177e4SLinus Torvalds }
14351da177e4SLinus Torvalds
14361da177e4SLinus Torvalds if (unit[drive].dirty == 1) {
14371da177e4SLinus Torvalds del_timer (flush_track_timer + drive);
14381da177e4SLinus Torvalds non_int_flush_track (drive);
14391da177e4SLinus Torvalds }
14401da177e4SLinus Torvalds errcnt = 0;
14411da177e4SLinus Torvalds while (errcnt < MAX_ERRORS) {
14421da177e4SLinus Torvalds if (!fd_seek(drive, track))
14431da177e4SLinus Torvalds return -1;
14441da177e4SLinus Torvalds raw_read(drive);
14451da177e4SLinus Torvalds error = (*unit[drive].dtype->read_fkt)(drive);
14461da177e4SLinus Torvalds if (error == 0) {
14471da177e4SLinus Torvalds rel_fdc();
14481da177e4SLinus Torvalds return 0;
14491da177e4SLinus Torvalds }
14501da177e4SLinus Torvalds /* Read Error Handling: recalibrate and try again */
14511da177e4SLinus Torvalds unit[drive].track = -1;
14521da177e4SLinus Torvalds errcnt++;
14531da177e4SLinus Torvalds }
14541da177e4SLinus Torvalds rel_fdc();
14551da177e4SLinus Torvalds return -1;
14561da177e4SLinus Torvalds }
14571da177e4SLinus Torvalds
amiflop_rw_cur_segment(struct amiga_floppy_struct * floppy,struct request * rq)145821b07f35SOmar Sandoval static blk_status_t amiflop_rw_cur_segment(struct amiga_floppy_struct *floppy,
145921b07f35SOmar Sandoval struct request *rq)
1460786029ffSVivek Goyal {
146121b07f35SOmar Sandoval int drive = floppy - unit;
14621da177e4SLinus Torvalds unsigned int cnt, block, track, sector;
14631da177e4SLinus Torvalds char *data;
14641da177e4SLinus Torvalds
146521b07f35SOmar Sandoval for (cnt = 0; cnt < blk_rq_cur_sectors(rq); cnt++) {
14661da177e4SLinus Torvalds #ifdef DEBUG
14671da177e4SLinus Torvalds printk("fd: sector %ld + %d requested for %s\n",
14689e31bebeSTejun Heo blk_rq_pos(rq), cnt,
14699e31bebeSTejun Heo (rq_data_dir(rq) == READ) ? "read" : "write");
14701da177e4SLinus Torvalds #endif
14719e31bebeSTejun Heo block = blk_rq_pos(rq) + cnt;
14721da177e4SLinus Torvalds track = block / (floppy->dtype->sects * floppy->type->sect_mult);
14731da177e4SLinus Torvalds sector = block % (floppy->dtype->sects * floppy->type->sect_mult);
1474b4f42e28SJens Axboe data = bio_data(rq->bio) + 512 * cnt;
14751da177e4SLinus Torvalds #ifdef DEBUG
14761da177e4SLinus Torvalds printk("access to track %d, sector %d, with buffer at "
14771da177e4SLinus Torvalds "0x%08lx\n", track, sector, data);
14781da177e4SLinus Torvalds #endif
14791da177e4SLinus Torvalds
148021b07f35SOmar Sandoval if (get_track(drive, track) == -1)
148121b07f35SOmar Sandoval return BLK_STS_IOERR;
14821da177e4SLinus Torvalds
14839e31bebeSTejun Heo if (rq_data_dir(rq) == READ) {
14841da177e4SLinus Torvalds memcpy(data, floppy->trackbuf + sector * 512, 512);
14859e31bebeSTejun Heo } else {
14861da177e4SLinus Torvalds memcpy(floppy->trackbuf + sector * 512, data, 512);
14871da177e4SLinus Torvalds
14881da177e4SLinus Torvalds /* keep the drive spinning while writes are scheduled */
148921b07f35SOmar Sandoval if (!fd_motor_on(drive))
149021b07f35SOmar Sandoval return BLK_STS_IOERR;
14911da177e4SLinus Torvalds /*
14921da177e4SLinus Torvalds * setup a callback to write the track buffer
14931da177e4SLinus Torvalds * after a short (1 tick) delay.
14941da177e4SLinus Torvalds */
14951da177e4SLinus Torvalds floppy->dirty = 1;
14961da177e4SLinus Torvalds /* reset the timer */
14971da177e4SLinus Torvalds mod_timer (flush_track_timer + drive, jiffies + 1);
14981da177e4SLinus Torvalds }
14991da177e4SLinus Torvalds }
15001da177e4SLinus Torvalds
150121b07f35SOmar Sandoval return BLK_STS_OK;
15021da177e4SLinus Torvalds }
15031da177e4SLinus Torvalds
amiflop_queue_rq(struct blk_mq_hw_ctx * hctx,const struct blk_mq_queue_data * bd)150421b07f35SOmar Sandoval static blk_status_t amiflop_queue_rq(struct blk_mq_hw_ctx *hctx,
150521b07f35SOmar Sandoval const struct blk_mq_queue_data *bd)
15061da177e4SLinus Torvalds {
150721b07f35SOmar Sandoval struct request *rq = bd->rq;
1508f3fa33acSChristoph Hellwig struct amiga_floppy_struct *floppy = rq->q->disk->private_data;
150921b07f35SOmar Sandoval blk_status_t err;
151021b07f35SOmar Sandoval
151121b07f35SOmar Sandoval if (!spin_trylock_irq(&amiflop_lock))
151221b07f35SOmar Sandoval return BLK_STS_DEV_RESOURCE;
151321b07f35SOmar Sandoval
151421b07f35SOmar Sandoval blk_mq_start_request(rq);
151521b07f35SOmar Sandoval
151621b07f35SOmar Sandoval do {
151721b07f35SOmar Sandoval err = amiflop_rw_cur_segment(floppy, rq);
151821b07f35SOmar Sandoval } while (blk_update_request(rq, err, blk_rq_cur_bytes(rq)));
151921b07f35SOmar Sandoval blk_mq_end_request(rq, err);
152021b07f35SOmar Sandoval
152121b07f35SOmar Sandoval spin_unlock_irq(&amiflop_lock);
152221b07f35SOmar Sandoval return BLK_STS_OK;
15231da177e4SLinus Torvalds }
15241da177e4SLinus Torvalds
fd_getgeo(struct block_device * bdev,struct hd_geometry * geo)1525a885c8c4SChristoph Hellwig static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1526a885c8c4SChristoph Hellwig {
1527a885c8c4SChristoph Hellwig int drive = MINOR(bdev->bd_dev) & 3;
1528a885c8c4SChristoph Hellwig
1529a885c8c4SChristoph Hellwig geo->heads = unit[drive].type->heads;
1530a885c8c4SChristoph Hellwig geo->sectors = unit[drive].dtype->sects * unit[drive].type->sect_mult;
1531a885c8c4SChristoph Hellwig geo->cylinders = unit[drive].type->tracks;
1532a885c8c4SChristoph Hellwig return 0;
1533a885c8c4SChristoph Hellwig }
1534a885c8c4SChristoph Hellwig
fd_locked_ioctl(struct block_device * bdev,blk_mode_t mode,unsigned int cmd,unsigned long param)1535*05bdb996SChristoph Hellwig static int fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode,
15361da177e4SLinus Torvalds unsigned int cmd, unsigned long param)
15371da177e4SLinus Torvalds {
153847225db5SAl Viro struct amiga_floppy_struct *p = bdev->bd_disk->private_data;
153947225db5SAl Viro int drive = p - unit;
15401da177e4SLinus Torvalds static struct floppy_struct getprm;
15418a423e54SAl Viro void __user *argp = (void __user *)param;
15421da177e4SLinus Torvalds
15431da177e4SLinus Torvalds switch(cmd){
15441da177e4SLinus Torvalds case FDFMTBEG:
15451da177e4SLinus Torvalds get_fdc(drive);
15461da177e4SLinus Torvalds if (fd_ref[drive] > 1) {
15471da177e4SLinus Torvalds rel_fdc();
15481da177e4SLinus Torvalds return -EBUSY;
15491da177e4SLinus Torvalds }
15501da177e4SLinus Torvalds if (fd_motor_on(drive) == 0) {
15511da177e4SLinus Torvalds rel_fdc();
15521da177e4SLinus Torvalds return -ENODEV;
15531da177e4SLinus Torvalds }
15541da177e4SLinus Torvalds if (fd_calibrate(drive) == 0) {
15551da177e4SLinus Torvalds rel_fdc();
15561da177e4SLinus Torvalds return -ENXIO;
15571da177e4SLinus Torvalds }
15581da177e4SLinus Torvalds floppy_off(drive);
15591da177e4SLinus Torvalds rel_fdc();
15601da177e4SLinus Torvalds break;
15611da177e4SLinus Torvalds case FDFMTTRK:
156247225db5SAl Viro if (param < p->type->tracks * p->type->heads)
15631da177e4SLinus Torvalds {
15641da177e4SLinus Torvalds get_fdc(drive);
15651da177e4SLinus Torvalds if (fd_seek(drive,param) != 0){
156647225db5SAl Viro memset(p->trackbuf, FD_FILL_BYTE,
156747225db5SAl Viro p->dtype->sects * p->type->sect_mult * 512);
15681da177e4SLinus Torvalds non_int_flush_track(drive);
15691da177e4SLinus Torvalds }
15701da177e4SLinus Torvalds floppy_off(drive);
15711da177e4SLinus Torvalds rel_fdc();
15721da177e4SLinus Torvalds }
15731da177e4SLinus Torvalds else
15741da177e4SLinus Torvalds return -EINVAL;
15751da177e4SLinus Torvalds break;
15761da177e4SLinus Torvalds case FDFMTEND:
15771da177e4SLinus Torvalds floppy_off(drive);
157847225db5SAl Viro invalidate_bdev(bdev);
15791da177e4SLinus Torvalds break;
15801da177e4SLinus Torvalds case FDGETPRM:
15811da177e4SLinus Torvalds memset((void *)&getprm, 0, sizeof (getprm));
158247225db5SAl Viro getprm.track=p->type->tracks;
158347225db5SAl Viro getprm.head=p->type->heads;
158447225db5SAl Viro getprm.sect=p->dtype->sects * p->type->sect_mult;
158547225db5SAl Viro getprm.size=p->blocks;
15868a423e54SAl Viro if (copy_to_user(argp, &getprm, sizeof(struct floppy_struct)))
15871da177e4SLinus Torvalds return -EFAULT;
15881da177e4SLinus Torvalds break;
15891da177e4SLinus Torvalds case FDSETPRM:
15901da177e4SLinus Torvalds case FDDEFPRM:
15911da177e4SLinus Torvalds return -EINVAL;
15921da177e4SLinus Torvalds case FDFLUSH: /* unconditionally, even if not needed */
15931da177e4SLinus Torvalds del_timer (flush_track_timer + drive);
15941da177e4SLinus Torvalds non_int_flush_track(drive);
15951da177e4SLinus Torvalds break;
15961da177e4SLinus Torvalds #ifdef RAW_IOCTL
15971da177e4SLinus Torvalds case IOCTL_RAW_TRACK:
159847225db5SAl Viro if (copy_to_user(argp, raw_buf, p->type->read_size))
15991da177e4SLinus Torvalds return -EFAULT;
16001da177e4SLinus Torvalds else
160147225db5SAl Viro return p->type->read_size;
16021da177e4SLinus Torvalds #endif
16031da177e4SLinus Torvalds default:
16041da177e4SLinus Torvalds return -ENOSYS;
16051da177e4SLinus Torvalds }
16061da177e4SLinus Torvalds return 0;
16071da177e4SLinus Torvalds }
16081da177e4SLinus Torvalds
fd_ioctl(struct block_device * bdev,blk_mode_t mode,unsigned int cmd,unsigned long param)1609*05bdb996SChristoph Hellwig static int fd_ioctl(struct block_device *bdev, blk_mode_t mode,
16108a6cfeb6SArnd Bergmann unsigned int cmd, unsigned long param)
16118a6cfeb6SArnd Bergmann {
16128a6cfeb6SArnd Bergmann int ret;
16138a6cfeb6SArnd Bergmann
16142a48fc0aSArnd Bergmann mutex_lock(&amiflop_mutex);
16158a6cfeb6SArnd Bergmann ret = fd_locked_ioctl(bdev, mode, cmd, param);
16162a48fc0aSArnd Bergmann mutex_unlock(&amiflop_mutex);
16178a6cfeb6SArnd Bergmann
16188a6cfeb6SArnd Bergmann return ret;
16198a6cfeb6SArnd Bergmann }
16208a6cfeb6SArnd Bergmann
fd_probe(int dev)16211da177e4SLinus Torvalds static void fd_probe(int dev)
16221da177e4SLinus Torvalds {
16231da177e4SLinus Torvalds unsigned long code;
16241da177e4SLinus Torvalds int type;
16251da177e4SLinus Torvalds int drive;
16261da177e4SLinus Torvalds
16271da177e4SLinus Torvalds drive = dev & 3;
16281da177e4SLinus Torvalds code = fd_get_drive_id(drive);
16291da177e4SLinus Torvalds
16301da177e4SLinus Torvalds /* get drive type */
16311da177e4SLinus Torvalds for (type = 0; type < num_dr_types; type++)
16321da177e4SLinus Torvalds if (drive_types[type].code == code)
16331da177e4SLinus Torvalds break;
16341da177e4SLinus Torvalds
16351da177e4SLinus Torvalds if (type >= num_dr_types) {
16361da177e4SLinus Torvalds printk(KERN_WARNING "fd_probe: unsupported drive type "
16371da177e4SLinus Torvalds "%08lx found\n", code);
16381da177e4SLinus Torvalds unit[drive].type = &drive_types[num_dr_types-1]; /* FD_NODRIVE */
16391da177e4SLinus Torvalds return;
16401da177e4SLinus Torvalds }
16411da177e4SLinus Torvalds
16421da177e4SLinus Torvalds unit[drive].type = drive_types + type;
16431da177e4SLinus Torvalds unit[drive].track = -1;
16441da177e4SLinus Torvalds
16451da177e4SLinus Torvalds unit[drive].disk = -1;
16461da177e4SLinus Torvalds unit[drive].motor = 0;
16471da177e4SLinus Torvalds unit[drive].busy = 0;
16481da177e4SLinus Torvalds unit[drive].status = -1;
16491da177e4SLinus Torvalds }
16501da177e4SLinus Torvalds
16511da177e4SLinus Torvalds /*
16521da177e4SLinus Torvalds * floppy_open check for aliasing (/dev/fd0 can be the same as
16531da177e4SLinus Torvalds * /dev/PS0 etc), and disallows simultaneous access to the same
16541da177e4SLinus Torvalds * drive with different device numbers.
16551da177e4SLinus Torvalds */
floppy_open(struct gendisk * disk,blk_mode_t mode)1656*05bdb996SChristoph Hellwig static int floppy_open(struct gendisk *disk, blk_mode_t mode)
16571da177e4SLinus Torvalds {
1658d32e2bf8SChristoph Hellwig int drive = disk->first_minor & 3;
1659d32e2bf8SChristoph Hellwig int system = (disk->first_minor & 4) >> 2;
16601da177e4SLinus Torvalds int old_dev;
16611da177e4SLinus Torvalds unsigned long flags;
16621da177e4SLinus Torvalds
16632a48fc0aSArnd Bergmann mutex_lock(&amiflop_mutex);
16641da177e4SLinus Torvalds old_dev = fd_device[drive];
16651da177e4SLinus Torvalds
16666e9624b8SArnd Bergmann if (fd_ref[drive] && old_dev != system) {
16672a48fc0aSArnd Bergmann mutex_unlock(&amiflop_mutex);
16681da177e4SLinus Torvalds return -EBUSY;
16696e9624b8SArnd Bergmann }
16701da177e4SLinus Torvalds
16710033a9b4SChristoph Hellwig if (unit[drive].type->code == FD_NODRIVE) {
16720033a9b4SChristoph Hellwig mutex_unlock(&amiflop_mutex);
16730033a9b4SChristoph Hellwig return -ENXIO;
16740033a9b4SChristoph Hellwig }
1675*05bdb996SChristoph Hellwig if (mode & (BLK_OPEN_READ | BLK_OPEN_WRITE)) {
1676d32e2bf8SChristoph Hellwig disk_check_media_change(disk);
1677*05bdb996SChristoph Hellwig if (mode & BLK_OPEN_WRITE) {
16781da177e4SLinus Torvalds int wrprot;
16791da177e4SLinus Torvalds
16801da177e4SLinus Torvalds get_fdc(drive);
16811da177e4SLinus Torvalds fd_select (drive);
16821da177e4SLinus Torvalds wrprot = !(ciaa.pra & DSKPROT);
16831da177e4SLinus Torvalds fd_deselect (drive);
16841da177e4SLinus Torvalds rel_fdc();
16851da177e4SLinus Torvalds
16866e9624b8SArnd Bergmann if (wrprot) {
16872a48fc0aSArnd Bergmann mutex_unlock(&amiflop_mutex);
16881da177e4SLinus Torvalds return -EROFS;
16891da177e4SLinus Torvalds }
16901da177e4SLinus Torvalds }
16916e9624b8SArnd Bergmann }
16921da177e4SLinus Torvalds local_irq_save(flags);
16931da177e4SLinus Torvalds fd_ref[drive]++;
16941da177e4SLinus Torvalds fd_device[drive] = system;
16951da177e4SLinus Torvalds local_irq_restore(flags);
16961da177e4SLinus Torvalds
16971da177e4SLinus Torvalds unit[drive].dtype=&data_types[system];
16981da177e4SLinus Torvalds unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks*
16991da177e4SLinus Torvalds data_types[system].sects*unit[drive].type->sect_mult;
17000033a9b4SChristoph Hellwig set_capacity(unit[drive].gendisk[system], unit[drive].blocks);
17011da177e4SLinus Torvalds
17021da177e4SLinus Torvalds printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive,
17031da177e4SLinus Torvalds unit[drive].type->name, data_types[system].name);
17041da177e4SLinus Torvalds
17052a48fc0aSArnd Bergmann mutex_unlock(&amiflop_mutex);
17061da177e4SLinus Torvalds return 0;
17071da177e4SLinus Torvalds }
17081da177e4SLinus Torvalds
floppy_release(struct gendisk * disk)1709ae220766SChristoph Hellwig static void floppy_release(struct gendisk *disk)
17101da177e4SLinus Torvalds {
171147225db5SAl Viro struct amiga_floppy_struct *p = disk->private_data;
171247225db5SAl Viro int drive = p - unit;
17131da177e4SLinus Torvalds
17142a48fc0aSArnd Bergmann mutex_lock(&amiflop_mutex);
17151da177e4SLinus Torvalds if (unit[drive].dirty == 1) {
17161da177e4SLinus Torvalds del_timer (flush_track_timer + drive);
17171da177e4SLinus Torvalds non_int_flush_track (drive);
17181da177e4SLinus Torvalds }
17191da177e4SLinus Torvalds
17201da177e4SLinus Torvalds if (!fd_ref[drive]--) {
17211da177e4SLinus Torvalds printk(KERN_CRIT "floppy_release with fd_ref == 0");
17221da177e4SLinus Torvalds fd_ref[drive] = 0;
17231da177e4SLinus Torvalds }
17241da177e4SLinus Torvalds #ifdef MODULE
1725cbb9d178SKees Cook floppy_off (drive);
17261da177e4SLinus Torvalds #endif
17272a48fc0aSArnd Bergmann mutex_unlock(&amiflop_mutex);
17281da177e4SLinus Torvalds }
17291da177e4SLinus Torvalds
17301da177e4SLinus Torvalds /*
17311a8a74f0STejun Heo * check_events is never called from an interrupt, so we can relax a bit
17321da177e4SLinus Torvalds * here, sleep etc. Note that floppy-on tries to set current_DOR to point
17331da177e4SLinus Torvalds * to the desired drive, but it will probably not survive the sleep if
17341da177e4SLinus Torvalds * several floppies are used at the same time: thus the loop.
17351da177e4SLinus Torvalds */
amiga_check_events(struct gendisk * disk,unsigned int clearing)17361a8a74f0STejun Heo static unsigned amiga_check_events(struct gendisk *disk, unsigned int clearing)
17371da177e4SLinus Torvalds {
17381da177e4SLinus Torvalds struct amiga_floppy_struct *p = disk->private_data;
17391da177e4SLinus Torvalds int drive = p - unit;
17401da177e4SLinus Torvalds int changed;
17411da177e4SLinus Torvalds static int first_time = 1;
17421da177e4SLinus Torvalds
17431da177e4SLinus Torvalds if (first_time)
17441da177e4SLinus Torvalds changed = first_time--;
17451da177e4SLinus Torvalds else {
17461da177e4SLinus Torvalds get_fdc(drive);
17471da177e4SLinus Torvalds fd_select (drive);
17481da177e4SLinus Torvalds changed = !(ciaa.pra & DSKCHANGE);
17491da177e4SLinus Torvalds fd_deselect (drive);
17501da177e4SLinus Torvalds rel_fdc();
17511da177e4SLinus Torvalds }
17521da177e4SLinus Torvalds
17531da177e4SLinus Torvalds if (changed) {
17541da177e4SLinus Torvalds fd_probe(drive);
17551da177e4SLinus Torvalds p->track = -1;
17561da177e4SLinus Torvalds p->dirty = 0;
17571da177e4SLinus Torvalds writepending = 0; /* if this was true before, too bad! */
17581da177e4SLinus Torvalds writefromint = 0;
17591a8a74f0STejun Heo return DISK_EVENT_MEDIA_CHANGE;
17601da177e4SLinus Torvalds }
17611da177e4SLinus Torvalds return 0;
17621da177e4SLinus Torvalds }
17631da177e4SLinus Torvalds
176483d5cde4SAlexey Dobriyan static const struct block_device_operations floppy_fops = {
17651da177e4SLinus Torvalds .owner = THIS_MODULE,
176647225db5SAl Viro .open = floppy_open,
176747225db5SAl Viro .release = floppy_release,
17688a6cfeb6SArnd Bergmann .ioctl = fd_ioctl,
1769a885c8c4SChristoph Hellwig .getgeo = fd_getgeo,
17701a8a74f0STejun Heo .check_events = amiga_check_events,
17711da177e4SLinus Torvalds };
17721da177e4SLinus Torvalds
177321b07f35SOmar Sandoval static const struct blk_mq_ops amiflop_mq_ops = {
177421b07f35SOmar Sandoval .queue_rq = amiflop_queue_rq,
177521b07f35SOmar Sandoval };
177621b07f35SOmar Sandoval
fd_alloc_disk(int drive,int system)17770033a9b4SChristoph Hellwig static int fd_alloc_disk(int drive, int system)
177853d0f8dbSOmar Sandoval {
177953d0f8dbSOmar Sandoval struct gendisk *disk;
1780a2379420SLuis Chamberlain int err;
178153d0f8dbSOmar Sandoval
1782f6d82974SChristoph Hellwig disk = blk_mq_alloc_disk(&unit[drive].tag_set, NULL);
1783f6d82974SChristoph Hellwig if (IS_ERR(disk))
1784f6d82974SChristoph Hellwig return PTR_ERR(disk);
178553d0f8dbSOmar Sandoval
17860033a9b4SChristoph Hellwig disk->major = FLOPPY_MAJOR;
17870033a9b4SChristoph Hellwig disk->first_minor = drive + system;
1788f6d82974SChristoph Hellwig disk->minors = 1;
17890033a9b4SChristoph Hellwig disk->fops = &floppy_fops;
17901ebe2e5fSChristoph Hellwig disk->flags |= GENHD_FL_NO_PART;
17910033a9b4SChristoph Hellwig disk->events = DISK_EVENT_MEDIA_CHANGE;
17920033a9b4SChristoph Hellwig if (system)
17930033a9b4SChristoph Hellwig sprintf(disk->disk_name, "fd%d_msdos", drive);
17940033a9b4SChristoph Hellwig else
17950033a9b4SChristoph Hellwig sprintf(disk->disk_name, "fd%d", drive);
17960033a9b4SChristoph Hellwig disk->private_data = &unit[drive];
17970033a9b4SChristoph Hellwig set_capacity(disk, 880 * 2);
179853d0f8dbSOmar Sandoval
17990033a9b4SChristoph Hellwig unit[drive].gendisk[system] = disk;
1800a2379420SLuis Chamberlain err = add_disk(disk);
1801a2379420SLuis Chamberlain if (err)
18028b9ab626SChristoph Hellwig put_disk(disk);
1803a2379420SLuis Chamberlain return err;
18040033a9b4SChristoph Hellwig }
18050033a9b4SChristoph Hellwig
fd_alloc_drive(int drive)18060033a9b4SChristoph Hellwig static int fd_alloc_drive(int drive)
18070033a9b4SChristoph Hellwig {
18080033a9b4SChristoph Hellwig unit[drive].trackbuf = kmalloc(FLOPPY_MAX_SECTORS * 512, GFP_KERNEL);
18090033a9b4SChristoph Hellwig if (!unit[drive].trackbuf)
18100033a9b4SChristoph Hellwig goto out;
18110033a9b4SChristoph Hellwig
18120033a9b4SChristoph Hellwig memset(&unit[drive].tag_set, 0, sizeof(unit[drive].tag_set));
18130033a9b4SChristoph Hellwig unit[drive].tag_set.ops = &amiflop_mq_ops;
18140033a9b4SChristoph Hellwig unit[drive].tag_set.nr_hw_queues = 1;
18150033a9b4SChristoph Hellwig unit[drive].tag_set.nr_maps = 1;
18160033a9b4SChristoph Hellwig unit[drive].tag_set.queue_depth = 2;
18170033a9b4SChristoph Hellwig unit[drive].tag_set.numa_node = NUMA_NO_NODE;
18180033a9b4SChristoph Hellwig unit[drive].tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
18190033a9b4SChristoph Hellwig if (blk_mq_alloc_tag_set(&unit[drive].tag_set))
18200033a9b4SChristoph Hellwig goto out_cleanup_trackbuf;
18210033a9b4SChristoph Hellwig
18220033a9b4SChristoph Hellwig pr_cont(" fd%d", drive);
18230033a9b4SChristoph Hellwig
18240033a9b4SChristoph Hellwig if (fd_alloc_disk(drive, 0) || fd_alloc_disk(drive, 1))
18250033a9b4SChristoph Hellwig goto out_cleanup_tagset;
18260033a9b4SChristoph Hellwig return 0;
18270033a9b4SChristoph Hellwig
18280033a9b4SChristoph Hellwig out_cleanup_tagset:
18290033a9b4SChristoph Hellwig blk_mq_free_tag_set(&unit[drive].tag_set);
18300033a9b4SChristoph Hellwig out_cleanup_trackbuf:
18310033a9b4SChristoph Hellwig kfree(unit[drive].trackbuf);
18320033a9b4SChristoph Hellwig out:
183353d0f8dbSOmar Sandoval unit[drive].type->code = FD_NODRIVE;
18340033a9b4SChristoph Hellwig return -ENOMEM;
183553d0f8dbSOmar Sandoval }
183653d0f8dbSOmar Sandoval
fd_probe_drives(void)18371da177e4SLinus Torvalds static int __init fd_probe_drives(void)
18381da177e4SLinus Torvalds {
18391da177e4SLinus Torvalds int drive,drives,nomem;
18401da177e4SLinus Torvalds
184153d0f8dbSOmar Sandoval pr_info("FD: probing units\nfound");
18421da177e4SLinus Torvalds drives=0;
18431da177e4SLinus Torvalds nomem=0;
18441da177e4SLinus Torvalds for(drive=0;drive<FD_MAX_UNITS;drive++) {
18451da177e4SLinus Torvalds fd_probe(drive);
18461da177e4SLinus Torvalds if (unit[drive].type->code == FD_NODRIVE)
18471da177e4SLinus Torvalds continue;
184853d0f8dbSOmar Sandoval
18490033a9b4SChristoph Hellwig if (fd_alloc_drive(drive) < 0) {
185053d0f8dbSOmar Sandoval pr_cont(" no mem for fd%d", drive);
185153d0f8dbSOmar Sandoval nomem = 1;
18521da177e4SLinus Torvalds continue;
18531da177e4SLinus Torvalds }
18541da177e4SLinus Torvalds drives++;
18551da177e4SLinus Torvalds }
18561da177e4SLinus Torvalds if ((drives > 0) || (nomem == 0)) {
18571da177e4SLinus Torvalds if (drives == 0)
185853d0f8dbSOmar Sandoval pr_cont(" no drives");
185953d0f8dbSOmar Sandoval pr_cont("\n");
18601da177e4SLinus Torvalds return drives;
18611da177e4SLinus Torvalds }
186253d0f8dbSOmar Sandoval pr_cont("\n");
18631da177e4SLinus Torvalds return -ENOMEM;
18641da177e4SLinus Torvalds }
18651da177e4SLinus Torvalds
amiga_floppy_probe(struct platform_device * pdev)186692183b34SGeert Uytterhoeven static int __init amiga_floppy_probe(struct platform_device *pdev)
18671da177e4SLinus Torvalds {
18681da177e4SLinus Torvalds int i, ret;
18691da177e4SLinus Torvalds
18701da177e4SLinus Torvalds if (register_blkdev(FLOPPY_MAJOR,"fd"))
18711da177e4SLinus Torvalds return -EBUSY;
18721da177e4SLinus Torvalds
18731da177e4SLinus Torvalds ret = -ENOMEM;
1874059718d5SGeert Uytterhoeven raw_buf = amiga_chip_alloc(RAW_BUF_SIZE, "Floppy");
1875059718d5SGeert Uytterhoeven if (!raw_buf) {
18761da177e4SLinus Torvalds printk("fd: cannot get chip mem buffer\n");
187792183b34SGeert Uytterhoeven goto out_blkdev;
18781da177e4SLinus Torvalds }
18791da177e4SLinus Torvalds
18801da177e4SLinus Torvalds ret = -EBUSY;
18811da177e4SLinus Torvalds if (request_irq(IRQ_AMIGA_DSKBLK, fd_block_done, 0, "floppy_dma", NULL)) {
18821da177e4SLinus Torvalds printk("fd: cannot get irq for dma\n");
18831da177e4SLinus Torvalds goto out_irq;
18841da177e4SLinus Torvalds }
18851da177e4SLinus Torvalds
18861da177e4SLinus Torvalds if (request_irq(IRQ_AMIGA_CIAA_TB, ms_isr, 0, "floppy_timer", NULL)) {
18871da177e4SLinus Torvalds printk("fd: cannot get irq for timer\n");
18881da177e4SLinus Torvalds goto out_irq2;
18891da177e4SLinus Torvalds }
18901da177e4SLinus Torvalds
1891fd5b462fSGeert Uytterhoeven ret = -ENODEV;
18921da177e4SLinus Torvalds if (fd_probe_drives() < 1) /* No usable drives */
18931da177e4SLinus Torvalds goto out_probe;
18941da177e4SLinus Torvalds
18951da177e4SLinus Torvalds /* initialize variables */
1896cbb9d178SKees Cook timer_setup(&motor_on_timer, motor_on_callback, 0);
18971da177e4SLinus Torvalds motor_on_timer.expires = 0;
18981da177e4SLinus Torvalds for (i = 0; i < FD_MAX_UNITS; i++) {
1899cbb9d178SKees Cook timer_setup(&motor_off_timer[i], fd_motor_off, 0);
19001da177e4SLinus Torvalds motor_off_timer[i].expires = 0;
1901cbb9d178SKees Cook timer_setup(&flush_track_timer[i], flush_track_callback, 0);
19021da177e4SLinus Torvalds flush_track_timer[i].expires = 0;
19031da177e4SLinus Torvalds
19041da177e4SLinus Torvalds unit[i].track = -1;
19051da177e4SLinus Torvalds }
19061da177e4SLinus Torvalds
1907cbb9d178SKees Cook timer_setup(&post_write_timer, post_write_callback, 0);
19081da177e4SLinus Torvalds post_write_timer.expires = 0;
19091da177e4SLinus Torvalds
19101da177e4SLinus Torvalds for (i = 0; i < 128; i++)
19111da177e4SLinus Torvalds mfmdecode[i]=255;
19121da177e4SLinus Torvalds for (i = 0; i < 16; i++)
19131da177e4SLinus Torvalds mfmdecode[mfmencode[i]]=i;
19141da177e4SLinus Torvalds
19151da177e4SLinus Torvalds /* make sure that disk DMA is enabled */
19161da177e4SLinus Torvalds custom.dmacon = DMAF_SETCLR | DMAF_DISK;
19171da177e4SLinus Torvalds
19181da177e4SLinus Torvalds /* init ms timer */
19191da177e4SLinus Torvalds ciaa.crb = 8; /* one-shot, stop */
19201da177e4SLinus Torvalds return 0;
19211da177e4SLinus Torvalds
19221da177e4SLinus Torvalds out_probe:
19231da177e4SLinus Torvalds free_irq(IRQ_AMIGA_CIAA_TB, NULL);
19241da177e4SLinus Torvalds out_irq2:
19251da177e4SLinus Torvalds free_irq(IRQ_AMIGA_DSKBLK, NULL);
19261da177e4SLinus Torvalds out_irq:
19271da177e4SLinus Torvalds amiga_chip_free(raw_buf);
19281da177e4SLinus Torvalds out_blkdev:
19291da177e4SLinus Torvalds unregister_blkdev(FLOPPY_MAJOR,"fd");
19301da177e4SLinus Torvalds return ret;
19311da177e4SLinus Torvalds }
19321da177e4SLinus Torvalds
193392183b34SGeert Uytterhoeven static struct platform_driver amiga_floppy_driver = {
193492183b34SGeert Uytterhoeven .driver = {
193592183b34SGeert Uytterhoeven .name = "amiga-floppy",
193692183b34SGeert Uytterhoeven },
193792183b34SGeert Uytterhoeven };
193892183b34SGeert Uytterhoeven
amiga_floppy_init(void)193992183b34SGeert Uytterhoeven static int __init amiga_floppy_init(void)
194092183b34SGeert Uytterhoeven {
194192183b34SGeert Uytterhoeven return platform_driver_probe(&amiga_floppy_driver, amiga_floppy_probe);
194292183b34SGeert Uytterhoeven }
194392183b34SGeert Uytterhoeven
194492183b34SGeert Uytterhoeven module_init(amiga_floppy_init);
194592183b34SGeert Uytterhoeven
194692183b34SGeert Uytterhoeven #ifndef MODULE
amiga_floppy_setup(char * str)194763907435SAl Viro static int __init amiga_floppy_setup (char *str)
194863907435SAl Viro {
194963907435SAl Viro int n;
195063907435SAl Viro if (!MACH_IS_AMIGA)
195163907435SAl Viro return 0;
195263907435SAl Viro if (!get_option(&str, &n))
195363907435SAl Viro return 0;
195463907435SAl Viro printk (KERN_INFO "amiflop: Setting default df0 to %x\n", n);
195563907435SAl Viro fd_def_df0 = n;
19569b41046cSOGAWA Hirofumi return 1;
195763907435SAl Viro }
195863907435SAl Viro
195963907435SAl Viro __setup("floppy=", amiga_floppy_setup);
19601da177e4SLinus Torvalds #endif
196192183b34SGeert Uytterhoeven
196292183b34SGeert Uytterhoeven MODULE_ALIAS("platform:amiga-floppy");
1963