Linux-2.6.12-rc2
[linux-2.6/next.git] / drivers / acorn / block / mfmhd.c
blob4b65f74d66b1a2465798618a347424260833c4cd
1 /*
2 * linux/arch/arm/drivers/block/mfmhd.c
4 * Copyright (C) 1995, 1996 Russell King, Dave Alan Gilbert (gilbertd@cs.man.ac.uk)
6 * MFM hard drive code [experimental]
7 */
9 /*
10 * Change list:
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.
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.
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.
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!
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.
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.
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
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).
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!!!!
60 * Added test for mainboard controller. - Removes need for separate
61 * define.
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.)
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.
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?
78 *16/11/96:DAG: Modified for 2.0.18; request_irq changed
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)!
87 * 17/1/97:RMK: Upgraded to 2.1 kernels.
89 * 4/3/98:RMK: Changed major number to 21.
91 * 27/6/98:RMK: Changed asm/delay.h to linux/delay.h for mdelay().
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.
101 #include <linux/module.h>
102 #include <linux/config.h>
103 #include <linux/sched.h>
104 #include <linux/fs.h>
105 #include <linux/interrupt.h>
106 #include <linux/kernel.h>
107 #include <linux/timer.h>
108 #include <linux/mm.h>
109 #include <linux/errno.h>
110 #include <linux/genhd.h>
111 #include <linux/major.h>
112 #include <linux/ioport.h>
113 #include <linux/delay.h>
114 #include <linux/blkpg.h>
116 #include <asm/system.h>
117 #include <asm/io.h>
118 #include <asm/irq.h>
119 #include <asm/uaccess.h>
120 #include <asm/dma.h>
121 #include <asm/hardware.h>
122 #include <asm/ecard.h>
123 #include <asm/hardware/ioc.h>
125 static void (*do_mfm)(void) = NULL;
126 static struct request_queue *mfm_queue;
127 static DEFINE_SPINLOCK(mfm_lock);
129 #define MAJOR_NR MFM_ACORN_MAJOR
130 #define QUEUE (mfm_queue)
131 #define CURRENT elv_next_request(mfm_queue)
133 * This sort of stuff should be in a header file shared with ide.c, hd.c, xd.c etc
135 #ifndef HDIO_GETGEO
136 #define HDIO_GETGEO 0x301
137 struct hd_geometry {
138 unsigned char heads;
139 unsigned char sectors;
140 unsigned short cylinders;
141 unsigned long start;
143 #endif
147 * Configuration section
149 * This is the maximum number of drives that we accept
151 #define MFM_MAXDRIVES 2
153 * Linux I/O address of onboard MFM controller or 0 to disable this
155 #define ONBOARD_MFM_ADDRESS ((0x002d0000 >> 2) | 0x80000000)
157 * Uncomment this to enable debugging in the MFM driver...
159 #ifndef DEBUG
160 /*#define DEBUG */
161 #endif
163 * End of configuration
168 * This structure contains all information to do with a particular physical
169 * device.
171 struct mfm_info {
172 unsigned char sectors;
173 unsigned char heads;
174 unsigned short cylinders;
175 unsigned short lowcurrent;
176 unsigned short precomp;
177 #define NO_TRACK -1
178 #define NEED_1_RECAL -2
179 #define NEED_2_RECAL -3
180 int cylinder;
181 struct {
182 char recal;
183 char report;
184 char abort;
185 } errors;
186 } mfm_info[MFM_MAXDRIVES];
188 #define MFM_DRV_INFO mfm_info[raw_cmd.dev]
190 /* Stuff from the assembly routines */
191 extern unsigned int hdc63463_baseaddress; /* Controller base address */
192 extern unsigned int hdc63463_irqpolladdress; /* Address to read to test for int */
193 extern unsigned int hdc63463_irqpollmask; /* Mask for irq register */
194 extern unsigned int hdc63463_dataptr; /* Pointer to kernel data space to DMA */
195 extern int hdc63463_dataleft; /* Number of bytes left to transfer */
200 static int lastspecifieddrive;
201 static unsigned Busy;
203 static unsigned int PartFragRead; /* The number of sectors which have been read
204 during a partial read split over two
205 cylinders. If 0 it means a partial
206 read did not occur. */
208 static unsigned int PartFragRead_RestartBlock; /* Where to restart on a split access */
209 static unsigned int PartFragRead_SectorsLeft; /* Where to restart on a split access */
211 static int Sectors256LeftInCurrent; /* i.e. 256 byte sectors left in current */
212 static int SectorsLeftInRequest; /* i.e. blocks left in the thing mfm_request was called for */
213 static int Copy_Sector; /* The 256 byte sector we are currently at - fragments need to know
214 where to take over */
215 static char *Copy_buffer;
218 static void mfm_seek(void);
219 static void mfm_rerequest(void);
220 static void mfm_request(void);
221 static void mfm_specify (void);
222 static void issue_request(unsigned int block, unsigned int nsect,
223 struct request *req);
225 static unsigned int mfm_addr; /* Controller address */
226 static unsigned int mfm_IRQPollLoc; /* Address to read for IRQ information */
227 static unsigned int mfm_irqenable; /* Podule IRQ enable location */
228 static unsigned char mfm_irq; /* Interrupt number */
229 static int mfm_drives = 0; /* drives available */
230 static int mfm_status = 0; /* interrupt status */
231 static int *errors;
233 static struct rawcmd {
234 unsigned int dev;
235 unsigned int cylinder;
236 unsigned int head;
237 unsigned int sector;
238 unsigned int cmdtype;
239 unsigned int cmdcode;
240 unsigned char cmddata[16];
241 unsigned int cmdlen;
242 } raw_cmd;
244 static unsigned char result[16];
246 static struct cont {
247 void (*interrupt) (void); /* interrupt handler */
248 void (*error) (void); /* error handler */
249 void (*redo) (void); /* redo handler */
250 void (*done) (int st); /* done handler */
251 } *cont = NULL;
253 #if 0
254 static struct tq_struct mfm_tq = {0, 0, (void (*)(void *)) NULL, 0};
255 #endif
257 int number_mfm_drives = 1;
259 /* ------------------------------------------------------------------------------------------ */
261 * From the HD63463 data sheet from Hitachi Ltd.
264 #define MFM_COMMAND (mfm_addr + 0)
265 #define MFM_DATAOUT (mfm_addr + 1)
266 #define MFM_STATUS (mfm_addr + 8)
267 #define MFM_DATAIN (mfm_addr + 9)
269 #define CMD_ABT 0xF0 /* Abort */
270 #define CMD_SPC 0xE8 /* Specify */
271 #define CMD_TST 0xE0 /* Test */
272 #define CMD_RCLB 0xC8 /* Recalibrate */
273 #define CMD_SEK 0xC0 /* Seek */
274 #define CMD_WFS 0xAB /* Write Format Skew */
275 #define CMD_WFM 0xA3 /* Write Format */
276 #define CMD_MTB 0x90 /* Memory to buffer */
277 #define CMD_CMPD 0x88 /* Compare data */
278 #define CMD_WD 0x87 /* Write data */
279 #define CMD_RED 0x70 /* Read erroneous data */
280 #define CMD_RIS 0x68 /* Read ID skew */
281 #define CMD_FID 0x61 /* Find ID */
282 #define CMD_RID 0x60 /* Read ID */
283 #define CMD_BTM 0x50 /* Buffer to memory */
284 #define CMD_CKD 0x48 /* Check data */
285 #define CMD_RD 0x40 /* Read data */
286 #define CMD_OPBW 0x38 /* Open buffer write */
287 #define CMD_OPBR 0x30 /* Open buffer read */
288 #define CMD_CKV 0x28 /* Check drive */
289 #define CMD_CKE 0x20 /* Check ECC */
290 #define CMD_POD 0x18 /* Polling disable */
291 #define CMD_POL 0x10 /* Polling enable */
292 #define CMD_RCAL 0x08 /* Recall */
294 #define STAT_BSY 0x8000 /* Busy */
295 #define STAT_CPR 0x4000 /* Command Parameter Rejection */
296 #define STAT_CED 0x2000 /* Command end */
297 #define STAT_SED 0x1000 /* Seek end */
298 #define STAT_DER 0x0800 /* Drive error */
299 #define STAT_ABN 0x0400 /* Abnormal end */
300 #define STAT_POL 0x0200 /* Polling */
302 /* ------------------------------------------------------------------------------------------ */
303 #ifdef DEBUG
304 static void console_printf(const char *fmt,...)
306 static char buffer[2048]; /* Arbitary! */
307 extern void console_print(const char *);
308 unsigned long flags;
309 va_list ap;
311 local_irq_save(flags);
313 va_start(ap, fmt);
314 vsprintf(buffer, fmt, ap);
315 console_print(buffer);
316 va_end(fmt);
318 local_irq_restore(flags);
319 }; /* console_printf */
321 #define DBG(x...) console_printf(x)
322 #else
323 #define DBG(x...)
324 #endif
326 static void print_status(void)
328 char *error;
329 static char *errors[] = {
330 "no error",
331 "command aborted",
332 "invalid command",
333 "parameter error",
334 "not initialised",
335 "rejected TEST",
336 "no useld",
337 "write fault",
338 "not ready",
339 "no scp",
340 "in seek",
341 "invalid NCA",
342 "invalid step rate",
343 "seek error",
344 "over run",
345 "invalid PHA",
346 "data field EEC error",
347 "data field CRC error",
348 "error corrected",
349 "data field fatal error",
350 "no data am",
351 "not hit",
352 "ID field CRC error",
353 "time over",
354 "no ID am",
355 "not writable"
357 if (result[1] < 0x65)
358 error = errors[result[1] >> 2];
359 else
360 error = "unknown";
361 printk("(");
362 if (mfm_status & STAT_BSY) printk("BSY ");
363 if (mfm_status & STAT_CPR) printk("CPR ");
364 if (mfm_status & STAT_CED) printk("CED ");
365 if (mfm_status & STAT_SED) printk("SED ");
366 if (mfm_status & STAT_DER) printk("DER ");
367 if (mfm_status & STAT_ABN) printk("ABN ");
368 if (mfm_status & STAT_POL) printk("POL ");
369 printk(") SSB = %X (%s)\n", result[1], error);
373 /* ------------------------------------------------------------------------------------- */
375 static void issue_command(int command, unsigned char *cmdb, int len)
377 int status;
378 #ifdef DEBUG
379 int i;
380 console_printf("issue_command: %02X: ", command);
381 for (i = 0; i < len; i++)
382 console_printf("%02X ", cmdb[i]);
383 console_printf("\n");
384 #endif
386 do {
387 status = inw(MFM_STATUS);
388 } while (status & (STAT_BSY | STAT_POL));
389 DBG("issue_command: status after pol/bsy loop: %02X:\n ", status >> 8);
391 if (status & (STAT_CPR | STAT_CED | STAT_SED | STAT_DER | STAT_ABN)) {
392 outw(CMD_RCAL, MFM_COMMAND);
393 while (inw(MFM_STATUS) & STAT_BSY);
395 status = inw(MFM_STATUS);
396 DBG("issue_command: status before parameter issue: %02X:\n ", status >> 8);
398 while (len > 0) {
399 outw(cmdb[1] | (cmdb[0] << 8), MFM_DATAOUT);
400 len -= 2;
401 cmdb += 2;
403 status = inw(MFM_STATUS);
404 DBG("issue_command: status before command issue: %02X:\n ", status >> 8);
406 outw(command, MFM_COMMAND);
407 status = inw(MFM_STATUS);
408 DBG("issue_command: status immediately after command issue: %02X:\n ", status >> 8);
411 static void wait_for_completion(void)
413 while ((mfm_status = inw(MFM_STATUS)) & STAT_BSY);
416 static void wait_for_command_end(void)
418 int i;
420 while (!((mfm_status = inw(MFM_STATUS)) & STAT_CED));
422 for (i = 0; i < 16;) {
423 int in;
424 in = inw(MFM_DATAIN);
425 result[i++] = in >> 8;
426 result[i++] = in;
428 outw (CMD_RCAL, MFM_COMMAND);
431 /* ------------------------------------------------------------------------------------- */
433 static void mfm_rw_intr(void)
435 int old_status; /* Holds status on entry, we read to see if the command just finished */
436 #ifdef DEBUG
437 console_printf("mfm_rw_intr...dataleft=%d\n", hdc63463_dataleft);
438 print_status();
439 #endif
441 /* Now don't handle the error until BSY drops */
442 if ((mfm_status & (STAT_DER | STAT_ABN)) && ((mfm_status&STAT_BSY)==0)) {
443 /* Something has gone wrong - let's try that again */
444 outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
445 if (cont) {
446 DBG("mfm_rw_intr: DER/ABN err\n");
447 cont->error();
448 cont->redo();
450 return;
453 /* OK so what ever happened it's not an error, now I reckon we are left between
454 a choice of command end or some data which is ready to be collected */
455 /* I think we have to transfer data while the interrupt line is on and its
456 not any other type of interrupt */
457 if (CURRENT->cmd == WRITE) {
458 extern void hdc63463_writedma(void);
459 if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) {
460 printk("mfm_rw_intr: Apparent DMA write request when no more to DMA\n");
461 if (cont) {
462 cont->error();
463 cont->redo();
465 return;
467 hdc63463_writedma();
468 } else {
469 extern void hdc63463_readdma(void);
470 if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) {
471 printk("mfm_rw_intr: Apparent DMA read request when no more to DMA\n");
472 if (cont) {
473 cont->error();
474 cont->redo();
476 return;
478 DBG("Going to try read dma..............status=0x%x, buffer=%p\n", mfm_status, hdc63463_dataptr);
479 hdc63463_readdma();
480 }; /* Read */
482 if (hdc63463_dataptr != ((unsigned int) Copy_buffer + 256)) {
483 /* If we didn't actually manage to get any data on this interrupt - but why? We got the interrupt */
484 /* Ah - well looking at the status its just when we get command end; so no problem */
485 /*console_printf("mfm: dataptr mismatch. dataptr=0x%08x Copy_buffer+256=0x%08p\n",
486 hdc63463_dataptr,Copy_buffer+256);
487 print_status(); */
488 } else {
489 Sectors256LeftInCurrent--;
490 Copy_buffer += 256;
491 Copy_Sector++;
493 /* We have come to the end of this request */
494 if (!Sectors256LeftInCurrent) {
495 DBG("mfm: end_request for CURRENT=0x%p CURRENT(sector=%d current_nr_sectors=%d nr_sectors=%d)\n",
496 CURRENT, CURRENT->sector, CURRENT->current_nr_sectors, CURRENT->nr_sectors);
498 CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
499 CURRENT->sector += CURRENT->current_nr_sectors;
500 SectorsLeftInRequest -= CURRENT->current_nr_sectors;
502 end_request(CURRENT, 1);
503 if (SectorsLeftInRequest) {
504 hdc63463_dataptr = (unsigned int) CURRENT->buffer;
505 Copy_buffer = CURRENT->buffer;
506 Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2;
507 errors = &(CURRENT->errors);
508 /* These should match the present calculations of the next logical sector
509 on the device
510 Copy_Sector=CURRENT->sector*2; */
512 if (Copy_Sector != CURRENT->sector * 2)
513 #ifdef DEBUG
514 /*console_printf*/printk("mfm: Copy_Sector mismatch. Copy_Sector=%d CURRENT->sector*2=%d\n",
515 Copy_Sector, CURRENT->sector * 2);
516 #else
517 printk("mfm: Copy_Sector mismatch! Eek!\n");
518 #endif
519 }; /* CURRENT */
520 }; /* Sectors256LeftInCurrent */
523 old_status = mfm_status;
524 mfm_status = inw(MFM_STATUS);
525 if (mfm_status & (STAT_DER | STAT_ABN)) {
526 /* Something has gone wrong - let's try that again */
527 if (cont) {
528 DBG("mfm_rw_intr: DER/ABN error\n");
529 cont->error();
530 cont->redo();
532 return;
535 /* If this code wasn't entered due to command_end but there is
536 now a command end we must read the command results out. If it was
537 entered like this then mfm_interrupt_handler would have done the
538 job. */
539 if ((!((old_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) &&
540 ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) {
541 int len = 0;
542 while (len < 16) {
543 int in;
544 in = inw(MFM_DATAIN);
545 result[len++] = in >> 8;
546 result[len++] = in;
548 }; /* Result read */
550 /*console_printf ("mfm_rw_intr nearexit [%02X]\n", __raw_readb(mfm_IRQPollLoc)); */
552 /* If end of command move on */
553 if (mfm_status & (STAT_CED)) {
554 outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
555 /* End of command - trigger the next command */
556 if (cont) {
557 cont->done(1);
559 DBG("mfm_rw_intr: returned from cont->done\n");
560 } else {
561 /* Its going to generate another interrupt */
562 do_mfm = mfm_rw_intr;
566 static void mfm_setup_rw(void)
568 DBG("setting up for rw...\n");
570 do_mfm = mfm_rw_intr;
571 issue_command(raw_cmd.cmdcode, raw_cmd.cmddata, raw_cmd.cmdlen);
574 static void mfm_recal_intr(void)
576 #ifdef DEBUG
577 console_printf("recal intr - status = ");
578 print_status();
579 #endif
580 outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
581 if (mfm_status & (STAT_DER | STAT_ABN)) {
582 printk("recal failed\n");
583 MFM_DRV_INFO.cylinder = NEED_2_RECAL;
584 if (cont) {
585 cont->error();
586 cont->redo();
588 return;
590 /* Thats seek end - we are finished */
591 if (mfm_status & STAT_SED) {
592 issue_command(CMD_POD, NULL, 0);
593 MFM_DRV_INFO.cylinder = 0;
594 mfm_seek();
595 return;
597 /* Command end without seek end (see data sheet p.20) for parallel seek
598 - we have to send a POL command to wait for the seek */
599 if (mfm_status & STAT_CED) {
600 do_mfm = mfm_recal_intr;
601 issue_command(CMD_POL, NULL, 0);
602 return;
604 printk("recal: unknown status\n");
607 static void mfm_seek_intr(void)
609 #ifdef DEBUG
610 console_printf("seek intr - status = ");
611 print_status();
612 #endif
613 outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
614 if (mfm_status & (STAT_DER | STAT_ABN)) {
615 printk("seek failed\n");
616 MFM_DRV_INFO.cylinder = NEED_2_RECAL;
617 if (cont) {
618 cont->error();
619 cont->redo();
621 return;
623 if (mfm_status & STAT_SED) {
624 issue_command(CMD_POD, NULL, 0);
625 MFM_DRV_INFO.cylinder = raw_cmd.cylinder;
626 mfm_seek();
627 return;
629 if (mfm_status & STAT_CED) {
630 do_mfm = mfm_seek_intr;
631 issue_command(CMD_POL, NULL, 0);
632 return;
634 printk("seek: unknown status\n");
637 /* IDEA2 seems to work better - its what RiscOS sets my
638 * disc to - on its SECOND call to specify!
640 #define IDEA2
641 #ifndef IDEA2
642 #define SPEC_SL 0x16
643 #define SPEC_SH 0xa9 /* Step pulse high=21, Record Length=001 (256 bytes) */
644 #else
645 #define SPEC_SL 0x00 /* OM2 - SL - step pulse low */
646 #define SPEC_SH 0x21 /* Step pulse high=4, Record Length=001 (256 bytes) */
647 #endif
649 static void mfm_setupspecify (int drive, unsigned char *cmdb)
651 cmdb[0] = 0x1f; /* OM0 - !SECT,!MOD,!DIF,PADP,ECD,CRCP,CRCI,ACOR */
652 cmdb[1] = 0xc3; /* OM1 - DTM,BRST,!CEDM,!SEDM,!DERM,0,AMEX,PSK */
653 cmdb[2] = SPEC_SL; /* OM2 - SL - step pulse low */
654 cmdb[3] = (number_mfm_drives == 1) ? 0x02 : 0x06; /* 1 or 2 drives */
655 cmdb[4] = 0xfc | ((mfm_info[drive].cylinders - 1) >> 8);/* RW time over/high part of number of cylinders */
656 cmdb[5] = mfm_info[drive].cylinders - 1; /* low part of number of cylinders */
657 cmdb[6] = mfm_info[drive].heads - 1; /* Number of heads */
658 cmdb[7] = mfm_info[drive].sectors - 1; /* Number of sectors */
659 cmdb[8] = SPEC_SH;
660 cmdb[9] = 0x0a; /* gap length 1 */
661 cmdb[10] = 0x0d; /* gap length 2 */
662 cmdb[11] = 0x0c; /* gap length 3 */
663 cmdb[12] = (mfm_info[drive].precomp - 1) >> 8; /* pre comp cylinder */
664 cmdb[13] = mfm_info[drive].precomp - 1;
665 cmdb[14] = (mfm_info[drive].lowcurrent - 1) >> 8; /* Low current cylinder */
666 cmdb[15] = mfm_info[drive].lowcurrent - 1;
669 static void mfm_specify (void)
671 unsigned char cmdb[16];
673 DBG("specify...dev=%d lastspecified=%d\n", raw_cmd.dev, lastspecifieddrive);
674 mfm_setupspecify (raw_cmd.dev, cmdb);
676 issue_command (CMD_SPC, cmdb, 16);
677 /* Ensure that we will do another specify if we move to the other drive */
678 lastspecifieddrive = raw_cmd.dev;
679 wait_for_completion();
682 static void mfm_seek(void)
684 unsigned char cmdb[4];
686 DBG("seeking...\n");
687 if (MFM_DRV_INFO.cylinder < 0) {
688 do_mfm = mfm_recal_intr;
689 DBG("mfm_seek: about to call specify\n");
690 mfm_specify (); /* DAG added this */
692 cmdb[0] = raw_cmd.dev + 1;
693 cmdb[1] = 0;
695 issue_command(CMD_RCLB, cmdb, 2);
696 return;
698 if (MFM_DRV_INFO.cylinder != raw_cmd.cylinder) {
699 cmdb[0] = raw_cmd.dev + 1;
700 cmdb[1] = 0; /* raw_cmd.head; DAG: My data sheet says this should be 0 */
701 cmdb[2] = raw_cmd.cylinder >> 8;
702 cmdb[3] = raw_cmd.cylinder;
704 do_mfm = mfm_seek_intr;
705 issue_command(CMD_SEK, cmdb, 4);
706 } else
707 mfm_setup_rw();
710 static void mfm_initialise(void)
712 DBG("init...\n");
713 mfm_seek();
716 static void request_done(int uptodate)
718 DBG("mfm:request_done\n");
719 if (uptodate) {
720 unsigned char block[2] = {0, 0};
722 /* Apparently worked - let's check bytes left to DMA */
723 if (hdc63463_dataleft != (PartFragRead_SectorsLeft * 256)) {
724 printk("mfm: request_done - dataleft=%d - should be %d - Eek!\n", hdc63463_dataleft, PartFragRead_SectorsLeft * 256);
725 end_request(CURRENT, 0);
726 Busy = 0;
728 /* Potentially this means that we've done; but we might be doing
729 a partial access, (over two cylinders) or we may have a number
730 of fragments in an image file. First let's deal with partial accesss
732 if (PartFragRead) {
733 /* Yep - a partial access */
735 /* and issue the remainder */
736 issue_request(PartFragRead_RestartBlock, PartFragRead_SectorsLeft, CURRENT);
737 return;
740 /* ah well - perhaps there is another fragment to go */
742 /* Increment pointers/counts to start of next fragment */
743 if (SectorsLeftInRequest > 0) printk("mfm: SectorsLeftInRequest>0 - Eek! Shouldn't happen!\n");
745 /* No - its the end of the line */
746 /* end_request's should have happened at the end of sector DMAs */
747 /* Turns Drive LEDs off - may slow it down? */
748 if (!elv_next_request(QUEUE))
749 issue_command(CMD_CKV, block, 2);
751 Busy = 0;
752 DBG("request_done: About to mfm_request\n");
753 /* Next one please */
754 mfm_request(); /* Moved from mfm_rw_intr */
755 DBG("request_done: returned from mfm_request\n");
756 } else {
757 printk("mfm:request_done: update=0\n");
758 end_request(CURRENT, 0);
759 Busy = 0;
763 static void error_handler(void)
765 printk("error detected... status = ");
766 print_status();
767 (*errors)++;
768 if (*errors > MFM_DRV_INFO.errors.abort)
769 cont->done(0);
770 if (*errors > MFM_DRV_INFO.errors.recal)
771 MFM_DRV_INFO.cylinder = NEED_2_RECAL;
774 static void rw_interrupt(void)
776 printk("rw_interrupt\n");
779 static struct cont rw_cont =
781 rw_interrupt,
782 error_handler,
783 mfm_rerequest,
784 request_done
788 * Actually gets round to issuing the request - note everything at this
789 * point is in 256 byte sectors not Linux 512 byte blocks
791 static void issue_request(unsigned int block, unsigned int nsect,
792 struct request *req)
794 struct gendisk *disk = req->rq_disk;
795 struct mfm_info *p = disk->private_data;
796 int track, start_head, start_sector;
797 int sectors_to_next_cyl;
798 dev = p - mfm_info;
800 track = block / p->sectors;
801 start_sector = block % p->sectors;
802 start_head = track % p->heads;
804 /* First get the number of whole tracks which are free before the next
805 track */
806 sectors_to_next_cyl = (p->heads - (start_head + 1)) * p->sectors;
807 /* Then add in the number of sectors left on this track */
808 sectors_to_next_cyl += (p->sectors - start_sector);
810 DBG("issue_request: mfm_info[dev].sectors=%d track=%d\n", p->sectors, track);
812 raw_cmd.dev = dev;
813 raw_cmd.sector = start_sector;
814 raw_cmd.head = start_head;
815 raw_cmd.cylinder = track / p->heads;
816 raw_cmd.cmdtype = CURRENT->cmd;
817 raw_cmd.cmdcode = CURRENT->cmd == WRITE ? CMD_WD : CMD_RD;
818 raw_cmd.cmddata[0] = dev + 1; /* DAG: +1 to get US */
819 raw_cmd.cmddata[1] = raw_cmd.head;
820 raw_cmd.cmddata[2] = raw_cmd.cylinder >> 8;
821 raw_cmd.cmddata[3] = raw_cmd.cylinder;
822 raw_cmd.cmddata[4] = raw_cmd.head;
823 raw_cmd.cmddata[5] = raw_cmd.sector;
825 /* Was == and worked - how the heck??? */
826 if (lastspecifieddrive != raw_cmd.dev)
827 mfm_specify ();
829 if (nsect <= sectors_to_next_cyl) {
830 raw_cmd.cmddata[6] = nsect >> 8;
831 raw_cmd.cmddata[7] = nsect;
832 PartFragRead = 0; /* All in one */
833 PartFragRead_SectorsLeft = 0; /* Must set this - used in DMA calcs */
834 } else {
835 raw_cmd.cmddata[6] = sectors_to_next_cyl >> 8;
836 raw_cmd.cmddata[7] = sectors_to_next_cyl;
837 PartFragRead = sectors_to_next_cyl; /* only do this many this time */
838 PartFragRead_RestartBlock = block + sectors_to_next_cyl; /* Where to restart from */
839 PartFragRead_SectorsLeft = nsect - sectors_to_next_cyl;
841 raw_cmd.cmdlen = 8;
843 /* Setup DMA pointers */
844 hdc63463_dataptr = (unsigned int) Copy_buffer;
845 hdc63463_dataleft = nsect * 256; /* Better way? */
847 DBG("mfm%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx (%p)\n",
848 raw_cmd.dev + 'a', (CURRENT->cmd == READ) ? "read" : "writ",
849 raw_cmd.cylinder,
850 raw_cmd.head,
851 raw_cmd.sector, nsect, (unsigned long) Copy_buffer, CURRENT);
853 cont = &rw_cont;
854 errors = &(CURRENT->errors);
855 #if 0
856 mfm_tq.routine = (void (*)(void *)) mfm_initialise;
857 queue_task(&mfm_tq, &tq_immediate);
858 mark_bh(IMMEDIATE_BH);
859 #else
860 mfm_initialise();
861 #endif
862 } /* issue_request */
865 * Called when an error has just happened - need to trick mfm_request
866 * into thinking we weren't busy
868 * Turn off ints - mfm_request expects them this way
870 static void mfm_rerequest(void)
872 DBG("mfm_rerequest\n");
873 cli();
874 Busy = 0;
875 mfm_request();
878 static struct gendisk *mfm_gendisk[2];
880 static void mfm_request(void)
882 DBG("mfm_request CURRENT=%p Busy=%d\n", CURRENT, Busy);
884 /* If we are still processing then return; we will get called again */
885 if (Busy) {
886 /* Again seems to be common in 1.3.45 */
887 /*DBG*/printk("mfm_request: Exiting due to busy\n");
888 return;
890 Busy = 1;
892 while (1) {
893 unsigned int block, nsect;
894 struct gendisk *disk;
896 DBG("mfm_request: loop start\n");
897 sti();
899 DBG("mfm_request: before !CURRENT\n");
901 if (!CURRENT) {
902 printk("mfm_request: Exiting due to empty queue (pre)\n");
903 do_mfm = NULL;
904 Busy = 0;
905 return;
908 DBG("mfm_request: before arg extraction\n");
910 disk = CURRENT->rq_disk;
911 block = CURRENT->sector;
912 nsect = CURRENT->nr_sectors;
913 if (block >= get_capacity(disk) ||
914 block+nsect > get_capacity(disk)) {
915 printk("%s: bad access: block=%d, count=%d, nr_sects=%ld\n",
916 disk->disk_name, block, nsect, get_capacity(disk));
917 printk("mfm: continue 1\n");
918 end_request(CURRENT, 0);
919 Busy = 0;
920 continue;
923 /* DAG: Linux doesn't cope with this - even though it has an array telling
924 it the hardware block size - silly */
925 block <<= 1; /* Now in 256 byte sectors */
926 nsect <<= 1; /* Ditto */
928 SectorsLeftInRequest = nsect >> 1;
929 Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2;
930 Copy_buffer = CURRENT->buffer;
931 Copy_Sector = CURRENT->sector << 1;
933 DBG("mfm_request: block after offset=%d\n", block);
935 if (CURRENT->cmd != READ && CURRENT->cmd != WRITE) {
936 printk("unknown mfm-command %d\n", CURRENT->cmd);
937 end_request(CURRENT, 0);
938 Busy = 0;
939 printk("mfm: continue 4\n");
940 continue;
942 issue_request(block, nsect, CURRENT);
944 break;
946 DBG("mfm_request: Dropping out bottom\n");
949 static void do_mfm_request(request_queue_t *q)
951 DBG("do_mfm_request: about to mfm_request\n");
952 mfm_request();
955 static void mfm_interrupt_handler(int unused, void *dev_id, struct pt_regs *regs)
957 void (*handler) (void) = do_mfm;
959 do_mfm = NULL;
961 DBG("mfm_interrupt_handler (handler=0x%p)\n", handler);
963 mfm_status = inw(MFM_STATUS);
965 /* If CPR (Command Parameter Reject) and not busy it means that the command
966 has some return message to give us */
967 if ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR) {
968 int len = 0;
969 while (len < 16) {
970 int in;
971 in = inw(MFM_DATAIN);
972 result[len++] = in >> 8;
973 result[len++] = in;
976 if (handler) {
977 handler();
978 return;
980 outw (CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
981 printk ("mfm: unexpected interrupt - status = ");
982 print_status ();
983 while (1);
991 * Tell the user about the drive if we decided it exists.
993 static void mfm_geometry(int drive)
995 struct mfm_info *p = mfm_info + drive;
996 struct gendisk *disk = mfm_gendisk[drive];
997 disk->private_data = p;
998 if (p->cylinders)
999 printk ("%s: %dMB CHS=%d/%d/%d LCC=%d RECOMP=%d\n",
1000 disk->disk_name,
1001 p->cylinders * p->heads * p->sectors / 4096,
1002 p->cylinders, p->heads, p->sectors,
1003 p->lowcurrent, p->precomp);
1004 set_capacity(disk, p->cylinders * p->heads * p->sectors / 2);
1007 #ifdef CONFIG_BLK_DEV_MFM_AUTODETECT
1009 * Attempt to detect a drive and find its geometry. The drive has already been
1010 * specified...
1012 * We first recalibrate the disk, then try to probe sectors, heads and then
1013 * cylinders. NOTE! the cylinder probe may break drives. The xd disk driver
1014 * does something along these lines, so I assume that most drives are up to
1015 * this mistreatment...
1017 static int mfm_detectdrive (int drive)
1019 unsigned int mingeo[3], maxgeo[3];
1020 unsigned int attribute, need_recal = 1;
1021 unsigned char cmdb[8];
1023 memset (mingeo, 0, sizeof (mingeo));
1024 maxgeo[0] = mfm_info[drive].sectors;
1025 maxgeo[1] = mfm_info[drive].heads;
1026 maxgeo[2] = mfm_info[drive].cylinders;
1028 cmdb[0] = drive + 1;
1029 cmdb[6] = 0;
1030 cmdb[7] = 1;
1031 for (attribute = 0; attribute < 3; attribute++) {
1032 while (mingeo[attribute] != maxgeo[attribute]) {
1033 unsigned int variable;
1035 variable = (maxgeo[attribute] + mingeo[attribute]) >> 1;
1036 cmdb[1] = cmdb[2] = cmdb[3] = cmdb[4] = cmdb[5] = 0;
1038 if (need_recal) {
1039 int tries = 5;
1041 do {
1042 issue_command (CMD_RCLB, cmdb, 2);
1043 wait_for_completion ();
1044 wait_for_command_end ();
1045 if (result[1] == 0x20)
1046 break;
1047 } while (result[1] && --tries);
1048 if (result[1]) {
1049 outw (CMD_RCAL, MFM_COMMAND);
1050 return 0;
1052 need_recal = 0;
1055 switch (attribute) {
1056 case 0:
1057 cmdb[5] = variable;
1058 issue_command (CMD_CMPD, cmdb, 8);
1059 break;
1060 case 1:
1061 cmdb[1] = variable;
1062 cmdb[4] = variable;
1063 issue_command (CMD_CMPD, cmdb, 8);
1064 break;
1065 case 2:
1066 cmdb[2] = variable >> 8;
1067 cmdb[3] = variable;
1068 issue_command (CMD_SEK, cmdb, 4);
1069 break;
1071 wait_for_completion ();
1072 wait_for_command_end ();
1074 switch (result[1]) {
1075 case 0x00:
1076 case 0x50:
1077 mingeo[attribute] = variable + 1;
1078 break;
1080 case 0x20:
1081 outw (CMD_RCAL, MFM_COMMAND);
1082 return 0;
1084 case 0x24:
1085 need_recal = 1;
1086 default:
1087 maxgeo[attribute] = variable;
1088 break;
1092 mfm_info[drive].cylinders = mingeo[2];
1093 mfm_info[drive].lowcurrent = mingeo[2];
1094 mfm_info[drive].precomp = mingeo[2] / 2;
1095 mfm_info[drive].heads = mingeo[1];
1096 mfm_info[drive].sectors = mingeo[0];
1097 outw (CMD_RCAL, MFM_COMMAND);
1098 return 1;
1100 #endif
1103 * Initialise all drive information for this controller.
1105 static int mfm_initdrives(void)
1107 int drive;
1109 if (number_mfm_drives > MFM_MAXDRIVES) {
1110 number_mfm_drives = MFM_MAXDRIVES;
1111 printk("No. of ADFS MFM drives is greater than MFM_MAXDRIVES - you can't have that many!\n");
1114 for (drive = 0; drive < number_mfm_drives; drive++) {
1115 mfm_info[drive].lowcurrent = 1;
1116 mfm_info[drive].precomp = 1;
1117 mfm_info[drive].cylinder = -1;
1118 mfm_info[drive].errors.recal = 0;
1119 mfm_info[drive].errors.report = 0;
1120 mfm_info[drive].errors.abort = 4;
1122 #ifdef CONFIG_BLK_DEV_MFM_AUTODETECT
1123 mfm_info[drive].cylinders = 1024;
1124 mfm_info[drive].heads = 8;
1125 mfm_info[drive].sectors = 64;
1127 unsigned char cmdb[16];
1129 mfm_setupspecify (drive, cmdb);
1130 cmdb[1] &= ~0x81;
1131 issue_command (CMD_SPC, cmdb, 16);
1132 wait_for_completion ();
1133 if (!mfm_detectdrive (drive)) {
1134 mfm_info[drive].cylinders = 0;
1135 mfm_info[drive].heads = 0;
1136 mfm_info[drive].sectors = 0;
1138 cmdb[0] = cmdb[1] = 0;
1139 issue_command (CMD_CKV, cmdb, 2);
1141 #else
1142 mfm_info[drive].cylinders = 1; /* its going to have to figure it out from the partition info */
1143 mfm_info[drive].heads = 4;
1144 mfm_info[drive].sectors = 32;
1145 #endif
1147 return number_mfm_drives;
1153 * The 'front' end of the mfm driver follows...
1156 static int mfm_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)
1158 struct mfm_info *p = inode->i_bdev->bd_disk->private_data;
1159 struct hd_geometry *geo = (struct hd_geometry *) arg;
1160 if (cmd != HDIO_GETGEO)
1161 return -EINVAL;
1162 if (!arg)
1163 return -EINVAL;
1164 if (put_user (p->heads, &geo->heads))
1165 return -EFAULT;
1166 if (put_user (p->sectors, &geo->sectors))
1167 return -EFAULT;
1168 if (put_user (p->cylinders, &geo->cylinders))
1169 return -EFAULT;
1170 if (put_user (get_start_sect(inode->i_bdev), &geo->start))
1171 return -EFAULT;
1172 return 0;
1176 * This is to handle various kernel command line parameters
1177 * specific to this driver.
1179 void mfm_setup(char *str, int *ints)
1181 return;
1185 * Set the CHS from the ADFS boot block if it is present. This is not ideal
1186 * since if there are any non-ADFS partitions on the disk, this won't work!
1187 * Hence, I want to get rid of this...
1189 void xd_set_geometry(struct block_device *bdev, unsigned char secsptrack,
1190 unsigned char heads, unsigned int secsize)
1192 struct mfm_info *p = bdev->bd_disk->private_data;
1193 int drive = p - mfm_info;
1194 unsigned long disksize = bdev->bd_inode->i_size;
1196 if (p->cylinders == 1) {
1197 p->sectors = secsptrack;
1198 p->heads = heads;
1199 p->cylinders = discsize / (secsptrack * heads * secsize);
1201 if ((heads < 1) || (p->cylinders > 1024)) {
1202 printk("%s: Insane disc shape! Setting to 512/4/32\n",
1203 bdev->bd_disk->disk_name);
1205 /* These values are fairly arbitary, but are there so that if your
1206 * lucky you can pick apart your disc to find out what is going on -
1207 * I reckon these figures won't hurt MOST drives
1209 p->sectors = 32;
1210 p->heads = 4;
1211 p->cylinders = 512;
1213 if (raw_cmd.dev == drive)
1214 mfm_specify ();
1215 mfm_geometry (drive);
1219 static struct block_device_operations mfm_fops =
1221 .owner = THIS_MODULE,
1222 .ioctl = mfm_ioctl,
1226 * See if there is a controller at the address presently at mfm_addr
1228 * We check to see if the controller is busy - if it is, we abort it first,
1229 * and check that the chip is no longer busy after at least 180 clock cycles.
1230 * We then issue a command and check that the BSY or CPR bits are set.
1232 static int mfm_probecontroller (unsigned int mfm_addr)
1234 if (inw (MFM_STATUS) & STAT_BSY) {
1235 outw (CMD_ABT, MFM_COMMAND);
1236 udelay (50);
1237 if (inw (MFM_STATUS) & STAT_BSY)
1238 return 0;
1241 if (inw (MFM_STATUS) & STAT_CED)
1242 outw (CMD_RCAL, MFM_COMMAND);
1244 outw (CMD_SEK, MFM_COMMAND);
1246 if (inw (MFM_STATUS) & (STAT_BSY | STAT_CPR)) {
1247 unsigned int count = 2000;
1248 while (inw (MFM_STATUS) & STAT_BSY) {
1249 udelay (500);
1250 if (!--count)
1251 return 0;
1254 outw (CMD_RCAL, MFM_COMMAND);
1256 return 1;
1259 static int mfm_do_init(unsigned char irqmask)
1261 int i, ret;
1263 printk("mfm: found at address %08X, interrupt %d\n", mfm_addr, mfm_irq);
1265 ret = -EBUSY;
1266 if (!request_region (mfm_addr, 10, "mfm"))
1267 goto out1;
1269 ret = register_blkdev(MAJOR_NR, "mfm");
1270 if (ret)
1271 goto out2;
1273 /* Stuff for the assembler routines to get to */
1274 hdc63463_baseaddress = ioaddr(mfm_addr);
1275 hdc63463_irqpolladdress = mfm_IRQPollLoc;
1276 hdc63463_irqpollmask = irqmask;
1278 mfm_queue = blk_init_queue(do_mfm_request, &mfm_lock);
1279 if (!mfm_queue)
1280 goto out2a;
1282 Busy = 0;
1283 lastspecifieddrive = -1;
1285 mfm_drives = mfm_initdrives();
1286 if (!mfm_drives) {
1287 ret = -ENODEV;
1288 goto out3;
1291 for (i = 0; i < mfm_drives; i++) {
1292 struct gendisk *disk = alloc_disk(64);
1293 if (!disk)
1294 goto Enomem;
1295 disk->major = MAJOR_NR;
1296 disk->first_minor = i << 6;
1297 disk->fops = &mfm_fops;
1298 sprintf(disk->disk_name, "mfm%c", 'a'+i);
1299 mfm_gendisk[i] = disk;
1302 printk("mfm: detected %d hard drive%s\n", mfm_drives,
1303 mfm_drives == 1 ? "" : "s");
1304 ret = request_irq(mfm_irq, mfm_interrupt_handler, SA_INTERRUPT, "MFM harddisk", NULL);
1305 if (ret) {
1306 printk("mfm: unable to get IRQ%d\n", mfm_irq);
1307 goto out4;
1310 if (mfm_irqenable)
1311 outw(0x80, mfm_irqenable); /* Required to enable IRQs from MFM podule */
1313 for (i = 0; i < mfm_drives; i++) {
1314 mfm_geometry(i);
1315 mfm_gendisk[i]->queue = mfm_queue;
1316 add_disk(mfm_gendisk[i]);
1318 return 0;
1320 out4:
1321 for (i = 0; i < mfm_drives; i++)
1322 put_disk(mfm_gendisk[i]);
1323 out3:
1324 blk_cleanup_queue(mfm_queue);
1325 out2a:
1326 unregister_blkdev(MAJOR_NR, "mfm");
1327 out2:
1328 release_region(mfm_addr, 10);
1329 out1:
1330 return ret;
1331 Enomem:
1332 while (i--)
1333 put_disk(mfm_gendisk[i]);
1334 goto out3;
1337 static void mfm_do_exit(void)
1339 int i;
1341 free_irq(mfm_irq, NULL);
1342 for (i = 0; i < mfm_drives; i++) {
1343 del_gendisk(mfm_gendisk[i]);
1344 put_disk(mfm_gendisk[i]);
1346 blk_cleanup_queue(mfm_queue);
1347 unregister_blkdev(MAJOR_NR, "mfm");
1348 if (mfm_addr)
1349 release_region(mfm_addr, 10);
1352 static int __devinit mfm_probe(struct expansion_card *ec, struct ecard_id *id)
1354 if (mfm_addr)
1355 return -EBUSY;
1357 mfm_addr = ecard_address(ec, ECARD_IOC, ECARD_MEDIUM) + 0x800;
1358 mfm_IRQPollLoc = ioaddr(mfm_addr + 0x400);
1359 mfm_irqenable = mfm_IRQPollLoc;
1360 mfm_irq = ec->irq;
1362 return mfm_do_init(0x08);
1365 static void __devexit mfm_remove(struct expansion_card *ec)
1367 outw (0, mfm_irqenable); /* Required to enable IRQs from MFM podule */
1368 mfm_do_exit();
1371 static const struct ecard_id mfm_cids[] = {
1372 { MANU_ACORN, PROD_ACORN_MFM },
1373 { 0xffff, 0xffff },
1376 static struct ecard_driver mfm_driver = {
1377 .probe = mfm_probe,
1378 .remove = __devexit(mfm_remove),
1379 .id_table = mfm_cids,
1380 .drv = {
1381 .name = "mfm",
1386 * Look for a MFM controller - first check the motherboard, then the podules
1387 * The podules have an extra interrupt enable that needs to be played with
1389 * The HDC is accessed at MEDIUM IOC speeds.
1391 static int __init mfm_init (void)
1393 unsigned char irqmask;
1395 if (mfm_probecontroller(ONBOARD_MFM_ADDRESS)) {
1396 mfm_addr = ONBOARD_MFM_ADDRESS;
1397 mfm_IRQPollLoc = IOC_IRQSTATB;
1398 mfm_irqenable = 0;
1399 mfm_irq = IRQ_HARDDISK;
1400 return mfm_do_init(0x08); /* IL3 pin */
1401 } else {
1402 return ecard_register_driver(&mfm_driver);
1406 static void __exit mfm_exit(void)
1408 if (mfm_addr == ONBOARD_MFM_ADDRESS)
1409 mfm_do_exit();
1410 else
1411 ecard_unregister_driver(&mfm_driver);
1414 module_init(mfm_init)
1415 module_exit(mfm_exit)
1416 MODULE_LICENSE("GPL");