diff options
author | Adrian Bunk <bunk@stusta.de> | 2007-07-31 03:38:19 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-31 18:39:39 -0400 |
commit | 99eb8a550dbccc0e1f6c7e866fe421810e0585f6 (patch) | |
tree | 130c6e3338a0655ba74355eba83afab9261e1ed0 /drivers/acorn | |
parent | 0d0ed42e5ca2e22465c591341839c18025748fe8 (diff) |
Remove the arm26 port
The arm26 port has been in a state where it was far from even compiling
for quite some time.
Ian Molton agreed with the removal.
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Cc: Ian Molton <spyro@f2s.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/acorn')
-rw-r--r-- | drivers/acorn/README | 1 | ||||
-rw-r--r-- | drivers/acorn/block/Kconfig | 36 | ||||
-rw-r--r-- | drivers/acorn/block/Makefile | 9 | ||||
-rw-r--r-- | drivers/acorn/block/fd1772.c | 1604 | ||||
-rw-r--r-- | drivers/acorn/block/fd1772dma.S | 100 | ||||
-rw-r--r-- | drivers/acorn/block/mfm.S | 162 | ||||
-rw-r--r-- | drivers/acorn/block/mfmhd.c | 1385 |
7 files changed, 0 insertions, 3297 deletions
diff --git a/drivers/acorn/README b/drivers/acorn/README deleted file mode 100644 index d399c09ca61c..000000000000 --- a/drivers/acorn/README +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | Drivers for the ACORN "podule" ARM specific bus. | ||
diff --git a/drivers/acorn/block/Kconfig b/drivers/acorn/block/Kconfig deleted file mode 100644 index a0ff25ea439f..000000000000 --- a/drivers/acorn/block/Kconfig +++ /dev/null | |||
@@ -1,36 +0,0 @@ | |||
1 | # | ||
2 | # Block device driver configuration | ||
3 | # | ||
4 | |||
5 | menu "Acorn-specific block devices" | ||
6 | depends on ARCH_ARC || ARCH_A5K | ||
7 | |||
8 | config BLK_DEV_FD1772 | ||
9 | tristate "Old Archimedes floppy (1772) support" | ||
10 | depends on ARCH_ARC || ARCH_A5K | ||
11 | help | ||
12 | Support the floppy drive on the Acorn Archimedes (A300, A4x0, A540, | ||
13 | R140 and R260) series of computers; it supports only 720K floppies | ||
14 | at the moment. If you don't have one of these machines just answer | ||
15 | N. | ||
16 | |||
17 | config BLK_DEV_MFM | ||
18 | tristate "MFM harddisk support" | ||
19 | depends on ARCH_ARC || ARCH_A5K | ||
20 | help | ||
21 | Support the MFM hard drives on the Acorn Archimedes both | ||
22 | on-board the A4x0 motherboards and via the Acorn MFM podules. | ||
23 | Drives up to 64MB are supported. If you haven't got one of these | ||
24 | machines or drives just say N. | ||
25 | |||
26 | config BLK_DEV_MFM_AUTODETECT | ||
27 | bool "Autodetect hard drive geometry" | ||
28 | depends on BLK_DEV_MFM | ||
29 | help | ||
30 | If you answer Y, the MFM code will attempt to automatically detect | ||
31 | the cylinders/heads/sectors count on your hard drive. WARNING: This | ||
32 | sometimes doesn't work and it also does some dodgy stuff which | ||
33 | potentially might damage your drive. | ||
34 | |||
35 | endmenu | ||
36 | |||
diff --git a/drivers/acorn/block/Makefile b/drivers/acorn/block/Makefile deleted file mode 100644 index 38a9afe8e03f..000000000000 --- a/drivers/acorn/block/Makefile +++ /dev/null | |||
@@ -1,9 +0,0 @@ | |||
1 | # | ||
2 | # Makefile for the Acorn block device drivers. | ||
3 | # | ||
4 | |||
5 | fd1772_mod-objs := fd1772.o fd1772dma.o | ||
6 | mfmhd_mod-objs := mfmhd.o mfm.o | ||
7 | |||
8 | obj-$(CONFIG_BLK_DEV_FD1772) += fd1772_mod.o | ||
9 | obj-$(CONFIG_BLK_DEV_MFM) += mfmhd_mod.o | ||
diff --git a/drivers/acorn/block/fd1772.c b/drivers/acorn/block/fd1772.c deleted file mode 100644 index d7e18ce8dad9..000000000000 --- a/drivers/acorn/block/fd1772.c +++ /dev/null | |||
@@ -1,1604 +0,0 @@ | |||
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); | ||
375 | static void do_fd_request(struct request_queue *); | ||
376 | |||
377 | /************************* End of Prototypes **************************/ | ||
378 | |||
379 | static DEFINE_TIMER(motor_off_timer, fd_motor_off_timer, 0, 0); | ||
380 | |||
381 | #ifdef TRACKBUFFER | ||
382 | static DEFINE_TIMER(readtrack_timer, fd_readtrack_check, 0, 0); | ||
383 | #endif | ||
384 | |||
385 | static DEFINE_TIMER(timeout_timer, fd_times_out, 0, 0); | ||
386 | |||
387 | static DEFINE_TIMER(fd_timer, check_change, 0, 0); | ||
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; | ||
1249 | ReqCmd = rq_data_dir(CURRENT); | ||
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 | |||
1274 | static void do_fd_request(struct request_queue* q) | ||
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(); | ||
1282 | wait_event(fdc_wait, !fdc_busy); | ||
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 */ | ||
1552 | DMABuffer = kmalloc((FD1772_MAX_SECTORS+1)*512,GFP_KERNEL); | ||
1553 | TrackBuffer = DMABuffer + 512; | ||
1554 | #else | ||
1555 | /* Allocate memory for the DMAbuffer - on the Atari this takes it | ||
1556 | out of some special memory... */ | ||
1557 | DMABuffer = kmalloc(2048); /* Copes with pretty large sectors */ | ||
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 | } | ||
diff --git a/drivers/acorn/block/fd1772dma.S b/drivers/acorn/block/fd1772dma.S deleted file mode 100644 index 7964435443ec..000000000000 --- a/drivers/acorn/block/fd1772dma.S +++ /dev/null | |||
@@ -1,100 +0,0 @@ | |||
1 | #include <asm/hardware.h> | ||
2 | |||
3 | @ Code for DMA with the 1772 fdc | ||
4 | .text | ||
5 | |||
6 | |||
7 | .global fdc1772_dataaddr | ||
8 | fdc1772_fiqdata: | ||
9 | @ Number of bytes left to DMA | ||
10 | .global fdc1772_bytestogo | ||
11 | fdc1772_bytestogo: | ||
12 | .word 0 | ||
13 | @ Place to put/get data from in DMA | ||
14 | .global fdc1772_dataaddr | ||
15 | fdc1772_dataaddr: | ||
16 | .word 0 | ||
17 | |||
18 | .global fdc1772_fdc_int_done | ||
19 | fdc1772_fdc_int_done: | ||
20 | .word 0 | ||
21 | .global fdc1772_comendstatus | ||
22 | fdc1772_comendstatus: | ||
23 | .word 0 | ||
24 | |||
25 | @ We hang this off DMA channel 1 | ||
26 | .global fdc1772_comendhandler | ||
27 | fdc1772_comendhandler: | ||
28 | mov r8,#IOC_BASE | ||
29 | ldrb r9,[r8,#0x34] @ IOC FIQ status | ||
30 | tst r9,#2 | ||
31 | subeqs pc,r14,#4 @ should I leave a space here | ||
32 | orr r9,r8,#0x10000 @ FDC base | ||
33 | adr r8,fdc1772_fdc_int_done | ||
34 | ldrb r10,[r9,#0] @ FDC status | ||
35 | mov r9,#1 @ Got a FIQ flag | ||
36 | stmia r8,{r9,r10} | ||
37 | subs pc,r14,#4 | ||
38 | |||
39 | |||
40 | .global fdc1772_dma_read | ||
41 | fdc1772_dma_read: | ||
42 | mov r8,#IOC_BASE | ||
43 | ldrb r9,[r8,#0x34] @ IOC FIQ status | ||
44 | tst r9,#1 | ||
45 | beq fdc1772_dma_read_notours | ||
46 | orr r8,r8,#0x10000 @ FDC base | ||
47 | ldrb r10,[r8,#0xc] @ Read from FDC data reg (also clears interrupt) | ||
48 | ldmia r11,{r8,r9} | ||
49 | subs r8,r8,#1 @ One less byte to go | ||
50 | @ If there was somewhere for this data to go then store it and update pointers | ||
51 | strplb r10,[r9],#1 @ Store the data and increment the pointer | ||
52 | stmplia r11,{r8,r9} @ Update count/pointers | ||
53 | @ Handle any other interrupts if there are any | ||
54 | fdc1772_dma_read_notours: | ||
55 | @ Cant branch because this code has been copied down to the FIQ vector | ||
56 | ldr pc,[pc,#-4] | ||
57 | .word fdc1772_comendhandler | ||
58 | .global fdc1772_dma_read_end | ||
59 | fdc1772_dma_read_end: | ||
60 | |||
61 | .global fdc1772_dma_write | ||
62 | fdc1772_dma_write: | ||
63 | mov r8,#IOC_BASE | ||
64 | ldrb r9,[r8,#0x34] @ IOC FIQ status | ||
65 | tst r9,#1 | ||
66 | beq fdc1772_dma_write_notours | ||
67 | orr r8,r8,#0x10000 @ FDC base | ||
68 | ldmia r11,{r9,r10} | ||
69 | subs r9,r9,#1 @ One less byte to go | ||
70 | @ If there really is some data then get it, store it and update count | ||
71 | ldrplb r12,[r10],#1 | ||
72 | strplb r12,[r8,#0xc] @ write it to FDC data reg | ||
73 | stmplia r11,{r9,r10} @ Update count and pointer - should clear interrupt | ||
74 | @ Handle any other interrupts | ||
75 | fdc1772_dma_write_notours: | ||
76 | @ Cant branch because this code has been copied down to the FIQ vector | ||
77 | ldr pc,[pc,#-4] | ||
78 | .word fdc1772_comendhandler | ||
79 | |||
80 | .global fdc1772_dma_write_end | ||
81 | fdc1772_dma_write_end: | ||
82 | |||
83 | |||
84 | @ Setup the FIQ R11 to point to the data and store the count, address | ||
85 | @ for this dma | ||
86 | @ R0=count | ||
87 | @ R1=address | ||
88 | .global fdc1772_setupdma | ||
89 | fdc1772_setupdma: | ||
90 | @ The big job is flipping in and out of FIQ mode | ||
91 | adr r2,fdc1772_fiqdata @ This is what we really came here for | ||
92 | stmia r2,{r0,r1} | ||
93 | mov r3, pc | ||
94 | teqp pc,#0x0c000001 @ Disable FIQs, IRQs and switch to FIQ mode | ||
95 | mov r0,r0 @ NOP | ||
96 | mov r11,r2 | ||
97 | teqp r3,#0 @ Normal mode | ||
98 | mov r0,r0 @ NOP | ||
99 | mov pc,r14 | ||
100 | |||
diff --git a/drivers/acorn/block/mfm.S b/drivers/acorn/block/mfm.S deleted file mode 100644 index c90cbd41ce21..000000000000 --- a/drivers/acorn/block/mfm.S +++ /dev/null | |||
@@ -1,162 +0,0 @@ | |||
1 | @ Read/Write DMA code for the ST506/MFM hard drive controllers on the A400 Acorn Archimedes | ||
2 | @ motherboard and on ST506 expansion podules. | ||
3 | @ (c) David Alan Gilbert (linux@treblig.org) 1996-1999 | ||
4 | |||
5 | #include <asm/assembler.h> | ||
6 | |||
7 | hdc63463_irqdata: | ||
8 | @ Controller base address | ||
9 | .global hdc63463_baseaddress | ||
10 | hdc63463_baseaddress: | ||
11 | .word 0 | ||
12 | |||
13 | .global hdc63463_irqpolladdress | ||
14 | hdc63463_irqpolladdress: | ||
15 | .word 0 | ||
16 | |||
17 | .global hdc63463_irqpollmask | ||
18 | hdc63463_irqpollmask: | ||
19 | .word 0 | ||
20 | |||
21 | @ where to read/write data from the kernel data space | ||
22 | .global hdc63463_dataptr | ||
23 | hdc63463_dataptr: | ||
24 | .word 0 | ||
25 | |||
26 | @ Number of bytes left to transfer | ||
27 | .global hdc63463_dataleft | ||
28 | hdc63463_dataleft: | ||
29 | .word 0 | ||
30 | |||
31 | @ ------------------------------------------------------------------------- | ||
32 | @ hdc63463_writedma: DMA from host to controller | ||
33 | @ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask | ||
34 | @ r3=data ptr, r4=data left, r5,r6=temporary | ||
35 | .global hdc63463_writedma | ||
36 | hdc63463_writedma: | ||
37 | stmfd sp!,{r4-r7} | ||
38 | adr r5,hdc63463_irqdata | ||
39 | ldmia r5,{r0,r1,r2,r3,r4} | ||
40 | |||
41 | writedma_again: | ||
42 | |||
43 | @ test number of remaining bytes to transfer | ||
44 | cmp r4,#0 | ||
45 | beq writedma_end | ||
46 | bmi writedma_end | ||
47 | |||
48 | @ Check the hdc is interrupting | ||
49 | ldrb r5,[r1,#0] | ||
50 | tst r5,r2 | ||
51 | beq writedma_end | ||
52 | |||
53 | @ Transfer a block of upto 256 bytes | ||
54 | cmp r4,#256 | ||
55 | movlt r7,r4 | ||
56 | movge r7,#256 | ||
57 | |||
58 | @ Check the hdc is still busy and command has not ended and no errors | ||
59 | ldr r5,[r0,#32] @ Status reg - 16 bit - its the top few bits which are status | ||
60 | @ think we should continue DMA until it drops busy - perhaps this was | ||
61 | @ the main problem with corrected errors causing a hang | ||
62 | @tst r5,#0x3c00 @ Test for things which should be off | ||
63 | @bne writedma_end | ||
64 | and r5,r5,#0x8000 @ This is test for things which should be on: Busy | ||
65 | cmp r5,#0x8000 | ||
66 | bne writedma_end | ||
67 | |||
68 | @ Bytes remaining at end | ||
69 | sub r4,r4,r7 | ||
70 | |||
71 | @ HDC Write register location | ||
72 | add r0,r0,#32+8 | ||
73 | |||
74 | writedma_loop: | ||
75 | @ OK - pretty sure we should be doing this | ||
76 | |||
77 | ldr r5,[r3],#4 @ Get a word to be written | ||
78 | @ get bottom half to be sent first | ||
79 | mov r6,r5,lsl#16 @ Separate the first 2 bytes | ||
80 | orr r2,r6,r6,lsr #16 @ Duplicate them in the bottom half of the word | ||
81 | @ now the top half | ||
82 | mov r6,r5,lsr#16 @ Get 2nd 2 bytes | ||
83 | orr r6,r6,r6,lsl#16 @ Duplicate | ||
84 | @str r6,[r0] @ to hdc | ||
85 | stmia r0,{r2,r6} | ||
86 | subs r7,r7,#4 @ Dec. number of bytes left | ||
87 | bne writedma_loop | ||
88 | |||
89 | @ If we were too slow we had better go through again - DAG - took out with new interrupt routine | ||
90 | @ sub r0,r0,#32+8 | ||
91 | @ adr r2,hdc63463_irqdata | ||
92 | @ ldr r2,[r2,#8] | ||
93 | @ b writedma_again | ||
94 | |||
95 | writedma_end: | ||
96 | adr r5,hdc63463_irqdata+12 | ||
97 | stmia r5,{r3,r4} | ||
98 | ldmfd sp!,{r4-r7} | ||
99 | RETINSTR(mov,pc,lr) | ||
100 | |||
101 | @ ------------------------------------------------------------------------- | ||
102 | @ hdc63463_readdma: DMA from controller to host | ||
103 | @ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask | ||
104 | @ r3=data ptr, r4=data left, r5,r6=temporary | ||
105 | .global hdc63463_readdma | ||
106 | hdc63463_readdma: | ||
107 | stmfd sp!,{r4-r7} | ||
108 | adr r5,hdc63463_irqdata | ||
109 | ldmia r5,{r0,r1,r2,r3,r4} | ||
110 | |||
111 | readdma_again: | ||
112 | @ test number of remaining bytes to transfer | ||
113 | cmp r4,#0 | ||
114 | beq readdma_end | ||
115 | bmi readdma_end | ||
116 | |||
117 | @ Check the hdc is interrupting | ||
118 | ldrb r5,[r1,#0] | ||
119 | tst r5,r2 | ||
120 | beq readdma_end | ||
121 | |||
122 | @ Check the hdc is still busy and command has not ended and no errors | ||
123 | ldr r5,[r0,#32] @ Status reg - 16 bit - its the top few bits which are status | ||
124 | @ think we should continue DMA until it drops busy - perhaps this was | ||
125 | @ the main problem with corrected errors causing a hang | ||
126 | @tst r5,#0x3c00 @ Test for things which should be off | ||
127 | @bne readdma_end | ||
128 | and r5,r5,#0x8000 @ This is test for things which should be on: Busy | ||
129 | cmp r5,#0x8000 | ||
130 | bne readdma_end | ||
131 | |||
132 | @ Transfer a block of upto 256 bytes | ||
133 | cmp r4,#256 | ||
134 | movlt r7,r4 | ||
135 | movge r7,#256 | ||
136 | |||
137 | @ Bytes remaining at end | ||
138 | sub r4,r4,r7 | ||
139 | |||
140 | @ Set a pointer to the data register in the HDC | ||
141 | add r0,r0,#8 | ||
142 | readdma_loop: | ||
143 | @ OK - pretty sure we should be doing this | ||
144 | ldmia r0,{r5,r6} | ||
145 | mov r5,r5,lsl#16 | ||
146 | mov r6,r6,lsl#16 | ||
147 | orr r6,r6,r5,lsr #16 | ||
148 | str r6,[r3],#4 | ||
149 | subs r7,r7,#4 @ Decrement bytes to go | ||
150 | bne readdma_loop | ||
151 | |||
152 | @ Try reading multiple blocks - if this was fast enough then I do not think | ||
153 | @ this should help - NO taken out DAG - new interrupt handler has | ||
154 | @ non-consecutive memory blocks | ||
155 | @ sub r0,r0,#8 | ||
156 | @ b readdma_again | ||
157 | |||
158 | readdma_end: | ||
159 | adr r5,hdc63463_irqdata+12 | ||
160 | stmia r5,{r3,r4} | ||
161 | ldmfd sp!,{r4-r7} | ||
162 | RETINSTR(mov,pc,lr) | ||
diff --git a/drivers/acorn/block/mfmhd.c b/drivers/acorn/block/mfmhd.c deleted file mode 100644 index 74058db674db..000000000000 --- a/drivers/acorn/block/mfmhd.c +++ /dev/null | |||
@@ -1,1385 +0,0 @@ | |||
1 | /* | ||
2 | * linux/drivers/acorn/block/mfmhd.c | ||
3 | * | ||
4 | * Copyright (C) 1995, 1996 Russell King, Dave Alan Gilbert (gilbertd@cs.man.ac.uk) | ||
5 | * | ||
6 | * MFM hard drive code [experimental] | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * Change list: | ||
11 | * | ||
12 | * 3/2/96:DAG: Started a change list :-) | ||
13 | * Set the hardsect_size pointers up since we are running 256 byte | ||
14 | * sectors | ||
15 | * Added DMA code, put it into the rw_intr | ||
16 | * Moved RCAL out of generic interrupt code - don't want to do it | ||
17 | * while DMA'ing - its now in individual handlers. | ||
18 | * Took interrupt handlers off task queue lists and called | ||
19 | * directly - not sure of implications. | ||
20 | * | ||
21 | * 18/2/96:DAG: Well its reading OK I think, well enough for image file code | ||
22 | * to find the image file; but now I've discovered that I actually | ||
23 | * have to put some code in for image files. | ||
24 | * | ||
25 | * Added stuff for image files; seems to work, but I've not | ||
26 | * got a multisegment image file (I don't think!). | ||
27 | * Put in a hack (yep a real hack) for multiple cylinder reads. | ||
28 | * Not convinced its working. | ||
29 | * | ||
30 | * 5/4/96:DAG: Added asm/hardware.h and use IOC_ macros | ||
31 | * Rewrote dma code in mfm.S (again!) - now takes a word at a time | ||
32 | * from main RAM for speed; still doesn't feel speedy! | ||
33 | * | ||
34 | * 20/4/96:DAG: After rewriting mfm.S a heck of a lot of times and speeding | ||
35 | * things up, I've finally figured out why its so damn slow. | ||
36 | * Linux is only reading a block at a time, and so you never | ||
37 | * get more than 1K per disc revoloution ~=60K/second. | ||
38 | * | ||
39 | * 27/4/96:DAG: On Russell's advice I change ll_rw_blk.c to ask it to | ||
40 | * join adjacent blocks together. Everything falls flat on its | ||
41 | * face. | ||
42 | * Four hours of debugging later; I hadn't realised that | ||
43 | * ll_rw_blk would be so generous as to join blocks whose | ||
44 | * results aren't going into consecutive buffers. | ||
45 | * | ||
46 | * OK; severe rehacking of mfm_rw_interrupt; now end_request's | ||
47 | * as soon as its DMA'd each request. Odd thing is that | ||
48 | * we are sometimes getting interrupts where we are not transferring | ||
49 | * any data; why? Is that what happens when you miss? I doubt | ||
50 | * it; are we too fast? No - its just at command ends. Got 240K/s | ||
51 | * better than before, but RiscOS hits 480K/s | ||
52 | * | ||
53 | * 25/6/96:RMK: Fixed init code to allow the MFM podule to work. Increased the | ||
54 | * number of errors for my Miniscribe drive (8425). | ||
55 | * | ||
56 | * 30/6/96:DAG: Russell suggested that a check drive 0 might turn the LEDs off | ||
57 | * - so in request_done just before it clears Busy it sends a | ||
58 | * check drive 0 - and the LEDs go off!!!! | ||
59 | * | ||
60 | * Added test for mainboard controller. - Removes need for separate | ||
61 | * define. | ||
62 | * | ||
63 | * 13/7/96:DAG: Changed hardware sectore size to 512 in attempt to make | ||
64 | * IM drivers work. | ||
65 | * 21/7/96:DAG: Took out old image file stuff (accessing it now produces an IO | ||
66 | * error.) | ||
67 | * | ||
68 | * 17/8/96:DAG: Ran through indent -kr -i8; evil - all my nice 2 character indents | ||
69 | * gone :-( Hand modified afterwards. | ||
70 | * Took out last remains of the older image map system. | ||
71 | * | ||
72 | * 22/9/96:DAG: Changed mfm.S so it will carry on DMA'ing til; BSY is dropped | ||
73 | * Changed mfm_rw_intr so that it doesn't follow the error | ||
74 | * code until BSY is dropped. Nope - still broke. Problem | ||
75 | * may revolve around when it reads the results for the error | ||
76 | * number? | ||
77 | * | ||
78 | *16/11/96:DAG: Modified for 2.0.18; request_irq changed | ||
79 | * | ||
80 | *17/12/96:RMK: Various cleanups, reorganisation, and the changes for new IO system. | ||
81 | * Improved probe for onboard MFM chip - it was hanging on my A5k. | ||
82 | * Added autodetect CHS code such that we don't rely on the presence | ||
83 | * of an ADFS boot block. Added ioport resource manager calls so | ||
84 | * that we don't clash with already-running hardware (eg. RiscPC Ether | ||
85 | * card slots if someone tries this)! | ||
86 | * | ||
87 | * 17/1/97:RMK: Upgraded to 2.1 kernels. | ||
88 | * | ||
89 | * 4/3/98:RMK: Changed major number to 21. | ||
90 | * | ||
91 | * 27/6/98:RMK: Changed asm/delay.h to linux/delay.h for mdelay(). | ||
92 | */ | ||
93 | |||
94 | /* | ||
95 | * Possible enhancements: | ||
96 | * Multi-thread the code so that it is possible that while one drive | ||
97 | * is seeking, the other one can be reading data/seeking as well. | ||
98 | * This would be a performance boost with dual drive systems. | ||
99 | */ | ||
100 | |||
101 | #include <linux/module.h> | ||
102 | #include <linux/fs.h> | ||
103 | #include <linux/interrupt.h> | ||
104 | #include <linux/kernel.h> | ||
105 | #include <linux/timer.h> | ||
106 | #include <linux/mm.h> | ||
107 | #include <linux/errno.h> | ||
108 | #include <linux/genhd.h> | ||
109 | #include <linux/major.h> | ||
110 | #include <linux/ioport.h> | ||
111 | #include <linux/delay.h> | ||
112 | #include <linux/blkpg.h> | ||
113 | |||
114 | #include <asm/system.h> | ||
115 | #include <asm/io.h> | ||
116 | #include <asm/irq.h> | ||
117 | #include <asm/uaccess.h> | ||
118 | #include <asm/dma.h> | ||
119 | #include <asm/hardware.h> | ||
120 | #include <asm/ecard.h> | ||
121 | #include <asm/hardware/ioc.h> | ||
122 | |||
123 | static void (*do_mfm)(void) = NULL; | ||
124 | static struct request_queue *mfm_queue; | ||
125 | static DEFINE_SPINLOCK(mfm_lock); | ||
126 | |||
127 | #define MAJOR_NR MFM_ACORN_MAJOR | ||
128 | #define QUEUE (mfm_queue) | ||
129 | #define CURRENT elv_next_request(mfm_queue) | ||
130 | |||
131 | /* | ||
132 | * Configuration section | ||
133 | * | ||
134 | * This is the maximum number of drives that we accept | ||
135 | */ | ||
136 | #define MFM_MAXDRIVES 2 | ||
137 | /* | ||
138 | * Linux I/O address of onboard MFM controller or 0 to disable this | ||
139 | */ | ||
140 | #define ONBOARD_MFM_ADDRESS ((0x002d0000 >> 2) | 0x80000000) | ||
141 | /* | ||
142 | * Uncomment this to enable debugging in the MFM driver... | ||
143 | */ | ||
144 | #ifndef DEBUG | ||
145 | /*#define DEBUG */ | ||
146 | #endif | ||
147 | /* | ||
148 | * End of configuration | ||
149 | */ | ||
150 | |||
151 | |||
152 | /* | ||
153 | * This structure contains all information to do with a particular physical | ||
154 | * device. | ||
155 | */ | ||
156 | struct mfm_info { | ||
157 | unsigned char sectors; | ||
158 | unsigned char heads; | ||
159 | unsigned short cylinders; | ||
160 | unsigned short lowcurrent; | ||
161 | unsigned short precomp; | ||
162 | #define NO_TRACK -1 | ||
163 | #define NEED_1_RECAL -2 | ||
164 | #define NEED_2_RECAL -3 | ||
165 | int cylinder; | ||
166 | struct { | ||
167 | char recal; | ||
168 | char report; | ||
169 | char abort; | ||
170 | } errors; | ||
171 | } mfm_info[MFM_MAXDRIVES]; | ||
172 | |||
173 | #define MFM_DRV_INFO mfm_info[raw_cmd.dev] | ||
174 | |||
175 | /* Stuff from the assembly routines */ | ||
176 | extern unsigned int hdc63463_baseaddress; /* Controller base address */ | ||
177 | extern unsigned int hdc63463_irqpolladdress; /* Address to read to test for int */ | ||
178 | extern unsigned int hdc63463_irqpollmask; /* Mask for irq register */ | ||
179 | extern unsigned int hdc63463_dataptr; /* Pointer to kernel data space to DMA */ | ||
180 | extern int hdc63463_dataleft; /* Number of bytes left to transfer */ | ||
181 | |||
182 | |||
183 | |||
184 | |||
185 | static int lastspecifieddrive; | ||
186 | static unsigned Busy; | ||
187 | |||
188 | static unsigned int PartFragRead; /* The number of sectors which have been read | ||
189 | during a partial read split over two | ||
190 | cylinders. If 0 it means a partial | ||
191 | read did not occur. */ | ||
192 | |||
193 | static unsigned int PartFragRead_RestartBlock; /* Where to restart on a split access */ | ||
194 | static unsigned int PartFragRead_SectorsLeft; /* Where to restart on a split access */ | ||
195 | |||
196 | static int Sectors256LeftInCurrent; /* i.e. 256 byte sectors left in current */ | ||
197 | static int SectorsLeftInRequest; /* i.e. blocks left in the thing mfm_request was called for */ | ||
198 | static int Copy_Sector; /* The 256 byte sector we are currently at - fragments need to know | ||
199 | where to take over */ | ||
200 | static char *Copy_buffer; | ||
201 | |||
202 | |||
203 | static void mfm_seek(void); | ||
204 | static void mfm_rerequest(void); | ||
205 | static void mfm_request(void); | ||
206 | static void mfm_specify (void); | ||
207 | static void issue_request(unsigned int block, unsigned int nsect, | ||
208 | struct request *req); | ||
209 | |||
210 | static unsigned int mfm_addr; /* Controller address */ | ||
211 | static unsigned int mfm_IRQPollLoc; /* Address to read for IRQ information */ | ||
212 | static unsigned int mfm_irqenable; /* Podule IRQ enable location */ | ||
213 | static unsigned char mfm_irq; /* Interrupt number */ | ||
214 | static int mfm_drives = 0; /* drives available */ | ||
215 | static int mfm_status = 0; /* interrupt status */ | ||
216 | static int *errors; | ||
217 | |||
218 | static struct rawcmd { | ||
219 | unsigned int dev; | ||
220 | unsigned int cylinder; | ||
221 | unsigned int head; | ||
222 | unsigned int sector; | ||
223 | unsigned int cmdtype; | ||
224 | unsigned int cmdcode; | ||
225 | unsigned char cmddata[16]; | ||
226 | unsigned int cmdlen; | ||
227 | } raw_cmd; | ||
228 | |||
229 | static unsigned char result[16]; | ||
230 | |||
231 | static struct cont { | ||
232 | void (*interrupt) (void); /* interrupt handler */ | ||
233 | void (*error) (void); /* error handler */ | ||
234 | void (*redo) (void); /* redo handler */ | ||
235 | void (*done) (int st); /* done handler */ | ||
236 | } *cont = NULL; | ||
237 | |||
238 | #if 0 | ||
239 | static struct tq_struct mfm_tq = {0, 0, (void (*)(void *)) NULL, 0}; | ||
240 | #endif | ||
241 | |||
242 | int number_mfm_drives = 1; | ||
243 | |||
244 | /* ------------------------------------------------------------------------------------------ */ | ||
245 | /* | ||
246 | * From the HD63463 data sheet from Hitachi Ltd. | ||
247 | */ | ||
248 | |||
249 | #define MFM_COMMAND (mfm_addr + 0) | ||
250 | #define MFM_DATAOUT (mfm_addr + 1) | ||
251 | #define MFM_STATUS (mfm_addr + 8) | ||
252 | #define MFM_DATAIN (mfm_addr + 9) | ||
253 | |||
254 | #define CMD_ABT 0xF0 /* Abort */ | ||
255 | #define CMD_SPC 0xE8 /* Specify */ | ||
256 | #define CMD_TST 0xE0 /* Test */ | ||
257 | #define CMD_RCLB 0xC8 /* Recalibrate */ | ||
258 | #define CMD_SEK 0xC0 /* Seek */ | ||
259 | #define CMD_WFS 0xAB /* Write Format Skew */ | ||
260 | #define CMD_WFM 0xA3 /* Write Format */ | ||
261 | #define CMD_MTB 0x90 /* Memory to buffer */ | ||
262 | #define CMD_CMPD 0x88 /* Compare data */ | ||
263 | #define CMD_WD 0x87 /* Write data */ | ||
264 | #define CMD_RED 0x70 /* Read erroneous data */ | ||
265 | #define CMD_RIS 0x68 /* Read ID skew */ | ||
266 | #define CMD_FID 0x61 /* Find ID */ | ||
267 | #define CMD_RID 0x60 /* Read ID */ | ||
268 | #define CMD_BTM 0x50 /* Buffer to memory */ | ||
269 | #define CMD_CKD 0x48 /* Check data */ | ||
270 | #define CMD_RD 0x40 /* Read data */ | ||
271 | #define CMD_OPBW 0x38 /* Open buffer write */ | ||
272 | #define CMD_OPBR 0x30 /* Open buffer read */ | ||
273 | #define CMD_CKV 0x28 /* Check drive */ | ||
274 | #define CMD_CKE 0x20 /* Check ECC */ | ||
275 | #define CMD_POD 0x18 /* Polling disable */ | ||
276 | #define CMD_POL 0x10 /* Polling enable */ | ||
277 | #define CMD_RCAL 0x08 /* Recall */ | ||
278 | |||
279 | #define STAT_BSY 0x8000 /* Busy */ | ||
280 | #define STAT_CPR 0x4000 /* Command Parameter Rejection */ | ||
281 | #define STAT_CED 0x2000 /* Command end */ | ||
282 | #define STAT_SED 0x1000 /* Seek end */ | ||
283 | #define STAT_DER 0x0800 /* Drive error */ | ||
284 | #define STAT_ABN 0x0400 /* Abnormal end */ | ||
285 | #define STAT_POL 0x0200 /* Polling */ | ||
286 | |||
287 | /* ------------------------------------------------------------------------------------------ */ | ||
288 | #ifdef DEBUG | ||
289 | static void console_printf(const char *fmt,...) | ||
290 | { | ||
291 | static char buffer[2048]; /* Arbitary! */ | ||
292 | extern void console_print(const char *); | ||
293 | unsigned long flags; | ||
294 | va_list ap; | ||
295 | |||
296 | local_irq_save(flags); | ||
297 | |||
298 | va_start(ap, fmt); | ||
299 | vsprintf(buffer, fmt, ap); | ||
300 | console_print(buffer); | ||
301 | va_end(fmt); | ||
302 | |||
303 | local_irq_restore(flags); | ||
304 | }; /* console_printf */ | ||
305 | |||
306 | #define DBG(x...) console_printf(x) | ||
307 | #else | ||
308 | #define DBG(x...) | ||
309 | #endif | ||
310 | |||
311 | static void print_status(void) | ||
312 | { | ||
313 | char *error; | ||
314 | static char *errors[] = { | ||
315 | "no error", | ||
316 | "command aborted", | ||
317 | "invalid command", | ||
318 | "parameter error", | ||
319 | "not initialised", | ||
320 | "rejected TEST", | ||
321 | "no useld", | ||
322 | "write fault", | ||
323 | "not ready", | ||
324 | "no scp", | ||
325 | "in seek", | ||
326 | "invalid NCA", | ||
327 | "invalid step rate", | ||
328 | "seek error", | ||
329 | "over run", | ||
330 | "invalid PHA", | ||
331 | "data field EEC error", | ||
332 | "data field CRC error", | ||
333 | "error corrected", | ||
334 | "data field fatal error", | ||
335 | "no data am", | ||
336 | "not hit", | ||
337 | "ID field CRC error", | ||
338 | "time over", | ||
339 | "no ID am", | ||
340 | "not writable" | ||
341 | }; | ||
342 | if (result[1] < 0x65) | ||
343 | error = errors[result[1] >> 2]; | ||
344 | else | ||
345 | error = "unknown"; | ||
346 | printk("("); | ||
347 | if (mfm_status & STAT_BSY) printk("BSY "); | ||
348 | if (mfm_status & STAT_CPR) printk("CPR "); | ||
349 | if (mfm_status & STAT_CED) printk("CED "); | ||
350 | if (mfm_status & STAT_SED) printk("SED "); | ||
351 | if (mfm_status & STAT_DER) printk("DER "); | ||
352 | if (mfm_status & STAT_ABN) printk("ABN "); | ||
353 | if (mfm_status & STAT_POL) printk("POL "); | ||
354 | printk(") SSB = %X (%s)\n", result[1], error); | ||
355 | |||
356 | } | ||
357 | |||
358 | /* ------------------------------------------------------------------------------------- */ | ||
359 | |||
360 | static void issue_command(int command, unsigned char *cmdb, int len) | ||
361 | { | ||
362 | int status; | ||
363 | #ifdef DEBUG | ||
364 | int i; | ||
365 | console_printf("issue_command: %02X: ", command); | ||
366 | for (i = 0; i < len; i++) | ||
367 | console_printf("%02X ", cmdb[i]); | ||
368 | console_printf("\n"); | ||
369 | #endif | ||
370 | |||
371 | do { | ||
372 | status = inw(MFM_STATUS); | ||
373 | } while (status & (STAT_BSY | STAT_POL)); | ||
374 | DBG("issue_command: status after pol/bsy loop: %02X:\n ", status >> 8); | ||
375 | |||
376 | if (status & (STAT_CPR | STAT_CED | STAT_SED | STAT_DER | STAT_ABN)) { | ||
377 | outw(CMD_RCAL, MFM_COMMAND); | ||
378 | while (inw(MFM_STATUS) & STAT_BSY); | ||
379 | } | ||
380 | status = inw(MFM_STATUS); | ||
381 | DBG("issue_command: status before parameter issue: %02X:\n ", status >> 8); | ||
382 | |||
383 | while (len > 0) { | ||
384 | outw(cmdb[1] | (cmdb[0] << 8), MFM_DATAOUT); | ||
385 | len -= 2; | ||
386 | cmdb += 2; | ||
387 | } | ||
388 | status = inw(MFM_STATUS); | ||
389 | DBG("issue_command: status before command issue: %02X:\n ", status >> 8); | ||
390 | |||
391 | outw(command, MFM_COMMAND); | ||
392 | status = inw(MFM_STATUS); | ||
393 | DBG("issue_command: status immediately after command issue: %02X:\n ", status >> 8); | ||
394 | } | ||
395 | |||
396 | static void wait_for_completion(void) | ||
397 | { | ||
398 | while ((mfm_status = inw(MFM_STATUS)) & STAT_BSY); | ||
399 | } | ||
400 | |||
401 | static void wait_for_command_end(void) | ||
402 | { | ||
403 | int i; | ||
404 | |||
405 | while (!((mfm_status = inw(MFM_STATUS)) & STAT_CED)); | ||
406 | |||
407 | for (i = 0; i < 16;) { | ||
408 | int in; | ||
409 | in = inw(MFM_DATAIN); | ||
410 | result[i++] = in >> 8; | ||
411 | result[i++] = in; | ||
412 | } | ||
413 | outw (CMD_RCAL, MFM_COMMAND); | ||
414 | } | ||
415 | |||
416 | /* ------------------------------------------------------------------------------------- */ | ||
417 | |||
418 | static void mfm_rw_intr(void) | ||
419 | { | ||
420 | int old_status; /* Holds status on entry, we read to see if the command just finished */ | ||
421 | #ifdef DEBUG | ||
422 | console_printf("mfm_rw_intr...dataleft=%d\n", hdc63463_dataleft); | ||
423 | print_status(); | ||
424 | #endif | ||
425 | |||
426 | /* Now don't handle the error until BSY drops */ | ||
427 | if ((mfm_status & (STAT_DER | STAT_ABN)) && ((mfm_status&STAT_BSY)==0)) { | ||
428 | /* Something has gone wrong - let's try that again */ | ||
429 | outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ | ||
430 | if (cont) { | ||
431 | DBG("mfm_rw_intr: DER/ABN err\n"); | ||
432 | cont->error(); | ||
433 | cont->redo(); | ||
434 | }; | ||
435 | return; | ||
436 | }; | ||
437 | |||
438 | /* OK so what ever happened it's not an error, now I reckon we are left between | ||
439 | a choice of command end or some data which is ready to be collected */ | ||
440 | /* I think we have to transfer data while the interrupt line is on and its | ||
441 | not any other type of interrupt */ | ||
442 | if (rq_data_dir(CURRENT) == WRITE) { | ||
443 | extern void hdc63463_writedma(void); | ||
444 | if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) { | ||
445 | printk("mfm_rw_intr: Apparent DMA write request when no more to DMA\n"); | ||
446 | if (cont) { | ||
447 | cont->error(); | ||
448 | cont->redo(); | ||
449 | }; | ||
450 | return; | ||
451 | }; | ||
452 | hdc63463_writedma(); | ||
453 | } else { | ||
454 | extern void hdc63463_readdma(void); | ||
455 | if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) { | ||
456 | printk("mfm_rw_intr: Apparent DMA read request when no more to DMA\n"); | ||
457 | if (cont) { | ||
458 | cont->error(); | ||
459 | cont->redo(); | ||
460 | }; | ||
461 | return; | ||
462 | }; | ||
463 | DBG("Going to try read dma..............status=0x%x, buffer=%p\n", mfm_status, hdc63463_dataptr); | ||
464 | hdc63463_readdma(); | ||
465 | }; /* Read */ | ||
466 | |||
467 | if (hdc63463_dataptr != ((unsigned int) Copy_buffer + 256)) { | ||
468 | /* If we didn't actually manage to get any data on this interrupt - but why? We got the interrupt */ | ||
469 | /* Ah - well looking at the status its just when we get command end; so no problem */ | ||
470 | /*console_printf("mfm: dataptr mismatch. dataptr=0x%08x Copy_buffer+256=0x%08p\n", | ||
471 | hdc63463_dataptr,Copy_buffer+256); | ||
472 | print_status(); */ | ||
473 | } else { | ||
474 | Sectors256LeftInCurrent--; | ||
475 | Copy_buffer += 256; | ||
476 | Copy_Sector++; | ||
477 | |||
478 | /* We have come to the end of this request */ | ||
479 | if (!Sectors256LeftInCurrent) { | ||
480 | DBG("mfm: end_request for CURRENT=0x%p CURRENT(sector=%d current_nr_sectors=%d nr_sectors=%d)\n", | ||
481 | CURRENT, CURRENT->sector, CURRENT->current_nr_sectors, CURRENT->nr_sectors); | ||
482 | |||
483 | CURRENT->nr_sectors -= CURRENT->current_nr_sectors; | ||
484 | CURRENT->sector += CURRENT->current_nr_sectors; | ||
485 | SectorsLeftInRequest -= CURRENT->current_nr_sectors; | ||
486 | |||
487 | end_request(CURRENT, 1); | ||
488 | if (SectorsLeftInRequest) { | ||
489 | hdc63463_dataptr = (unsigned int) CURRENT->buffer; | ||
490 | Copy_buffer = CURRENT->buffer; | ||
491 | Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2; | ||
492 | errors = &(CURRENT->errors); | ||
493 | /* These should match the present calculations of the next logical sector | ||
494 | on the device | ||
495 | Copy_Sector=CURRENT->sector*2; */ | ||
496 | |||
497 | if (Copy_Sector != CURRENT->sector * 2) | ||
498 | #ifdef DEBUG | ||
499 | /*console_printf*/printk("mfm: Copy_Sector mismatch. Copy_Sector=%d CURRENT->sector*2=%d\n", | ||
500 | Copy_Sector, CURRENT->sector * 2); | ||
501 | #else | ||
502 | printk("mfm: Copy_Sector mismatch! Eek!\n"); | ||
503 | #endif | ||
504 | }; /* CURRENT */ | ||
505 | }; /* Sectors256LeftInCurrent */ | ||
506 | }; | ||
507 | |||
508 | old_status = mfm_status; | ||
509 | mfm_status = inw(MFM_STATUS); | ||
510 | if (mfm_status & (STAT_DER | STAT_ABN)) { | ||
511 | /* Something has gone wrong - let's try that again */ | ||
512 | if (cont) { | ||
513 | DBG("mfm_rw_intr: DER/ABN error\n"); | ||
514 | cont->error(); | ||
515 | cont->redo(); | ||
516 | }; | ||
517 | return; | ||
518 | }; | ||
519 | |||
520 | /* If this code wasn't entered due to command_end but there is | ||
521 | now a command end we must read the command results out. If it was | ||
522 | entered like this then mfm_interrupt_handler would have done the | ||
523 | job. */ | ||
524 | if ((!((old_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) && | ||
525 | ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) { | ||
526 | int len = 0; | ||
527 | while (len < 16) { | ||
528 | int in; | ||
529 | in = inw(MFM_DATAIN); | ||
530 | result[len++] = in >> 8; | ||
531 | result[len++] = in; | ||
532 | }; | ||
533 | }; /* Result read */ | ||
534 | |||
535 | /*console_printf ("mfm_rw_intr nearexit [%02X]\n", __raw_readb(mfm_IRQPollLoc)); */ | ||
536 | |||
537 | /* If end of command move on */ | ||
538 | if (mfm_status & (STAT_CED)) { | ||
539 | outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ | ||
540 | /* End of command - trigger the next command */ | ||
541 | if (cont) { | ||
542 | cont->done(1); | ||
543 | } | ||
544 | DBG("mfm_rw_intr: returned from cont->done\n"); | ||
545 | } else { | ||
546 | /* Its going to generate another interrupt */ | ||
547 | do_mfm = mfm_rw_intr; | ||
548 | }; | ||
549 | } | ||
550 | |||
551 | static void mfm_setup_rw(void) | ||
552 | { | ||
553 | DBG("setting up for rw...\n"); | ||
554 | |||
555 | do_mfm = mfm_rw_intr; | ||
556 | issue_command(raw_cmd.cmdcode, raw_cmd.cmddata, raw_cmd.cmdlen); | ||
557 | } | ||
558 | |||
559 | static void mfm_recal_intr(void) | ||
560 | { | ||
561 | #ifdef DEBUG | ||
562 | console_printf("recal intr - status = "); | ||
563 | print_status(); | ||
564 | #endif | ||
565 | outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ | ||
566 | if (mfm_status & (STAT_DER | STAT_ABN)) { | ||
567 | printk("recal failed\n"); | ||
568 | MFM_DRV_INFO.cylinder = NEED_2_RECAL; | ||
569 | if (cont) { | ||
570 | cont->error(); | ||
571 | cont->redo(); | ||
572 | } | ||
573 | return; | ||
574 | } | ||
575 | /* Thats seek end - we are finished */ | ||
576 | if (mfm_status & STAT_SED) { | ||
577 | issue_command(CMD_POD, NULL, 0); | ||
578 | MFM_DRV_INFO.cylinder = 0; | ||
579 | mfm_seek(); | ||
580 | return; | ||
581 | } | ||
582 | /* Command end without seek end (see data sheet p.20) for parallel seek | ||
583 | - we have to send a POL command to wait for the seek */ | ||
584 | if (mfm_status & STAT_CED) { | ||
585 | do_mfm = mfm_recal_intr; | ||
586 | issue_command(CMD_POL, NULL, 0); | ||
587 | return; | ||
588 | } | ||
589 | printk("recal: unknown status\n"); | ||
590 | } | ||
591 | |||
592 | static void mfm_seek_intr(void) | ||
593 | { | ||
594 | #ifdef DEBUG | ||
595 | console_printf("seek intr - status = "); | ||
596 | print_status(); | ||
597 | #endif | ||
598 | outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ | ||
599 | if (mfm_status & (STAT_DER | STAT_ABN)) { | ||
600 | printk("seek failed\n"); | ||
601 | MFM_DRV_INFO.cylinder = NEED_2_RECAL; | ||
602 | if (cont) { | ||
603 | cont->error(); | ||
604 | cont->redo(); | ||
605 | } | ||
606 | return; | ||
607 | } | ||
608 | if (mfm_status & STAT_SED) { | ||
609 | issue_command(CMD_POD, NULL, 0); | ||
610 | MFM_DRV_INFO.cylinder = raw_cmd.cylinder; | ||
611 | mfm_seek(); | ||
612 | return; | ||
613 | } | ||
614 | if (mfm_status & STAT_CED) { | ||
615 | do_mfm = mfm_seek_intr; | ||
616 | issue_command(CMD_POL, NULL, 0); | ||
617 | return; | ||
618 | } | ||
619 | printk("seek: unknown status\n"); | ||
620 | } | ||
621 | |||
622 | /* IDEA2 seems to work better - its what RiscOS sets my | ||
623 | * disc to - on its SECOND call to specify! | ||
624 | */ | ||
625 | #define IDEA2 | ||
626 | #ifndef IDEA2 | ||
627 | #define SPEC_SL 0x16 | ||
628 | #define SPEC_SH 0xa9 /* Step pulse high=21, Record Length=001 (256 bytes) */ | ||
629 | #else | ||
630 | #define SPEC_SL 0x00 /* OM2 - SL - step pulse low */ | ||
631 | #define SPEC_SH 0x21 /* Step pulse high=4, Record Length=001 (256 bytes) */ | ||
632 | #endif | ||
633 | |||
634 | static void mfm_setupspecify (int drive, unsigned char *cmdb) | ||
635 | { | ||
636 | cmdb[0] = 0x1f; /* OM0 - !SECT,!MOD,!DIF,PADP,ECD,CRCP,CRCI,ACOR */ | ||
637 | cmdb[1] = 0xc3; /* OM1 - DTM,BRST,!CEDM,!SEDM,!DERM,0,AMEX,PSK */ | ||
638 | cmdb[2] = SPEC_SL; /* OM2 - SL - step pulse low */ | ||
639 | cmdb[3] = (number_mfm_drives == 1) ? 0x02 : 0x06; /* 1 or 2 drives */ | ||
640 | cmdb[4] = 0xfc | ((mfm_info[drive].cylinders - 1) >> 8);/* RW time over/high part of number of cylinders */ | ||
641 | cmdb[5] = mfm_info[drive].cylinders - 1; /* low part of number of cylinders */ | ||
642 | cmdb[6] = mfm_info[drive].heads - 1; /* Number of heads */ | ||
643 | cmdb[7] = mfm_info[drive].sectors - 1; /* Number of sectors */ | ||
644 | cmdb[8] = SPEC_SH; | ||
645 | cmdb[9] = 0x0a; /* gap length 1 */ | ||
646 | cmdb[10] = 0x0d; /* gap length 2 */ | ||
647 | cmdb[11] = 0x0c; /* gap length 3 */ | ||
648 | cmdb[12] = (mfm_info[drive].precomp - 1) >> 8; /* pre comp cylinder */ | ||
649 | cmdb[13] = mfm_info[drive].precomp - 1; | ||
650 | cmdb[14] = (mfm_info[drive].lowcurrent - 1) >> 8; /* Low current cylinder */ | ||
651 | cmdb[15] = mfm_info[drive].lowcurrent - 1; | ||
652 | } | ||
653 | |||
654 | static void mfm_specify (void) | ||
655 | { | ||
656 | unsigned char cmdb[16]; | ||
657 | |||
658 | DBG("specify...dev=%d lastspecified=%d\n", raw_cmd.dev, lastspecifieddrive); | ||
659 | mfm_setupspecify (raw_cmd.dev, cmdb); | ||
660 | |||
661 | issue_command (CMD_SPC, cmdb, 16); | ||
662 | /* Ensure that we will do another specify if we move to the other drive */ | ||
663 | lastspecifieddrive = raw_cmd.dev; | ||
664 | wait_for_completion(); | ||
665 | } | ||
666 | |||
667 | static void mfm_seek(void) | ||
668 | { | ||
669 | unsigned char cmdb[4]; | ||
670 | |||
671 | DBG("seeking...\n"); | ||
672 | if (MFM_DRV_INFO.cylinder < 0) { | ||
673 | do_mfm = mfm_recal_intr; | ||
674 | DBG("mfm_seek: about to call specify\n"); | ||
675 | mfm_specify (); /* DAG added this */ | ||
676 | |||
677 | cmdb[0] = raw_cmd.dev + 1; | ||
678 | cmdb[1] = 0; | ||
679 | |||
680 | issue_command(CMD_RCLB, cmdb, 2); | ||
681 | return; | ||
682 | } | ||
683 | if (MFM_DRV_INFO.cylinder != raw_cmd.cylinder) { | ||
684 | cmdb[0] = raw_cmd.dev + 1; | ||
685 | cmdb[1] = 0; /* raw_cmd.head; DAG: My data sheet says this should be 0 */ | ||
686 | cmdb[2] = raw_cmd.cylinder >> 8; | ||
687 | cmdb[3] = raw_cmd.cylinder; | ||
688 | |||
689 | do_mfm = mfm_seek_intr; | ||
690 | issue_command(CMD_SEK, cmdb, 4); | ||
691 | } else | ||
692 | mfm_setup_rw(); | ||
693 | } | ||
694 | |||
695 | static void mfm_initialise(void) | ||
696 | { | ||
697 | DBG("init...\n"); | ||
698 | mfm_seek(); | ||
699 | } | ||
700 | |||
701 | static void request_done(int uptodate) | ||
702 | { | ||
703 | DBG("mfm:request_done\n"); | ||
704 | if (uptodate) { | ||
705 | unsigned char block[2] = {0, 0}; | ||
706 | |||
707 | /* Apparently worked - let's check bytes left to DMA */ | ||
708 | if (hdc63463_dataleft != (PartFragRead_SectorsLeft * 256)) { | ||
709 | printk("mfm: request_done - dataleft=%d - should be %d - Eek!\n", hdc63463_dataleft, PartFragRead_SectorsLeft * 256); | ||
710 | end_request(CURRENT, 0); | ||
711 | Busy = 0; | ||
712 | }; | ||
713 | /* Potentially this means that we've done; but we might be doing | ||
714 | a partial access, (over two cylinders) or we may have a number | ||
715 | of fragments in an image file. First let's deal with partial accesss | ||
716 | */ | ||
717 | if (PartFragRead) { | ||
718 | /* Yep - a partial access */ | ||
719 | |||
720 | /* and issue the remainder */ | ||
721 | issue_request(PartFragRead_RestartBlock, PartFragRead_SectorsLeft, CURRENT); | ||
722 | return; | ||
723 | } | ||
724 | |||
725 | /* ah well - perhaps there is another fragment to go */ | ||
726 | |||
727 | /* Increment pointers/counts to start of next fragment */ | ||
728 | if (SectorsLeftInRequest > 0) printk("mfm: SectorsLeftInRequest>0 - Eek! Shouldn't happen!\n"); | ||
729 | |||
730 | /* No - its the end of the line */ | ||
731 | /* end_request's should have happened at the end of sector DMAs */ | ||
732 | /* Turns Drive LEDs off - may slow it down? */ | ||
733 | if (!elv_next_request(QUEUE)) | ||
734 | issue_command(CMD_CKV, block, 2); | ||
735 | |||
736 | Busy = 0; | ||
737 | DBG("request_done: About to mfm_request\n"); | ||
738 | /* Next one please */ | ||
739 | mfm_request(); /* Moved from mfm_rw_intr */ | ||
740 | DBG("request_done: returned from mfm_request\n"); | ||
741 | } else { | ||
742 | printk("mfm:request_done: update=0\n"); | ||
743 | end_request(CURRENT, 0); | ||
744 | Busy = 0; | ||
745 | } | ||
746 | } | ||
747 | |||
748 | static void error_handler(void) | ||
749 | { | ||
750 | printk("error detected... status = "); | ||
751 | print_status(); | ||
752 | (*errors)++; | ||
753 | if (*errors > MFM_DRV_INFO.errors.abort) | ||
754 | cont->done(0); | ||
755 | if (*errors > MFM_DRV_INFO.errors.recal) | ||
756 | MFM_DRV_INFO.cylinder = NEED_2_RECAL; | ||
757 | } | ||
758 | |||
759 | static void rw_interrupt(void) | ||
760 | { | ||
761 | printk("rw_interrupt\n"); | ||
762 | } | ||
763 | |||
764 | static struct cont rw_cont = | ||
765 | { | ||
766 | rw_interrupt, | ||
767 | error_handler, | ||
768 | mfm_rerequest, | ||
769 | request_done | ||
770 | }; | ||
771 | |||
772 | /* | ||
773 | * Actually gets round to issuing the request - note everything at this | ||
774 | * point is in 256 byte sectors not Linux 512 byte blocks | ||
775 | */ | ||
776 | static void issue_request(unsigned int block, unsigned int nsect, | ||
777 | struct request *req) | ||
778 | { | ||
779 | struct gendisk *disk = req->rq_disk; | ||
780 | struct mfm_info *p = disk->private_data; | ||
781 | int track, start_head, start_sector; | ||
782 | int sectors_to_next_cyl; | ||
783 | dev = p - mfm_info; | ||
784 | |||
785 | track = block / p->sectors; | ||
786 | start_sector = block % p->sectors; | ||
787 | start_head = track % p->heads; | ||
788 | |||
789 | /* First get the number of whole tracks which are free before the next | ||
790 | track */ | ||
791 | sectors_to_next_cyl = (p->heads - (start_head + 1)) * p->sectors; | ||
792 | /* Then add in the number of sectors left on this track */ | ||
793 | sectors_to_next_cyl += (p->sectors - start_sector); | ||
794 | |||
795 | DBG("issue_request: mfm_info[dev].sectors=%d track=%d\n", p->sectors, track); | ||
796 | |||
797 | raw_cmd.dev = dev; | ||
798 | raw_cmd.sector = start_sector; | ||
799 | raw_cmd.head = start_head; | ||
800 | raw_cmd.cylinder = track / p->heads; | ||
801 | raw_cmd.cmdtype = CURRENT->cmd; | ||
802 | raw_cmd.cmdcode = rq_data_dir(CURRENT) == WRITE ? CMD_WD : CMD_RD; | ||
803 | raw_cmd.cmddata[0] = dev + 1; /* DAG: +1 to get US */ | ||
804 | raw_cmd.cmddata[1] = raw_cmd.head; | ||
805 | raw_cmd.cmddata[2] = raw_cmd.cylinder >> 8; | ||
806 | raw_cmd.cmddata[3] = raw_cmd.cylinder; | ||
807 | raw_cmd.cmddata[4] = raw_cmd.head; | ||
808 | raw_cmd.cmddata[5] = raw_cmd.sector; | ||
809 | |||
810 | /* Was == and worked - how the heck??? */ | ||
811 | if (lastspecifieddrive != raw_cmd.dev) | ||
812 | mfm_specify (); | ||
813 | |||
814 | if (nsect <= sectors_to_next_cyl) { | ||
815 | raw_cmd.cmddata[6] = nsect >> 8; | ||
816 | raw_cmd.cmddata[7] = nsect; | ||
817 | PartFragRead = 0; /* All in one */ | ||
818 | PartFragRead_SectorsLeft = 0; /* Must set this - used in DMA calcs */ | ||
819 | } else { | ||
820 | raw_cmd.cmddata[6] = sectors_to_next_cyl >> 8; | ||
821 | raw_cmd.cmddata[7] = sectors_to_next_cyl; | ||
822 | PartFragRead = sectors_to_next_cyl; /* only do this many this time */ | ||
823 | PartFragRead_RestartBlock = block + sectors_to_next_cyl; /* Where to restart from */ | ||
824 | PartFragRead_SectorsLeft = nsect - sectors_to_next_cyl; | ||
825 | } | ||
826 | raw_cmd.cmdlen = 8; | ||
827 | |||
828 | /* Setup DMA pointers */ | ||
829 | hdc63463_dataptr = (unsigned int) Copy_buffer; | ||
830 | hdc63463_dataleft = nsect * 256; /* Better way? */ | ||
831 | |||
832 | DBG("mfm%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx (%p)\n", | ||
833 | raw_cmd.dev + 'a', rq_data_dir(CURRENT) == READ ? "read" : "writ", | ||
834 | raw_cmd.cylinder, | ||
835 | raw_cmd.head, | ||
836 | raw_cmd.sector, nsect, (unsigned long) Copy_buffer, CURRENT); | ||
837 | |||
838 | cont = &rw_cont; | ||
839 | errors = &(CURRENT->errors); | ||
840 | #if 0 | ||
841 | mfm_tq.routine = (void (*)(void *)) mfm_initialise; | ||
842 | queue_task(&mfm_tq, &tq_immediate); | ||
843 | mark_bh(IMMEDIATE_BH); | ||
844 | #else | ||
845 | mfm_initialise(); | ||
846 | #endif | ||
847 | } /* issue_request */ | ||
848 | |||
849 | /* | ||
850 | * Called when an error has just happened - need to trick mfm_request | ||
851 | * into thinking we weren't busy | ||
852 | * | ||
853 | * Turn off ints - mfm_request expects them this way | ||
854 | */ | ||
855 | static void mfm_rerequest(void) | ||
856 | { | ||
857 | DBG("mfm_rerequest\n"); | ||
858 | cli(); | ||
859 | Busy = 0; | ||
860 | mfm_request(); | ||
861 | } | ||
862 | |||
863 | static struct gendisk *mfm_gendisk[2]; | ||
864 | |||
865 | static void mfm_request(void) | ||
866 | { | ||
867 | DBG("mfm_request CURRENT=%p Busy=%d\n", CURRENT, Busy); | ||
868 | |||
869 | /* If we are still processing then return; we will get called again */ | ||
870 | if (Busy) { | ||
871 | /* Again seems to be common in 1.3.45 */ | ||
872 | /*DBG*/printk("mfm_request: Exiting due to busy\n"); | ||
873 | return; | ||
874 | } | ||
875 | Busy = 1; | ||
876 | |||
877 | while (1) { | ||
878 | unsigned int block, nsect; | ||
879 | struct gendisk *disk; | ||
880 | |||
881 | DBG("mfm_request: loop start\n"); | ||
882 | sti(); | ||
883 | |||
884 | DBG("mfm_request: before !CURRENT\n"); | ||
885 | |||
886 | if (!CURRENT) { | ||
887 | printk("mfm_request: Exiting due to empty queue (pre)\n"); | ||
888 | do_mfm = NULL; | ||
889 | Busy = 0; | ||
890 | return; | ||
891 | } | ||
892 | |||
893 | DBG("mfm_request: before arg extraction\n"); | ||
894 | |||
895 | disk = CURRENT->rq_disk; | ||
896 | block = CURRENT->sector; | ||
897 | nsect = CURRENT->nr_sectors; | ||
898 | if (block >= get_capacity(disk) || | ||
899 | block+nsect > get_capacity(disk)) { | ||
900 | printk("%s: bad access: block=%d, count=%d, nr_sects=%ld\n", | ||
901 | disk->disk_name, block, nsect, get_capacity(disk)); | ||
902 | printk("mfm: continue 1\n"); | ||
903 | end_request(CURRENT, 0); | ||
904 | Busy = 0; | ||
905 | continue; | ||
906 | } | ||
907 | |||
908 | /* DAG: Linux doesn't cope with this - even though it has an array telling | ||
909 | it the hardware block size - silly */ | ||
910 | block <<= 1; /* Now in 256 byte sectors */ | ||
911 | nsect <<= 1; /* Ditto */ | ||
912 | |||
913 | SectorsLeftInRequest = nsect >> 1; | ||
914 | Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2; | ||
915 | Copy_buffer = CURRENT->buffer; | ||
916 | Copy_Sector = CURRENT->sector << 1; | ||
917 | |||
918 | DBG("mfm_request: block after offset=%d\n", block); | ||
919 | |||
920 | issue_request(block, nsect, CURRENT); | ||
921 | |||
922 | break; | ||
923 | } | ||
924 | DBG("mfm_request: Dropping out bottom\n"); | ||
925 | } | ||
926 | |||
927 | static void do_mfm_request(struct request_queue *q) | ||
928 | { | ||
929 | DBG("do_mfm_request: about to mfm_request\n"); | ||
930 | mfm_request(); | ||
931 | } | ||
932 | |||
933 | static void mfm_interrupt_handler(int unused, void *dev_id) | ||
934 | { | ||
935 | void (*handler) (void) = do_mfm; | ||
936 | |||
937 | do_mfm = NULL; | ||
938 | |||
939 | DBG("mfm_interrupt_handler (handler=0x%p)\n", handler); | ||
940 | |||
941 | mfm_status = inw(MFM_STATUS); | ||
942 | |||
943 | /* If CPR (Command Parameter Reject) and not busy it means that the command | ||
944 | has some return message to give us */ | ||
945 | if ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR) { | ||
946 | int len = 0; | ||
947 | while (len < 16) { | ||
948 | int in; | ||
949 | in = inw(MFM_DATAIN); | ||
950 | result[len++] = in >> 8; | ||
951 | result[len++] = in; | ||
952 | } | ||
953 | } | ||
954 | if (handler) { | ||
955 | handler(); | ||
956 | return; | ||
957 | } | ||
958 | outw (CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ | ||
959 | printk ("mfm: unexpected interrupt - status = "); | ||
960 | print_status (); | ||
961 | while (1); | ||
962 | } | ||
963 | |||
964 | |||
965 | |||
966 | |||
967 | |||
968 | /* | ||
969 | * Tell the user about the drive if we decided it exists. | ||
970 | */ | ||
971 | static void mfm_geometry(int drive) | ||
972 | { | ||
973 | struct mfm_info *p = mfm_info + drive; | ||
974 | struct gendisk *disk = mfm_gendisk[drive]; | ||
975 | disk->private_data = p; | ||
976 | if (p->cylinders) | ||
977 | printk ("%s: %dMB CHS=%d/%d/%d LCC=%d RECOMP=%d\n", | ||
978 | disk->disk_name, | ||
979 | p->cylinders * p->heads * p->sectors / 4096, | ||
980 | p->cylinders, p->heads, p->sectors, | ||
981 | p->lowcurrent, p->precomp); | ||
982 | set_capacity(disk, p->cylinders * p->heads * p->sectors / 2); | ||
983 | } | ||
984 | |||
985 | #ifdef CONFIG_BLK_DEV_MFM_AUTODETECT | ||
986 | /* | ||
987 | * Attempt to detect a drive and find its geometry. The drive has already been | ||
988 | * specified... | ||
989 | * | ||
990 | * We first recalibrate the disk, then try to probe sectors, heads and then | ||
991 | * cylinders. NOTE! the cylinder probe may break drives. The xd disk driver | ||
992 | * does something along these lines, so I assume that most drives are up to | ||
993 | * this mistreatment... | ||
994 | */ | ||
995 | static int mfm_detectdrive (int drive) | ||
996 | { | ||
997 | unsigned int mingeo[3], maxgeo[3]; | ||
998 | unsigned int attribute, need_recal = 1; | ||
999 | unsigned char cmdb[8]; | ||
1000 | |||
1001 | memset (mingeo, 0, sizeof (mingeo)); | ||
1002 | maxgeo[0] = mfm_info[drive].sectors; | ||
1003 | maxgeo[1] = mfm_info[drive].heads; | ||
1004 | maxgeo[2] = mfm_info[drive].cylinders; | ||
1005 | |||
1006 | cmdb[0] = drive + 1; | ||
1007 | cmdb[6] = 0; | ||
1008 | cmdb[7] = 1; | ||
1009 | for (attribute = 0; attribute < 3; attribute++) { | ||
1010 | while (mingeo[attribute] != maxgeo[attribute]) { | ||
1011 | unsigned int variable; | ||
1012 | |||
1013 | variable = (maxgeo[attribute] + mingeo[attribute]) >> 1; | ||
1014 | cmdb[1] = cmdb[2] = cmdb[3] = cmdb[4] = cmdb[5] = 0; | ||
1015 | |||
1016 | if (need_recal) { | ||
1017 | int tries = 5; | ||
1018 | |||
1019 | do { | ||
1020 | issue_command (CMD_RCLB, cmdb, 2); | ||
1021 | wait_for_completion (); | ||
1022 | wait_for_command_end (); | ||
1023 | if (result[1] == 0x20) | ||
1024 | break; | ||
1025 | } while (result[1] && --tries); | ||
1026 | if (result[1]) { | ||
1027 | outw (CMD_RCAL, MFM_COMMAND); | ||
1028 | return 0; | ||
1029 | } | ||
1030 | need_recal = 0; | ||
1031 | } | ||
1032 | |||
1033 | switch (attribute) { | ||
1034 | case 0: | ||
1035 | cmdb[5] = variable; | ||
1036 | issue_command (CMD_CMPD, cmdb, 8); | ||
1037 | break; | ||
1038 | case 1: | ||
1039 | cmdb[1] = variable; | ||
1040 | cmdb[4] = variable; | ||
1041 | issue_command (CMD_CMPD, cmdb, 8); | ||
1042 | break; | ||
1043 | case 2: | ||
1044 | cmdb[2] = variable >> 8; | ||
1045 | cmdb[3] = variable; | ||
1046 | issue_command (CMD_SEK, cmdb, 4); | ||
1047 | break; | ||
1048 | } | ||
1049 | wait_for_completion (); | ||
1050 | wait_for_command_end (); | ||
1051 | |||
1052 | switch (result[1]) { | ||
1053 | case 0x00: | ||
1054 | case 0x50: | ||
1055 | mingeo[attribute] = variable + 1; | ||
1056 | break; | ||
1057 | |||
1058 | case 0x20: | ||
1059 | outw (CMD_RCAL, MFM_COMMAND); | ||
1060 | return 0; | ||
1061 | |||
1062 | case 0x24: | ||
1063 | need_recal = 1; | ||
1064 | default: | ||
1065 | maxgeo[attribute] = variable; | ||
1066 | break; | ||
1067 | } | ||
1068 | } | ||
1069 | } | ||
1070 | mfm_info[drive].cylinders = mingeo[2]; | ||
1071 | mfm_info[drive].lowcurrent = mingeo[2]; | ||
1072 | mfm_info[drive].precomp = mingeo[2] / 2; | ||
1073 | mfm_info[drive].heads = mingeo[1]; | ||
1074 | mfm_info[drive].sectors = mingeo[0]; | ||
1075 | outw (CMD_RCAL, MFM_COMMAND); | ||
1076 | return 1; | ||
1077 | } | ||
1078 | #endif | ||
1079 | |||
1080 | /* | ||
1081 | * Initialise all drive information for this controller. | ||
1082 | */ | ||
1083 | static int mfm_initdrives(void) | ||
1084 | { | ||
1085 | int drive; | ||
1086 | |||
1087 | if (number_mfm_drives > MFM_MAXDRIVES) { | ||
1088 | number_mfm_drives = MFM_MAXDRIVES; | ||
1089 | printk("No. of ADFS MFM drives is greater than MFM_MAXDRIVES - you can't have that many!\n"); | ||
1090 | } | ||
1091 | |||
1092 | for (drive = 0; drive < number_mfm_drives; drive++) { | ||
1093 | mfm_info[drive].lowcurrent = 1; | ||
1094 | mfm_info[drive].precomp = 1; | ||
1095 | mfm_info[drive].cylinder = -1; | ||
1096 | mfm_info[drive].errors.recal = 0; | ||
1097 | mfm_info[drive].errors.report = 0; | ||
1098 | mfm_info[drive].errors.abort = 4; | ||
1099 | |||
1100 | #ifdef CONFIG_BLK_DEV_MFM_AUTODETECT | ||
1101 | mfm_info[drive].cylinders = 1024; | ||
1102 | mfm_info[drive].heads = 8; | ||
1103 | mfm_info[drive].sectors = 64; | ||
1104 | { | ||
1105 | unsigned char cmdb[16]; | ||
1106 | |||
1107 | mfm_setupspecify (drive, cmdb); | ||
1108 | cmdb[1] &= ~0x81; | ||
1109 | issue_command (CMD_SPC, cmdb, 16); | ||
1110 | wait_for_completion (); | ||
1111 | if (!mfm_detectdrive (drive)) { | ||
1112 | mfm_info[drive].cylinders = 0; | ||
1113 | mfm_info[drive].heads = 0; | ||
1114 | mfm_info[drive].sectors = 0; | ||
1115 | } | ||
1116 | cmdb[0] = cmdb[1] = 0; | ||
1117 | issue_command (CMD_CKV, cmdb, 2); | ||
1118 | } | ||
1119 | #else | ||
1120 | mfm_info[drive].cylinders = 1; /* its going to have to figure it out from the partition info */ | ||
1121 | mfm_info[drive].heads = 4; | ||
1122 | mfm_info[drive].sectors = 32; | ||
1123 | #endif | ||
1124 | } | ||
1125 | return number_mfm_drives; | ||
1126 | } | ||
1127 | |||
1128 | |||
1129 | |||
1130 | /* | ||
1131 | * The 'front' end of the mfm driver follows... | ||
1132 | */ | ||
1133 | |||
1134 | static int mfm_getgeo(struct block_device *bdev, struct hd_geometry *geo) | ||
1135 | { | ||
1136 | struct mfm_info *p = bdev->bd_disk->private_data; | ||
1137 | |||
1138 | geo->heads = p->heads; | ||
1139 | geo->sectors = p->sectors; | ||
1140 | geo->cylinders = p->cylinders; | ||
1141 | return 0; | ||
1142 | } | ||
1143 | |||
1144 | /* | ||
1145 | * This is to handle various kernel command line parameters | ||
1146 | * specific to this driver. | ||
1147 | */ | ||
1148 | void mfm_setup(char *str, int *ints) | ||
1149 | { | ||
1150 | return; | ||
1151 | } | ||
1152 | |||
1153 | /* | ||
1154 | * Set the CHS from the ADFS boot block if it is present. This is not ideal | ||
1155 | * since if there are any non-ADFS partitions on the disk, this won't work! | ||
1156 | * Hence, I want to get rid of this... | ||
1157 | */ | ||
1158 | void xd_set_geometry(struct block_device *bdev, unsigned char secsptrack, | ||
1159 | unsigned char heads, unsigned int secsize) | ||
1160 | { | ||
1161 | struct mfm_info *p = bdev->bd_disk->private_data; | ||
1162 | int drive = p - mfm_info; | ||
1163 | unsigned long disksize = bdev->bd_inode->i_size; | ||
1164 | |||
1165 | if (p->cylinders == 1) { | ||
1166 | p->sectors = secsptrack; | ||
1167 | p->heads = heads; | ||
1168 | p->cylinders = discsize / (secsptrack * heads * secsize); | ||
1169 | |||
1170 | if ((heads < 1) || (p->cylinders > 1024)) { | ||
1171 | printk("%s: Insane disc shape! Setting to 512/4/32\n", | ||
1172 | bdev->bd_disk->disk_name); | ||
1173 | |||
1174 | /* These values are fairly arbitary, but are there so that if your | ||
1175 | * lucky you can pick apart your disc to find out what is going on - | ||
1176 | * I reckon these figures won't hurt MOST drives | ||
1177 | */ | ||
1178 | p->sectors = 32; | ||
1179 | p->heads = 4; | ||
1180 | p->cylinders = 512; | ||
1181 | } | ||
1182 | if (raw_cmd.dev == drive) | ||
1183 | mfm_specify (); | ||
1184 | mfm_geometry (drive); | ||
1185 | } | ||
1186 | } | ||
1187 | |||
1188 | static struct block_device_operations mfm_fops = | ||
1189 | { | ||
1190 | .owner = THIS_MODULE, | ||
1191 | .getgeo = mfm_getgeo, | ||
1192 | }; | ||
1193 | |||
1194 | /* | ||
1195 | * See if there is a controller at the address presently at mfm_addr | ||
1196 | * | ||
1197 | * We check to see if the controller is busy - if it is, we abort it first, | ||
1198 | * and check that the chip is no longer busy after at least 180 clock cycles. | ||
1199 | * We then issue a command and check that the BSY or CPR bits are set. | ||
1200 | */ | ||
1201 | static int mfm_probecontroller (unsigned int mfm_addr) | ||
1202 | { | ||
1203 | if (inw (MFM_STATUS) & STAT_BSY) { | ||
1204 | outw (CMD_ABT, MFM_COMMAND); | ||
1205 | udelay (50); | ||
1206 | if (inw (MFM_STATUS) & STAT_BSY) | ||
1207 | return 0; | ||
1208 | } | ||
1209 | |||
1210 | if (inw (MFM_STATUS) & STAT_CED) | ||
1211 | outw (CMD_RCAL, MFM_COMMAND); | ||
1212 | |||
1213 | outw (CMD_SEK, MFM_COMMAND); | ||
1214 | |||
1215 | if (inw (MFM_STATUS) & (STAT_BSY | STAT_CPR)) { | ||
1216 | unsigned int count = 2000; | ||
1217 | while (inw (MFM_STATUS) & STAT_BSY) { | ||
1218 | udelay (500); | ||
1219 | if (!--count) | ||
1220 | return 0; | ||
1221 | } | ||
1222 | |||
1223 | outw (CMD_RCAL, MFM_COMMAND); | ||
1224 | } | ||
1225 | return 1; | ||
1226 | } | ||
1227 | |||
1228 | static int mfm_do_init(unsigned char irqmask) | ||
1229 | { | ||
1230 | int i, ret; | ||
1231 | |||
1232 | printk("mfm: found at address %08X, interrupt %d\n", mfm_addr, mfm_irq); | ||
1233 | |||
1234 | ret = -EBUSY; | ||
1235 | if (!request_region (mfm_addr, 10, "mfm")) | ||
1236 | goto out1; | ||
1237 | |||
1238 | ret = register_blkdev(MAJOR_NR, "mfm"); | ||
1239 | if (ret) | ||
1240 | goto out2; | ||
1241 | |||
1242 | /* Stuff for the assembler routines to get to */ | ||
1243 | hdc63463_baseaddress = ioaddr(mfm_addr); | ||
1244 | hdc63463_irqpolladdress = mfm_IRQPollLoc; | ||
1245 | hdc63463_irqpollmask = irqmask; | ||
1246 | |||
1247 | mfm_queue = blk_init_queue(do_mfm_request, &mfm_lock); | ||
1248 | if (!mfm_queue) | ||
1249 | goto out2a; | ||
1250 | |||
1251 | Busy = 0; | ||
1252 | lastspecifieddrive = -1; | ||
1253 | |||
1254 | mfm_drives = mfm_initdrives(); | ||
1255 | if (!mfm_drives) { | ||
1256 | ret = -ENODEV; | ||
1257 | goto out3; | ||
1258 | } | ||
1259 | |||
1260 | for (i = 0; i < mfm_drives; i++) { | ||
1261 | struct gendisk *disk = alloc_disk(64); | ||
1262 | if (!disk) | ||
1263 | goto Enomem; | ||
1264 | disk->major = MAJOR_NR; | ||
1265 | disk->first_minor = i << 6; | ||
1266 | disk->fops = &mfm_fops; | ||
1267 | sprintf(disk->disk_name, "mfm%c", 'a'+i); | ||
1268 | mfm_gendisk[i] = disk; | ||
1269 | } | ||
1270 | |||
1271 | printk("mfm: detected %d hard drive%s\n", mfm_drives, | ||
1272 | mfm_drives == 1 ? "" : "s"); | ||
1273 | ret = request_irq(mfm_irq, mfm_interrupt_handler, IRQF_DISABLED, "MFM harddisk", NULL); | ||
1274 | if (ret) { | ||
1275 | printk("mfm: unable to get IRQ%d\n", mfm_irq); | ||
1276 | goto out4; | ||
1277 | } | ||
1278 | |||
1279 | if (mfm_irqenable) | ||
1280 | outw(0x80, mfm_irqenable); /* Required to enable IRQs from MFM podule */ | ||
1281 | |||
1282 | for (i = 0; i < mfm_drives; i++) { | ||
1283 | mfm_geometry(i); | ||
1284 | mfm_gendisk[i]->queue = mfm_queue; | ||
1285 | add_disk(mfm_gendisk[i]); | ||
1286 | } | ||
1287 | return 0; | ||
1288 | |||
1289 | out4: | ||
1290 | for (i = 0; i < mfm_drives; i++) | ||
1291 | put_disk(mfm_gendisk[i]); | ||
1292 | out3: | ||
1293 | blk_cleanup_queue(mfm_queue); | ||
1294 | out2a: | ||
1295 | unregister_blkdev(MAJOR_NR, "mfm"); | ||
1296 | out2: | ||
1297 | release_region(mfm_addr, 10); | ||
1298 | out1: | ||
1299 | return ret; | ||
1300 | Enomem: | ||
1301 | while (i--) | ||
1302 | put_disk(mfm_gendisk[i]); | ||
1303 | goto out3; | ||
1304 | } | ||
1305 | |||
1306 | static void mfm_do_exit(void) | ||
1307 | { | ||
1308 | int i; | ||
1309 | |||
1310 | free_irq(mfm_irq, NULL); | ||
1311 | for (i = 0; i < mfm_drives; i++) { | ||
1312 | del_gendisk(mfm_gendisk[i]); | ||
1313 | put_disk(mfm_gendisk[i]); | ||
1314 | } | ||
1315 | blk_cleanup_queue(mfm_queue); | ||
1316 | unregister_blkdev(MAJOR_NR, "mfm"); | ||
1317 | if (mfm_addr) | ||
1318 | release_region(mfm_addr, 10); | ||
1319 | } | ||
1320 | |||
1321 | static int __devinit mfm_probe(struct expansion_card *ec, struct ecard_id *id) | ||
1322 | { | ||
1323 | if (mfm_addr) | ||
1324 | return -EBUSY; | ||
1325 | |||
1326 | mfm_addr = ecard_address(ec, ECARD_IOC, ECARD_MEDIUM) + 0x800; | ||
1327 | mfm_IRQPollLoc = ioaddr(mfm_addr + 0x400); | ||
1328 | mfm_irqenable = mfm_IRQPollLoc; | ||
1329 | mfm_irq = ec->irq; | ||
1330 | |||
1331 | return mfm_do_init(0x08); | ||
1332 | } | ||
1333 | |||
1334 | static void __devexit mfm_remove(struct expansion_card *ec) | ||
1335 | { | ||
1336 | outw (0, mfm_irqenable); /* Required to enable IRQs from MFM podule */ | ||
1337 | mfm_do_exit(); | ||
1338 | } | ||
1339 | |||
1340 | static const struct ecard_id mfm_cids[] = { | ||
1341 | { MANU_ACORN, PROD_ACORN_MFM }, | ||
1342 | { 0xffff, 0xffff }, | ||
1343 | }; | ||
1344 | |||
1345 | static struct ecard_driver mfm_driver = { | ||
1346 | .probe = mfm_probe, | ||
1347 | .remove = __devexit(mfm_remove), | ||
1348 | .id_table = mfm_cids, | ||
1349 | .drv = { | ||
1350 | .name = "mfm", | ||
1351 | }, | ||
1352 | }; | ||
1353 | |||
1354 | /* | ||
1355 | * Look for a MFM controller - first check the motherboard, then the podules | ||
1356 | * The podules have an extra interrupt enable that needs to be played with | ||
1357 | * | ||
1358 | * The HDC is accessed at MEDIUM IOC speeds. | ||
1359 | */ | ||
1360 | static int __init mfm_init (void) | ||
1361 | { | ||
1362 | unsigned char irqmask; | ||
1363 | |||
1364 | if (mfm_probecontroller(ONBOARD_MFM_ADDRESS)) { | ||
1365 | mfm_addr = ONBOARD_MFM_ADDRESS; | ||
1366 | mfm_IRQPollLoc = IOC_IRQSTATB; | ||
1367 | mfm_irqenable = 0; | ||
1368 | mfm_irq = IRQ_HARDDISK; | ||
1369 | return mfm_do_init(0x08); /* IL3 pin */ | ||
1370 | } else { | ||
1371 | return ecard_register_driver(&mfm_driver); | ||
1372 | } | ||
1373 | } | ||
1374 | |||
1375 | static void __exit mfm_exit(void) | ||
1376 | { | ||
1377 | if (mfm_addr == ONBOARD_MFM_ADDRESS) | ||
1378 | mfm_do_exit(); | ||
1379 | else | ||
1380 | ecard_unregister_driver(&mfm_driver); | ||
1381 | } | ||
1382 | |||
1383 | module_init(mfm_init) | ||
1384 | module_exit(mfm_exit) | ||
1385 | MODULE_LICENSE("GPL"); | ||