Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/kernel/arch/arm/drivers/block/fd1772.c | |
3 | * Based on ataflop.c in the m68k Linux | |
4 | * Copyright (C) 1993 Greg Harp | |
5 | * Atari Support by Bjoern Brauel, Roman Hodek | |
6 | * Archimedes Support by Dave Gilbert (linux@treblig.org) | |
7 | * | |
8 | * Big cleanup Sep 11..14 1994 Roman Hodek: | |
9 | * - Driver now works interrupt driven | |
10 | * - Support for two drives; should work, but I cannot test that :-( | |
11 | * - Reading is done in whole tracks and buffered to speed up things | |
12 | * - Disk change detection and drive deselecting after motor-off | |
13 | * similar to TOS | |
14 | * - Autodetection of disk format (DD/HD); untested yet, because I | |
15 | * don't have an HD drive :-( | |
16 | * | |
17 | * Fixes Nov 13 1994 Martin Schaller: | |
18 | * - Autodetection works now | |
19 | * - Support for 5 1/4" disks | |
20 | * - Removed drive type (unknown on atari) | |
21 | * - Do seeks with 8 Mhz | |
22 | * | |
23 | * Changes by Andreas Schwab: | |
24 | * - After errors in multiple read mode try again reading single sectors | |
25 | * (Feb 1995): | |
26 | * - Clean up error handling | |
27 | * - Set blk_size for proper size checking | |
28 | * - Initialize track register when testing presence of floppy | |
29 | * - Implement some ioctl's | |
30 | * | |
31 | * Changes by Torsten Lang: | |
32 | * - When probing the floppies we should add the FDC1772CMDADD_H flag since | |
33 | * the FDC1772 will otherwise wait forever when no disk is inserted... | |
34 | * | |
35 | * Things left to do: | |
36 | * - Formatting | |
37 | * - Maybe a better strategy for disk change detection (does anyone | |
38 | * know one?) | |
39 | * - There are some strange problems left: The strangest one is | |
40 | * that, at least on my TT (4+4MB), the first 2 Bytes of the last | |
41 | * page of the TT-Ram (!) change their contents (some bits get | |
42 | * set) while a floppy DMA is going on. But there are no accesses | |
43 | * to these memory locations from the kernel... (I tested that by | |
44 | * making the page read-only). I cannot explain what's going on... | |
45 | * - Sometimes the drive-change-detection stops to work. The | |
46 | * function is still called, but the WP bit always reads as 0... | |
47 | * Maybe a problem with the status reg mode or a timing problem. | |
48 | * Note 10/12/94: The change detection now seems to work reliably. | |
49 | * There is no proof, but I've seen no hang for a long time... | |
50 | * | |
51 | * ARCHIMEDES changes: (gilbertd@cs.man.ac.uk) | |
52 | * 26/12/95 - Changed all names starting with FDC to FDC1772 | |
53 | * Removed all references to clock speed of FDC - we're stuck with 8MHz | |
54 | * Modified disk_type structure to remove HD formats | |
55 | * | |
56 | * 7/ 1/96 - Wrote FIQ code, removed most remaining atariisms | |
57 | * | |
58 | * 13/ 1/96 - Well I think its read a single sector; but there is a problem | |
59 | * fd_rwsec_done which is called in FIQ mode starts another transfer | |
60 | * off (in fd_rwsec) while still in FIQ mode. Because its still in | |
61 | * FIQ mode it can't service the DMA and loses data. So need to | |
62 | * heavily restructure. | |
63 | * 14/ 1/96 - Found that the definitions of the register numbers of the | |
64 | * FDC were multiplied by 2 in the header for the 16bit words | |
65 | * of the atari so half the writes were going in the wrong place. | |
66 | * Also realised that the FIQ entry didn't make any attempt to | |
67 | * preserve registers or return correctly; now in assembler. | |
68 | * | |
69 | * 11/ 2/96 - Hmm - doesn't work on real machine. Auto detect doesn't | |
70 | * and hacking that past seems to wait forever - check motor | |
71 | * being turned on. | |
72 | * | |
73 | * 17/ 2/96 - still having problems - forcing track to -1 when selecting | |
74 | * new drives seems to allow it to read first few sectors | |
75 | * but then we get solid hangs at apparently random places | |
76 | * which change depending what is happening. | |
77 | * | |
78 | * 9/ 3/96 - Fiddled a lot of stuff around to move to kernel 1.3.35 | |
79 | * A lot of fiddling in DMA stuff. Having problems with it | |
80 | * constnatly thinking its timeing out. Ah - its timeout | |
81 | * was set to (6*HZ) rather than jiffies+(6*HZ). Now giving | |
82 | * duff data! | |
83 | * | |
84 | * 5/ 4/96 - Made it use the new IOC_ macros rather than *ioc | |
85 | * Hmm - giving unexpected FIQ and then timeouts | |
86 | * 18/ 8/96 - Ran through indent -kr -i8 | |
87 | * Some changes to disc change detect; don't know how well it | |
88 | * works. | |
89 | * 24/ 8/96 - Put all the track buffering code back in from the atari | |
90 | * code - I wonder if it will still work... No :-) | |
91 | * Still works if I turn off track buffering. | |
92 | * 25/ 8/96 - Changed the timer expires that I'd added back to be | |
93 | * jiffies + ....; and it all sprang to life! Got 2.8K/sec | |
94 | * off a cp -r of a 679K disc (showed 94% cpu usage!) | |
95 | * (PC gets 14.3K/sec - 0% CPU!) Hmm - hard drive corrupt! | |
96 | * Also perhaps that compile was with cache off. | |
97 | * changed cli in fd_readtrack_check to cliIF | |
98 | * changed vmallocs to kmalloc (whats the difference!!) | |
99 | * Removed the busy wait loop in do_fd_request and replaced | |
100 | * by a routine on tq_immediate; only 11% cpu on a dd off the | |
101 | * raw disc - but the speed is the same. | |
102 | * 1/ 9/96 - Idea (failed!) - set the 'disable spin-up sequence' | |
103 | * when we read the track if we know the motor is on; didn't | |
104 | * help - perhaps we have to do it in stepping as well. | |
105 | * Nope. Still doesn't help. | |
106 | * Hmm - what seems to be happening is that fd_readtrack_check | |
107 | * is never getting called. Its job is to terminate the read | |
108 | * just after we think we should have got the data; otherwise | |
109 | * the fdc takes 1 second to timeout; which is what's happening | |
110 | * Now I can see 'readtrack_timer' being set (which should do the | |
111 | * call); but it never seems to be called - hmm! | |
112 | * OK - I've moved the check to my tq_immediate code - | |
113 | * and it WORKS! 13.95K/second at 19% CPU. | |
114 | * I wish I knew why that timer didn't work..... | |
115 | * | |
116 | * 16/11/96 - Fiddled and frigged for 2.0.18 | |
117 | * | |
118 | * DAG 30/01/99 - Started frobbing for 2.2.1 | |
119 | * DAG 20/06/99 - A little more frobbing: | |
120 | * Included include/asm/uaccess.h for get_user/put_user | |
121 | * | |
122 | * DAG 1/09/00 - Dusted off for 2.4.0-test7 | |
123 | * MAX_SECTORS was name clashing so it is now FD1772_... | |
124 | * Minor parameter, name layouts for 2.4.x differences | |
125 | */ | |
126 | ||
127 | #include <linux/sched.h> | |
128 | #include <linux/fs.h> | |
129 | #include <linux/fcntl.h> | |
130 | #include <linux/slab.h> | |
131 | #include <linux/kernel.h> | |
132 | #include <linux/interrupt.h> | |
133 | #include <linux/timer.h> | |
134 | #include <linux/workqueue.h> | |
135 | #include <linux/fd.h> | |
136 | #include <linux/fd1772.h> | |
137 | #include <linux/errno.h> | |
138 | #include <linux/types.h> | |
139 | #include <linux/delay.h> | |
140 | #include <linux/mm.h> | |
141 | #include <linux/bitops.h> | |
142 | ||
143 | #include <asm/arch/oldlatches.h> | |
144 | #include <asm/dma.h> | |
145 | #include <asm/hardware.h> | |
146 | #include <asm/hardware/ioc.h> | |
147 | #include <asm/io.h> | |
148 | #include <asm/irq.h> | |
149 | #include <asm/mach-types.h> | |
150 | #include <asm/pgtable.h> | |
151 | #include <asm/system.h> | |
152 | #include <asm/uaccess.h> | |
153 | ||
154 | ||
155 | /* Note: FD_MAX_UNITS could be redefined to 2 for the Atari (with | |
156 | * little additional rework in this file). But I'm not yet sure if | |
157 | * some other code depends on the number of floppies... (It is defined | |
158 | * in a public header!) | |
159 | */ | |
160 | #if 0 | |
161 | #undef FD_MAX_UNITS | |
162 | #define FD_MAX_UNITS 2 | |
163 | #endif | |
164 | ||
165 | /* Ditto worries for Arc - DAG */ | |
166 | #define FD_MAX_UNITS 4 | |
167 | #define TRACKBUFFER 0 | |
168 | /*#define DEBUG*/ | |
169 | ||
170 | #ifdef DEBUG | |
171 | #define DPRINT(a) printk a | |
172 | #else | |
173 | #define DPRINT(a) | |
174 | #endif | |
175 | ||
176 | static struct request_queue *floppy_queue; | |
177 | ||
178 | #define MAJOR_NR FLOPPY_MAJOR | |
179 | #define FLOPPY_DMA 0 | |
180 | #define DEVICE_NAME "floppy" | |
181 | #define QUEUE (floppy_queue) | |
182 | #define CURRENT elv_next_request(floppy_queue) | |
183 | ||
184 | /* Disk types: DD */ | |
185 | static struct archy_disk_type { | |
186 | const char *name; | |
187 | unsigned spt; /* sectors per track */ | |
188 | unsigned blocks; /* total number of blocks */ | |
189 | unsigned stretch; /* track doubling ? */ | |
190 | } disk_type[] = { | |
191 | ||
192 | { "d360", 9, 720, 0 }, /* 360kB diskette */ | |
193 | { "D360", 9, 720, 1 }, /* 360kb in 720kb drive */ | |
194 | { "D720", 9, 1440, 0 }, /* 720kb diskette (DD) */ | |
195 | /*{ "D820", 10,1640, 0}, *//* DD disk with 82 tracks/10 sectors | |
196 | - DAG - can't see how type detect can distinguish this | |
197 | from 720K until it reads block 4 by which time its too late! */ | |
198 | }; | |
199 | ||
200 | #define NUM_DISK_TYPES (sizeof(disk_type)/sizeof(*disk_type)) | |
201 | ||
202 | /* | |
203 | * Maximum disk size (in kilobytes). This default is used whenever the | |
204 | * current disk size is unknown. | |
205 | */ | |
206 | #define MAX_DISK_SIZE 720 | |
207 | ||
208 | static struct gendisk *disks[FD_MAX_UNIT]; | |
209 | ||
210 | /* current info on each unit */ | |
211 | static struct archy_floppy_struct { | |
212 | int connected; /* !=0 : drive is connected */ | |
213 | int autoprobe; /* !=0 : do autoprobe */ | |
214 | ||
215 | struct archy_disk_type *disktype; /* current type of disk */ | |
216 | ||
217 | int track; /* current head position or -1 | |
218 | * if unknown */ | |
219 | unsigned int steprate; /* steprate setting */ | |
220 | unsigned int wpstat; /* current state of WP signal | |
221 | * (for disk change detection) */ | |
222 | } unit[FD_MAX_UNITS]; | |
223 | ||
224 | /* DAG: On Arc we spin on a flag being cleared by fdc1772_comendhandler which | |
225 | is an assembler routine */ | |
226 | extern void fdc1772_comendhandler(void); /* Actually doens't have these parameters - see fd1772.S */ | |
227 | extern volatile int fdc1772_comendstatus; | |
228 | extern volatile int fdc1772_fdc_int_done; | |
229 | ||
230 | #define FDC1772BASE ((0x210000>>2)|0x80000000) | |
231 | ||
232 | #define FDC1772_READ(reg) inb(FDC1772BASE+(reg/2)) | |
233 | ||
234 | /* DAG: You wouldn't be silly to ask why FDC1772_WRITE is a function rather | |
235 | than the #def below - well simple - the #def won't compile - and I | |
236 | don't understand why (__outwc not defined) */ | |
237 | /* NOTE: Reg is 0,2,4,6 as opposed to 0,1,2,3 or 0,4,8,12 to keep compatibility | |
238 | with the ST version of fd1772.h */ | |
239 | /*#define FDC1772_WRITE(reg,val) outw(val,(reg+FDC1772BASE)); */ | |
240 | void FDC1772_WRITE(int reg, unsigned char val) | |
241 | { | |
242 | if (reg == FDC1772REG_CMD) { | |
243 | DPRINT(("FDC1772_WRITE new command 0x%x @ %d\n", val,jiffies)); | |
244 | if (fdc1772_fdc_int_done) { | |
245 | DPRINT(("FDC1772_WRITE: Hmm fdc1772_fdc_int_done true - resetting\n")); | |
246 | fdc1772_fdc_int_done = 0; | |
247 | }; | |
248 | }; | |
249 | outb(val, (reg / 2) + FDC1772BASE); | |
250 | }; | |
251 | ||
252 | #define FD1772_MAX_SECTORS 22 | |
253 | ||
254 | unsigned char *DMABuffer; /* buffer for writes */ | |
255 | /*static unsigned long PhysDMABuffer; *//* physical address */ | |
256 | /* DAG: On Arc we just go straight for the DMA buffer */ | |
257 | #define PhysDMABuffer DMABuffer | |
258 | ||
259 | #ifdef TRACKBUFFER | |
260 | unsigned char *TrackBuffer; /* buffer for reads */ | |
261 | #define PhysTrackBuffer TrackBuffer /* physical address */ | |
262 | static int BufferDrive, BufferSide, BufferTrack; | |
263 | static int read_track; /* non-zero if we are reading whole tracks */ | |
264 | ||
265 | #define SECTOR_BUFFER(sec) (TrackBuffer + ((sec)-1)*512) | |
266 | #define IS_BUFFERED(drive,side,track) \ | |
267 | (BufferDrive == (drive) && BufferSide == (side) && BufferTrack == (track)) | |
268 | #endif | |
269 | ||
270 | /* | |
271 | * These are global variables, as that's the easiest way to give | |
272 | * information to interrupts. They are the data used for the current | |
273 | * request. | |
274 | */ | |
275 | static int SelectedDrive = 0; | |
276 | static int ReqCmd, ReqBlock; | |
277 | static int ReqSide, ReqTrack, ReqSector, ReqCnt; | |
278 | static int HeadSettleFlag = 0; | |
279 | static unsigned char *ReqData, *ReqBuffer; | |
280 | static int MotorOn = 0, MotorOffTrys; | |
281 | ||
282 | /* Synchronization of FDC1772 access. */ | |
283 | static volatile int fdc_busy = 0; | |
284 | static DECLARE_WAIT_QUEUE_HEAD(fdc_wait); | |
285 | ||
286 | ||
287 | /* long req'd for set_bit --RR */ | |
288 | static unsigned long changed_floppies = 0xff, fake_change = 0; | |
289 | #define CHECK_CHANGE_DELAY HZ/2 | |
290 | ||
291 | /* DAG - increased to 30*HZ - not sure if this is the correct thing to do */ | |
292 | #define FD_MOTOR_OFF_DELAY (10*HZ) | |
293 | #define FD_MOTOR_OFF_MAXTRY (10*20) | |
294 | ||
295 | #define FLOPPY_TIMEOUT (6*HZ) | |
296 | #define RECALIBRATE_ERRORS 4 /* After this many errors the drive | |
297 | * will be recalibrated. */ | |
298 | #define MAX_ERRORS 8 /* After this many errors the driver | |
299 | * will give up. */ | |
300 | ||
301 | #define START_MOTOR_OFF_TIMER(delay) \ | |
302 | do { \ | |
303 | motor_off_timer.expires = jiffies + (delay); \ | |
304 | add_timer( &motor_off_timer ); \ | |
305 | MotorOffTrys = 0; \ | |
306 | } while(0) | |
307 | ||
308 | #define START_CHECK_CHANGE_TIMER(delay) \ | |
309 | do { \ | |
310 | mod_timer(&fd_timer, jiffies + (delay)); \ | |
311 | } while(0) | |
312 | ||
313 | #define START_TIMEOUT() \ | |
314 | do { \ | |
315 | mod_timer(&timeout_timer, jiffies+FLOPPY_TIMEOUT); \ | |
316 | } while(0) | |
317 | ||
318 | #define STOP_TIMEOUT() \ | |
319 | do { \ | |
320 | del_timer( &timeout_timer ); \ | |
321 | } while(0) | |
322 | ||
323 | #define ENABLE_IRQ() enable_irq(FIQ_FD1772+64); | |
324 | ||
325 | #define DISABLE_IRQ() disable_irq(FIQ_FD1772+64); | |
326 | ||
327 | static void fd1772_checkint(void); | |
328 | ||
329 | DECLARE_WORK(fd1772_tq, (void *)fd1772_checkint, NULL); | |
330 | /* | |
331 | * The driver is trying to determine the correct media format | |
332 | * while Probing is set. fd_rwsec_done() clears it after a | |
333 | * successful access. | |
334 | */ | |
335 | static int Probing = 0; | |
336 | ||
337 | /* This flag is set when a dummy seek is necessary to make the WP | |
338 | * status bit accessible. | |
339 | */ | |
340 | static int NeedSeek = 0; | |
341 | ||
342 | ||
343 | /***************************** Prototypes *****************************/ | |
344 | ||
345 | static void fd_select_side(int side); | |
346 | static void fd_select_drive(int drive); | |
347 | static void fd_deselect(void); | |
348 | static void fd_motor_off_timer(unsigned long dummy); | |
349 | static void check_change(unsigned long dummy); | |
350 | static void floppy_irqconsequencehandler(void); | |
351 | static void fd_error(void); | |
352 | static void do_fd_action(int drive); | |
353 | static void fd_calibrate(void); | |
354 | static void fd_calibrate_done(int status); | |
355 | static void fd_seek(void); | |
356 | static void fd_seek_done(int status); | |
357 | static void fd_rwsec(void); | |
358 | #ifdef TRACKBUFFER | |
359 | static void fd_readtrack_check( unsigned long dummy ); | |
360 | #endif | |
361 | static void fd_rwsec_done(int status); | |
362 | static void fd_times_out(unsigned long dummy); | |
363 | static void finish_fdc(void); | |
364 | static void finish_fdc_done(int dummy); | |
365 | static void floppy_off(unsigned int nr); | |
366 | static void setup_req_params(int drive); | |
367 | static void redo_fd_request(void); | |
368 | static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int | |
369 | cmd, unsigned long param); | |
370 | static void fd_probe(int drive); | |
371 | static int fd_test_drive_present(int drive); | |
372 | static void config_types(void); | |
373 | static int floppy_open(struct inode *inode, struct file *filp); | |
374 | static int floppy_release(struct inode *inode, struct file *filp); | |
165125e1 | 375 | static void do_fd_request(struct request_queue *); |
1da177e4 LT |
376 | |
377 | /************************* End of Prototypes **************************/ | |
378 | ||
8d06afab | 379 | static DEFINE_TIMER(motor_off_timer, fd_motor_off_timer, 0, 0); |
1da177e4 LT |
380 | |
381 | #ifdef TRACKBUFFER | |
8d06afab | 382 | static DEFINE_TIMER(readtrack_timer, fd_readtrack_check, 0, 0); |
1da177e4 LT |
383 | #endif |
384 | ||
8d06afab | 385 | static DEFINE_TIMER(timeout_timer, fd_times_out, 0, 0); |
1da177e4 | 386 | |
8d06afab | 387 | static DEFINE_TIMER(fd_timer, check_change, 0, 0); |
1da177e4 LT |
388 | |
389 | /* DAG: Haven't got a clue what this is? */ | |
390 | int stdma_islocked(void) | |
391 | { | |
392 | return 0; | |
393 | }; | |
394 | ||
395 | /* Select the side to use. */ | |
396 | ||
397 | static void fd_select_side(int side) | |
398 | { | |
399 | oldlatch_aupdate(LATCHA_SIDESEL, side ? 0 : LATCHA_SIDESEL); | |
400 | } | |
401 | ||
402 | ||
403 | /* Select a drive, update the FDC1772's track register | |
404 | */ | |
405 | ||
406 | static void fd_select_drive(int drive) | |
407 | { | |
408 | #ifdef DEBUG | |
409 | printk("fd_select_drive:%d\n", drive); | |
410 | #endif | |
411 | /* Hmm - nowhere do we seem to turn the motor on - I'm going to do it here! */ | |
412 | oldlatch_aupdate(LATCHA_MOTOR | LATCHA_INUSE, 0); | |
413 | ||
414 | if (drive == SelectedDrive) | |
415 | return; | |
416 | ||
417 | oldlatch_aupdate(LATCHA_FDSELALL, 0xf - (1 << drive)); | |
418 | ||
419 | /* restore track register to saved value */ | |
420 | FDC1772_WRITE(FDC1772REG_TRACK, unit[drive].track); | |
421 | udelay(25); | |
422 | ||
423 | SelectedDrive = drive; | |
424 | } | |
425 | ||
426 | ||
427 | /* Deselect both drives. */ | |
428 | ||
429 | static void fd_deselect(void) | |
430 | { | |
431 | unsigned long flags; | |
432 | ||
433 | DPRINT(("fd_deselect\n")); | |
434 | ||
435 | oldlatch_aupdate(LATCHA_FDSELALL | LATCHA_MOTOR | LATCHA_INUSE, 0xf | LATCHA_MOTOR | LATCHA_INUSE); | |
436 | ||
437 | SelectedDrive = -1; | |
438 | } | |
439 | ||
440 | ||
441 | /* This timer function deselects the drives when the FDC1772 switched the | |
442 | * motor off. The deselection cannot happen earlier because the FDC1772 | |
443 | * counts the index signals, which arrive only if one drive is selected. | |
444 | */ | |
445 | ||
446 | static void fd_motor_off_timer(unsigned long dummy) | |
447 | { | |
448 | unsigned long flags; | |
449 | unsigned char status; | |
450 | int delay; | |
451 | ||
452 | del_timer(&motor_off_timer); | |
453 | ||
454 | if (SelectedDrive < 0) | |
455 | /* no drive selected, needn't deselect anyone */ | |
456 | return; | |
457 | ||
458 | save_flags(flags); | |
459 | cli(); | |
460 | ||
461 | if (fdc_busy) /* was stdma_islocked */ | |
462 | goto retry; | |
463 | ||
464 | status = FDC1772_READ(FDC1772REG_STATUS); | |
465 | ||
466 | if (!(status & 0x80)) { | |
467 | /* | |
468 | * motor already turned off by FDC1772 -> deselect drives | |
469 | * In actual fact its this deselection which turns the motor | |
470 | * off on the Arc, since the motor control is actually on | |
471 | * Latch A | |
472 | */ | |
473 | DPRINT(("fdc1772: deselecting in fd_motor_off_timer\n")); | |
474 | fd_deselect(); | |
475 | MotorOn = 0; | |
476 | restore_flags(flags); | |
477 | return; | |
478 | } | |
479 | /* not yet off, try again */ | |
480 | ||
481 | retry: | |
482 | restore_flags(flags); | |
483 | /* Test again later; if tested too often, it seems there is no disk | |
484 | * in the drive and the FDC1772 will leave the motor on forever (or, | |
485 | * at least until a disk is inserted). So we'll test only twice | |
486 | * per second from then on... | |
487 | */ | |
488 | delay = (MotorOffTrys < FD_MOTOR_OFF_MAXTRY) ? | |
489 | (++MotorOffTrys, HZ / 20) : HZ / 2; | |
490 | START_MOTOR_OFF_TIMER(delay); | |
491 | } | |
492 | ||
493 | ||
494 | /* This function is repeatedly called to detect disk changes (as good | |
495 | * as possible) and keep track of the current state of the write protection. | |
496 | */ | |
497 | ||
498 | static void check_change(unsigned long dummy) | |
499 | { | |
500 | static int drive = 0; | |
501 | ||
502 | unsigned long flags; | |
503 | int stat; | |
504 | ||
505 | if (fdc_busy) | |
506 | return; /* Don't start poking about if the fdc is busy */ | |
507 | ||
508 | return; /* let's just forget it for the mo DAG */ | |
509 | ||
510 | if (++drive > 1 || !unit[drive].connected) | |
511 | drive = 0; | |
512 | ||
513 | save_flags(flags); | |
514 | cli(); | |
515 | ||
516 | if (!stdma_islocked()) { | |
517 | stat = !!(FDC1772_READ(FDC1772REG_STATUS) & FDC1772STAT_WPROT); | |
518 | ||
519 | /* The idea here is that if the write protect line has changed then | |
520 | the disc must have changed */ | |
521 | if (stat != unit[drive].wpstat) { | |
522 | DPRINT(("wpstat[%d] = %d\n", drive, stat)); | |
523 | unit[drive].wpstat = stat; | |
524 | set_bit(drive, &changed_floppies); | |
525 | } | |
526 | } | |
527 | restore_flags(flags); | |
528 | ||
529 | START_CHECK_CHANGE_TIMER(CHECK_CHANGE_DELAY); | |
530 | } | |
531 | ||
532 | ||
533 | /* Handling of the Head Settling Flag: This flag should be set after each | |
534 | * seek operation, because we don't use seeks with verify. | |
535 | */ | |
536 | ||
537 | static inline void set_head_settle_flag(void) | |
538 | { | |
539 | HeadSettleFlag = FDC1772CMDADD_E; | |
540 | } | |
541 | ||
542 | static inline int get_head_settle_flag(void) | |
543 | { | |
544 | int tmp = HeadSettleFlag; | |
545 | HeadSettleFlag = 0; | |
546 | return (tmp); | |
547 | } | |
548 | ||
549 | ||
550 | ||
551 | ||
552 | /* General Interrupt Handling */ | |
553 | ||
554 | static inline void copy_buffer(void *from, void *to) | |
555 | { | |
556 | ulong *p1 = (ulong *) from, *p2 = (ulong *) to; | |
557 | int cnt; | |
558 | ||
559 | for (cnt = 512 / 4; cnt; cnt--) | |
560 | *p2++ = *p1++; | |
561 | } | |
562 | ||
563 | static void (*FloppyIRQHandler) (int status) = NULL; | |
564 | ||
565 | static void floppy_irqconsequencehandler(void) | |
566 | { | |
567 | unsigned char status; | |
568 | void (*handler) (int); | |
569 | ||
570 | fdc1772_fdc_int_done = 0; | |
571 | ||
572 | handler = FloppyIRQHandler; | |
573 | FloppyIRQHandler = NULL; | |
574 | ||
575 | if (handler) { | |
576 | nop(); | |
577 | status = (unsigned char) fdc1772_comendstatus; | |
578 | DPRINT(("FDC1772 irq, status = %02x handler = %08lx\n", (unsigned int) status, (unsigned long) handler)); | |
579 | handler(status); | |
580 | } else { | |
581 | DPRINT(("FDC1772 irq, no handler status=%02x\n", fdc1772_comendstatus)); | |
582 | } | |
583 | DPRINT(("FDC1772 irq: end of floppy_irq\n")); | |
584 | } | |
585 | ||
586 | ||
587 | /* Error handling: If some error happened, retry some times, then | |
588 | * recalibrate, then try again, and fail after MAX_ERRORS. | |
589 | */ | |
590 | ||
591 | static void fd_error(void) | |
592 | { | |
593 | printk("FDC1772: fd_error\n"); | |
594 | /*panic("fd1772: fd_error"); *//* DAG tmp */ | |
595 | if (!CURRENT) | |
596 | return; | |
597 | CURRENT->errors++; | |
598 | if (CURRENT->errors >= MAX_ERRORS) { | |
599 | printk("fd%d: too many errors.\n", SelectedDrive); | |
600 | end_request(CURRENT, 0); | |
601 | } else if (CURRENT->errors == RECALIBRATE_ERRORS) { | |
602 | printk("fd%d: recalibrating\n", SelectedDrive); | |
603 | if (SelectedDrive != -1) | |
604 | unit[SelectedDrive].track = -1; | |
605 | } | |
606 | redo_fd_request(); | |
607 | } | |
608 | ||
609 | ||
610 | ||
611 | #define SET_IRQ_HANDLER(proc) do { FloppyIRQHandler = (proc); } while(0) | |
612 | ||
613 | ||
614 | /* do_fd_action() is the general procedure for a fd request: All | |
615 | * required parameter settings (drive select, side select, track | |
616 | * position) are checked and set if needed. For each of these | |
617 | * parameters and the actual reading or writing exist two functions: | |
618 | * one that starts the setting (or skips it if possible) and one | |
619 | * callback for the "done" interrupt. Each done func calls the next | |
620 | * set function to propagate the request down to fd_rwsec_done(). | |
621 | */ | |
622 | ||
623 | static void do_fd_action(int drive) | |
624 | { | |
625 | struct request *req; | |
626 | DPRINT(("do_fd_action unit[drive].track=%d\n", unit[drive].track)); | |
627 | ||
628 | #ifdef TRACKBUFFER | |
629 | repeat: | |
630 | ||
631 | if (IS_BUFFERED( drive, ReqSide, ReqTrack )) { | |
632 | req = CURRENT; | |
633 | if (ReqCmd == READ) { | |
634 | copy_buffer( SECTOR_BUFFER(ReqSector), ReqData ); | |
635 | if (++ReqCnt < req->current_nr_sectors) { | |
636 | /* read next sector */ | |
637 | setup_req_params( drive ); | |
638 | goto repeat; | |
639 | } else { | |
640 | /* all sectors finished */ | |
641 | req->nr_sectors -= req->current_nr_sectors; | |
642 | req->sector += req->current_nr_sectors; | |
643 | end_request(req, 1); | |
644 | redo_fd_request(); | |
645 | return; | |
646 | } | |
647 | } else { | |
648 | /* cmd == WRITE, pay attention to track buffer | |
649 | * consistency! */ | |
650 | copy_buffer( ReqData, SECTOR_BUFFER(ReqSector) ); | |
651 | } | |
652 | } | |
653 | #endif | |
654 | ||
655 | if (SelectedDrive != drive) { | |
656 | /*unit[drive].track = -1; DAG */ | |
657 | fd_select_drive(drive); | |
658 | }; | |
659 | ||
660 | ||
661 | if (unit[drive].track == -1) | |
662 | fd_calibrate(); | |
663 | else if (unit[drive].track != ReqTrack << unit[drive].disktype->stretch) | |
664 | fd_seek(); | |
665 | else | |
666 | fd_rwsec(); | |
667 | } | |
668 | ||
669 | ||
670 | /* Seek to track 0 if the current track is unknown */ | |
671 | ||
672 | static void fd_calibrate(void) | |
673 | { | |
674 | DPRINT(("fd_calibrate\n")); | |
675 | if (unit[SelectedDrive].track >= 0) { | |
676 | fd_calibrate_done(0); | |
677 | return; | |
678 | } | |
679 | DPRINT(("fd_calibrate (after track compare)\n")); | |
680 | SET_IRQ_HANDLER(fd_calibrate_done); | |
681 | /* we can't verify, since the speed may be incorrect */ | |
682 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_RESTORE | unit[SelectedDrive].steprate); | |
683 | ||
684 | NeedSeek = 1; | |
685 | MotorOn = 1; | |
686 | START_TIMEOUT(); | |
687 | /* wait for IRQ */ | |
688 | } | |
689 | ||
690 | ||
691 | static void fd_calibrate_done(int status) | |
692 | { | |
693 | DPRINT(("fd_calibrate_done()\n")); | |
694 | STOP_TIMEOUT(); | |
695 | ||
696 | /* set the correct speed now */ | |
697 | if (status & FDC1772STAT_RECNF) { | |
698 | printk("fd%d: restore failed\n", SelectedDrive); | |
699 | fd_error(); | |
700 | } else { | |
701 | unit[SelectedDrive].track = 0; | |
702 | fd_seek(); | |
703 | } | |
704 | } | |
705 | ||
706 | ||
707 | /* Seek the drive to the requested track. The drive must have been | |
708 | * calibrated at some point before this. | |
709 | */ | |
710 | ||
711 | static void fd_seek(void) | |
712 | { | |
713 | unsigned long flags; | |
714 | DPRINT(("fd_seek() to track %d (unit[SelectedDrive].track=%d)\n", ReqTrack, | |
715 | unit[SelectedDrive].track)); | |
716 | if (unit[SelectedDrive].track == ReqTrack << | |
717 | unit[SelectedDrive].disktype->stretch) { | |
718 | fd_seek_done(0); | |
719 | return; | |
720 | } | |
721 | FDC1772_WRITE(FDC1772REG_DATA, ReqTrack << | |
722 | unit[SelectedDrive].disktype->stretch); | |
723 | udelay(25); | |
724 | save_flags(flags); | |
725 | clf(); | |
726 | SET_IRQ_HANDLER(fd_seek_done); | |
727 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK | unit[SelectedDrive].steprate | | |
728 | /* DAG */ | |
729 | (MotorOn?FDC1772CMDADD_H:0)); | |
730 | ||
731 | restore_flags(flags); | |
732 | MotorOn = 1; | |
733 | set_head_settle_flag(); | |
734 | START_TIMEOUT(); | |
735 | /* wait for IRQ */ | |
736 | } | |
737 | ||
738 | ||
739 | static void fd_seek_done(int status) | |
740 | { | |
741 | DPRINT(("fd_seek_done()\n")); | |
742 | STOP_TIMEOUT(); | |
743 | ||
744 | /* set the correct speed */ | |
745 | if (status & FDC1772STAT_RECNF) { | |
746 | printk("fd%d: seek error (to track %d)\n", | |
747 | SelectedDrive, ReqTrack); | |
748 | /* we don't know exactly which track we are on now! */ | |
749 | unit[SelectedDrive].track = -1; | |
750 | fd_error(); | |
751 | } else { | |
752 | unit[SelectedDrive].track = ReqTrack << | |
753 | unit[SelectedDrive].disktype->stretch; | |
754 | NeedSeek = 0; | |
755 | fd_rwsec(); | |
756 | } | |
757 | } | |
758 | ||
759 | ||
760 | /* This does the actual reading/writing after positioning the head | |
761 | * over the correct track. | |
762 | */ | |
763 | ||
764 | #ifdef TRACKBUFFER | |
765 | static int MultReadInProgress = 0; | |
766 | #endif | |
767 | ||
768 | ||
769 | static void fd_rwsec(void) | |
770 | { | |
771 | unsigned long paddr, flags; | |
772 | unsigned int rwflag, old_motoron; | |
773 | unsigned int track; | |
774 | ||
775 | DPRINT(("fd_rwsec(), Sec=%d, Access=%c\n", ReqSector, ReqCmd == WRITE ? 'w' : 'r')); | |
776 | if (ReqCmd == WRITE) { | |
777 | /*cache_push( (unsigned long)ReqData, 512 ); */ | |
778 | paddr = (unsigned long) ReqData; | |
779 | rwflag = 0x100; | |
780 | } else { | |
781 | paddr = (unsigned long) PhysDMABuffer; | |
782 | #ifdef TRACKBUFFER | |
783 | if (read_track) | |
784 | paddr = (unsigned long)PhysTrackBuffer; | |
785 | #endif | |
786 | rwflag = 0; | |
787 | } | |
788 | ||
789 | DPRINT(("fd_rwsec() before sidesel rwflag=%d sec=%d trk=%d\n", rwflag, | |
790 | ReqSector, FDC1772_READ(FDC1772REG_TRACK))); | |
791 | fd_select_side(ReqSide); | |
792 | ||
793 | /*DPRINT(("fd_rwsec() before start sector \n")); */ | |
794 | /* Start sector of this operation */ | |
795 | #ifdef TRACKBUFFER | |
796 | FDC1772_WRITE( FDC1772REG_SECTOR, !read_track ? ReqSector : 1 ); | |
797 | #else | |
798 | FDC1772_WRITE( FDC1772REG_SECTOR, ReqSector ); | |
799 | #endif | |
800 | ||
801 | /* Cheat for track if stretch != 0 */ | |
802 | if (unit[SelectedDrive].disktype->stretch) { | |
803 | track = FDC1772_READ(FDC1772REG_TRACK); | |
804 | FDC1772_WRITE(FDC1772REG_TRACK, track >> | |
805 | unit[SelectedDrive].disktype->stretch); | |
806 | } | |
807 | udelay(25); | |
808 | ||
809 | DPRINT(("fd_rwsec() before setup DMA \n")); | |
810 | /* Setup DMA - Heavily modified by DAG */ | |
811 | save_flags(flags); | |
812 | clf(); | |
813 | disable_dma(FLOPPY_DMA); | |
814 | set_dma_mode(FLOPPY_DMA, rwflag ? DMA_MODE_WRITE : DMA_MODE_READ); | |
815 | set_dma_addr(FLOPPY_DMA, (long) paddr); /* DAG - changed from Atari specific */ | |
816 | #ifdef TRACKBUFFER | |
817 | set_dma_count(FLOPPY_DMA,(!read_track ? 1 : unit[SelectedDrive].disktype->spt)*512); | |
818 | #else | |
819 | set_dma_count(FLOPPY_DMA, 512); /* Block/sector size - going to have to change */ | |
820 | #endif | |
821 | SET_IRQ_HANDLER(fd_rwsec_done); | |
822 | /* Turn on dma int */ | |
823 | enable_dma(FLOPPY_DMA); | |
824 | /* Now give it something to do */ | |
825 | FDC1772_WRITE(FDC1772REG_CMD, (rwflag ? (FDC1772CMD_WRSEC | FDC1772CMDADD_P) : | |
826 | #ifdef TRACKBUFFER | |
827 | (FDC1772CMD_RDSEC | (read_track ? FDC1772CMDADD_M : 0) | | |
828 | /* Hmm - the idea here is to stop the FDC spinning the disc | |
829 | up when we know that we already still have it spinning */ | |
830 | (MotorOn?FDC1772CMDADD_H:0)) | |
831 | #else | |
832 | FDC1772CMD_RDSEC | |
833 | #endif | |
834 | )); | |
835 | ||
836 | restore_flags(flags); | |
837 | DPRINT(("fd_rwsec() after DMA setup flags=0x%08x\n", flags)); | |
838 | /*sti(); *//* DAG - Hmm */ | |
839 | /* Hmm - should do something DAG */ | |
840 | old_motoron = MotorOn; | |
841 | MotorOn = 1; | |
842 | NeedSeek = 1; | |
843 | ||
844 | /* wait for interrupt */ | |
845 | ||
846 | #ifdef TRACKBUFFER | |
847 | if (read_track) { | |
848 | /* | |
849 | * If reading a whole track, wait about one disk rotation and | |
850 | * then check if all sectors are read. The FDC will even | |
851 | * search for the first non-existant sector and need 1 sec to | |
852 | * recognise that it isn't present :-( | |
853 | */ | |
854 | /* 1 rot. + 5 rot.s if motor was off */ | |
855 | mod_timer(&readtrack_timer, jiffies + HZ/5 + (old_motoron ? 0 : HZ)); | |
856 | DPRINT(("Setting readtrack_timer to %d @ %d\n", | |
857 | readtrack_timer.expires,jiffies)); | |
858 | MultReadInProgress = 1; | |
859 | } | |
860 | #endif | |
861 | ||
862 | /*DPRINT(("fd_rwsec() before START_TIMEOUT \n")); */ | |
863 | START_TIMEOUT(); | |
864 | /*DPRINT(("fd_rwsec() after START_TIMEOUT \n")); */ | |
865 | } | |
866 | ||
867 | ||
868 | #ifdef TRACKBUFFER | |
869 | ||
870 | static void fd_readtrack_check(unsigned long dummy) | |
871 | { | |
872 | unsigned long flags, addr; | |
873 | extern unsigned char *fdc1772_dataaddr; | |
874 | ||
875 | DPRINT(("fd_readtrack_check @ %d\n",jiffies)); | |
876 | ||
877 | save_flags(flags); | |
878 | clf(); | |
879 | ||
880 | del_timer( &readtrack_timer ); | |
881 | ||
882 | if (!MultReadInProgress) { | |
883 | /* This prevents a race condition that could arise if the | |
884 | * interrupt is triggered while the calling of this timer | |
885 | * callback function takes place. The IRQ function then has | |
886 | * already cleared 'MultReadInProgress' when control flow | |
887 | * gets here. | |
888 | */ | |
889 | restore_flags(flags); | |
890 | return; | |
891 | } | |
892 | ||
893 | /* get the current DMA address */ | |
894 | addr=(unsigned long)fdc1772_dataaddr; /* DAG - ? */ | |
895 | DPRINT(("fd_readtrack_check: addr=%x PhysTrackBuffer=%x\n",addr,PhysTrackBuffer)); | |
896 | ||
897 | if (addr >= (unsigned int)PhysTrackBuffer + unit[SelectedDrive].disktype->spt*512) { | |
898 | /* already read enough data, force an FDC interrupt to stop | |
899 | * the read operation | |
900 | */ | |
901 | SET_IRQ_HANDLER( NULL ); | |
902 | restore_flags(flags); | |
903 | DPRINT(("fd_readtrack_check(): done\n")); | |
904 | FDC1772_WRITE( FDC1772REG_CMD, FDC1772CMD_FORCI ); | |
905 | udelay(25); | |
906 | ||
907 | /* No error until now -- the FDC would have interrupted | |
908 | * otherwise! | |
909 | */ | |
910 | fd_rwsec_done( 0 ); | |
911 | } else { | |
912 | /* not yet finished, wait another tenth rotation */ | |
913 | restore_flags(flags); | |
914 | DPRINT(("fd_readtrack_check(): not yet finished\n")); | |
915 | readtrack_timer.expires = jiffies + HZ/5/10; | |
916 | add_timer( &readtrack_timer ); | |
917 | } | |
918 | } | |
919 | ||
920 | #endif | |
921 | ||
922 | static void fd_rwsec_done(int status) | |
923 | { | |
924 | unsigned int track; | |
925 | ||
926 | DPRINT(("fd_rwsec_done() status=%d @ %d\n", status,jiffies)); | |
927 | ||
928 | #ifdef TRACKBUFFER | |
929 | if (read_track && !MultReadInProgress) | |
930 | return; | |
931 | ||
932 | MultReadInProgress = 0; | |
933 | ||
934 | STOP_TIMEOUT(); | |
935 | ||
936 | if (read_track) | |
937 | del_timer( &readtrack_timer ); | |
938 | #endif | |
939 | ||
940 | ||
941 | /* Correct the track if stretch != 0 */ | |
942 | if (unit[SelectedDrive].disktype->stretch) { | |
943 | track = FDC1772_READ(FDC1772REG_TRACK); | |
944 | FDC1772_WRITE(FDC1772REG_TRACK, track << | |
945 | unit[SelectedDrive].disktype->stretch); | |
946 | } | |
947 | if (ReqCmd == WRITE && (status & FDC1772STAT_WPROT)) { | |
948 | printk("fd%d: is write protected\n", SelectedDrive); | |
949 | goto err_end; | |
950 | } | |
951 | if ((status & FDC1772STAT_RECNF) | |
952 | #ifdef TRACKBUFFER | |
953 | /* RECNF is no error after a multiple read when the FDC | |
954 | * searched for a non-existant sector! | |
955 | */ | |
956 | && !(read_track && | |
957 | FDC1772_READ(FDC1772REG_SECTOR) > unit[SelectedDrive].disktype->spt) | |
958 | #endif | |
959 | ) { | |
960 | if (Probing) { | |
961 | if (unit[SelectedDrive].disktype > disk_type) { | |
962 | /* try another disk type */ | |
963 | unit[SelectedDrive].disktype--; | |
964 | set_capacity(disks[SelectedDrive], | |
965 | unit[SelectedDrive].disktype->blocks); | |
966 | } else | |
967 | Probing = 0; | |
968 | } else { | |
969 | /* record not found, but not probing. Maybe stretch wrong ? Restart probing */ | |
970 | if (unit[SelectedDrive].autoprobe) { | |
971 | unit[SelectedDrive].disktype = disk_type + NUM_DISK_TYPES - 1; | |
972 | set_capacity(disks[SelectedDrive], | |
973 | unit[SelectedDrive].disktype->blocks); | |
974 | Probing = 1; | |
975 | } | |
976 | } | |
977 | if (Probing) { | |
978 | setup_req_params(SelectedDrive); | |
979 | #ifdef TRACKBUFFER | |
980 | BufferDrive = -1; | |
981 | #endif | |
982 | do_fd_action(SelectedDrive); | |
983 | return; | |
984 | } | |
985 | printk("fd%d: sector %d not found (side %d, track %d)\n", | |
986 | SelectedDrive, FDC1772_READ(FDC1772REG_SECTOR), ReqSide, ReqTrack); | |
987 | goto err_end; | |
988 | } | |
989 | if (status & FDC1772STAT_CRC) { | |
990 | printk("fd%d: CRC error (side %d, track %d, sector %d)\n", | |
991 | SelectedDrive, ReqSide, ReqTrack, FDC1772_READ(FDC1772REG_SECTOR)); | |
992 | goto err_end; | |
993 | } | |
994 | if (status & FDC1772STAT_LOST) { | |
995 | printk("fd%d: lost data (side %d, track %d, sector %d)\n", | |
996 | SelectedDrive, ReqSide, ReqTrack, FDC1772_READ(FDC1772REG_SECTOR)); | |
997 | goto err_end; | |
998 | } | |
999 | Probing = 0; | |
1000 | ||
1001 | if (ReqCmd == READ) { | |
1002 | #ifdef TRACKBUFFER | |
1003 | if (!read_track) { | |
1004 | /*cache_clear (PhysDMABuffer, 512);*/ | |
1005 | copy_buffer (DMABuffer, ReqData); | |
1006 | } else { | |
1007 | /*cache_clear (PhysTrackBuffer, FD1772_MAX_SECTORS * 512);*/ | |
1008 | BufferDrive = SelectedDrive; | |
1009 | BufferSide = ReqSide; | |
1010 | BufferTrack = ReqTrack; | |
1011 | copy_buffer (SECTOR_BUFFER (ReqSector), ReqData); | |
1012 | } | |
1013 | #else | |
1014 | /*cache_clear( PhysDMABuffer, 512 ); */ | |
1015 | copy_buffer(DMABuffer, ReqData); | |
1016 | #endif | |
1017 | } | |
1018 | if (++ReqCnt < CURRENT->current_nr_sectors) { | |
1019 | /* read next sector */ | |
1020 | setup_req_params(SelectedDrive); | |
1021 | do_fd_action(SelectedDrive); | |
1022 | } else { | |
1023 | /* all sectors finished */ | |
1024 | CURRENT->nr_sectors -= CURRENT->current_nr_sectors; | |
1025 | CURRENT->sector += CURRENT->current_nr_sectors; | |
1026 | end_request(CURRENT, 1); | |
1027 | redo_fd_request(); | |
1028 | } | |
1029 | return; | |
1030 | ||
1031 | err_end: | |
1032 | #ifdef TRACKBUFFER | |
1033 | BufferDrive = -1; | |
1034 | #endif | |
1035 | ||
1036 | fd_error(); | |
1037 | } | |
1038 | ||
1039 | ||
1040 | static void fd_times_out(unsigned long dummy) | |
1041 | { | |
1042 | SET_IRQ_HANDLER(NULL); | |
1043 | /* If the timeout occurred while the readtrack_check timer was | |
1044 | * active, we need to cancel it, else bad things will happen */ | |
1045 | del_timer( &readtrack_timer ); | |
1046 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI); | |
1047 | udelay(25); | |
1048 | ||
1049 | printk("floppy timeout\n"); | |
1050 | STOP_TIMEOUT(); /* hmm - should we do this ? */ | |
1051 | fd_error(); | |
1052 | } | |
1053 | ||
1054 | ||
1055 | /* The (noop) seek operation here is needed to make the WP bit in the | |
1056 | * FDC1772 status register accessible for check_change. If the last disk | |
1057 | * operation would have been a RDSEC, this bit would always read as 0 | |
1058 | * no matter what :-( To save time, the seek goes to the track we're | |
1059 | * already on. | |
1060 | */ | |
1061 | ||
1062 | static void finish_fdc(void) | |
1063 | { | |
1064 | /* DAG - just try without this dummy seek! */ | |
1065 | finish_fdc_done(0); | |
1066 | return; | |
1067 | ||
1068 | if (!NeedSeek) { | |
1069 | finish_fdc_done(0); | |
1070 | } else { | |
1071 | DPRINT(("finish_fdc: dummy seek started\n")); | |
1072 | FDC1772_WRITE(FDC1772REG_DATA, unit[SelectedDrive].track); | |
1073 | SET_IRQ_HANDLER(finish_fdc_done); | |
1074 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK); | |
1075 | MotorOn = 1; | |
1076 | START_TIMEOUT(); | |
1077 | /* we must wait for the IRQ here, because the ST-DMA is | |
1078 | * released immediately afterwards and the interrupt may be | |
1079 | * delivered to the wrong driver. | |
1080 | */ | |
1081 | } | |
1082 | } | |
1083 | ||
1084 | ||
1085 | static void finish_fdc_done(int dummy) | |
1086 | { | |
1087 | unsigned long flags; | |
1088 | ||
1089 | DPRINT(("finish_fdc_done entered\n")); | |
1090 | STOP_TIMEOUT(); | |
1091 | NeedSeek = 0; | |
1092 | ||
1093 | if (timer_pending(&fd_timer) && | |
1094 | time_after(jiffies + 5, fd_timer.expires)) | |
1095 | /* If the check for a disk change is done too early after this | |
1096 | * last seek command, the WP bit still reads wrong :-(( | |
1097 | */ | |
1098 | mod_timer(&fd_timer, jiffies + 5); | |
1099 | else { | |
1100 | /* START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY ); */ | |
1101 | }; | |
1102 | del_timer(&motor_off_timer); | |
1103 | START_MOTOR_OFF_TIMER(FD_MOTOR_OFF_DELAY); | |
1104 | ||
1105 | save_flags(flags); | |
1106 | cli(); | |
1107 | /* stdma_release(); - not sure if I should do something DAG */ | |
1108 | fdc_busy = 0; | |
1109 | wake_up(&fdc_wait); | |
1110 | restore_flags(flags); | |
1111 | ||
1112 | DPRINT(("finish_fdc() finished\n")); | |
1113 | } | |
1114 | ||
1115 | ||
1116 | /* Prevent "aliased" accesses. */ | |
1117 | static int fd_ref[4]; | |
1118 | static int fd_device[4]; | |
1119 | ||
1120 | /* dummy for blk.h */ | |
1121 | static void floppy_off(unsigned int nr) | |
1122 | { | |
1123 | } | |
1124 | ||
1125 | ||
1126 | /* On the old arcs write protect depends on the particular model | |
1127 | of machine. On the A310, R140, and A440 there is a disc changed | |
1128 | detect, however on the A4x0/1 range there is not. There | |
1129 | is nothing to tell you which machine your on. | |
1130 | At the moment I'm just marking changed always. I've | |
1131 | left the Atari's 'change on write protect change' code in this | |
1132 | part (but nothing sets it). | |
1133 | RiscOS apparently checks the disc serial number etc. to detect changes | |
1134 | - but if it sees a disc change line go high (?) it flips to using | |
1135 | it. Well maybe I'll add that in the future (!?) | |
1136 | */ | |
1137 | static int check_floppy_change(struct gendisk *disk) | |
1138 | { | |
1139 | struct archy_floppy_struct *p = disk->private_data; | |
1140 | unsigned int drive = p - unit; | |
1141 | ||
1142 | if (test_bit(drive, &fake_change)) { | |
1143 | /* simulated change (e.g. after formatting) */ | |
1144 | return 1; | |
1145 | } | |
1146 | if (test_bit(drive, &changed_floppies)) { | |
1147 | /* surely changed (the WP signal changed at least once) */ | |
1148 | return 1; | |
1149 | } | |
1150 | if (p->wpstat) { | |
1151 | /* WP is on -> could be changed: to be sure, buffers should be | |
1152 | * invalidated... | |
1153 | */ | |
1154 | return 1; | |
1155 | } | |
1156 | return 1; /* DAG - was 0 */ | |
1157 | } | |
1158 | ||
1159 | static int floppy_revalidate(struct gendisk *disk) | |
1160 | { | |
1161 | struct archy_floppy_struct *p = disk->private_data; | |
1162 | unsigned int drive = p - unit; | |
1163 | ||
1164 | if (test_bit(drive, &changed_floppies) || test_bit(drive, &fake_change) | |
1165 | || unit[drive].disktype == 0) { | |
1166 | #ifdef TRACKBUFFER | |
1167 | BufferDrive = -1; | |
1168 | #endif | |
1169 | clear_bit(drive, &fake_change); | |
1170 | clear_bit(drive, &changed_floppies); | |
1171 | p->disktype = 0; | |
1172 | } | |
1173 | return 0; | |
1174 | } | |
1175 | ||
1176 | /* This sets up the global variables describing the current request. */ | |
1177 | ||
1178 | static void setup_req_params(int drive) | |
1179 | { | |
1180 | int block = ReqBlock + ReqCnt; | |
1181 | ||
1182 | ReqTrack = block / unit[drive].disktype->spt; | |
1183 | ReqSector = block - ReqTrack * unit[drive].disktype->spt + 1; | |
1184 | ReqSide = ReqTrack & 1; | |
1185 | ReqTrack >>= 1; | |
1186 | ReqData = ReqBuffer + 512 * ReqCnt; | |
1187 | ||
1188 | #ifdef TRACKBUFFER | |
1189 | read_track = (ReqCmd == READ && CURRENT->errors == 0); | |
1190 | #endif | |
1191 | ||
1192 | DPRINT(("Request params: Si=%d Tr=%d Se=%d Data=%08lx\n", ReqSide, | |
1193 | ReqTrack, ReqSector, (unsigned long) ReqData)); | |
1194 | } | |
1195 | ||
1196 | ||
1197 | static void redo_fd_request(void) | |
1198 | { | |
1199 | int drive, type; | |
1200 | struct archy_floppy_struct *floppy; | |
1201 | ||
1202 | DPRINT(("redo_fd_request: CURRENT=%p dev=%s CURRENT->sector=%ld\n", | |
1203 | CURRENT, CURRENT ? CURRENT->rq_disk->disk_name : "", | |
1204 | CURRENT ? CURRENT->sector : 0)); | |
1205 | ||
1206 | repeat: | |
1207 | ||
1208 | if (!CURRENT) | |
1209 | goto the_end; | |
1210 | ||
1211 | floppy = CURRENT->rq_disk->private_data; | |
1212 | drive = floppy - unit; | |
1213 | type = fd_device[drive]; | |
1214 | ||
1215 | if (!floppy->connected) { | |
1216 | /* drive not connected */ | |
1217 | printk("Unknown Device: fd%d\n", drive); | |
1218 | end_request(CURRENT, 0); | |
1219 | goto repeat; | |
1220 | } | |
1221 | if (type == 0) { | |
1222 | if (!floppy->disktype) { | |
1223 | Probing = 1; | |
1224 | floppy->disktype = disk_type + NUM_DISK_TYPES - 1; | |
1225 | set_capacity(disks[drive], floppy->disktype->blocks); | |
1226 | floppy->autoprobe = 1; | |
1227 | } | |
1228 | } else { | |
1229 | /* user supplied disk type */ | |
1230 | --type; | |
1231 | if (type >= NUM_DISK_TYPES) { | |
1232 | printk("fd%d: invalid disk format", drive); | |
1233 | end_request(CURRENT, 0); | |
1234 | goto repeat; | |
1235 | } | |
1236 | floppy->disktype = &disk_type[type]; | |
1237 | set_capacity(disks[drive], floppy->disktype->blocks); | |
1238 | floppy->autoprobe = 0; | |
1239 | } | |
1240 | ||
1241 | if (CURRENT->sector + 1 > floppy->disktype->blocks) { | |
1242 | end_request(CURRENT, 0); | |
1243 | goto repeat; | |
1244 | } | |
1245 | /* stop deselect timer */ | |
1246 | del_timer(&motor_off_timer); | |
1247 | ||
1248 | ReqCnt = 0; | |
e654bc43 | 1249 | ReqCmd = rq_data_dir(CURRENT); |
1da177e4 LT |
1250 | ReqBlock = CURRENT->sector; |
1251 | ReqBuffer = CURRENT->buffer; | |
1252 | setup_req_params(drive); | |
1253 | do_fd_action(drive); | |
1254 | ||
1255 | return; | |
1256 | ||
1257 | the_end: | |
1258 | finish_fdc(); | |
1259 | } | |
1260 | ||
1261 | static void fd1772_checkint(void) | |
1262 | { | |
1263 | extern int fdc1772_bytestogo; | |
1264 | ||
1265 | /*printk("fd1772_checkint %d\n",fdc1772_fdc_int_done);*/ | |
1266 | if (fdc1772_fdc_int_done) | |
1267 | floppy_irqconsequencehandler(); | |
1268 | if ((MultReadInProgress) && (fdc1772_bytestogo==0)) fd_readtrack_check(0); | |
1269 | if (fdc_busy) { | |
1270 | schedule_work(&fd1772_tq); | |
1271 | } | |
1272 | } | |
1273 | ||
165125e1 | 1274 | static void do_fd_request(struct request_queue* q) |
1da177e4 LT |
1275 | { |
1276 | unsigned long flags; | |
1277 | ||
1278 | DPRINT(("do_fd_request for pid %d\n", current->pid)); | |
1279 | if (fdc_busy) return; | |
1280 | save_flags(flags); | |
1281 | cli(); | |
71abe999 | 1282 | wait_event(fdc_wait, !fdc_busy); |
1da177e4 LT |
1283 | fdc_busy = 1; |
1284 | ENABLE_IRQ(); | |
1285 | restore_flags(flags); | |
1286 | ||
1287 | fdc1772_fdc_int_done = 0; | |
1288 | ||
1289 | redo_fd_request(); | |
1290 | ||
1291 | schedule_work(&fd1772_tq); | |
1292 | } | |
1293 | ||
1294 | ||
1295 | static int invalidate_drive(struct block_device *bdev) | |
1296 | { | |
1297 | struct archy_floppy_struct *p = bdev->bd_disk->private_data; | |
1298 | /* invalidate the buffer track to force a reread */ | |
1299 | #ifdef TRACKBUFFER | |
1300 | BufferDrive = -1; | |
1301 | #endif | |
1302 | ||
1303 | set_bit(p - unit, &fake_change); | |
1304 | return 0; | |
1305 | } | |
1306 | ||
1307 | static int fd_ioctl(struct inode *inode, struct file *filp, | |
1308 | unsigned int cmd, unsigned long param) | |
1309 | { | |
1310 | struct block_device *bdev = inode->i_bdev; | |
1311 | ||
1312 | switch (cmd) { | |
1313 | case FDFMTEND: | |
1314 | case FDFLUSH: | |
1315 | invalidate_drive(bdev); | |
1316 | check_disk_change(bdev); | |
1317 | case FDFMTBEG: | |
1318 | return 0; | |
1319 | default: | |
1320 | return -EINVAL; | |
1321 | } | |
1322 | } | |
1323 | ||
1324 | ||
1325 | /* Initialize the 'unit' variable for drive 'drive' */ | |
1326 | ||
1327 | static void fd_probe(int drive) | |
1328 | { | |
1329 | unit[drive].connected = 0; | |
1330 | unit[drive].disktype = NULL; | |
1331 | ||
1332 | if (!fd_test_drive_present(drive)) | |
1333 | return; | |
1334 | ||
1335 | unit[drive].connected = 1; | |
1336 | unit[drive].track = -1; /* If we put the auto detect back in this can go to 0 */ | |
1337 | unit[drive].steprate = FDC1772STEP_6; | |
1338 | MotorOn = 1; /* from probe restore operation! */ | |
1339 | } | |
1340 | ||
1341 | ||
1342 | /* This function tests the physical presence of a floppy drive (not | |
1343 | * whether a disk is inserted). This is done by issuing a restore | |
1344 | * command, waiting max. 2 seconds (that should be enough to move the | |
1345 | * head across the whole disk) and looking at the state of the "TR00" | |
1346 | * signal. This should now be raised if there is a drive connected | |
1347 | * (and there is no hardware failure :-) Otherwise, the drive is | |
1348 | * declared absent. | |
1349 | */ | |
1350 | ||
1351 | static int fd_test_drive_present(int drive) | |
1352 | { | |
1353 | unsigned long timeout; | |
1354 | unsigned char status; | |
1355 | int ok; | |
1356 | ||
1357 | printk("fd_test_drive_present %d\n", drive); | |
1358 | if (drive > 1) | |
1359 | return (0); | |
1360 | return (1); /* Simple hack for the moment - the autodetect doesn't seem to work on arc */ | |
1361 | fd_select_drive(drive); | |
1362 | ||
1363 | /* disable interrupt temporarily */ | |
1364 | DISABLE_IRQ(); | |
1365 | FDC1772_WRITE(FDC1772REG_TRACK, 0x00); /* was ff00 why? */ | |
1366 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_RESTORE | FDC1772CMDADD_H | FDC1772STEP_6); | |
1367 | ||
1368 | /*printk("fd_test_drive_present: Going into timeout loop\n"); */ | |
1369 | for (ok = 0, timeout = jiffies + 2 * HZ + HZ / 2; time_before(jiffies, timeout);) { | |
1370 | /* What does this piece of atariism do? - query for an interrupt? */ | |
1371 | /* if (!(mfp.par_dt_reg & 0x20)) | |
1372 | break; */ | |
1373 | /* Well this is my nearest guess - quit when we get an FDC interrupt */ | |
1374 | if (ioc_readb(IOC_FIQSTAT) & 2) | |
1375 | break; | |
1376 | } | |
1377 | ||
1378 | /*printk("fd_test_drive_present: Coming out of timeout loop\n"); */ | |
1379 | status = FDC1772_READ(FDC1772REG_STATUS); | |
1380 | ok = (status & FDC1772STAT_TR00) != 0; | |
1381 | ||
1382 | /*printk("fd_test_drive_present: ok=%d\n",ok); */ | |
1383 | /* force interrupt to abort restore operation (FDC1772 would try | |
1384 | * about 50 seconds!) */ | |
1385 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI); | |
1386 | udelay(500); | |
1387 | status = FDC1772_READ(FDC1772REG_STATUS); | |
1388 | udelay(20); | |
1389 | /*printk("fd_test_drive_present: just before OK code %d\n",ok); */ | |
1390 | ||
1391 | if (ok) { | |
1392 | /* dummy seek command to make WP bit accessible */ | |
1393 | FDC1772_WRITE(FDC1772REG_DATA, 0); | |
1394 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK); | |
1395 | printk("fd_test_drive_present: just before wait for int\n"); | |
1396 | /* DAG: Guess means wait for interrupt */ | |
1397 | while (!(ioc_readb(IOC_FIQSTAT) & 2)); | |
1398 | printk("fd_test_drive_present: just after wait for int\n"); | |
1399 | status = FDC1772_READ(FDC1772REG_STATUS); | |
1400 | } | |
1401 | printk("fd_test_drive_present: just before ENABLE_IRQ\n"); | |
1402 | ENABLE_IRQ(); | |
1403 | printk("fd_test_drive_present: about to return\n"); | |
1404 | return (ok); | |
1405 | } | |
1406 | ||
1407 | ||
1408 | /* Look how many and which kind of drives are connected. If there are | |
1409 | * floppies, additionally start the disk-change and motor-off timers. | |
1410 | */ | |
1411 | ||
1412 | static void config_types(void) | |
1413 | { | |
1414 | int drive, cnt = 0; | |
1415 | ||
1416 | printk("Probing floppy drive(s):\n"); | |
1417 | for (drive = 0; drive < FD_MAX_UNITS; drive++) { | |
1418 | fd_probe(drive); | |
1419 | if (unit[drive].connected) { | |
1420 | printk("fd%d\n", drive); | |
1421 | ++cnt; | |
1422 | } | |
1423 | } | |
1424 | ||
1425 | if (FDC1772_READ(FDC1772REG_STATUS) & FDC1772STAT_BUSY) { | |
1426 | /* If FDC1772 is still busy from probing, give it another FORCI | |
1427 | * command to abort the operation. If this isn't done, the FDC1772 | |
1428 | * will interrupt later and its IRQ line stays low, because | |
1429 | * the status register isn't read. And this will block any | |
1430 | * interrupts on this IRQ line :-( | |
1431 | */ | |
1432 | FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI); | |
1433 | udelay(500); | |
1434 | FDC1772_READ(FDC1772REG_STATUS); | |
1435 | udelay(20); | |
1436 | } | |
1437 | if (cnt > 0) { | |
1438 | START_MOTOR_OFF_TIMER(FD_MOTOR_OFF_DELAY); | |
1439 | if (cnt == 1) | |
1440 | fd_select_drive(0); | |
1441 | /*START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY ); */ | |
1442 | } | |
1443 | } | |
1444 | ||
1445 | /* | |
1446 | * floppy_open check for aliasing (/dev/fd0 can be the same as | |
1447 | * /dev/PS0 etc), and disallows simultaneous access to the same | |
1448 | * drive with different device numbers. | |
1449 | */ | |
1450 | ||
1451 | static int floppy_open(struct inode *inode, struct file *filp) | |
1452 | { | |
1453 | int drive = iminor(inode) & 3; | |
1454 | int type = iminor(inode) >> 2; | |
1455 | int old_dev = fd_device[drive]; | |
1456 | ||
1457 | if (fd_ref[drive] && old_dev != type) | |
1458 | return -EBUSY; | |
1459 | ||
1460 | if (fd_ref[drive] == -1 || (fd_ref[drive] && filp->f_flags & O_EXCL)) | |
1461 | return -EBUSY; | |
1462 | ||
1463 | if (filp->f_flags & O_EXCL) | |
1464 | fd_ref[drive] = -1; | |
1465 | else | |
1466 | fd_ref[drive]++; | |
1467 | ||
1468 | fd_device[drive] = type; | |
1469 | ||
1470 | if (filp->f_flags & O_NDELAY) | |
1471 | return 0; | |
1472 | ||
1473 | if (filp->f_mode & 3) { | |
1474 | check_disk_change(inode->i_bdev); | |
1475 | if (filp->f_mode & 2) { | |
1476 | if (unit[drive].wpstat) { | |
1477 | floppy_release(inode, filp); | |
1478 | return -EROFS; | |
1479 | } | |
1480 | } | |
1481 | } | |
1482 | return 0; | |
1483 | } | |
1484 | ||
1485 | ||
1486 | static int floppy_release(struct inode *inode, struct file *filp) | |
1487 | { | |
1488 | int drive = iminor(inode) & 3; | |
1489 | ||
1490 | if (fd_ref[drive] < 0) | |
1491 | fd_ref[drive] = 0; | |
1492 | else if (!fd_ref[drive]--) { | |
1493 | printk("floppy_release with fd_ref == 0"); | |
1494 | fd_ref[drive] = 0; | |
1495 | } | |
1496 | ||
1497 | return 0; | |
1498 | } | |
1499 | ||
1500 | static struct block_device_operations floppy_fops = | |
1501 | { | |
1502 | .open = floppy_open, | |
1503 | .release = floppy_release, | |
1504 | .ioctl = fd_ioctl, | |
1505 | .media_changed = check_floppy_change, | |
1506 | .revalidate_disk= floppy_revalidate, | |
1507 | }; | |
1508 | ||
1509 | static struct kobject *floppy_find(dev_t dev, int *part, void *data) | |
1510 | { | |
1511 | int drive = *part & 3; | |
1512 | if ((*part >> 2) > NUM_DISK_TYPES || drive >= FD_MAX_UNITS) | |
1513 | return NULL; | |
1514 | *part = 0; | |
1515 | return get_disk(disks[drive]); | |
1516 | } | |
1517 | ||
1518 | int fd1772_init(void) | |
1519 | { | |
1520 | static DEFINE_SPINLOCK(lock); | |
1521 | int i, err = -ENOMEM; | |
1522 | ||
1523 | if (!machine_is_archimedes()) | |
1524 | return 0; | |
1525 | ||
1526 | for (i = 0; i < FD_MAX_UNITS; i++) { | |
1527 | disks[i] = alloc_disk(1); | |
1528 | if (!disks[i]) | |
1529 | goto err_disk; | |
1530 | } | |
1531 | ||
1532 | err = register_blkdev(MAJOR_NR, "fd"); | |
1533 | if (err) | |
1534 | goto err_disk; | |
1535 | ||
1536 | err = -EBUSY; | |
1537 | if (request_dma(FLOPPY_DMA, "fd1772")) { | |
1538 | printk("Unable to grab DMA%d for the floppy (1772) driver\n", FLOPPY_DMA); | |
1539 | goto err_blkdev; | |
1540 | }; | |
1541 | ||
1542 | if (request_dma(FIQ_FD1772, "fd1772 end")) { | |
1543 | printk("Unable to grab DMA%d for the floppy (1772) driver\n", FIQ_FD1772); | |
1544 | goto err_dma1; | |
1545 | }; | |
1546 | ||
1547 | /* initialize variables */ | |
1548 | SelectedDrive = -1; | |
1549 | #ifdef TRACKBUFFER | |
1550 | BufferDrive = BufferSide = BufferTrack = -1; | |
1551 | /* Atari uses 512 - I want to eventually cope with 1K sectors */ | |
5cbded58 | 1552 | DMABuffer = kmalloc((FD1772_MAX_SECTORS+1)*512,GFP_KERNEL); |
1da177e4 LT |
1553 | TrackBuffer = DMABuffer + 512; |
1554 | #else | |
1555 | /* Allocate memory for the DMAbuffer - on the Atari this takes it | |
1556 | out of some special memory... */ | |
5cbded58 | 1557 | DMABuffer = kmalloc(2048); /* Copes with pretty large sectors */ |
1da177e4 LT |
1558 | #endif |
1559 | err = -ENOMEM; | |
1560 | if (!DMAbuffer) | |
1561 | goto err_dma2; | |
1562 | ||
1563 | enable_dma(FIQ_FD1772); /* This inserts a call to our command end routine */ | |
1564 | ||
1565 | floppy_queue = blk_init_queue(do_fd_request, &lock); | |
1566 | if (!floppy_queue) | |
1567 | goto err_queue; | |
1568 | ||
1569 | for (i = 0; i < FD_MAX_UNITS; i++) { | |
1570 | unit[i].track = -1; | |
1571 | disks[i]->major = MAJOR_NR; | |
1572 | disks[i]->first_minor = 0; | |
1573 | disks[i]->fops = &floppy_fops; | |
1574 | sprintf(disks[i]->disk_name, "fd%d", i); | |
1575 | disks[i]->private_data = &unit[i]; | |
1576 | disks[i]->queue = floppy_queue; | |
1577 | set_capacity(disks[i], MAX_DISK_SIZE * 2); | |
1578 | } | |
1579 | blk_register_region(MKDEV(MAJOR_NR, 0), 256, THIS_MODULE, | |
1580 | floppy_find, NULL, NULL); | |
1581 | ||
1582 | for (i = 0; i < FD_MAX_UNITS; i++) | |
1583 | add_disk(disks[i]); | |
1584 | ||
1585 | config_types(); | |
1586 | ||
1587 | return 0; | |
1588 | ||
1589 | err_queue: | |
1590 | kfree(DMAbuffer); | |
1591 | err_dma2: | |
1592 | free_dma(FIQ_FD1772); | |
1593 | ||
1594 | err_dma1: | |
1595 | free_dma(FLOPPY_DMA); | |
1596 | ||
1597 | err_blkdev: | |
1598 | unregister_blkdev(MAJOR_NR, "fd"); | |
1599 | ||
1600 | err_disk: | |
1601 | while (i--) | |
1602 | put_disk(disks[i]); | |
1603 | return err; | |
1604 | } |