diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/ide/legacy/hd.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/ide/legacy/hd.c')
-rw-r--r-- | drivers/ide/legacy/hd.c | 864 |
1 files changed, 864 insertions, 0 deletions
diff --git a/drivers/ide/legacy/hd.c b/drivers/ide/legacy/hd.c new file mode 100644 index 000000000000..c4090550ec13 --- /dev/null +++ b/drivers/ide/legacy/hd.c | |||
@@ -0,0 +1,864 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
3 | * | ||
4 | * This is the low-level hd interrupt support. It traverses the | ||
5 | * request-list, using interrupts to jump between functions. As | ||
6 | * all the functions are called within interrupts, we may not | ||
7 | * sleep. Special care is recommended. | ||
8 | * | ||
9 | * modified by Drew Eckhardt to check nr of hd's from the CMOS. | ||
10 | * | ||
11 | * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug | ||
12 | * in the early extended-partition checks and added DM partitions | ||
13 | * | ||
14 | * IRQ-unmask, drive-id, multiple-mode, support for ">16 heads", | ||
15 | * and general streamlining by Mark Lord. | ||
16 | * | ||
17 | * Removed 99% of above. Use Mark's ide driver for those options. | ||
18 | * This is now a lightweight ST-506 driver. (Paul Gortmaker) | ||
19 | * | ||
20 | * Modified 1995 Russell King for ARM processor. | ||
21 | * | ||
22 | * Bugfix: max_sectors must be <= 255 or the wheels tend to come | ||
23 | * off in a hurry once you queue things up - Paul G. 02/2001 | ||
24 | */ | ||
25 | |||
26 | /* Uncomment the following if you want verbose error reports. */ | ||
27 | /* #define VERBOSE_ERRORS */ | ||
28 | |||
29 | #include <linux/blkdev.h> | ||
30 | #include <linux/errno.h> | ||
31 | #include <linux/signal.h> | ||
32 | #include <linux/interrupt.h> | ||
33 | #include <linux/timer.h> | ||
34 | #include <linux/fs.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/genhd.h> | ||
37 | #include <linux/slab.h> | ||
38 | #include <linux/string.h> | ||
39 | #include <linux/ioport.h> | ||
40 | #include <linux/mc146818rtc.h> /* CMOS defines */ | ||
41 | #include <linux/init.h> | ||
42 | #include <linux/blkpg.h> | ||
43 | #include <linux/hdreg.h> | ||
44 | |||
45 | #define REALLY_SLOW_IO | ||
46 | #include <asm/system.h> | ||
47 | #include <asm/io.h> | ||
48 | #include <asm/uaccess.h> | ||
49 | |||
50 | #ifdef __arm__ | ||
51 | #undef HD_IRQ | ||
52 | #endif | ||
53 | #include <asm/irq.h> | ||
54 | #ifdef __arm__ | ||
55 | #define HD_IRQ IRQ_HARDDISK | ||
56 | #endif | ||
57 | |||
58 | /* Hd controller regster ports */ | ||
59 | |||
60 | #define HD_DATA 0x1f0 /* _CTL when writing */ | ||
61 | #define HD_ERROR 0x1f1 /* see err-bits */ | ||
62 | #define HD_NSECTOR 0x1f2 /* nr of sectors to read/write */ | ||
63 | #define HD_SECTOR 0x1f3 /* starting sector */ | ||
64 | #define HD_LCYL 0x1f4 /* starting cylinder */ | ||
65 | #define HD_HCYL 0x1f5 /* high byte of starting cyl */ | ||
66 | #define HD_CURRENT 0x1f6 /* 101dhhhh , d=drive, hhhh=head */ | ||
67 | #define HD_STATUS 0x1f7 /* see status-bits */ | ||
68 | #define HD_FEATURE HD_ERROR /* same io address, read=error, write=feature */ | ||
69 | #define HD_PRECOMP HD_FEATURE /* obsolete use of this port - predates IDE */ | ||
70 | #define HD_COMMAND HD_STATUS /* same io address, read=status, write=cmd */ | ||
71 | |||
72 | #define HD_CMD 0x3f6 /* used for resets */ | ||
73 | #define HD_ALTSTATUS 0x3f6 /* same as HD_STATUS but doesn't clear irq */ | ||
74 | |||
75 | /* Bits of HD_STATUS */ | ||
76 | #define ERR_STAT 0x01 | ||
77 | #define INDEX_STAT 0x02 | ||
78 | #define ECC_STAT 0x04 /* Corrected error */ | ||
79 | #define DRQ_STAT 0x08 | ||
80 | #define SEEK_STAT 0x10 | ||
81 | #define SERVICE_STAT SEEK_STAT | ||
82 | #define WRERR_STAT 0x20 | ||
83 | #define READY_STAT 0x40 | ||
84 | #define BUSY_STAT 0x80 | ||
85 | |||
86 | /* Bits for HD_ERROR */ | ||
87 | #define MARK_ERR 0x01 /* Bad address mark */ | ||
88 | #define TRK0_ERR 0x02 /* couldn't find track 0 */ | ||
89 | #define ABRT_ERR 0x04 /* Command aborted */ | ||
90 | #define MCR_ERR 0x08 /* media change request */ | ||
91 | #define ID_ERR 0x10 /* ID field not found */ | ||
92 | #define MC_ERR 0x20 /* media changed */ | ||
93 | #define ECC_ERR 0x40 /* Uncorrectable ECC error */ | ||
94 | #define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */ | ||
95 | #define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */ | ||
96 | |||
97 | static DEFINE_SPINLOCK(hd_lock); | ||
98 | static struct request_queue *hd_queue; | ||
99 | |||
100 | #define MAJOR_NR HD_MAJOR | ||
101 | #define QUEUE (hd_queue) | ||
102 | #define CURRENT elv_next_request(hd_queue) | ||
103 | |||
104 | #define TIMEOUT_VALUE (6*HZ) | ||
105 | #define HD_DELAY 0 | ||
106 | |||
107 | #define MAX_ERRORS 16 /* Max read/write errors/sector */ | ||
108 | #define RESET_FREQ 8 /* Reset controller every 8th retry */ | ||
109 | #define RECAL_FREQ 4 /* Recalibrate every 4th retry */ | ||
110 | #define MAX_HD 2 | ||
111 | |||
112 | #define STAT_OK (READY_STAT|SEEK_STAT) | ||
113 | #define OK_STATUS(s) (((s)&(STAT_OK|(BUSY_STAT|WRERR_STAT|ERR_STAT)))==STAT_OK) | ||
114 | |||
115 | static void recal_intr(void); | ||
116 | static void bad_rw_intr(void); | ||
117 | |||
118 | static int reset; | ||
119 | static int hd_error; | ||
120 | |||
121 | /* | ||
122 | * This struct defines the HD's and their types. | ||
123 | */ | ||
124 | struct hd_i_struct { | ||
125 | unsigned int head,sect,cyl,wpcom,lzone,ctl; | ||
126 | int unit; | ||
127 | int recalibrate; | ||
128 | int special_op; | ||
129 | }; | ||
130 | |||
131 | #ifdef HD_TYPE | ||
132 | static struct hd_i_struct hd_info[] = { HD_TYPE }; | ||
133 | static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct))); | ||
134 | #else | ||
135 | static struct hd_i_struct hd_info[MAX_HD]; | ||
136 | static int NR_HD; | ||
137 | #endif | ||
138 | |||
139 | static struct gendisk *hd_gendisk[MAX_HD]; | ||
140 | |||
141 | static struct timer_list device_timer; | ||
142 | |||
143 | #define TIMEOUT_VALUE (6*HZ) | ||
144 | |||
145 | #define SET_TIMER \ | ||
146 | do { \ | ||
147 | mod_timer(&device_timer, jiffies + TIMEOUT_VALUE); \ | ||
148 | } while (0) | ||
149 | |||
150 | static void (*do_hd)(void) = NULL; | ||
151 | #define SET_HANDLER(x) \ | ||
152 | if ((do_hd = (x)) != NULL) \ | ||
153 | SET_TIMER; \ | ||
154 | else \ | ||
155 | del_timer(&device_timer); | ||
156 | |||
157 | |||
158 | #if (HD_DELAY > 0) | ||
159 | unsigned long last_req; | ||
160 | |||
161 | unsigned long read_timer(void) | ||
162 | { | ||
163 | extern spinlock_t i8253_lock; | ||
164 | unsigned long t, flags; | ||
165 | int i; | ||
166 | |||
167 | spin_lock_irqsave(&i8253_lock, flags); | ||
168 | t = jiffies * 11932; | ||
169 | outb_p(0, 0x43); | ||
170 | i = inb_p(0x40); | ||
171 | i |= inb(0x40) << 8; | ||
172 | spin_unlock_irqrestore(&i8253_lock, flags); | ||
173 | return(t - i); | ||
174 | } | ||
175 | #endif | ||
176 | |||
177 | static void __init hd_setup(char *str, int *ints) | ||
178 | { | ||
179 | int hdind = 0; | ||
180 | |||
181 | if (ints[0] != 3) | ||
182 | return; | ||
183 | if (hd_info[0].head != 0) | ||
184 | hdind=1; | ||
185 | hd_info[hdind].head = ints[2]; | ||
186 | hd_info[hdind].sect = ints[3]; | ||
187 | hd_info[hdind].cyl = ints[1]; | ||
188 | hd_info[hdind].wpcom = 0; | ||
189 | hd_info[hdind].lzone = ints[1]; | ||
190 | hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0); | ||
191 | NR_HD = hdind+1; | ||
192 | } | ||
193 | |||
194 | static void dump_status (const char *msg, unsigned int stat) | ||
195 | { | ||
196 | char *name = "hd?"; | ||
197 | if (CURRENT) | ||
198 | name = CURRENT->rq_disk->disk_name; | ||
199 | |||
200 | #ifdef VERBOSE_ERRORS | ||
201 | printk("%s: %s: status=0x%02x { ", name, msg, stat & 0xff); | ||
202 | if (stat & BUSY_STAT) printk("Busy "); | ||
203 | if (stat & READY_STAT) printk("DriveReady "); | ||
204 | if (stat & WRERR_STAT) printk("WriteFault "); | ||
205 | if (stat & SEEK_STAT) printk("SeekComplete "); | ||
206 | if (stat & DRQ_STAT) printk("DataRequest "); | ||
207 | if (stat & ECC_STAT) printk("CorrectedError "); | ||
208 | if (stat & INDEX_STAT) printk("Index "); | ||
209 | if (stat & ERR_STAT) printk("Error "); | ||
210 | printk("}\n"); | ||
211 | if ((stat & ERR_STAT) == 0) { | ||
212 | hd_error = 0; | ||
213 | } else { | ||
214 | hd_error = inb(HD_ERROR); | ||
215 | printk("%s: %s: error=0x%02x { ", name, msg, hd_error & 0xff); | ||
216 | if (hd_error & BBD_ERR) printk("BadSector "); | ||
217 | if (hd_error & ECC_ERR) printk("UncorrectableError "); | ||
218 | if (hd_error & ID_ERR) printk("SectorIdNotFound "); | ||
219 | if (hd_error & ABRT_ERR) printk("DriveStatusError "); | ||
220 | if (hd_error & TRK0_ERR) printk("TrackZeroNotFound "); | ||
221 | if (hd_error & MARK_ERR) printk("AddrMarkNotFound "); | ||
222 | printk("}"); | ||
223 | if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) { | ||
224 | printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL), | ||
225 | inb(HD_CURRENT) & 0xf, inb(HD_SECTOR)); | ||
226 | if (CURRENT) | ||
227 | printk(", sector=%ld", CURRENT->sector); | ||
228 | } | ||
229 | printk("\n"); | ||
230 | } | ||
231 | #else | ||
232 | printk("%s: %s: status=0x%02x.\n", name, msg, stat & 0xff); | ||
233 | if ((stat & ERR_STAT) == 0) { | ||
234 | hd_error = 0; | ||
235 | } else { | ||
236 | hd_error = inb(HD_ERROR); | ||
237 | printk("%s: %s: error=0x%02x.\n", name, msg, hd_error & 0xff); | ||
238 | } | ||
239 | #endif | ||
240 | } | ||
241 | |||
242 | static void check_status(void) | ||
243 | { | ||
244 | int i = inb_p(HD_STATUS); | ||
245 | |||
246 | if (!OK_STATUS(i)) { | ||
247 | dump_status("check_status", i); | ||
248 | bad_rw_intr(); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | static int controller_busy(void) | ||
253 | { | ||
254 | int retries = 100000; | ||
255 | unsigned char status; | ||
256 | |||
257 | do { | ||
258 | status = inb_p(HD_STATUS); | ||
259 | } while ((status & BUSY_STAT) && --retries); | ||
260 | return status; | ||
261 | } | ||
262 | |||
263 | static int status_ok(void) | ||
264 | { | ||
265 | unsigned char status = inb_p(HD_STATUS); | ||
266 | |||
267 | if (status & BUSY_STAT) | ||
268 | return 1; /* Ancient, but does it make sense??? */ | ||
269 | if (status & WRERR_STAT) | ||
270 | return 0; | ||
271 | if (!(status & READY_STAT)) | ||
272 | return 0; | ||
273 | if (!(status & SEEK_STAT)) | ||
274 | return 0; | ||
275 | return 1; | ||
276 | } | ||
277 | |||
278 | static int controller_ready(unsigned int drive, unsigned int head) | ||
279 | { | ||
280 | int retry = 100; | ||
281 | |||
282 | do { | ||
283 | if (controller_busy() & BUSY_STAT) | ||
284 | return 0; | ||
285 | outb_p(0xA0 | (drive<<4) | head, HD_CURRENT); | ||
286 | if (status_ok()) | ||
287 | return 1; | ||
288 | } while (--retry); | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | |||
293 | static void hd_out(struct hd_i_struct *disk, | ||
294 | unsigned int nsect, | ||
295 | unsigned int sect, | ||
296 | unsigned int head, | ||
297 | unsigned int cyl, | ||
298 | unsigned int cmd, | ||
299 | void (*intr_addr)(void)) | ||
300 | { | ||
301 | unsigned short port; | ||
302 | |||
303 | #if (HD_DELAY > 0) | ||
304 | while (read_timer() - last_req < HD_DELAY) | ||
305 | /* nothing */; | ||
306 | #endif | ||
307 | if (reset) | ||
308 | return; | ||
309 | if (!controller_ready(disk->unit, head)) { | ||
310 | reset = 1; | ||
311 | return; | ||
312 | } | ||
313 | SET_HANDLER(intr_addr); | ||
314 | outb_p(disk->ctl,HD_CMD); | ||
315 | port=HD_DATA; | ||
316 | outb_p(disk->wpcom>>2,++port); | ||
317 | outb_p(nsect,++port); | ||
318 | outb_p(sect,++port); | ||
319 | outb_p(cyl,++port); | ||
320 | outb_p(cyl>>8,++port); | ||
321 | outb_p(0xA0|(disk->unit<<4)|head,++port); | ||
322 | outb_p(cmd,++port); | ||
323 | } | ||
324 | |||
325 | static void hd_request (void); | ||
326 | |||
327 | static int drive_busy(void) | ||
328 | { | ||
329 | unsigned int i; | ||
330 | unsigned char c; | ||
331 | |||
332 | for (i = 0; i < 500000 ; i++) { | ||
333 | c = inb_p(HD_STATUS); | ||
334 | if ((c & (BUSY_STAT | READY_STAT | SEEK_STAT)) == STAT_OK) | ||
335 | return 0; | ||
336 | } | ||
337 | dump_status("reset timed out", c); | ||
338 | return 1; | ||
339 | } | ||
340 | |||
341 | static void reset_controller(void) | ||
342 | { | ||
343 | int i; | ||
344 | |||
345 | outb_p(4,HD_CMD); | ||
346 | for(i = 0; i < 1000; i++) barrier(); | ||
347 | outb_p(hd_info[0].ctl & 0x0f,HD_CMD); | ||
348 | for(i = 0; i < 1000; i++) barrier(); | ||
349 | if (drive_busy()) | ||
350 | printk("hd: controller still busy\n"); | ||
351 | else if ((hd_error = inb(HD_ERROR)) != 1) | ||
352 | printk("hd: controller reset failed: %02x\n",hd_error); | ||
353 | } | ||
354 | |||
355 | static void reset_hd(void) | ||
356 | { | ||
357 | static int i; | ||
358 | |||
359 | repeat: | ||
360 | if (reset) { | ||
361 | reset = 0; | ||
362 | i = -1; | ||
363 | reset_controller(); | ||
364 | } else { | ||
365 | check_status(); | ||
366 | if (reset) | ||
367 | goto repeat; | ||
368 | } | ||
369 | if (++i < NR_HD) { | ||
370 | struct hd_i_struct *disk = &hd_info[i]; | ||
371 | disk->special_op = disk->recalibrate = 1; | ||
372 | hd_out(disk,disk->sect,disk->sect,disk->head-1, | ||
373 | disk->cyl,WIN_SPECIFY,&reset_hd); | ||
374 | if (reset) | ||
375 | goto repeat; | ||
376 | } else | ||
377 | hd_request(); | ||
378 | } | ||
379 | |||
380 | /* | ||
381 | * Ok, don't know what to do with the unexpected interrupts: on some machines | ||
382 | * doing a reset and a retry seems to result in an eternal loop. Right now I | ||
383 | * ignore it, and just set the timeout. | ||
384 | * | ||
385 | * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the | ||
386 | * drive enters "idle", "standby", or "sleep" mode, so if the status looks | ||
387 | * "good", we just ignore the interrupt completely. | ||
388 | */ | ||
389 | static void unexpected_hd_interrupt(void) | ||
390 | { | ||
391 | unsigned int stat = inb_p(HD_STATUS); | ||
392 | |||
393 | if (stat & (BUSY_STAT|DRQ_STAT|ECC_STAT|ERR_STAT)) { | ||
394 | dump_status ("unexpected interrupt", stat); | ||
395 | SET_TIMER; | ||
396 | } | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | * bad_rw_intr() now tries to be a bit smarter and does things | ||
401 | * according to the error returned by the controller. | ||
402 | * -Mika Liljeberg (liljeber@cs.Helsinki.FI) | ||
403 | */ | ||
404 | static void bad_rw_intr(void) | ||
405 | { | ||
406 | struct request *req = CURRENT; | ||
407 | if (req != NULL) { | ||
408 | struct hd_i_struct *disk = req->rq_disk->private_data; | ||
409 | if (++req->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) { | ||
410 | end_request(req, 0); | ||
411 | disk->special_op = disk->recalibrate = 1; | ||
412 | } else if (req->errors % RESET_FREQ == 0) | ||
413 | reset = 1; | ||
414 | else if ((hd_error & TRK0_ERR) || req->errors % RECAL_FREQ == 0) | ||
415 | disk->special_op = disk->recalibrate = 1; | ||
416 | /* Otherwise just retry */ | ||
417 | } | ||
418 | } | ||
419 | |||
420 | static inline int wait_DRQ(void) | ||
421 | { | ||
422 | int retries = 100000, stat; | ||
423 | |||
424 | while (--retries > 0) | ||
425 | if ((stat = inb_p(HD_STATUS)) & DRQ_STAT) | ||
426 | return 0; | ||
427 | dump_status("wait_DRQ", stat); | ||
428 | return -1; | ||
429 | } | ||
430 | |||
431 | static void read_intr(void) | ||
432 | { | ||
433 | struct request *req; | ||
434 | int i, retries = 100000; | ||
435 | |||
436 | do { | ||
437 | i = (unsigned) inb_p(HD_STATUS); | ||
438 | if (i & BUSY_STAT) | ||
439 | continue; | ||
440 | if (!OK_STATUS(i)) | ||
441 | break; | ||
442 | if (i & DRQ_STAT) | ||
443 | goto ok_to_read; | ||
444 | } while (--retries > 0); | ||
445 | dump_status("read_intr", i); | ||
446 | bad_rw_intr(); | ||
447 | hd_request(); | ||
448 | return; | ||
449 | ok_to_read: | ||
450 | req = CURRENT; | ||
451 | insw(HD_DATA,req->buffer,256); | ||
452 | req->sector++; | ||
453 | req->buffer += 512; | ||
454 | req->errors = 0; | ||
455 | i = --req->nr_sectors; | ||
456 | --req->current_nr_sectors; | ||
457 | #ifdef DEBUG | ||
458 | printk("%s: read: sector %ld, remaining = %ld, buffer=%p\n", | ||
459 | req->rq_disk->disk_name, req->sector, req->nr_sectors, | ||
460 | req->buffer+512)); | ||
461 | #endif | ||
462 | if (req->current_nr_sectors <= 0) | ||
463 | end_request(req, 1); | ||
464 | if (i > 0) { | ||
465 | SET_HANDLER(&read_intr); | ||
466 | return; | ||
467 | } | ||
468 | (void) inb_p(HD_STATUS); | ||
469 | #if (HD_DELAY > 0) | ||
470 | last_req = read_timer(); | ||
471 | #endif | ||
472 | if (elv_next_request(QUEUE)) | ||
473 | hd_request(); | ||
474 | return; | ||
475 | } | ||
476 | |||
477 | static void write_intr(void) | ||
478 | { | ||
479 | struct request *req = CURRENT; | ||
480 | int i; | ||
481 | int retries = 100000; | ||
482 | |||
483 | do { | ||
484 | i = (unsigned) inb_p(HD_STATUS); | ||
485 | if (i & BUSY_STAT) | ||
486 | continue; | ||
487 | if (!OK_STATUS(i)) | ||
488 | break; | ||
489 | if ((req->nr_sectors <= 1) || (i & DRQ_STAT)) | ||
490 | goto ok_to_write; | ||
491 | } while (--retries > 0); | ||
492 | dump_status("write_intr", i); | ||
493 | bad_rw_intr(); | ||
494 | hd_request(); | ||
495 | return; | ||
496 | ok_to_write: | ||
497 | req->sector++; | ||
498 | i = --req->nr_sectors; | ||
499 | --req->current_nr_sectors; | ||
500 | req->buffer += 512; | ||
501 | if (!i || (req->bio && req->current_nr_sectors <= 0)) | ||
502 | end_request(req, 1); | ||
503 | if (i > 0) { | ||
504 | SET_HANDLER(&write_intr); | ||
505 | outsw(HD_DATA,req->buffer,256); | ||
506 | local_irq_enable(); | ||
507 | } else { | ||
508 | #if (HD_DELAY > 0) | ||
509 | last_req = read_timer(); | ||
510 | #endif | ||
511 | hd_request(); | ||
512 | } | ||
513 | return; | ||
514 | } | ||
515 | |||
516 | static void recal_intr(void) | ||
517 | { | ||
518 | check_status(); | ||
519 | #if (HD_DELAY > 0) | ||
520 | last_req = read_timer(); | ||
521 | #endif | ||
522 | hd_request(); | ||
523 | } | ||
524 | |||
525 | /* | ||
526 | * This is another of the error-routines I don't know what to do with. The | ||
527 | * best idea seems to just set reset, and start all over again. | ||
528 | */ | ||
529 | static void hd_times_out(unsigned long dummy) | ||
530 | { | ||
531 | char *name; | ||
532 | |||
533 | do_hd = NULL; | ||
534 | |||
535 | if (!CURRENT) | ||
536 | return; | ||
537 | |||
538 | disable_irq(HD_IRQ); | ||
539 | local_irq_enable(); | ||
540 | reset = 1; | ||
541 | name = CURRENT->rq_disk->disk_name; | ||
542 | printk("%s: timeout\n", name); | ||
543 | if (++CURRENT->errors >= MAX_ERRORS) { | ||
544 | #ifdef DEBUG | ||
545 | printk("%s: too many errors\n", name); | ||
546 | #endif | ||
547 | end_request(CURRENT, 0); | ||
548 | } | ||
549 | local_irq_disable(); | ||
550 | hd_request(); | ||
551 | enable_irq(HD_IRQ); | ||
552 | } | ||
553 | |||
554 | static int do_special_op(struct hd_i_struct *disk, struct request *req) | ||
555 | { | ||
556 | if (disk->recalibrate) { | ||
557 | disk->recalibrate = 0; | ||
558 | hd_out(disk,disk->sect,0,0,0,WIN_RESTORE,&recal_intr); | ||
559 | return reset; | ||
560 | } | ||
561 | if (disk->head > 16) { | ||
562 | printk ("%s: cannot handle device with more than 16 heads - giving up\n", req->rq_disk->disk_name); | ||
563 | end_request(req, 0); | ||
564 | } | ||
565 | disk->special_op = 0; | ||
566 | return 1; | ||
567 | } | ||
568 | |||
569 | /* | ||
570 | * The driver enables interrupts as much as possible. In order to do this, | ||
571 | * (a) the device-interrupt is disabled before entering hd_request(), | ||
572 | * and (b) the timeout-interrupt is disabled before the sti(). | ||
573 | * | ||
574 | * Interrupts are still masked (by default) whenever we are exchanging | ||
575 | * data/cmds with a drive, because some drives seem to have very poor | ||
576 | * tolerance for latency during I/O. The IDE driver has support to unmask | ||
577 | * interrupts for non-broken hardware, so use that driver if required. | ||
578 | */ | ||
579 | static void hd_request(void) | ||
580 | { | ||
581 | unsigned int block, nsect, sec, track, head, cyl; | ||
582 | struct hd_i_struct *disk; | ||
583 | struct request *req; | ||
584 | |||
585 | if (do_hd) | ||
586 | return; | ||
587 | repeat: | ||
588 | del_timer(&device_timer); | ||
589 | local_irq_enable(); | ||
590 | |||
591 | req = CURRENT; | ||
592 | if (!req) { | ||
593 | do_hd = NULL; | ||
594 | return; | ||
595 | } | ||
596 | |||
597 | if (reset) { | ||
598 | local_irq_disable(); | ||
599 | reset_hd(); | ||
600 | return; | ||
601 | } | ||
602 | disk = req->rq_disk->private_data; | ||
603 | block = req->sector; | ||
604 | nsect = req->nr_sectors; | ||
605 | if (block >= get_capacity(req->rq_disk) || | ||
606 | ((block+nsect) > get_capacity(req->rq_disk))) { | ||
607 | printk("%s: bad access: block=%d, count=%d\n", | ||
608 | req->rq_disk->disk_name, block, nsect); | ||
609 | end_request(req, 0); | ||
610 | goto repeat; | ||
611 | } | ||
612 | |||
613 | if (disk->special_op) { | ||
614 | if (do_special_op(disk, req)) | ||
615 | goto repeat; | ||
616 | return; | ||
617 | } | ||
618 | sec = block % disk->sect + 1; | ||
619 | track = block / disk->sect; | ||
620 | head = track % disk->head; | ||
621 | cyl = track / disk->head; | ||
622 | #ifdef DEBUG | ||
623 | printk("%s: %sing: CHS=%d/%d/%d, sectors=%d, buffer=%p\n", | ||
624 | req->rq_disk->disk_name, (req->cmd == READ)?"read":"writ", | ||
625 | cyl, head, sec, nsect, req->buffer); | ||
626 | #endif | ||
627 | if (req->flags & REQ_CMD) { | ||
628 | switch (rq_data_dir(req)) { | ||
629 | case READ: | ||
630 | hd_out(disk,nsect,sec,head,cyl,WIN_READ,&read_intr); | ||
631 | if (reset) | ||
632 | goto repeat; | ||
633 | break; | ||
634 | case WRITE: | ||
635 | hd_out(disk,nsect,sec,head,cyl,WIN_WRITE,&write_intr); | ||
636 | if (reset) | ||
637 | goto repeat; | ||
638 | if (wait_DRQ()) { | ||
639 | bad_rw_intr(); | ||
640 | goto repeat; | ||
641 | } | ||
642 | outsw(HD_DATA,req->buffer,256); | ||
643 | break; | ||
644 | default: | ||
645 | printk("unknown hd-command\n"); | ||
646 | end_request(req, 0); | ||
647 | break; | ||
648 | } | ||
649 | } | ||
650 | } | ||
651 | |||
652 | static void do_hd_request (request_queue_t * q) | ||
653 | { | ||
654 | disable_irq(HD_IRQ); | ||
655 | hd_request(); | ||
656 | enable_irq(HD_IRQ); | ||
657 | } | ||
658 | |||
659 | static int hd_ioctl(struct inode * inode, struct file * file, | ||
660 | unsigned int cmd, unsigned long arg) | ||
661 | { | ||
662 | struct hd_i_struct *disk = inode->i_bdev->bd_disk->private_data; | ||
663 | struct hd_geometry __user *loc = (struct hd_geometry __user *) arg; | ||
664 | struct hd_geometry g; | ||
665 | |||
666 | if (cmd != HDIO_GETGEO) | ||
667 | return -EINVAL; | ||
668 | if (!loc) | ||
669 | return -EINVAL; | ||
670 | g.heads = disk->head; | ||
671 | g.sectors = disk->sect; | ||
672 | g.cylinders = disk->cyl; | ||
673 | g.start = get_start_sect(inode->i_bdev); | ||
674 | return copy_to_user(loc, &g, sizeof g) ? -EFAULT : 0; | ||
675 | } | ||
676 | |||
677 | /* | ||
678 | * Releasing a block device means we sync() it, so that it can safely | ||
679 | * be forgotten about... | ||
680 | */ | ||
681 | |||
682 | static irqreturn_t hd_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
683 | { | ||
684 | void (*handler)(void) = do_hd; | ||
685 | |||
686 | do_hd = NULL; | ||
687 | del_timer(&device_timer); | ||
688 | if (!handler) | ||
689 | handler = unexpected_hd_interrupt; | ||
690 | handler(); | ||
691 | local_irq_enable(); | ||
692 | return IRQ_HANDLED; | ||
693 | } | ||
694 | |||
695 | static struct block_device_operations hd_fops = { | ||
696 | .ioctl = hd_ioctl, | ||
697 | }; | ||
698 | |||
699 | /* | ||
700 | * This is the hard disk IRQ description. The SA_INTERRUPT in sa_flags | ||
701 | * means we run the IRQ-handler with interrupts disabled: this is bad for | ||
702 | * interrupt latency, but anything else has led to problems on some | ||
703 | * machines. | ||
704 | * | ||
705 | * We enable interrupts in some of the routines after making sure it's | ||
706 | * safe. | ||
707 | */ | ||
708 | |||
709 | static int __init hd_init(void) | ||
710 | { | ||
711 | int drive; | ||
712 | |||
713 | if (register_blkdev(MAJOR_NR,"hd")) | ||
714 | return -1; | ||
715 | |||
716 | hd_queue = blk_init_queue(do_hd_request, &hd_lock); | ||
717 | if (!hd_queue) { | ||
718 | unregister_blkdev(MAJOR_NR,"hd"); | ||
719 | return -ENOMEM; | ||
720 | } | ||
721 | |||
722 | blk_queue_max_sectors(hd_queue, 255); | ||
723 | init_timer(&device_timer); | ||
724 | device_timer.function = hd_times_out; | ||
725 | blk_queue_hardsect_size(hd_queue, 512); | ||
726 | |||
727 | #ifdef __i386__ | ||
728 | if (!NR_HD) { | ||
729 | extern struct drive_info drive_info; | ||
730 | unsigned char *BIOS = (unsigned char *) &drive_info; | ||
731 | unsigned long flags; | ||
732 | int cmos_disks; | ||
733 | |||
734 | for (drive=0 ; drive<2 ; drive++) { | ||
735 | hd_info[drive].cyl = *(unsigned short *) BIOS; | ||
736 | hd_info[drive].head = *(2+BIOS); | ||
737 | hd_info[drive].wpcom = *(unsigned short *) (5+BIOS); | ||
738 | hd_info[drive].ctl = *(8+BIOS); | ||
739 | hd_info[drive].lzone = *(unsigned short *) (12+BIOS); | ||
740 | hd_info[drive].sect = *(14+BIOS); | ||
741 | #ifdef does_not_work_for_everybody_with_scsi_but_helps_ibm_vp | ||
742 | if (hd_info[drive].cyl && NR_HD == drive) | ||
743 | NR_HD++; | ||
744 | #endif | ||
745 | BIOS += 16; | ||
746 | } | ||
747 | |||
748 | /* | ||
749 | We query CMOS about hard disks : it could be that | ||
750 | we have a SCSI/ESDI/etc controller that is BIOS | ||
751 | compatible with ST-506, and thus showing up in our | ||
752 | BIOS table, but not register compatible, and therefore | ||
753 | not present in CMOS. | ||
754 | |||
755 | Furthermore, we will assume that our ST-506 drives | ||
756 | <if any> are the primary drives in the system, and | ||
757 | the ones reflected as drive 1 or 2. | ||
758 | |||
759 | The first drive is stored in the high nibble of CMOS | ||
760 | byte 0x12, the second in the low nibble. This will be | ||
761 | either a 4 bit drive type or 0xf indicating use byte 0x19 | ||
762 | for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. | ||
763 | |||
764 | Needless to say, a non-zero value means we have | ||
765 | an AT controller hard disk for that drive. | ||
766 | |||
767 | Currently the rtc_lock is a bit academic since this | ||
768 | driver is non-modular, but someday... ? Paul G. | ||
769 | */ | ||
770 | |||
771 | spin_lock_irqsave(&rtc_lock, flags); | ||
772 | cmos_disks = CMOS_READ(0x12); | ||
773 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
774 | |||
775 | if (cmos_disks & 0xf0) { | ||
776 | if (cmos_disks & 0x0f) | ||
777 | NR_HD = 2; | ||
778 | else | ||
779 | NR_HD = 1; | ||
780 | } | ||
781 | } | ||
782 | #endif /* __i386__ */ | ||
783 | #ifdef __arm__ | ||
784 | if (!NR_HD) { | ||
785 | /* We don't know anything about the drive. This means | ||
786 | * that you *MUST* specify the drive parameters to the | ||
787 | * kernel yourself. | ||
788 | */ | ||
789 | printk("hd: no drives specified - use hd=cyl,head,sectors" | ||
790 | " on kernel command line\n"); | ||
791 | } | ||
792 | #endif | ||
793 | if (!NR_HD) | ||
794 | goto out; | ||
795 | |||
796 | for (drive=0 ; drive < NR_HD ; drive++) { | ||
797 | struct gendisk *disk = alloc_disk(64); | ||
798 | struct hd_i_struct *p = &hd_info[drive]; | ||
799 | if (!disk) | ||
800 | goto Enomem; | ||
801 | disk->major = MAJOR_NR; | ||
802 | disk->first_minor = drive << 6; | ||
803 | disk->fops = &hd_fops; | ||
804 | sprintf(disk->disk_name, "hd%c", 'a'+drive); | ||
805 | disk->private_data = p; | ||
806 | set_capacity(disk, p->head * p->sect * p->cyl); | ||
807 | disk->queue = hd_queue; | ||
808 | p->unit = drive; | ||
809 | hd_gendisk[drive] = disk; | ||
810 | printk ("%s: %luMB, CHS=%d/%d/%d\n", | ||
811 | disk->disk_name, (unsigned long)get_capacity(disk)/2048, | ||
812 | p->cyl, p->head, p->sect); | ||
813 | } | ||
814 | |||
815 | if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd", NULL)) { | ||
816 | printk("hd: unable to get IRQ%d for the hard disk driver\n", | ||
817 | HD_IRQ); | ||
818 | goto out1; | ||
819 | } | ||
820 | if (!request_region(HD_DATA, 8, "hd")) { | ||
821 | printk(KERN_WARNING "hd: port 0x%x busy\n", HD_DATA); | ||
822 | goto out2; | ||
823 | } | ||
824 | if (!request_region(HD_CMD, 1, "hd(cmd)")) { | ||
825 | printk(KERN_WARNING "hd: port 0x%x busy\n", HD_CMD); | ||
826 | goto out3; | ||
827 | } | ||
828 | |||
829 | /* Let them fly */ | ||
830 | for(drive=0; drive < NR_HD; drive++) | ||
831 | add_disk(hd_gendisk[drive]); | ||
832 | |||
833 | return 0; | ||
834 | |||
835 | out3: | ||
836 | release_region(HD_DATA, 8); | ||
837 | out2: | ||
838 | free_irq(HD_IRQ, NULL); | ||
839 | out1: | ||
840 | for (drive = 0; drive < NR_HD; drive++) | ||
841 | put_disk(hd_gendisk[drive]); | ||
842 | NR_HD = 0; | ||
843 | out: | ||
844 | del_timer(&device_timer); | ||
845 | unregister_blkdev(MAJOR_NR,"hd"); | ||
846 | blk_cleanup_queue(hd_queue); | ||
847 | return -1; | ||
848 | Enomem: | ||
849 | while (drive--) | ||
850 | put_disk(hd_gendisk[drive]); | ||
851 | goto out; | ||
852 | } | ||
853 | |||
854 | static int parse_hd_setup (char *line) { | ||
855 | int ints[6]; | ||
856 | |||
857 | (void) get_options(line, ARRAY_SIZE(ints), ints); | ||
858 | hd_setup(NULL, ints); | ||
859 | |||
860 | return 1; | ||
861 | } | ||
862 | __setup("hd=", parse_hd_setup); | ||
863 | |||
864 | module_init(hd_init); | ||