diff options
Diffstat (limited to 'drivers/acorn/block/mfmhd.c')
-rw-r--r-- | drivers/acorn/block/mfmhd.c | 1385 |
1 files changed, 0 insertions, 1385 deletions
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"); | ||