Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * linux/arch/arm/drivers/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/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> |
| 115 | |
| 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> |
| 124 | |
| 125 | static void (*do_mfm)(void) = NULL; |
| 126 | static struct request_queue *mfm_queue; |
| 127 | static DEFINE_SPINLOCK(mfm_lock); |
| 128 | |
| 129 | #define MAJOR_NR MFM_ACORN_MAJOR |
| 130 | #define QUEUE (mfm_queue) |
| 131 | #define CURRENT elv_next_request(mfm_queue) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 132 | |
| 133 | /* |
| 134 | * Configuration section |
| 135 | * |
| 136 | * This is the maximum number of drives that we accept |
| 137 | */ |
| 138 | #define MFM_MAXDRIVES 2 |
| 139 | /* |
| 140 | * Linux I/O address of onboard MFM controller or 0 to disable this |
| 141 | */ |
| 142 | #define ONBOARD_MFM_ADDRESS ((0x002d0000 >> 2) | 0x80000000) |
| 143 | /* |
| 144 | * Uncomment this to enable debugging in the MFM driver... |
| 145 | */ |
| 146 | #ifndef DEBUG |
| 147 | /*#define DEBUG */ |
| 148 | #endif |
| 149 | /* |
| 150 | * End of configuration |
| 151 | */ |
| 152 | |
| 153 | |
| 154 | /* |
| 155 | * This structure contains all information to do with a particular physical |
| 156 | * device. |
| 157 | */ |
| 158 | struct mfm_info { |
| 159 | unsigned char sectors; |
| 160 | unsigned char heads; |
| 161 | unsigned short cylinders; |
| 162 | unsigned short lowcurrent; |
| 163 | unsigned short precomp; |
| 164 | #define NO_TRACK -1 |
| 165 | #define NEED_1_RECAL -2 |
| 166 | #define NEED_2_RECAL -3 |
| 167 | int cylinder; |
| 168 | struct { |
| 169 | char recal; |
| 170 | char report; |
| 171 | char abort; |
| 172 | } errors; |
| 173 | } mfm_info[MFM_MAXDRIVES]; |
| 174 | |
| 175 | #define MFM_DRV_INFO mfm_info[raw_cmd.dev] |
| 176 | |
| 177 | /* Stuff from the assembly routines */ |
| 178 | extern unsigned int hdc63463_baseaddress; /* Controller base address */ |
| 179 | extern unsigned int hdc63463_irqpolladdress; /* Address to read to test for int */ |
| 180 | extern unsigned int hdc63463_irqpollmask; /* Mask for irq register */ |
| 181 | extern unsigned int hdc63463_dataptr; /* Pointer to kernel data space to DMA */ |
| 182 | extern int hdc63463_dataleft; /* Number of bytes left to transfer */ |
| 183 | |
| 184 | |
| 185 | |
| 186 | |
| 187 | static int lastspecifieddrive; |
| 188 | static unsigned Busy; |
| 189 | |
| 190 | static unsigned int PartFragRead; /* The number of sectors which have been read |
| 191 | during a partial read split over two |
| 192 | cylinders. If 0 it means a partial |
| 193 | read did not occur. */ |
| 194 | |
| 195 | static unsigned int PartFragRead_RestartBlock; /* Where to restart on a split access */ |
| 196 | static unsigned int PartFragRead_SectorsLeft; /* Where to restart on a split access */ |
| 197 | |
| 198 | static int Sectors256LeftInCurrent; /* i.e. 256 byte sectors left in current */ |
| 199 | static int SectorsLeftInRequest; /* i.e. blocks left in the thing mfm_request was called for */ |
| 200 | static int Copy_Sector; /* The 256 byte sector we are currently at - fragments need to know |
| 201 | where to take over */ |
| 202 | static char *Copy_buffer; |
| 203 | |
| 204 | |
| 205 | static void mfm_seek(void); |
| 206 | static void mfm_rerequest(void); |
| 207 | static void mfm_request(void); |
| 208 | static void mfm_specify (void); |
| 209 | static void issue_request(unsigned int block, unsigned int nsect, |
| 210 | struct request *req); |
| 211 | |
| 212 | static unsigned int mfm_addr; /* Controller address */ |
| 213 | static unsigned int mfm_IRQPollLoc; /* Address to read for IRQ information */ |
| 214 | static unsigned int mfm_irqenable; /* Podule IRQ enable location */ |
| 215 | static unsigned char mfm_irq; /* Interrupt number */ |
| 216 | static int mfm_drives = 0; /* drives available */ |
| 217 | static int mfm_status = 0; /* interrupt status */ |
| 218 | static int *errors; |
| 219 | |
| 220 | static struct rawcmd { |
| 221 | unsigned int dev; |
| 222 | unsigned int cylinder; |
| 223 | unsigned int head; |
| 224 | unsigned int sector; |
| 225 | unsigned int cmdtype; |
| 226 | unsigned int cmdcode; |
| 227 | unsigned char cmddata[16]; |
| 228 | unsigned int cmdlen; |
| 229 | } raw_cmd; |
| 230 | |
| 231 | static unsigned char result[16]; |
| 232 | |
| 233 | static struct cont { |
| 234 | void (*interrupt) (void); /* interrupt handler */ |
| 235 | void (*error) (void); /* error handler */ |
| 236 | void (*redo) (void); /* redo handler */ |
| 237 | void (*done) (int st); /* done handler */ |
| 238 | } *cont = NULL; |
| 239 | |
| 240 | #if 0 |
| 241 | static struct tq_struct mfm_tq = {0, 0, (void (*)(void *)) NULL, 0}; |
| 242 | #endif |
| 243 | |
| 244 | int number_mfm_drives = 1; |
| 245 | |
| 246 | /* ------------------------------------------------------------------------------------------ */ |
| 247 | /* |
| 248 | * From the HD63463 data sheet from Hitachi Ltd. |
| 249 | */ |
| 250 | |
| 251 | #define MFM_COMMAND (mfm_addr + 0) |
| 252 | #define MFM_DATAOUT (mfm_addr + 1) |
| 253 | #define MFM_STATUS (mfm_addr + 8) |
| 254 | #define MFM_DATAIN (mfm_addr + 9) |
| 255 | |
| 256 | #define CMD_ABT 0xF0 /* Abort */ |
| 257 | #define CMD_SPC 0xE8 /* Specify */ |
| 258 | #define CMD_TST 0xE0 /* Test */ |
| 259 | #define CMD_RCLB 0xC8 /* Recalibrate */ |
| 260 | #define CMD_SEK 0xC0 /* Seek */ |
| 261 | #define CMD_WFS 0xAB /* Write Format Skew */ |
| 262 | #define CMD_WFM 0xA3 /* Write Format */ |
| 263 | #define CMD_MTB 0x90 /* Memory to buffer */ |
| 264 | #define CMD_CMPD 0x88 /* Compare data */ |
| 265 | #define CMD_WD 0x87 /* Write data */ |
| 266 | #define CMD_RED 0x70 /* Read erroneous data */ |
| 267 | #define CMD_RIS 0x68 /* Read ID skew */ |
| 268 | #define CMD_FID 0x61 /* Find ID */ |
| 269 | #define CMD_RID 0x60 /* Read ID */ |
| 270 | #define CMD_BTM 0x50 /* Buffer to memory */ |
| 271 | #define CMD_CKD 0x48 /* Check data */ |
| 272 | #define CMD_RD 0x40 /* Read data */ |
| 273 | #define CMD_OPBW 0x38 /* Open buffer write */ |
| 274 | #define CMD_OPBR 0x30 /* Open buffer read */ |
| 275 | #define CMD_CKV 0x28 /* Check drive */ |
| 276 | #define CMD_CKE 0x20 /* Check ECC */ |
| 277 | #define CMD_POD 0x18 /* Polling disable */ |
| 278 | #define CMD_POL 0x10 /* Polling enable */ |
| 279 | #define CMD_RCAL 0x08 /* Recall */ |
| 280 | |
| 281 | #define STAT_BSY 0x8000 /* Busy */ |
| 282 | #define STAT_CPR 0x4000 /* Command Parameter Rejection */ |
| 283 | #define STAT_CED 0x2000 /* Command end */ |
| 284 | #define STAT_SED 0x1000 /* Seek end */ |
| 285 | #define STAT_DER 0x0800 /* Drive error */ |
| 286 | #define STAT_ABN 0x0400 /* Abnormal end */ |
| 287 | #define STAT_POL 0x0200 /* Polling */ |
| 288 | |
| 289 | /* ------------------------------------------------------------------------------------------ */ |
| 290 | #ifdef DEBUG |
| 291 | static void console_printf(const char *fmt,...) |
| 292 | { |
| 293 | static char buffer[2048]; /* Arbitary! */ |
| 294 | extern void console_print(const char *); |
| 295 | unsigned long flags; |
| 296 | va_list ap; |
| 297 | |
| 298 | local_irq_save(flags); |
| 299 | |
| 300 | va_start(ap, fmt); |
| 301 | vsprintf(buffer, fmt, ap); |
| 302 | console_print(buffer); |
| 303 | va_end(fmt); |
| 304 | |
| 305 | local_irq_restore(flags); |
| 306 | }; /* console_printf */ |
| 307 | |
| 308 | #define DBG(x...) console_printf(x) |
| 309 | #else |
| 310 | #define DBG(x...) |
| 311 | #endif |
| 312 | |
| 313 | static void print_status(void) |
| 314 | { |
| 315 | char *error; |
| 316 | static char *errors[] = { |
| 317 | "no error", |
| 318 | "command aborted", |
| 319 | "invalid command", |
| 320 | "parameter error", |
| 321 | "not initialised", |
| 322 | "rejected TEST", |
| 323 | "no useld", |
| 324 | "write fault", |
| 325 | "not ready", |
| 326 | "no scp", |
| 327 | "in seek", |
| 328 | "invalid NCA", |
| 329 | "invalid step rate", |
| 330 | "seek error", |
| 331 | "over run", |
| 332 | "invalid PHA", |
| 333 | "data field EEC error", |
| 334 | "data field CRC error", |
| 335 | "error corrected", |
| 336 | "data field fatal error", |
| 337 | "no data am", |
| 338 | "not hit", |
| 339 | "ID field CRC error", |
| 340 | "time over", |
| 341 | "no ID am", |
| 342 | "not writable" |
| 343 | }; |
| 344 | if (result[1] < 0x65) |
| 345 | error = errors[result[1] >> 2]; |
| 346 | else |
| 347 | error = "unknown"; |
| 348 | printk("("); |
| 349 | if (mfm_status & STAT_BSY) printk("BSY "); |
| 350 | if (mfm_status & STAT_CPR) printk("CPR "); |
| 351 | if (mfm_status & STAT_CED) printk("CED "); |
| 352 | if (mfm_status & STAT_SED) printk("SED "); |
| 353 | if (mfm_status & STAT_DER) printk("DER "); |
| 354 | if (mfm_status & STAT_ABN) printk("ABN "); |
| 355 | if (mfm_status & STAT_POL) printk("POL "); |
| 356 | printk(") SSB = %X (%s)\n", result[1], error); |
| 357 | |
| 358 | } |
| 359 | |
| 360 | /* ------------------------------------------------------------------------------------- */ |
| 361 | |
| 362 | static void issue_command(int command, unsigned char *cmdb, int len) |
| 363 | { |
| 364 | int status; |
| 365 | #ifdef DEBUG |
| 366 | int i; |
| 367 | console_printf("issue_command: %02X: ", command); |
| 368 | for (i = 0; i < len; i++) |
| 369 | console_printf("%02X ", cmdb[i]); |
| 370 | console_printf("\n"); |
| 371 | #endif |
| 372 | |
| 373 | do { |
| 374 | status = inw(MFM_STATUS); |
| 375 | } while (status & (STAT_BSY | STAT_POL)); |
| 376 | DBG("issue_command: status after pol/bsy loop: %02X:\n ", status >> 8); |
| 377 | |
| 378 | if (status & (STAT_CPR | STAT_CED | STAT_SED | STAT_DER | STAT_ABN)) { |
| 379 | outw(CMD_RCAL, MFM_COMMAND); |
| 380 | while (inw(MFM_STATUS) & STAT_BSY); |
| 381 | } |
| 382 | status = inw(MFM_STATUS); |
| 383 | DBG("issue_command: status before parameter issue: %02X:\n ", status >> 8); |
| 384 | |
| 385 | while (len > 0) { |
| 386 | outw(cmdb[1] | (cmdb[0] << 8), MFM_DATAOUT); |
| 387 | len -= 2; |
| 388 | cmdb += 2; |
| 389 | } |
| 390 | status = inw(MFM_STATUS); |
| 391 | DBG("issue_command: status before command issue: %02X:\n ", status >> 8); |
| 392 | |
| 393 | outw(command, MFM_COMMAND); |
| 394 | status = inw(MFM_STATUS); |
| 395 | DBG("issue_command: status immediately after command issue: %02X:\n ", status >> 8); |
| 396 | } |
| 397 | |
| 398 | static void wait_for_completion(void) |
| 399 | { |
| 400 | while ((mfm_status = inw(MFM_STATUS)) & STAT_BSY); |
| 401 | } |
| 402 | |
| 403 | static void wait_for_command_end(void) |
| 404 | { |
| 405 | int i; |
| 406 | |
| 407 | while (!((mfm_status = inw(MFM_STATUS)) & STAT_CED)); |
| 408 | |
| 409 | for (i = 0; i < 16;) { |
| 410 | int in; |
| 411 | in = inw(MFM_DATAIN); |
| 412 | result[i++] = in >> 8; |
| 413 | result[i++] = in; |
| 414 | } |
| 415 | outw (CMD_RCAL, MFM_COMMAND); |
| 416 | } |
| 417 | |
| 418 | /* ------------------------------------------------------------------------------------- */ |
| 419 | |
| 420 | static void mfm_rw_intr(void) |
| 421 | { |
| 422 | int old_status; /* Holds status on entry, we read to see if the command just finished */ |
| 423 | #ifdef DEBUG |
| 424 | console_printf("mfm_rw_intr...dataleft=%d\n", hdc63463_dataleft); |
| 425 | print_status(); |
| 426 | #endif |
| 427 | |
| 428 | /* Now don't handle the error until BSY drops */ |
| 429 | if ((mfm_status & (STAT_DER | STAT_ABN)) && ((mfm_status&STAT_BSY)==0)) { |
| 430 | /* Something has gone wrong - let's try that again */ |
| 431 | outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ |
| 432 | if (cont) { |
| 433 | DBG("mfm_rw_intr: DER/ABN err\n"); |
| 434 | cont->error(); |
| 435 | cont->redo(); |
| 436 | }; |
| 437 | return; |
| 438 | }; |
| 439 | |
| 440 | /* OK so what ever happened it's not an error, now I reckon we are left between |
| 441 | a choice of command end or some data which is ready to be collected */ |
| 442 | /* I think we have to transfer data while the interrupt line is on and its |
| 443 | not any other type of interrupt */ |
| 444 | if (CURRENT->cmd == WRITE) { |
| 445 | extern void hdc63463_writedma(void); |
| 446 | if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) { |
| 447 | printk("mfm_rw_intr: Apparent DMA write request when no more to DMA\n"); |
| 448 | if (cont) { |
| 449 | cont->error(); |
| 450 | cont->redo(); |
| 451 | }; |
| 452 | return; |
| 453 | }; |
| 454 | hdc63463_writedma(); |
| 455 | } else { |
| 456 | extern void hdc63463_readdma(void); |
| 457 | if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) { |
| 458 | printk("mfm_rw_intr: Apparent DMA read request when no more to DMA\n"); |
| 459 | if (cont) { |
| 460 | cont->error(); |
| 461 | cont->redo(); |
| 462 | }; |
| 463 | return; |
| 464 | }; |
| 465 | DBG("Going to try read dma..............status=0x%x, buffer=%p\n", mfm_status, hdc63463_dataptr); |
| 466 | hdc63463_readdma(); |
| 467 | }; /* Read */ |
| 468 | |
| 469 | if (hdc63463_dataptr != ((unsigned int) Copy_buffer + 256)) { |
| 470 | /* If we didn't actually manage to get any data on this interrupt - but why? We got the interrupt */ |
| 471 | /* Ah - well looking at the status its just when we get command end; so no problem */ |
| 472 | /*console_printf("mfm: dataptr mismatch. dataptr=0x%08x Copy_buffer+256=0x%08p\n", |
| 473 | hdc63463_dataptr,Copy_buffer+256); |
| 474 | print_status(); */ |
| 475 | } else { |
| 476 | Sectors256LeftInCurrent--; |
| 477 | Copy_buffer += 256; |
| 478 | Copy_Sector++; |
| 479 | |
| 480 | /* We have come to the end of this request */ |
| 481 | if (!Sectors256LeftInCurrent) { |
| 482 | DBG("mfm: end_request for CURRENT=0x%p CURRENT(sector=%d current_nr_sectors=%d nr_sectors=%d)\n", |
| 483 | CURRENT, CURRENT->sector, CURRENT->current_nr_sectors, CURRENT->nr_sectors); |
| 484 | |
| 485 | CURRENT->nr_sectors -= CURRENT->current_nr_sectors; |
| 486 | CURRENT->sector += CURRENT->current_nr_sectors; |
| 487 | SectorsLeftInRequest -= CURRENT->current_nr_sectors; |
| 488 | |
| 489 | end_request(CURRENT, 1); |
| 490 | if (SectorsLeftInRequest) { |
| 491 | hdc63463_dataptr = (unsigned int) CURRENT->buffer; |
| 492 | Copy_buffer = CURRENT->buffer; |
| 493 | Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2; |
| 494 | errors = &(CURRENT->errors); |
| 495 | /* These should match the present calculations of the next logical sector |
| 496 | on the device |
| 497 | Copy_Sector=CURRENT->sector*2; */ |
| 498 | |
| 499 | if (Copy_Sector != CURRENT->sector * 2) |
| 500 | #ifdef DEBUG |
| 501 | /*console_printf*/printk("mfm: Copy_Sector mismatch. Copy_Sector=%d CURRENT->sector*2=%d\n", |
| 502 | Copy_Sector, CURRENT->sector * 2); |
| 503 | #else |
| 504 | printk("mfm: Copy_Sector mismatch! Eek!\n"); |
| 505 | #endif |
| 506 | }; /* CURRENT */ |
| 507 | }; /* Sectors256LeftInCurrent */ |
| 508 | }; |
| 509 | |
| 510 | old_status = mfm_status; |
| 511 | mfm_status = inw(MFM_STATUS); |
| 512 | if (mfm_status & (STAT_DER | STAT_ABN)) { |
| 513 | /* Something has gone wrong - let's try that again */ |
| 514 | if (cont) { |
| 515 | DBG("mfm_rw_intr: DER/ABN error\n"); |
| 516 | cont->error(); |
| 517 | cont->redo(); |
| 518 | }; |
| 519 | return; |
| 520 | }; |
| 521 | |
| 522 | /* If this code wasn't entered due to command_end but there is |
| 523 | now a command end we must read the command results out. If it was |
| 524 | entered like this then mfm_interrupt_handler would have done the |
| 525 | job. */ |
| 526 | if ((!((old_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) && |
| 527 | ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) { |
| 528 | int len = 0; |
| 529 | while (len < 16) { |
| 530 | int in; |
| 531 | in = inw(MFM_DATAIN); |
| 532 | result[len++] = in >> 8; |
| 533 | result[len++] = in; |
| 534 | }; |
| 535 | }; /* Result read */ |
| 536 | |
| 537 | /*console_printf ("mfm_rw_intr nearexit [%02X]\n", __raw_readb(mfm_IRQPollLoc)); */ |
| 538 | |
| 539 | /* If end of command move on */ |
| 540 | if (mfm_status & (STAT_CED)) { |
| 541 | outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ |
| 542 | /* End of command - trigger the next command */ |
| 543 | if (cont) { |
| 544 | cont->done(1); |
| 545 | } |
| 546 | DBG("mfm_rw_intr: returned from cont->done\n"); |
| 547 | } else { |
| 548 | /* Its going to generate another interrupt */ |
| 549 | do_mfm = mfm_rw_intr; |
| 550 | }; |
| 551 | } |
| 552 | |
| 553 | static void mfm_setup_rw(void) |
| 554 | { |
| 555 | DBG("setting up for rw...\n"); |
| 556 | |
| 557 | do_mfm = mfm_rw_intr; |
| 558 | issue_command(raw_cmd.cmdcode, raw_cmd.cmddata, raw_cmd.cmdlen); |
| 559 | } |
| 560 | |
| 561 | static void mfm_recal_intr(void) |
| 562 | { |
| 563 | #ifdef DEBUG |
| 564 | console_printf("recal intr - status = "); |
| 565 | print_status(); |
| 566 | #endif |
| 567 | outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ |
| 568 | if (mfm_status & (STAT_DER | STAT_ABN)) { |
| 569 | printk("recal failed\n"); |
| 570 | MFM_DRV_INFO.cylinder = NEED_2_RECAL; |
| 571 | if (cont) { |
| 572 | cont->error(); |
| 573 | cont->redo(); |
| 574 | } |
| 575 | return; |
| 576 | } |
| 577 | /* Thats seek end - we are finished */ |
| 578 | if (mfm_status & STAT_SED) { |
| 579 | issue_command(CMD_POD, NULL, 0); |
| 580 | MFM_DRV_INFO.cylinder = 0; |
| 581 | mfm_seek(); |
| 582 | return; |
| 583 | } |
| 584 | /* Command end without seek end (see data sheet p.20) for parallel seek |
| 585 | - we have to send a POL command to wait for the seek */ |
| 586 | if (mfm_status & STAT_CED) { |
| 587 | do_mfm = mfm_recal_intr; |
| 588 | issue_command(CMD_POL, NULL, 0); |
| 589 | return; |
| 590 | } |
| 591 | printk("recal: unknown status\n"); |
| 592 | } |
| 593 | |
| 594 | static void mfm_seek_intr(void) |
| 595 | { |
| 596 | #ifdef DEBUG |
| 597 | console_printf("seek intr - status = "); |
| 598 | print_status(); |
| 599 | #endif |
| 600 | outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ |
| 601 | if (mfm_status & (STAT_DER | STAT_ABN)) { |
| 602 | printk("seek failed\n"); |
| 603 | MFM_DRV_INFO.cylinder = NEED_2_RECAL; |
| 604 | if (cont) { |
| 605 | cont->error(); |
| 606 | cont->redo(); |
| 607 | } |
| 608 | return; |
| 609 | } |
| 610 | if (mfm_status & STAT_SED) { |
| 611 | issue_command(CMD_POD, NULL, 0); |
| 612 | MFM_DRV_INFO.cylinder = raw_cmd.cylinder; |
| 613 | mfm_seek(); |
| 614 | return; |
| 615 | } |
| 616 | if (mfm_status & STAT_CED) { |
| 617 | do_mfm = mfm_seek_intr; |
| 618 | issue_command(CMD_POL, NULL, 0); |
| 619 | return; |
| 620 | } |
| 621 | printk("seek: unknown status\n"); |
| 622 | } |
| 623 | |
| 624 | /* IDEA2 seems to work better - its what RiscOS sets my |
| 625 | * disc to - on its SECOND call to specify! |
| 626 | */ |
| 627 | #define IDEA2 |
| 628 | #ifndef IDEA2 |
| 629 | #define SPEC_SL 0x16 |
| 630 | #define SPEC_SH 0xa9 /* Step pulse high=21, Record Length=001 (256 bytes) */ |
| 631 | #else |
| 632 | #define SPEC_SL 0x00 /* OM2 - SL - step pulse low */ |
| 633 | #define SPEC_SH 0x21 /* Step pulse high=4, Record Length=001 (256 bytes) */ |
| 634 | #endif |
| 635 | |
| 636 | static void mfm_setupspecify (int drive, unsigned char *cmdb) |
| 637 | { |
| 638 | cmdb[0] = 0x1f; /* OM0 - !SECT,!MOD,!DIF,PADP,ECD,CRCP,CRCI,ACOR */ |
| 639 | cmdb[1] = 0xc3; /* OM1 - DTM,BRST,!CEDM,!SEDM,!DERM,0,AMEX,PSK */ |
| 640 | cmdb[2] = SPEC_SL; /* OM2 - SL - step pulse low */ |
| 641 | cmdb[3] = (number_mfm_drives == 1) ? 0x02 : 0x06; /* 1 or 2 drives */ |
| 642 | cmdb[4] = 0xfc | ((mfm_info[drive].cylinders - 1) >> 8);/* RW time over/high part of number of cylinders */ |
| 643 | cmdb[5] = mfm_info[drive].cylinders - 1; /* low part of number of cylinders */ |
| 644 | cmdb[6] = mfm_info[drive].heads - 1; /* Number of heads */ |
| 645 | cmdb[7] = mfm_info[drive].sectors - 1; /* Number of sectors */ |
| 646 | cmdb[8] = SPEC_SH; |
| 647 | cmdb[9] = 0x0a; /* gap length 1 */ |
| 648 | cmdb[10] = 0x0d; /* gap length 2 */ |
| 649 | cmdb[11] = 0x0c; /* gap length 3 */ |
| 650 | cmdb[12] = (mfm_info[drive].precomp - 1) >> 8; /* pre comp cylinder */ |
| 651 | cmdb[13] = mfm_info[drive].precomp - 1; |
| 652 | cmdb[14] = (mfm_info[drive].lowcurrent - 1) >> 8; /* Low current cylinder */ |
| 653 | cmdb[15] = mfm_info[drive].lowcurrent - 1; |
| 654 | } |
| 655 | |
| 656 | static void mfm_specify (void) |
| 657 | { |
| 658 | unsigned char cmdb[16]; |
| 659 | |
| 660 | DBG("specify...dev=%d lastspecified=%d\n", raw_cmd.dev, lastspecifieddrive); |
| 661 | mfm_setupspecify (raw_cmd.dev, cmdb); |
| 662 | |
| 663 | issue_command (CMD_SPC, cmdb, 16); |
| 664 | /* Ensure that we will do another specify if we move to the other drive */ |
| 665 | lastspecifieddrive = raw_cmd.dev; |
| 666 | wait_for_completion(); |
| 667 | } |
| 668 | |
| 669 | static void mfm_seek(void) |
| 670 | { |
| 671 | unsigned char cmdb[4]; |
| 672 | |
| 673 | DBG("seeking...\n"); |
| 674 | if (MFM_DRV_INFO.cylinder < 0) { |
| 675 | do_mfm = mfm_recal_intr; |
| 676 | DBG("mfm_seek: about to call specify\n"); |
| 677 | mfm_specify (); /* DAG added this */ |
| 678 | |
| 679 | cmdb[0] = raw_cmd.dev + 1; |
| 680 | cmdb[1] = 0; |
| 681 | |
| 682 | issue_command(CMD_RCLB, cmdb, 2); |
| 683 | return; |
| 684 | } |
| 685 | if (MFM_DRV_INFO.cylinder != raw_cmd.cylinder) { |
| 686 | cmdb[0] = raw_cmd.dev + 1; |
| 687 | cmdb[1] = 0; /* raw_cmd.head; DAG: My data sheet says this should be 0 */ |
| 688 | cmdb[2] = raw_cmd.cylinder >> 8; |
| 689 | cmdb[3] = raw_cmd.cylinder; |
| 690 | |
| 691 | do_mfm = mfm_seek_intr; |
| 692 | issue_command(CMD_SEK, cmdb, 4); |
| 693 | } else |
| 694 | mfm_setup_rw(); |
| 695 | } |
| 696 | |
| 697 | static void mfm_initialise(void) |
| 698 | { |
| 699 | DBG("init...\n"); |
| 700 | mfm_seek(); |
| 701 | } |
| 702 | |
| 703 | static void request_done(int uptodate) |
| 704 | { |
| 705 | DBG("mfm:request_done\n"); |
| 706 | if (uptodate) { |
| 707 | unsigned char block[2] = {0, 0}; |
| 708 | |
| 709 | /* Apparently worked - let's check bytes left to DMA */ |
| 710 | if (hdc63463_dataleft != (PartFragRead_SectorsLeft * 256)) { |
| 711 | printk("mfm: request_done - dataleft=%d - should be %d - Eek!\n", hdc63463_dataleft, PartFragRead_SectorsLeft * 256); |
| 712 | end_request(CURRENT, 0); |
| 713 | Busy = 0; |
| 714 | }; |
| 715 | /* Potentially this means that we've done; but we might be doing |
| 716 | a partial access, (over two cylinders) or we may have a number |
| 717 | of fragments in an image file. First let's deal with partial accesss |
| 718 | */ |
| 719 | if (PartFragRead) { |
| 720 | /* Yep - a partial access */ |
| 721 | |
| 722 | /* and issue the remainder */ |
| 723 | issue_request(PartFragRead_RestartBlock, PartFragRead_SectorsLeft, CURRENT); |
| 724 | return; |
| 725 | } |
| 726 | |
| 727 | /* ah well - perhaps there is another fragment to go */ |
| 728 | |
| 729 | /* Increment pointers/counts to start of next fragment */ |
| 730 | if (SectorsLeftInRequest > 0) printk("mfm: SectorsLeftInRequest>0 - Eek! Shouldn't happen!\n"); |
| 731 | |
| 732 | /* No - its the end of the line */ |
| 733 | /* end_request's should have happened at the end of sector DMAs */ |
| 734 | /* Turns Drive LEDs off - may slow it down? */ |
| 735 | if (!elv_next_request(QUEUE)) |
| 736 | issue_command(CMD_CKV, block, 2); |
| 737 | |
| 738 | Busy = 0; |
| 739 | DBG("request_done: About to mfm_request\n"); |
| 740 | /* Next one please */ |
| 741 | mfm_request(); /* Moved from mfm_rw_intr */ |
| 742 | DBG("request_done: returned from mfm_request\n"); |
| 743 | } else { |
| 744 | printk("mfm:request_done: update=0\n"); |
| 745 | end_request(CURRENT, 0); |
| 746 | Busy = 0; |
| 747 | } |
| 748 | } |
| 749 | |
| 750 | static void error_handler(void) |
| 751 | { |
| 752 | printk("error detected... status = "); |
| 753 | print_status(); |
| 754 | (*errors)++; |
| 755 | if (*errors > MFM_DRV_INFO.errors.abort) |
| 756 | cont->done(0); |
| 757 | if (*errors > MFM_DRV_INFO.errors.recal) |
| 758 | MFM_DRV_INFO.cylinder = NEED_2_RECAL; |
| 759 | } |
| 760 | |
| 761 | static void rw_interrupt(void) |
| 762 | { |
| 763 | printk("rw_interrupt\n"); |
| 764 | } |
| 765 | |
| 766 | static struct cont rw_cont = |
| 767 | { |
| 768 | rw_interrupt, |
| 769 | error_handler, |
| 770 | mfm_rerequest, |
| 771 | request_done |
| 772 | }; |
| 773 | |
| 774 | /* |
| 775 | * Actually gets round to issuing the request - note everything at this |
| 776 | * point is in 256 byte sectors not Linux 512 byte blocks |
| 777 | */ |
| 778 | static void issue_request(unsigned int block, unsigned int nsect, |
| 779 | struct request *req) |
| 780 | { |
| 781 | struct gendisk *disk = req->rq_disk; |
| 782 | struct mfm_info *p = disk->private_data; |
| 783 | int track, start_head, start_sector; |
| 784 | int sectors_to_next_cyl; |
| 785 | dev = p - mfm_info; |
| 786 | |
| 787 | track = block / p->sectors; |
| 788 | start_sector = block % p->sectors; |
| 789 | start_head = track % p->heads; |
| 790 | |
| 791 | /* First get the number of whole tracks which are free before the next |
| 792 | track */ |
| 793 | sectors_to_next_cyl = (p->heads - (start_head + 1)) * p->sectors; |
| 794 | /* Then add in the number of sectors left on this track */ |
| 795 | sectors_to_next_cyl += (p->sectors - start_sector); |
| 796 | |
| 797 | DBG("issue_request: mfm_info[dev].sectors=%d track=%d\n", p->sectors, track); |
| 798 | |
| 799 | raw_cmd.dev = dev; |
| 800 | raw_cmd.sector = start_sector; |
| 801 | raw_cmd.head = start_head; |
| 802 | raw_cmd.cylinder = track / p->heads; |
| 803 | raw_cmd.cmdtype = CURRENT->cmd; |
| 804 | raw_cmd.cmdcode = CURRENT->cmd == WRITE ? CMD_WD : CMD_RD; |
| 805 | raw_cmd.cmddata[0] = dev + 1; /* DAG: +1 to get US */ |
| 806 | raw_cmd.cmddata[1] = raw_cmd.head; |
| 807 | raw_cmd.cmddata[2] = raw_cmd.cylinder >> 8; |
| 808 | raw_cmd.cmddata[3] = raw_cmd.cylinder; |
| 809 | raw_cmd.cmddata[4] = raw_cmd.head; |
| 810 | raw_cmd.cmddata[5] = raw_cmd.sector; |
| 811 | |
| 812 | /* Was == and worked - how the heck??? */ |
| 813 | if (lastspecifieddrive != raw_cmd.dev) |
| 814 | mfm_specify (); |
| 815 | |
| 816 | if (nsect <= sectors_to_next_cyl) { |
| 817 | raw_cmd.cmddata[6] = nsect >> 8; |
| 818 | raw_cmd.cmddata[7] = nsect; |
| 819 | PartFragRead = 0; /* All in one */ |
| 820 | PartFragRead_SectorsLeft = 0; /* Must set this - used in DMA calcs */ |
| 821 | } else { |
| 822 | raw_cmd.cmddata[6] = sectors_to_next_cyl >> 8; |
| 823 | raw_cmd.cmddata[7] = sectors_to_next_cyl; |
| 824 | PartFragRead = sectors_to_next_cyl; /* only do this many this time */ |
| 825 | PartFragRead_RestartBlock = block + sectors_to_next_cyl; /* Where to restart from */ |
| 826 | PartFragRead_SectorsLeft = nsect - sectors_to_next_cyl; |
| 827 | } |
| 828 | raw_cmd.cmdlen = 8; |
| 829 | |
| 830 | /* Setup DMA pointers */ |
| 831 | hdc63463_dataptr = (unsigned int) Copy_buffer; |
| 832 | hdc63463_dataleft = nsect * 256; /* Better way? */ |
| 833 | |
| 834 | DBG("mfm%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx (%p)\n", |
| 835 | raw_cmd.dev + 'a', (CURRENT->cmd == READ) ? "read" : "writ", |
| 836 | raw_cmd.cylinder, |
| 837 | raw_cmd.head, |
| 838 | raw_cmd.sector, nsect, (unsigned long) Copy_buffer, CURRENT); |
| 839 | |
| 840 | cont = &rw_cont; |
| 841 | errors = &(CURRENT->errors); |
| 842 | #if 0 |
| 843 | mfm_tq.routine = (void (*)(void *)) mfm_initialise; |
| 844 | queue_task(&mfm_tq, &tq_immediate); |
| 845 | mark_bh(IMMEDIATE_BH); |
| 846 | #else |
| 847 | mfm_initialise(); |
| 848 | #endif |
| 849 | } /* issue_request */ |
| 850 | |
| 851 | /* |
| 852 | * Called when an error has just happened - need to trick mfm_request |
| 853 | * into thinking we weren't busy |
| 854 | * |
| 855 | * Turn off ints - mfm_request expects them this way |
| 856 | */ |
| 857 | static void mfm_rerequest(void) |
| 858 | { |
| 859 | DBG("mfm_rerequest\n"); |
| 860 | cli(); |
| 861 | Busy = 0; |
| 862 | mfm_request(); |
| 863 | } |
| 864 | |
| 865 | static struct gendisk *mfm_gendisk[2]; |
| 866 | |
| 867 | static void mfm_request(void) |
| 868 | { |
| 869 | DBG("mfm_request CURRENT=%p Busy=%d\n", CURRENT, Busy); |
| 870 | |
| 871 | /* If we are still processing then return; we will get called again */ |
| 872 | if (Busy) { |
| 873 | /* Again seems to be common in 1.3.45 */ |
| 874 | /*DBG*/printk("mfm_request: Exiting due to busy\n"); |
| 875 | return; |
| 876 | } |
| 877 | Busy = 1; |
| 878 | |
| 879 | while (1) { |
| 880 | unsigned int block, nsect; |
| 881 | struct gendisk *disk; |
| 882 | |
| 883 | DBG("mfm_request: loop start\n"); |
| 884 | sti(); |
| 885 | |
| 886 | DBG("mfm_request: before !CURRENT\n"); |
| 887 | |
| 888 | if (!CURRENT) { |
| 889 | printk("mfm_request: Exiting due to empty queue (pre)\n"); |
| 890 | do_mfm = NULL; |
| 891 | Busy = 0; |
| 892 | return; |
| 893 | } |
| 894 | |
| 895 | DBG("mfm_request: before arg extraction\n"); |
| 896 | |
| 897 | disk = CURRENT->rq_disk; |
| 898 | block = CURRENT->sector; |
| 899 | nsect = CURRENT->nr_sectors; |
| 900 | if (block >= get_capacity(disk) || |
| 901 | block+nsect > get_capacity(disk)) { |
| 902 | printk("%s: bad access: block=%d, count=%d, nr_sects=%ld\n", |
| 903 | disk->disk_name, block, nsect, get_capacity(disk)); |
| 904 | printk("mfm: continue 1\n"); |
| 905 | end_request(CURRENT, 0); |
| 906 | Busy = 0; |
| 907 | continue; |
| 908 | } |
| 909 | |
| 910 | /* DAG: Linux doesn't cope with this - even though it has an array telling |
| 911 | it the hardware block size - silly */ |
| 912 | block <<= 1; /* Now in 256 byte sectors */ |
| 913 | nsect <<= 1; /* Ditto */ |
| 914 | |
| 915 | SectorsLeftInRequest = nsect >> 1; |
| 916 | Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2; |
| 917 | Copy_buffer = CURRENT->buffer; |
| 918 | Copy_Sector = CURRENT->sector << 1; |
| 919 | |
| 920 | DBG("mfm_request: block after offset=%d\n", block); |
| 921 | |
| 922 | if (CURRENT->cmd != READ && CURRENT->cmd != WRITE) { |
| 923 | printk("unknown mfm-command %d\n", CURRENT->cmd); |
| 924 | end_request(CURRENT, 0); |
| 925 | Busy = 0; |
| 926 | printk("mfm: continue 4\n"); |
| 927 | continue; |
| 928 | } |
| 929 | issue_request(block, nsect, CURRENT); |
| 930 | |
| 931 | break; |
| 932 | } |
| 933 | DBG("mfm_request: Dropping out bottom\n"); |
| 934 | } |
| 935 | |
| 936 | static void do_mfm_request(request_queue_t *q) |
| 937 | { |
| 938 | DBG("do_mfm_request: about to mfm_request\n"); |
| 939 | mfm_request(); |
| 940 | } |
| 941 | |
| 942 | static void mfm_interrupt_handler(int unused, void *dev_id, struct pt_regs *regs) |
| 943 | { |
| 944 | void (*handler) (void) = do_mfm; |
| 945 | |
| 946 | do_mfm = NULL; |
| 947 | |
| 948 | DBG("mfm_interrupt_handler (handler=0x%p)\n", handler); |
| 949 | |
| 950 | mfm_status = inw(MFM_STATUS); |
| 951 | |
| 952 | /* If CPR (Command Parameter Reject) and not busy it means that the command |
| 953 | has some return message to give us */ |
| 954 | if ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR) { |
| 955 | int len = 0; |
| 956 | while (len < 16) { |
| 957 | int in; |
| 958 | in = inw(MFM_DATAIN); |
| 959 | result[len++] = in >> 8; |
| 960 | result[len++] = in; |
| 961 | } |
| 962 | } |
| 963 | if (handler) { |
| 964 | handler(); |
| 965 | return; |
| 966 | } |
| 967 | outw (CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ |
| 968 | printk ("mfm: unexpected interrupt - status = "); |
| 969 | print_status (); |
| 970 | while (1); |
| 971 | } |
| 972 | |
| 973 | |
| 974 | |
| 975 | |
| 976 | |
| 977 | /* |
| 978 | * Tell the user about the drive if we decided it exists. |
| 979 | */ |
| 980 | static void mfm_geometry(int drive) |
| 981 | { |
| 982 | struct mfm_info *p = mfm_info + drive; |
| 983 | struct gendisk *disk = mfm_gendisk[drive]; |
| 984 | disk->private_data = p; |
| 985 | if (p->cylinders) |
| 986 | printk ("%s: %dMB CHS=%d/%d/%d LCC=%d RECOMP=%d\n", |
| 987 | disk->disk_name, |
| 988 | p->cylinders * p->heads * p->sectors / 4096, |
| 989 | p->cylinders, p->heads, p->sectors, |
| 990 | p->lowcurrent, p->precomp); |
| 991 | set_capacity(disk, p->cylinders * p->heads * p->sectors / 2); |
| 992 | } |
| 993 | |
| 994 | #ifdef CONFIG_BLK_DEV_MFM_AUTODETECT |
| 995 | /* |
| 996 | * Attempt to detect a drive and find its geometry. The drive has already been |
| 997 | * specified... |
| 998 | * |
| 999 | * We first recalibrate the disk, then try to probe sectors, heads and then |
| 1000 | * cylinders. NOTE! the cylinder probe may break drives. The xd disk driver |
| 1001 | * does something along these lines, so I assume that most drives are up to |
| 1002 | * this mistreatment... |
| 1003 | */ |
| 1004 | static int mfm_detectdrive (int drive) |
| 1005 | { |
| 1006 | unsigned int mingeo[3], maxgeo[3]; |
| 1007 | unsigned int attribute, need_recal = 1; |
| 1008 | unsigned char cmdb[8]; |
| 1009 | |
| 1010 | memset (mingeo, 0, sizeof (mingeo)); |
| 1011 | maxgeo[0] = mfm_info[drive].sectors; |
| 1012 | maxgeo[1] = mfm_info[drive].heads; |
| 1013 | maxgeo[2] = mfm_info[drive].cylinders; |
| 1014 | |
| 1015 | cmdb[0] = drive + 1; |
| 1016 | cmdb[6] = 0; |
| 1017 | cmdb[7] = 1; |
| 1018 | for (attribute = 0; attribute < 3; attribute++) { |
| 1019 | while (mingeo[attribute] != maxgeo[attribute]) { |
| 1020 | unsigned int variable; |
| 1021 | |
| 1022 | variable = (maxgeo[attribute] + mingeo[attribute]) >> 1; |
| 1023 | cmdb[1] = cmdb[2] = cmdb[3] = cmdb[4] = cmdb[5] = 0; |
| 1024 | |
| 1025 | if (need_recal) { |
| 1026 | int tries = 5; |
| 1027 | |
| 1028 | do { |
| 1029 | issue_command (CMD_RCLB, cmdb, 2); |
| 1030 | wait_for_completion (); |
| 1031 | wait_for_command_end (); |
| 1032 | if (result[1] == 0x20) |
| 1033 | break; |
| 1034 | } while (result[1] && --tries); |
| 1035 | if (result[1]) { |
| 1036 | outw (CMD_RCAL, MFM_COMMAND); |
| 1037 | return 0; |
| 1038 | } |
| 1039 | need_recal = 0; |
| 1040 | } |
| 1041 | |
| 1042 | switch (attribute) { |
| 1043 | case 0: |
| 1044 | cmdb[5] = variable; |
| 1045 | issue_command (CMD_CMPD, cmdb, 8); |
| 1046 | break; |
| 1047 | case 1: |
| 1048 | cmdb[1] = variable; |
| 1049 | cmdb[4] = variable; |
| 1050 | issue_command (CMD_CMPD, cmdb, 8); |
| 1051 | break; |
| 1052 | case 2: |
| 1053 | cmdb[2] = variable >> 8; |
| 1054 | cmdb[3] = variable; |
| 1055 | issue_command (CMD_SEK, cmdb, 4); |
| 1056 | break; |
| 1057 | } |
| 1058 | wait_for_completion (); |
| 1059 | wait_for_command_end (); |
| 1060 | |
| 1061 | switch (result[1]) { |
| 1062 | case 0x00: |
| 1063 | case 0x50: |
| 1064 | mingeo[attribute] = variable + 1; |
| 1065 | break; |
| 1066 | |
| 1067 | case 0x20: |
| 1068 | outw (CMD_RCAL, MFM_COMMAND); |
| 1069 | return 0; |
| 1070 | |
| 1071 | case 0x24: |
| 1072 | need_recal = 1; |
| 1073 | default: |
| 1074 | maxgeo[attribute] = variable; |
| 1075 | break; |
| 1076 | } |
| 1077 | } |
| 1078 | } |
| 1079 | mfm_info[drive].cylinders = mingeo[2]; |
| 1080 | mfm_info[drive].lowcurrent = mingeo[2]; |
| 1081 | mfm_info[drive].precomp = mingeo[2] / 2; |
| 1082 | mfm_info[drive].heads = mingeo[1]; |
| 1083 | mfm_info[drive].sectors = mingeo[0]; |
| 1084 | outw (CMD_RCAL, MFM_COMMAND); |
| 1085 | return 1; |
| 1086 | } |
| 1087 | #endif |
| 1088 | |
| 1089 | /* |
| 1090 | * Initialise all drive information for this controller. |
| 1091 | */ |
| 1092 | static int mfm_initdrives(void) |
| 1093 | { |
| 1094 | int drive; |
| 1095 | |
| 1096 | if (number_mfm_drives > MFM_MAXDRIVES) { |
| 1097 | number_mfm_drives = MFM_MAXDRIVES; |
| 1098 | printk("No. of ADFS MFM drives is greater than MFM_MAXDRIVES - you can't have that many!\n"); |
| 1099 | } |
| 1100 | |
| 1101 | for (drive = 0; drive < number_mfm_drives; drive++) { |
| 1102 | mfm_info[drive].lowcurrent = 1; |
| 1103 | mfm_info[drive].precomp = 1; |
| 1104 | mfm_info[drive].cylinder = -1; |
| 1105 | mfm_info[drive].errors.recal = 0; |
| 1106 | mfm_info[drive].errors.report = 0; |
| 1107 | mfm_info[drive].errors.abort = 4; |
| 1108 | |
| 1109 | #ifdef CONFIG_BLK_DEV_MFM_AUTODETECT |
| 1110 | mfm_info[drive].cylinders = 1024; |
| 1111 | mfm_info[drive].heads = 8; |
| 1112 | mfm_info[drive].sectors = 64; |
| 1113 | { |
| 1114 | unsigned char cmdb[16]; |
| 1115 | |
| 1116 | mfm_setupspecify (drive, cmdb); |
| 1117 | cmdb[1] &= ~0x81; |
| 1118 | issue_command (CMD_SPC, cmdb, 16); |
| 1119 | wait_for_completion (); |
| 1120 | if (!mfm_detectdrive (drive)) { |
| 1121 | mfm_info[drive].cylinders = 0; |
| 1122 | mfm_info[drive].heads = 0; |
| 1123 | mfm_info[drive].sectors = 0; |
| 1124 | } |
| 1125 | cmdb[0] = cmdb[1] = 0; |
| 1126 | issue_command (CMD_CKV, cmdb, 2); |
| 1127 | } |
| 1128 | #else |
| 1129 | mfm_info[drive].cylinders = 1; /* its going to have to figure it out from the partition info */ |
| 1130 | mfm_info[drive].heads = 4; |
| 1131 | mfm_info[drive].sectors = 32; |
| 1132 | #endif |
| 1133 | } |
| 1134 | return number_mfm_drives; |
| 1135 | } |
| 1136 | |
| 1137 | |
| 1138 | |
| 1139 | /* |
| 1140 | * The 'front' end of the mfm driver follows... |
| 1141 | */ |
| 1142 | |
Christoph Hellwig | a885c8c | 2006-01-08 01:02:50 -0800 | [diff] [blame] | 1143 | static int mfm_getgeo(struct block_device *bdev, struct hd_geometry *geo) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1144 | { |
Christoph Hellwig | a885c8c | 2006-01-08 01:02:50 -0800 | [diff] [blame] | 1145 | struct mfm_info *p = bdev->bd_disk->private_data; |
| 1146 | |
| 1147 | geo->heads = p->heads; |
| 1148 | geo->sectors = p->sectors; |
| 1149 | geo->cylinders = p->cylinders; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1150 | return 0; |
| 1151 | } |
| 1152 | |
| 1153 | /* |
| 1154 | * This is to handle various kernel command line parameters |
| 1155 | * specific to this driver. |
| 1156 | */ |
| 1157 | void mfm_setup(char *str, int *ints) |
| 1158 | { |
| 1159 | return; |
| 1160 | } |
| 1161 | |
| 1162 | /* |
| 1163 | * Set the CHS from the ADFS boot block if it is present. This is not ideal |
| 1164 | * since if there are any non-ADFS partitions on the disk, this won't work! |
| 1165 | * Hence, I want to get rid of this... |
| 1166 | */ |
| 1167 | void xd_set_geometry(struct block_device *bdev, unsigned char secsptrack, |
| 1168 | unsigned char heads, unsigned int secsize) |
| 1169 | { |
| 1170 | struct mfm_info *p = bdev->bd_disk->private_data; |
| 1171 | int drive = p - mfm_info; |
| 1172 | unsigned long disksize = bdev->bd_inode->i_size; |
| 1173 | |
| 1174 | if (p->cylinders == 1) { |
| 1175 | p->sectors = secsptrack; |
| 1176 | p->heads = heads; |
| 1177 | p->cylinders = discsize / (secsptrack * heads * secsize); |
| 1178 | |
| 1179 | if ((heads < 1) || (p->cylinders > 1024)) { |
| 1180 | printk("%s: Insane disc shape! Setting to 512/4/32\n", |
| 1181 | bdev->bd_disk->disk_name); |
| 1182 | |
| 1183 | /* These values are fairly arbitary, but are there so that if your |
| 1184 | * lucky you can pick apart your disc to find out what is going on - |
| 1185 | * I reckon these figures won't hurt MOST drives |
| 1186 | */ |
| 1187 | p->sectors = 32; |
| 1188 | p->heads = 4; |
| 1189 | p->cylinders = 512; |
| 1190 | } |
| 1191 | if (raw_cmd.dev == drive) |
| 1192 | mfm_specify (); |
| 1193 | mfm_geometry (drive); |
| 1194 | } |
| 1195 | } |
| 1196 | |
| 1197 | static struct block_device_operations mfm_fops = |
| 1198 | { |
| 1199 | .owner = THIS_MODULE, |
Christoph Hellwig | a885c8c | 2006-01-08 01:02:50 -0800 | [diff] [blame] | 1200 | .getgeo = mfm_getgeo, |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1201 | }; |
| 1202 | |
| 1203 | /* |
| 1204 | * See if there is a controller at the address presently at mfm_addr |
| 1205 | * |
| 1206 | * We check to see if the controller is busy - if it is, we abort it first, |
| 1207 | * and check that the chip is no longer busy after at least 180 clock cycles. |
| 1208 | * We then issue a command and check that the BSY or CPR bits are set. |
| 1209 | */ |
| 1210 | static int mfm_probecontroller (unsigned int mfm_addr) |
| 1211 | { |
| 1212 | if (inw (MFM_STATUS) & STAT_BSY) { |
| 1213 | outw (CMD_ABT, MFM_COMMAND); |
| 1214 | udelay (50); |
| 1215 | if (inw (MFM_STATUS) & STAT_BSY) |
| 1216 | return 0; |
| 1217 | } |
| 1218 | |
| 1219 | if (inw (MFM_STATUS) & STAT_CED) |
| 1220 | outw (CMD_RCAL, MFM_COMMAND); |
| 1221 | |
| 1222 | outw (CMD_SEK, MFM_COMMAND); |
| 1223 | |
| 1224 | if (inw (MFM_STATUS) & (STAT_BSY | STAT_CPR)) { |
| 1225 | unsigned int count = 2000; |
| 1226 | while (inw (MFM_STATUS) & STAT_BSY) { |
| 1227 | udelay (500); |
| 1228 | if (!--count) |
| 1229 | return 0; |
| 1230 | } |
| 1231 | |
| 1232 | outw (CMD_RCAL, MFM_COMMAND); |
| 1233 | } |
| 1234 | return 1; |
| 1235 | } |
| 1236 | |
| 1237 | static int mfm_do_init(unsigned char irqmask) |
| 1238 | { |
| 1239 | int i, ret; |
| 1240 | |
| 1241 | printk("mfm: found at address %08X, interrupt %d\n", mfm_addr, mfm_irq); |
| 1242 | |
| 1243 | ret = -EBUSY; |
| 1244 | if (!request_region (mfm_addr, 10, "mfm")) |
| 1245 | goto out1; |
| 1246 | |
| 1247 | ret = register_blkdev(MAJOR_NR, "mfm"); |
| 1248 | if (ret) |
| 1249 | goto out2; |
| 1250 | |
| 1251 | /* Stuff for the assembler routines to get to */ |
| 1252 | hdc63463_baseaddress = ioaddr(mfm_addr); |
| 1253 | hdc63463_irqpolladdress = mfm_IRQPollLoc; |
| 1254 | hdc63463_irqpollmask = irqmask; |
| 1255 | |
| 1256 | mfm_queue = blk_init_queue(do_mfm_request, &mfm_lock); |
| 1257 | if (!mfm_queue) |
| 1258 | goto out2a; |
| 1259 | |
| 1260 | Busy = 0; |
| 1261 | lastspecifieddrive = -1; |
| 1262 | |
| 1263 | mfm_drives = mfm_initdrives(); |
| 1264 | if (!mfm_drives) { |
| 1265 | ret = -ENODEV; |
| 1266 | goto out3; |
| 1267 | } |
| 1268 | |
| 1269 | for (i = 0; i < mfm_drives; i++) { |
| 1270 | struct gendisk *disk = alloc_disk(64); |
| 1271 | if (!disk) |
| 1272 | goto Enomem; |
| 1273 | disk->major = MAJOR_NR; |
| 1274 | disk->first_minor = i << 6; |
| 1275 | disk->fops = &mfm_fops; |
| 1276 | sprintf(disk->disk_name, "mfm%c", 'a'+i); |
| 1277 | mfm_gendisk[i] = disk; |
| 1278 | } |
| 1279 | |
| 1280 | printk("mfm: detected %d hard drive%s\n", mfm_drives, |
| 1281 | mfm_drives == 1 ? "" : "s"); |
| 1282 | ret = request_irq(mfm_irq, mfm_interrupt_handler, SA_INTERRUPT, "MFM harddisk", NULL); |
| 1283 | if (ret) { |
| 1284 | printk("mfm: unable to get IRQ%d\n", mfm_irq); |
| 1285 | goto out4; |
| 1286 | } |
| 1287 | |
| 1288 | if (mfm_irqenable) |
| 1289 | outw(0x80, mfm_irqenable); /* Required to enable IRQs from MFM podule */ |
| 1290 | |
| 1291 | for (i = 0; i < mfm_drives; i++) { |
| 1292 | mfm_geometry(i); |
| 1293 | mfm_gendisk[i]->queue = mfm_queue; |
| 1294 | add_disk(mfm_gendisk[i]); |
| 1295 | } |
| 1296 | return 0; |
| 1297 | |
| 1298 | out4: |
| 1299 | for (i = 0; i < mfm_drives; i++) |
| 1300 | put_disk(mfm_gendisk[i]); |
| 1301 | out3: |
| 1302 | blk_cleanup_queue(mfm_queue); |
| 1303 | out2a: |
| 1304 | unregister_blkdev(MAJOR_NR, "mfm"); |
| 1305 | out2: |
| 1306 | release_region(mfm_addr, 10); |
| 1307 | out1: |
| 1308 | return ret; |
| 1309 | Enomem: |
| 1310 | while (i--) |
| 1311 | put_disk(mfm_gendisk[i]); |
| 1312 | goto out3; |
| 1313 | } |
| 1314 | |
| 1315 | static void mfm_do_exit(void) |
| 1316 | { |
| 1317 | int i; |
| 1318 | |
| 1319 | free_irq(mfm_irq, NULL); |
| 1320 | for (i = 0; i < mfm_drives; i++) { |
| 1321 | del_gendisk(mfm_gendisk[i]); |
| 1322 | put_disk(mfm_gendisk[i]); |
| 1323 | } |
| 1324 | blk_cleanup_queue(mfm_queue); |
| 1325 | unregister_blkdev(MAJOR_NR, "mfm"); |
| 1326 | if (mfm_addr) |
| 1327 | release_region(mfm_addr, 10); |
| 1328 | } |
| 1329 | |
| 1330 | static int __devinit mfm_probe(struct expansion_card *ec, struct ecard_id *id) |
| 1331 | { |
| 1332 | if (mfm_addr) |
| 1333 | return -EBUSY; |
| 1334 | |
| 1335 | mfm_addr = ecard_address(ec, ECARD_IOC, ECARD_MEDIUM) + 0x800; |
| 1336 | mfm_IRQPollLoc = ioaddr(mfm_addr + 0x400); |
| 1337 | mfm_irqenable = mfm_IRQPollLoc; |
| 1338 | mfm_irq = ec->irq; |
| 1339 | |
| 1340 | return mfm_do_init(0x08); |
| 1341 | } |
| 1342 | |
| 1343 | static void __devexit mfm_remove(struct expansion_card *ec) |
| 1344 | { |
| 1345 | outw (0, mfm_irqenable); /* Required to enable IRQs from MFM podule */ |
| 1346 | mfm_do_exit(); |
| 1347 | } |
| 1348 | |
| 1349 | static const struct ecard_id mfm_cids[] = { |
| 1350 | { MANU_ACORN, PROD_ACORN_MFM }, |
| 1351 | { 0xffff, 0xffff }, |
| 1352 | }; |
| 1353 | |
| 1354 | static struct ecard_driver mfm_driver = { |
| 1355 | .probe = mfm_probe, |
| 1356 | .remove = __devexit(mfm_remove), |
| 1357 | .id_table = mfm_cids, |
| 1358 | .drv = { |
| 1359 | .name = "mfm", |
| 1360 | }, |
| 1361 | }; |
| 1362 | |
| 1363 | /* |
| 1364 | * Look for a MFM controller - first check the motherboard, then the podules |
| 1365 | * The podules have an extra interrupt enable that needs to be played with |
| 1366 | * |
| 1367 | * The HDC is accessed at MEDIUM IOC speeds. |
| 1368 | */ |
| 1369 | static int __init mfm_init (void) |
| 1370 | { |
| 1371 | unsigned char irqmask; |
| 1372 | |
| 1373 | if (mfm_probecontroller(ONBOARD_MFM_ADDRESS)) { |
| 1374 | mfm_addr = ONBOARD_MFM_ADDRESS; |
| 1375 | mfm_IRQPollLoc = IOC_IRQSTATB; |
| 1376 | mfm_irqenable = 0; |
| 1377 | mfm_irq = IRQ_HARDDISK; |
| 1378 | return mfm_do_init(0x08); /* IL3 pin */ |
| 1379 | } else { |
| 1380 | return ecard_register_driver(&mfm_driver); |
| 1381 | } |
| 1382 | } |
| 1383 | |
| 1384 | static void __exit mfm_exit(void) |
| 1385 | { |
| 1386 | if (mfm_addr == ONBOARD_MFM_ADDRESS) |
| 1387 | mfm_do_exit(); |
| 1388 | else |
| 1389 | ecard_unregister_driver(&mfm_driver); |
| 1390 | } |
| 1391 | |
| 1392 | module_init(mfm_init) |
| 1393 | module_exit(mfm_exit) |
| 1394 | MODULE_LICENSE("GPL"); |