diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-07-18 16:00:54 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-07-18 16:00:54 -0400 |
commit | bb2c018b09b681d43f5e08124b83e362647ea82b (patch) | |
tree | d794902c78f9fdd04ed88a4b8d451ed6f9292ec0 /drivers/block/hd.c | |
parent | 82638844d9a8581bbf33201cc209a14876eca167 (diff) | |
parent | 5b664cb235e97afbf34db9c4d77f08ebd725335e (diff) |
Merge branch 'linus' into cpus4096
Conflicts:
drivers/acpi/processor_throttling.c
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'drivers/block/hd.c')
-rw-r--r-- | drivers/block/hd.c | 814 |
1 files changed, 814 insertions, 0 deletions
diff --git a/drivers/block/hd.c b/drivers/block/hd.c new file mode 100644 index 000000000000..682243bf2e46 --- /dev/null +++ b/drivers/block/hd.c | |||
@@ -0,0 +1,814 @@ | |||
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/init.h> | ||
41 | #include <linux/blkpg.h> | ||
42 | #include <linux/hdreg.h> | ||
43 | |||
44 | #define REALLY_SLOW_IO | ||
45 | #include <asm/system.h> | ||
46 | #include <asm/io.h> | ||
47 | #include <asm/uaccess.h> | ||
48 | |||
49 | #ifdef __arm__ | ||
50 | #undef HD_IRQ | ||
51 | #endif | ||
52 | #include <asm/irq.h> | ||
53 | #ifdef __arm__ | ||
54 | #define HD_IRQ IRQ_HARDDISK | ||
55 | #endif | ||
56 | |||
57 | /* Hd controller regster ports */ | ||
58 | |||
59 | #define HD_DATA 0x1f0 /* _CTL when writing */ | ||
60 | #define HD_ERROR 0x1f1 /* see err-bits */ | ||
61 | #define HD_NSECTOR 0x1f2 /* nr of sectors to read/write */ | ||
62 | #define HD_SECTOR 0x1f3 /* starting sector */ | ||
63 | #define HD_LCYL 0x1f4 /* starting cylinder */ | ||
64 | #define HD_HCYL 0x1f5 /* high byte of starting cyl */ | ||
65 | #define HD_CURRENT 0x1f6 /* 101dhhhh , d=drive, hhhh=head */ | ||
66 | #define HD_STATUS 0x1f7 /* see status-bits */ | ||
67 | #define HD_FEATURE HD_ERROR /* same io address, read=error, write=feature */ | ||
68 | #define HD_PRECOMP HD_FEATURE /* obsolete use of this port - predates IDE */ | ||
69 | #define HD_COMMAND HD_STATUS /* same io address, read=status, write=cmd */ | ||
70 | |||
71 | #define HD_CMD 0x3f6 /* used for resets */ | ||
72 | #define HD_ALTSTATUS 0x3f6 /* same as HD_STATUS but doesn't clear irq */ | ||
73 | |||
74 | /* Bits of HD_STATUS */ | ||
75 | #define ERR_STAT 0x01 | ||
76 | #define INDEX_STAT 0x02 | ||
77 | #define ECC_STAT 0x04 /* Corrected error */ | ||
78 | #define DRQ_STAT 0x08 | ||
79 | #define SEEK_STAT 0x10 | ||
80 | #define SERVICE_STAT SEEK_STAT | ||
81 | #define WRERR_STAT 0x20 | ||
82 | #define READY_STAT 0x40 | ||
83 | #define BUSY_STAT 0x80 | ||
84 | |||
85 | /* Bits for HD_ERROR */ | ||
86 | #define MARK_ERR 0x01 /* Bad address mark */ | ||
87 | #define TRK0_ERR 0x02 /* couldn't find track 0 */ | ||
88 | #define ABRT_ERR 0x04 /* Command aborted */ | ||
89 | #define MCR_ERR 0x08 /* media change request */ | ||
90 | #define ID_ERR 0x10 /* ID field not found */ | ||
91 | #define MC_ERR 0x20 /* media changed */ | ||
92 | #define ECC_ERR 0x40 /* Uncorrectable ECC error */ | ||
93 | #define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */ | ||
94 | #define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */ | ||
95 | |||
96 | static DEFINE_SPINLOCK(hd_lock); | ||
97 | static struct request_queue *hd_queue; | ||
98 | |||
99 | #define MAJOR_NR HD_MAJOR | ||
100 | #define QUEUE (hd_queue) | ||
101 | #define CURRENT elv_next_request(hd_queue) | ||
102 | |||
103 | #define TIMEOUT_VALUE (6*HZ) | ||
104 | #define HD_DELAY 0 | ||
105 | |||
106 | #define MAX_ERRORS 16 /* Max read/write errors/sector */ | ||
107 | #define RESET_FREQ 8 /* Reset controller every 8th retry */ | ||
108 | #define RECAL_FREQ 4 /* Recalibrate every 4th retry */ | ||
109 | #define MAX_HD 2 | ||
110 | |||
111 | #define STAT_OK (READY_STAT|SEEK_STAT) | ||
112 | #define OK_STATUS(s) (((s)&(STAT_OK|(BUSY_STAT|WRERR_STAT|ERR_STAT)))==STAT_OK) | ||
113 | |||
114 | static void recal_intr(void); | ||
115 | static void bad_rw_intr(void); | ||
116 | |||
117 | static int reset; | ||
118 | static int hd_error; | ||
119 | |||
120 | /* | ||
121 | * This struct defines the HD's and their types. | ||
122 | */ | ||
123 | struct hd_i_struct { | ||
124 | unsigned int head, sect, cyl, wpcom, lzone, ctl; | ||
125 | int unit; | ||
126 | int recalibrate; | ||
127 | int special_op; | ||
128 | }; | ||
129 | |||
130 | #ifdef HD_TYPE | ||
131 | static struct hd_i_struct hd_info[] = { HD_TYPE }; | ||
132 | static int NR_HD = ARRAY_SIZE(hd_info); | ||
133 | #else | ||
134 | static struct hd_i_struct hd_info[MAX_HD]; | ||
135 | static int NR_HD; | ||
136 | #endif | ||
137 | |||
138 | static struct gendisk *hd_gendisk[MAX_HD]; | ||
139 | |||
140 | static struct timer_list device_timer; | ||
141 | |||
142 | #define TIMEOUT_VALUE (6*HZ) | ||
143 | |||
144 | #define SET_TIMER \ | ||
145 | do { \ | ||
146 | mod_timer(&device_timer, jiffies + TIMEOUT_VALUE); \ | ||
147 | } while (0) | ||
148 | |||
149 | static void (*do_hd)(void) = NULL; | ||
150 | #define SET_HANDLER(x) \ | ||
151 | if ((do_hd = (x)) != NULL) \ | ||
152 | SET_TIMER; \ | ||
153 | else \ | ||
154 | del_timer(&device_timer); | ||
155 | |||
156 | |||
157 | #if (HD_DELAY > 0) | ||
158 | |||
159 | #include <asm/i8253.h> | ||
160 | |||
161 | unsigned long last_req; | ||
162 | |||
163 | unsigned long read_timer(void) | ||
164 | { | ||
165 | unsigned long t, flags; | ||
166 | int i; | ||
167 | |||
168 | spin_lock_irqsave(&i8253_lock, flags); | ||
169 | t = jiffies * 11932; | ||
170 | outb_p(0, 0x43); | ||
171 | i = inb_p(0x40); | ||
172 | i |= inb(0x40) << 8; | ||
173 | spin_unlock_irqrestore(&i8253_lock, flags); | ||
174 | return(t - i); | ||
175 | } | ||
176 | #endif | ||
177 | |||
178 | static void __init hd_setup(char *str, int *ints) | ||
179 | { | ||
180 | int hdind = 0; | ||
181 | |||
182 | if (ints[0] != 3) | ||
183 | return; | ||
184 | if (hd_info[0].head != 0) | ||
185 | hdind = 1; | ||
186 | hd_info[hdind].head = ints[2]; | ||
187 | hd_info[hdind].sect = ints[3]; | ||
188 | hd_info[hdind].cyl = ints[1]; | ||
189 | hd_info[hdind].wpcom = 0; | ||
190 | hd_info[hdind].lzone = ints[1]; | ||
191 | hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0); | ||
192 | NR_HD = hdind+1; | ||
193 | } | ||
194 | |||
195 | static void dump_status(const char *msg, unsigned int stat) | ||
196 | { | ||
197 | char *name = "hd?"; | ||
198 | if (CURRENT) | ||
199 | name = CURRENT->rq_disk->disk_name; | ||
200 | |||
201 | #ifdef VERBOSE_ERRORS | ||
202 | printk("%s: %s: status=0x%02x { ", name, msg, stat & 0xff); | ||
203 | if (stat & BUSY_STAT) printk("Busy "); | ||
204 | if (stat & READY_STAT) printk("DriveReady "); | ||
205 | if (stat & WRERR_STAT) printk("WriteFault "); | ||
206 | if (stat & SEEK_STAT) printk("SeekComplete "); | ||
207 | if (stat & DRQ_STAT) printk("DataRequest "); | ||
208 | if (stat & ECC_STAT) printk("CorrectedError "); | ||
209 | if (stat & INDEX_STAT) printk("Index "); | ||
210 | if (stat & ERR_STAT) printk("Error "); | ||
211 | printk("}\n"); | ||
212 | if ((stat & ERR_STAT) == 0) { | ||
213 | hd_error = 0; | ||
214 | } else { | ||
215 | hd_error = inb(HD_ERROR); | ||
216 | printk("%s: %s: error=0x%02x { ", name, msg, hd_error & 0xff); | ||
217 | if (hd_error & BBD_ERR) printk("BadSector "); | ||
218 | if (hd_error & ECC_ERR) printk("UncorrectableError "); | ||
219 | if (hd_error & ID_ERR) printk("SectorIdNotFound "); | ||
220 | if (hd_error & ABRT_ERR) printk("DriveStatusError "); | ||
221 | if (hd_error & TRK0_ERR) printk("TrackZeroNotFound "); | ||
222 | if (hd_error & MARK_ERR) printk("AddrMarkNotFound "); | ||
223 | printk("}"); | ||
224 | if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) { | ||
225 | printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL), | ||
226 | inb(HD_CURRENT) & 0xf, inb(HD_SECTOR)); | ||
227 | if (CURRENT) | ||
228 | printk(", sector=%ld", CURRENT->sector); | ||
229 | } | ||
230 | printk("\n"); | ||
231 | } | ||
232 | #else | ||
233 | printk("%s: %s: status=0x%02x.\n", name, msg, stat & 0xff); | ||
234 | if ((stat & ERR_STAT) == 0) { | ||
235 | hd_error = 0; | ||
236 | } else { | ||
237 | hd_error = inb(HD_ERROR); | ||
238 | printk("%s: %s: error=0x%02x.\n", name, msg, hd_error & 0xff); | ||
239 | } | ||
240 | #endif | ||
241 | } | ||
242 | |||
243 | static void check_status(void) | ||
244 | { | ||
245 | int i = inb_p(HD_STATUS); | ||
246 | |||
247 | if (!OK_STATUS(i)) { | ||
248 | dump_status("check_status", i); | ||
249 | bad_rw_intr(); | ||
250 | } | ||
251 | } | ||
252 | |||
253 | static int controller_busy(void) | ||
254 | { | ||
255 | int retries = 100000; | ||
256 | unsigned char status; | ||
257 | |||
258 | do { | ||
259 | status = inb_p(HD_STATUS); | ||
260 | } while ((status & BUSY_STAT) && --retries); | ||
261 | return status; | ||
262 | } | ||
263 | |||
264 | static int status_ok(void) | ||
265 | { | ||
266 | unsigned char status = inb_p(HD_STATUS); | ||
267 | |||
268 | if (status & BUSY_STAT) | ||
269 | return 1; /* Ancient, but does it make sense??? */ | ||
270 | if (status & WRERR_STAT) | ||
271 | return 0; | ||
272 | if (!(status & READY_STAT)) | ||
273 | return 0; | ||
274 | if (!(status & SEEK_STAT)) | ||
275 | return 0; | ||
276 | return 1; | ||
277 | } | ||
278 | |||
279 | static int controller_ready(unsigned int drive, unsigned int head) | ||
280 | { | ||
281 | int retry = 100; | ||
282 | |||
283 | do { | ||
284 | if (controller_busy() & BUSY_STAT) | ||
285 | return 0; | ||
286 | outb_p(0xA0 | (drive<<4) | head, HD_CURRENT); | ||
287 | if (status_ok()) | ||
288 | return 1; | ||
289 | } while (--retry); | ||
290 | return 0; | ||
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; | ||
423 | int stat; | ||
424 | |||
425 | for (retries = 0; retries < 100000; retries++) { | ||
426 | stat = inb_p(HD_STATUS); | ||
427 | if (stat & DRQ_STAT) | ||
428 | return 0; | ||
429 | } | ||
430 | dump_status("wait_DRQ", stat); | ||
431 | return -1; | ||
432 | } | ||
433 | |||
434 | static void read_intr(void) | ||
435 | { | ||
436 | struct request *req; | ||
437 | int i, retries = 100000; | ||
438 | |||
439 | do { | ||
440 | i = (unsigned) inb_p(HD_STATUS); | ||
441 | if (i & BUSY_STAT) | ||
442 | continue; | ||
443 | if (!OK_STATUS(i)) | ||
444 | break; | ||
445 | if (i & DRQ_STAT) | ||
446 | goto ok_to_read; | ||
447 | } while (--retries > 0); | ||
448 | dump_status("read_intr", i); | ||
449 | bad_rw_intr(); | ||
450 | hd_request(); | ||
451 | return; | ||
452 | ok_to_read: | ||
453 | req = CURRENT; | ||
454 | insw(HD_DATA, req->buffer, 256); | ||
455 | req->sector++; | ||
456 | req->buffer += 512; | ||
457 | req->errors = 0; | ||
458 | i = --req->nr_sectors; | ||
459 | --req->current_nr_sectors; | ||
460 | #ifdef DEBUG | ||
461 | printk("%s: read: sector %ld, remaining = %ld, buffer=%p\n", | ||
462 | req->rq_disk->disk_name, req->sector, req->nr_sectors, | ||
463 | req->buffer+512); | ||
464 | #endif | ||
465 | if (req->current_nr_sectors <= 0) | ||
466 | end_request(req, 1); | ||
467 | if (i > 0) { | ||
468 | SET_HANDLER(&read_intr); | ||
469 | return; | ||
470 | } | ||
471 | (void) inb_p(HD_STATUS); | ||
472 | #if (HD_DELAY > 0) | ||
473 | last_req = read_timer(); | ||
474 | #endif | ||
475 | if (elv_next_request(QUEUE)) | ||
476 | hd_request(); | ||
477 | return; | ||
478 | } | ||
479 | |||
480 | static void write_intr(void) | ||
481 | { | ||
482 | struct request *req = CURRENT; | ||
483 | int i; | ||
484 | int retries = 100000; | ||
485 | |||
486 | do { | ||
487 | i = (unsigned) inb_p(HD_STATUS); | ||
488 | if (i & BUSY_STAT) | ||
489 | continue; | ||
490 | if (!OK_STATUS(i)) | ||
491 | break; | ||
492 | if ((req->nr_sectors <= 1) || (i & DRQ_STAT)) | ||
493 | goto ok_to_write; | ||
494 | } while (--retries > 0); | ||
495 | dump_status("write_intr", i); | ||
496 | bad_rw_intr(); | ||
497 | hd_request(); | ||
498 | return; | ||
499 | ok_to_write: | ||
500 | req->sector++; | ||
501 | i = --req->nr_sectors; | ||
502 | --req->current_nr_sectors; | ||
503 | req->buffer += 512; | ||
504 | if (!i || (req->bio && req->current_nr_sectors <= 0)) | ||
505 | end_request(req, 1); | ||
506 | if (i > 0) { | ||
507 | SET_HANDLER(&write_intr); | ||
508 | outsw(HD_DATA, req->buffer, 256); | ||
509 | local_irq_enable(); | ||
510 | } else { | ||
511 | #if (HD_DELAY > 0) | ||
512 | last_req = read_timer(); | ||
513 | #endif | ||
514 | hd_request(); | ||
515 | } | ||
516 | return; | ||
517 | } | ||
518 | |||
519 | static void recal_intr(void) | ||
520 | { | ||
521 | check_status(); | ||
522 | #if (HD_DELAY > 0) | ||
523 | last_req = read_timer(); | ||
524 | #endif | ||
525 | hd_request(); | ||
526 | } | ||
527 | |||
528 | /* | ||
529 | * This is another of the error-routines I don't know what to do with. The | ||
530 | * best idea seems to just set reset, and start all over again. | ||
531 | */ | ||
532 | static void hd_times_out(unsigned long dummy) | ||
533 | { | ||
534 | char *name; | ||
535 | |||
536 | do_hd = NULL; | ||
537 | |||
538 | if (!CURRENT) | ||
539 | return; | ||
540 | |||
541 | disable_irq(HD_IRQ); | ||
542 | local_irq_enable(); | ||
543 | reset = 1; | ||
544 | name = CURRENT->rq_disk->disk_name; | ||
545 | printk("%s: timeout\n", name); | ||
546 | if (++CURRENT->errors >= MAX_ERRORS) { | ||
547 | #ifdef DEBUG | ||
548 | printk("%s: too many errors\n", name); | ||
549 | #endif | ||
550 | end_request(CURRENT, 0); | ||
551 | } | ||
552 | local_irq_disable(); | ||
553 | hd_request(); | ||
554 | enable_irq(HD_IRQ); | ||
555 | } | ||
556 | |||
557 | static int do_special_op(struct hd_i_struct *disk, struct request *req) | ||
558 | { | ||
559 | if (disk->recalibrate) { | ||
560 | disk->recalibrate = 0; | ||
561 | hd_out(disk, disk->sect, 0, 0, 0, WIN_RESTORE, &recal_intr); | ||
562 | return reset; | ||
563 | } | ||
564 | if (disk->head > 16) { | ||
565 | printk("%s: cannot handle device with more than 16 heads - giving up\n", req->rq_disk->disk_name); | ||
566 | end_request(req, 0); | ||
567 | } | ||
568 | disk->special_op = 0; | ||
569 | return 1; | ||
570 | } | ||
571 | |||
572 | /* | ||
573 | * The driver enables interrupts as much as possible. In order to do this, | ||
574 | * (a) the device-interrupt is disabled before entering hd_request(), | ||
575 | * and (b) the timeout-interrupt is disabled before the sti(). | ||
576 | * | ||
577 | * Interrupts are still masked (by default) whenever we are exchanging | ||
578 | * data/cmds with a drive, because some drives seem to have very poor | ||
579 | * tolerance for latency during I/O. The IDE driver has support to unmask | ||
580 | * interrupts for non-broken hardware, so use that driver if required. | ||
581 | */ | ||
582 | static void hd_request(void) | ||
583 | { | ||
584 | unsigned int block, nsect, sec, track, head, cyl; | ||
585 | struct hd_i_struct *disk; | ||
586 | struct request *req; | ||
587 | |||
588 | if (do_hd) | ||
589 | return; | ||
590 | repeat: | ||
591 | del_timer(&device_timer); | ||
592 | local_irq_enable(); | ||
593 | |||
594 | req = CURRENT; | ||
595 | if (!req) { | ||
596 | do_hd = NULL; | ||
597 | return; | ||
598 | } | ||
599 | |||
600 | if (reset) { | ||
601 | local_irq_disable(); | ||
602 | reset_hd(); | ||
603 | return; | ||
604 | } | ||
605 | disk = req->rq_disk->private_data; | ||
606 | block = req->sector; | ||
607 | nsect = req->nr_sectors; | ||
608 | if (block >= get_capacity(req->rq_disk) || | ||
609 | ((block+nsect) > get_capacity(req->rq_disk))) { | ||
610 | printk("%s: bad access: block=%d, count=%d\n", | ||
611 | req->rq_disk->disk_name, block, nsect); | ||
612 | end_request(req, 0); | ||
613 | goto repeat; | ||
614 | } | ||
615 | |||
616 | if (disk->special_op) { | ||
617 | if (do_special_op(disk, req)) | ||
618 | goto repeat; | ||
619 | return; | ||
620 | } | ||
621 | sec = block % disk->sect + 1; | ||
622 | track = block / disk->sect; | ||
623 | head = track % disk->head; | ||
624 | cyl = track / disk->head; | ||
625 | #ifdef DEBUG | ||
626 | printk("%s: %sing: CHS=%d/%d/%d, sectors=%d, buffer=%p\n", | ||
627 | req->rq_disk->disk_name, | ||
628 | req_data_dir(req) == READ ? "read" : "writ", | ||
629 | cyl, head, sec, nsect, req->buffer); | ||
630 | #endif | ||
631 | if (blk_fs_request(req)) { | ||
632 | switch (rq_data_dir(req)) { | ||
633 | case READ: | ||
634 | hd_out(disk, nsect, sec, head, cyl, WIN_READ, | ||
635 | &read_intr); | ||
636 | if (reset) | ||
637 | goto repeat; | ||
638 | break; | ||
639 | case WRITE: | ||
640 | hd_out(disk, nsect, sec, head, cyl, WIN_WRITE, | ||
641 | &write_intr); | ||
642 | if (reset) | ||
643 | goto repeat; | ||
644 | if (wait_DRQ()) { | ||
645 | bad_rw_intr(); | ||
646 | goto repeat; | ||
647 | } | ||
648 | outsw(HD_DATA, req->buffer, 256); | ||
649 | break; | ||
650 | default: | ||
651 | printk("unknown hd-command\n"); | ||
652 | end_request(req, 0); | ||
653 | break; | ||
654 | } | ||
655 | } | ||
656 | } | ||
657 | |||
658 | static void do_hd_request(struct request_queue *q) | ||
659 | { | ||
660 | disable_irq(HD_IRQ); | ||
661 | hd_request(); | ||
662 | enable_irq(HD_IRQ); | ||
663 | } | ||
664 | |||
665 | static int hd_getgeo(struct block_device *bdev, struct hd_geometry *geo) | ||
666 | { | ||
667 | struct hd_i_struct *disk = bdev->bd_disk->private_data; | ||
668 | |||
669 | geo->heads = disk->head; | ||
670 | geo->sectors = disk->sect; | ||
671 | geo->cylinders = disk->cyl; | ||
672 | return 0; | ||
673 | } | ||
674 | |||
675 | /* | ||
676 | * Releasing a block device means we sync() it, so that it can safely | ||
677 | * be forgotten about... | ||
678 | */ | ||
679 | |||
680 | static irqreturn_t hd_interrupt(int irq, void *dev_id) | ||
681 | { | ||
682 | void (*handler)(void) = do_hd; | ||
683 | |||
684 | do_hd = NULL; | ||
685 | del_timer(&device_timer); | ||
686 | if (!handler) | ||
687 | handler = unexpected_hd_interrupt; | ||
688 | handler(); | ||
689 | local_irq_enable(); | ||
690 | return IRQ_HANDLED; | ||
691 | } | ||
692 | |||
693 | static struct block_device_operations hd_fops = { | ||
694 | .getgeo = hd_getgeo, | ||
695 | }; | ||
696 | |||
697 | /* | ||
698 | * This is the hard disk IRQ description. The IRQF_DISABLED in sa_flags | ||
699 | * means we run the IRQ-handler with interrupts disabled: this is bad for | ||
700 | * interrupt latency, but anything else has led to problems on some | ||
701 | * machines. | ||
702 | * | ||
703 | * We enable interrupts in some of the routines after making sure it's | ||
704 | * safe. | ||
705 | */ | ||
706 | |||
707 | static int __init hd_init(void) | ||
708 | { | ||
709 | int drive; | ||
710 | |||
711 | if (register_blkdev(MAJOR_NR, "hd")) | ||
712 | return -1; | ||
713 | |||
714 | hd_queue = blk_init_queue(do_hd_request, &hd_lock); | ||
715 | if (!hd_queue) { | ||
716 | unregister_blkdev(MAJOR_NR, "hd"); | ||
717 | return -ENOMEM; | ||
718 | } | ||
719 | |||
720 | blk_queue_max_sectors(hd_queue, 255); | ||
721 | init_timer(&device_timer); | ||
722 | device_timer.function = hd_times_out; | ||
723 | blk_queue_hardsect_size(hd_queue, 512); | ||
724 | |||
725 | if (!NR_HD) { | ||
726 | /* | ||
727 | * We don't know anything about the drive. This means | ||
728 | * that you *MUST* specify the drive parameters to the | ||
729 | * kernel yourself. | ||
730 | * | ||
731 | * If we were on an i386, we used to read this info from | ||
732 | * the BIOS or CMOS. This doesn't work all that well, | ||
733 | * since this assumes that this is a primary or secondary | ||
734 | * drive, and if we're using this legacy driver, it's | ||
735 | * probably an auxilliary controller added to recover | ||
736 | * legacy data off an ST-506 drive. Either way, it's | ||
737 | * definitely safest to have the user explicitly specify | ||
738 | * the information. | ||
739 | */ | ||
740 | printk("hd: no drives specified - use hd=cyl,head,sectors" | ||
741 | " on kernel command line\n"); | ||
742 | goto out; | ||
743 | } | ||
744 | |||
745 | for (drive = 0 ; drive < NR_HD ; drive++) { | ||
746 | struct gendisk *disk = alloc_disk(64); | ||
747 | struct hd_i_struct *p = &hd_info[drive]; | ||
748 | if (!disk) | ||
749 | goto Enomem; | ||
750 | disk->major = MAJOR_NR; | ||
751 | disk->first_minor = drive << 6; | ||
752 | disk->fops = &hd_fops; | ||
753 | sprintf(disk->disk_name, "hd%c", 'a'+drive); | ||
754 | disk->private_data = p; | ||
755 | set_capacity(disk, p->head * p->sect * p->cyl); | ||
756 | disk->queue = hd_queue; | ||
757 | p->unit = drive; | ||
758 | hd_gendisk[drive] = disk; | ||
759 | printk("%s: %luMB, CHS=%d/%d/%d\n", | ||
760 | disk->disk_name, (unsigned long)get_capacity(disk)/2048, | ||
761 | p->cyl, p->head, p->sect); | ||
762 | } | ||
763 | |||
764 | if (request_irq(HD_IRQ, hd_interrupt, IRQF_DISABLED, "hd", NULL)) { | ||
765 | printk("hd: unable to get IRQ%d for the hard disk driver\n", | ||
766 | HD_IRQ); | ||
767 | goto out1; | ||
768 | } | ||
769 | if (!request_region(HD_DATA, 8, "hd")) { | ||
770 | printk(KERN_WARNING "hd: port 0x%x busy\n", HD_DATA); | ||
771 | goto out2; | ||
772 | } | ||
773 | if (!request_region(HD_CMD, 1, "hd(cmd)")) { | ||
774 | printk(KERN_WARNING "hd: port 0x%x busy\n", HD_CMD); | ||
775 | goto out3; | ||
776 | } | ||
777 | |||
778 | /* Let them fly */ | ||
779 | for (drive = 0; drive < NR_HD; drive++) | ||
780 | add_disk(hd_gendisk[drive]); | ||
781 | |||
782 | return 0; | ||
783 | |||
784 | out3: | ||
785 | release_region(HD_DATA, 8); | ||
786 | out2: | ||
787 | free_irq(HD_IRQ, NULL); | ||
788 | out1: | ||
789 | for (drive = 0; drive < NR_HD; drive++) | ||
790 | put_disk(hd_gendisk[drive]); | ||
791 | NR_HD = 0; | ||
792 | out: | ||
793 | del_timer(&device_timer); | ||
794 | unregister_blkdev(MAJOR_NR, "hd"); | ||
795 | blk_cleanup_queue(hd_queue); | ||
796 | return -1; | ||
797 | Enomem: | ||
798 | while (drive--) | ||
799 | put_disk(hd_gendisk[drive]); | ||
800 | goto out; | ||
801 | } | ||
802 | |||
803 | static int __init parse_hd_setup(char *line) | ||
804 | { | ||
805 | int ints[6]; | ||
806 | |||
807 | (void) get_options(line, ARRAY_SIZE(ints), ints); | ||
808 | hd_setup(NULL, ints); | ||
809 | |||
810 | return 1; | ||
811 | } | ||
812 | __setup("hd=", parse_hd_setup); | ||
813 | |||
814 | late_initcall(hd_init); | ||