]>
Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
f30c2269 | 2 | * linux/drivers/acorn/block/mfmhd.c |
1da177e4 LT |
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> | |
1da177e4 LT |
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) | |
1da177e4 LT |
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 */ | |
e654bc43 | 442 | if (rq_data_dir(CURRENT) == WRITE) { |
1da177e4 LT |
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; | |
e654bc43 | 802 | raw_cmd.cmdcode = rq_data_dir(CURRENT) == WRITE ? CMD_WD : CMD_RD; |
1da177e4 LT |
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", | |
e654bc43 | 833 | raw_cmd.dev + 'a', rq_data_dir(CURRENT) == READ ? "read" : "writ", |
1da177e4 LT |
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 | ||
1da177e4 LT |
920 | issue_request(block, nsect, CURRENT); |
921 | ||
922 | break; | |
923 | } | |
924 | DBG("mfm_request: Dropping out bottom\n"); | |
925 | } | |
926 | ||
165125e1 | 927 | static void do_mfm_request(struct request_queue *q) |
1da177e4 LT |
928 | { |
929 | DBG("do_mfm_request: about to mfm_request\n"); | |
930 | mfm_request(); | |
931 | } | |
932 | ||
7d12e780 | 933 | static void mfm_interrupt_handler(int unused, void *dev_id) |
1da177e4 LT |
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 | ||
a885c8c4 | 1134 | static int mfm_getgeo(struct block_device *bdev, struct hd_geometry *geo) |
1da177e4 | 1135 | { |
a885c8c4 CH |
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; | |
1da177e4 LT |
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, | |
a885c8c4 | 1191 | .getgeo = mfm_getgeo, |
1da177e4 LT |
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"); | |
dace1453 | 1273 | ret = request_irq(mfm_irq, mfm_interrupt_handler, IRQF_DISABLED, "MFM harddisk", NULL); |
1da177e4 LT |
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"); |