diff options
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/Kconfig | 24 | ||||
-rw-r--r-- | drivers/block/Makefile | 1 | ||||
-rw-r--r-- | drivers/block/hd.c | 815 |
3 files changed, 840 insertions, 0 deletions
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 0d1d2133d9bc..c3909743c484 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig | |||
@@ -433,4 +433,28 @@ config VIRTIO_BLK | |||
433 | This is the virtual block driver for virtio. It can be used with | 433 | This is the virtual block driver for virtio. It can be used with |
434 | lguest or QEMU based VMMs (like KVM or Xen). Say Y or M. | 434 | lguest or QEMU based VMMs (like KVM or Xen). Say Y or M. |
435 | 435 | ||
436 | config BLK_DEV_HD | ||
437 | bool "Old hard disk (MFM/RLL/IDE) driver" | ||
438 | depends on HAVE_IDE | ||
439 | depends on !ARM || ARCH_RPC || ARCH_SHARK || BROKEN | ||
440 | help | ||
441 | There are two drivers for MFM/RLL/IDE hard disks. Most people use | ||
442 | the newer enhanced driver, but this old one is still around for two | ||
443 | reasons. Some older systems have strange timing problems and seem to | ||
444 | work only with the old driver (which itself does not work with some | ||
445 | newer systems). The other reason is that the old driver is smaller, | ||
446 | since it lacks the enhanced functionality of the new one. This makes | ||
447 | it a good choice for systems with very tight memory restrictions, or | ||
448 | for systems with only older MFM/RLL/ESDI drives. Choosing the old | ||
449 | driver can save 13 KB or so of kernel memory. | ||
450 | |||
451 | If you want to use this driver together with the new one you have | ||
452 | to use "hda=noprobe hdb=noprobe" kernel parameters to prevent the new | ||
453 | driver from probing the primary interface. | ||
454 | |||
455 | If you are unsure, then just choose the Enhanced IDE/MFM/RLL driver | ||
456 | instead of this one. For more detailed information, read the | ||
457 | Disk-HOWTO, available from | ||
458 | <http://www.tldp.org/docs.html#howto>. | ||
459 | |||
436 | endif # BLK_DEV | 460 | endif # BLK_DEV |
diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 5e584306be99..204332b29578 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile | |||
@@ -29,5 +29,6 @@ obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o | |||
29 | obj-$(CONFIG_VIODASD) += viodasd.o | 29 | obj-$(CONFIG_VIODASD) += viodasd.o |
30 | obj-$(CONFIG_BLK_DEV_SX8) += sx8.o | 30 | obj-$(CONFIG_BLK_DEV_SX8) += sx8.o |
31 | obj-$(CONFIG_BLK_DEV_UB) += ub.o | 31 | obj-$(CONFIG_BLK_DEV_UB) += ub.o |
32 | obj-$(CONFIG_BLK_DEV_HD) += hd.o | ||
32 | 33 | ||
33 | obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o | 34 | obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o |
diff --git a/drivers/block/hd.c b/drivers/block/hd.c new file mode 100644 index 000000000000..00b695652b2a --- /dev/null +++ b/drivers/block/hd.c | |||
@@ -0,0 +1,815 @@ | |||
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 = ARRAY_SIZE(hd_info); | ||
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 | |||
160 | #include <asm/i8253.h> | ||
161 | |||
162 | unsigned long last_req; | ||
163 | |||
164 | unsigned long read_timer(void) | ||
165 | { | ||
166 | unsigned long t, flags; | ||
167 | int i; | ||
168 | |||
169 | spin_lock_irqsave(&i8253_lock, flags); | ||
170 | t = jiffies * 11932; | ||
171 | outb_p(0, 0x43); | ||
172 | i = inb_p(0x40); | ||
173 | i |= inb(0x40) << 8; | ||
174 | spin_unlock_irqrestore(&i8253_lock, flags); | ||
175 | return(t - i); | ||
176 | } | ||
177 | #endif | ||
178 | |||
179 | static void __init hd_setup(char *str, int *ints) | ||
180 | { | ||
181 | int hdind = 0; | ||
182 | |||
183 | if (ints[0] != 3) | ||
184 | return; | ||
185 | if (hd_info[0].head != 0) | ||
186 | hdind = 1; | ||
187 | hd_info[hdind].head = ints[2]; | ||
188 | hd_info[hdind].sect = ints[3]; | ||
189 | hd_info[hdind].cyl = ints[1]; | ||
190 | hd_info[hdind].wpcom = 0; | ||
191 | hd_info[hdind].lzone = ints[1]; | ||
192 | hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0); | ||
193 | NR_HD = hdind+1; | ||
194 | } | ||
195 | |||
196 | static void dump_status(const char *msg, unsigned int stat) | ||
197 | { | ||
198 | char *name = "hd?"; | ||
199 | if (CURRENT) | ||
200 | name = CURRENT->rq_disk->disk_name; | ||
201 | |||
202 | #ifdef VERBOSE_ERRORS | ||
203 | printk("%s: %s: status=0x%02x { ", name, msg, stat & 0xff); | ||
204 | if (stat & BUSY_STAT) printk("Busy "); | ||
205 | if (stat & READY_STAT) printk("DriveReady "); | ||
206 | if (stat & WRERR_STAT) printk("WriteFault "); | ||
207 | if (stat & SEEK_STAT) printk("SeekComplete "); | ||
208 | if (stat & DRQ_STAT) printk("DataRequest "); | ||
209 | if (stat & ECC_STAT) printk("CorrectedError "); | ||
210 | if (stat & INDEX_STAT) printk("Index "); | ||
211 | if (stat & ERR_STAT) printk("Error "); | ||
212 | printk("}\n"); | ||
213 | if ((stat & ERR_STAT) == 0) { | ||
214 | hd_error = 0; | ||
215 | } else { | ||
216 | hd_error = inb(HD_ERROR); | ||
217 | printk("%s: %s: error=0x%02x { ", name, msg, hd_error & 0xff); | ||
218 | if (hd_error & BBD_ERR) printk("BadSector "); | ||
219 | if (hd_error & ECC_ERR) printk("UncorrectableError "); | ||
220 | if (hd_error & ID_ERR) printk("SectorIdNotFound "); | ||
221 | if (hd_error & ABRT_ERR) printk("DriveStatusError "); | ||
222 | if (hd_error & TRK0_ERR) printk("TrackZeroNotFound "); | ||
223 | if (hd_error & MARK_ERR) printk("AddrMarkNotFound "); | ||
224 | printk("}"); | ||
225 | if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) { | ||
226 | printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL), | ||
227 | inb(HD_CURRENT) & 0xf, inb(HD_SECTOR)); | ||
228 | if (CURRENT) | ||
229 | printk(", sector=%ld", CURRENT->sector); | ||
230 | } | ||
231 | printk("\n"); | ||
232 | } | ||
233 | #else | ||
234 | printk("%s: %s: status=0x%02x.\n", name, msg, stat & 0xff); | ||
235 | if ((stat & ERR_STAT) == 0) { | ||
236 | hd_error = 0; | ||
237 | } else { | ||
238 | hd_error = inb(HD_ERROR); | ||
239 | printk("%s: %s: error=0x%02x.\n", name, msg, hd_error & 0xff); | ||
240 | } | ||
241 | #endif | ||
242 | } | ||
243 | |||
244 | static void check_status(void) | ||
245 | { | ||
246 | int i = inb_p(HD_STATUS); | ||
247 | |||
248 | if (!OK_STATUS(i)) { | ||
249 | dump_status("check_status", i); | ||
250 | bad_rw_intr(); | ||
251 | } | ||
252 | } | ||
253 | |||
254 | static int controller_busy(void) | ||
255 | { | ||
256 | int retries = 100000; | ||
257 | unsigned char status; | ||
258 | |||
259 | do { | ||
260 | status = inb_p(HD_STATUS); | ||
261 | } while ((status & BUSY_STAT) && --retries); | ||
262 | return status; | ||
263 | } | ||
264 | |||
265 | static int status_ok(void) | ||
266 | { | ||
267 | unsigned char status = inb_p(HD_STATUS); | ||
268 | |||
269 | if (status & BUSY_STAT) | ||
270 | return 1; /* Ancient, but does it make sense??? */ | ||
271 | if (status & WRERR_STAT) | ||
272 | return 0; | ||
273 | if (!(status & READY_STAT)) | ||
274 | return 0; | ||
275 | if (!(status & SEEK_STAT)) | ||
276 | return 0; | ||
277 | return 1; | ||
278 | } | ||
279 | |||
280 | static int controller_ready(unsigned int drive, unsigned int head) | ||
281 | { | ||
282 | int retry = 100; | ||
283 | |||
284 | do { | ||
285 | if (controller_busy() & BUSY_STAT) | ||
286 | return 0; | ||
287 | outb_p(0xA0 | (drive<<4) | head, HD_CURRENT); | ||
288 | if (status_ok()) | ||
289 | return 1; | ||
290 | } while (--retry); | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static void hd_out(struct hd_i_struct *disk, | ||
295 | unsigned int nsect, | ||
296 | unsigned int sect, | ||
297 | unsigned int head, | ||
298 | unsigned int cyl, | ||
299 | unsigned int cmd, | ||
300 | void (*intr_addr)(void)) | ||
301 | { | ||
302 | unsigned short port; | ||
303 | |||
304 | #if (HD_DELAY > 0) | ||
305 | while (read_timer() - last_req < HD_DELAY) | ||
306 | /* nothing */; | ||
307 | #endif | ||
308 | if (reset) | ||
309 | return; | ||
310 | if (!controller_ready(disk->unit, head)) { | ||
311 | reset = 1; | ||
312 | return; | ||
313 | } | ||
314 | SET_HANDLER(intr_addr); | ||
315 | outb_p(disk->ctl, HD_CMD); | ||
316 | port = HD_DATA; | ||
317 | outb_p(disk->wpcom >> 2, ++port); | ||
318 | outb_p(nsect, ++port); | ||
319 | outb_p(sect, ++port); | ||
320 | outb_p(cyl, ++port); | ||
321 | outb_p(cyl >> 8, ++port); | ||
322 | outb_p(0xA0 | (disk->unit << 4) | head, ++port); | ||
323 | outb_p(cmd, ++port); | ||
324 | } | ||
325 | |||
326 | static void hd_request (void); | ||
327 | |||
328 | static int drive_busy(void) | ||
329 | { | ||
330 | unsigned int i; | ||
331 | unsigned char c; | ||
332 | |||
333 | for (i = 0; i < 500000 ; i++) { | ||
334 | c = inb_p(HD_STATUS); | ||
335 | if ((c & (BUSY_STAT | READY_STAT | SEEK_STAT)) == STAT_OK) | ||
336 | return 0; | ||
337 | } | ||
338 | dump_status("reset timed out", c); | ||
339 | return 1; | ||
340 | } | ||
341 | |||
342 | static void reset_controller(void) | ||
343 | { | ||
344 | int i; | ||
345 | |||
346 | outb_p(4, HD_CMD); | ||
347 | for (i = 0; i < 1000; i++) barrier(); | ||
348 | outb_p(hd_info[0].ctl & 0x0f, HD_CMD); | ||
349 | for (i = 0; i < 1000; i++) barrier(); | ||
350 | if (drive_busy()) | ||
351 | printk("hd: controller still busy\n"); | ||
352 | else if ((hd_error = inb(HD_ERROR)) != 1) | ||
353 | printk("hd: controller reset failed: %02x\n", hd_error); | ||
354 | } | ||
355 | |||
356 | static void reset_hd(void) | ||
357 | { | ||
358 | static int i; | ||
359 | |||
360 | repeat: | ||
361 | if (reset) { | ||
362 | reset = 0; | ||
363 | i = -1; | ||
364 | reset_controller(); | ||
365 | } else { | ||
366 | check_status(); | ||
367 | if (reset) | ||
368 | goto repeat; | ||
369 | } | ||
370 | if (++i < NR_HD) { | ||
371 | struct hd_i_struct *disk = &hd_info[i]; | ||
372 | disk->special_op = disk->recalibrate = 1; | ||
373 | hd_out(disk, disk->sect, disk->sect, disk->head-1, | ||
374 | disk->cyl, WIN_SPECIFY, &reset_hd); | ||
375 | if (reset) | ||
376 | goto repeat; | ||
377 | } else | ||
378 | hd_request(); | ||
379 | } | ||
380 | |||
381 | /* | ||
382 | * Ok, don't know what to do with the unexpected interrupts: on some machines | ||
383 | * doing a reset and a retry seems to result in an eternal loop. Right now I | ||
384 | * ignore it, and just set the timeout. | ||
385 | * | ||
386 | * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the | ||
387 | * drive enters "idle", "standby", or "sleep" mode, so if the status looks | ||
388 | * "good", we just ignore the interrupt completely. | ||
389 | */ | ||
390 | static void unexpected_hd_interrupt(void) | ||
391 | { | ||
392 | unsigned int stat = inb_p(HD_STATUS); | ||
393 | |||
394 | if (stat & (BUSY_STAT|DRQ_STAT|ECC_STAT|ERR_STAT)) { | ||
395 | dump_status("unexpected interrupt", stat); | ||
396 | SET_TIMER; | ||
397 | } | ||
398 | } | ||
399 | |||
400 | /* | ||
401 | * bad_rw_intr() now tries to be a bit smarter and does things | ||
402 | * according to the error returned by the controller. | ||
403 | * -Mika Liljeberg (liljeber@cs.Helsinki.FI) | ||
404 | */ | ||
405 | static void bad_rw_intr(void) | ||
406 | { | ||
407 | struct request *req = CURRENT; | ||
408 | if (req != NULL) { | ||
409 | struct hd_i_struct *disk = req->rq_disk->private_data; | ||
410 | if (++req->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) { | ||
411 | end_request(req, 0); | ||
412 | disk->special_op = disk->recalibrate = 1; | ||
413 | } else if (req->errors % RESET_FREQ == 0) | ||
414 | reset = 1; | ||
415 | else if ((hd_error & TRK0_ERR) || req->errors % RECAL_FREQ == 0) | ||
416 | disk->special_op = disk->recalibrate = 1; | ||
417 | /* Otherwise just retry */ | ||
418 | } | ||
419 | } | ||
420 | |||
421 | static inline int wait_DRQ(void) | ||
422 | { | ||
423 | int retries; | ||
424 | int stat; | ||
425 | |||
426 | for (retries = 0; retries < 100000; retries++) { | ||
427 | stat = inb_p(HD_STATUS); | ||
428 | if (stat & DRQ_STAT) | ||
429 | return 0; | ||
430 | } | ||
431 | dump_status("wait_DRQ", stat); | ||
432 | return -1; | ||
433 | } | ||
434 | |||
435 | static void read_intr(void) | ||
436 | { | ||
437 | struct request *req; | ||
438 | int i, retries = 100000; | ||
439 | |||
440 | do { | ||
441 | i = (unsigned) inb_p(HD_STATUS); | ||
442 | if (i & BUSY_STAT) | ||
443 | continue; | ||
444 | if (!OK_STATUS(i)) | ||
445 | break; | ||
446 | if (i & DRQ_STAT) | ||
447 | goto ok_to_read; | ||
448 | } while (--retries > 0); | ||
449 | dump_status("read_intr", i); | ||
450 | bad_rw_intr(); | ||
451 | hd_request(); | ||
452 | return; | ||
453 | ok_to_read: | ||
454 | req = CURRENT; | ||
455 | insw(HD_DATA, req->buffer, 256); | ||
456 | req->sector++; | ||
457 | req->buffer += 512; | ||
458 | req->errors = 0; | ||
459 | i = --req->nr_sectors; | ||
460 | --req->current_nr_sectors; | ||
461 | #ifdef DEBUG | ||
462 | printk("%s: read: sector %ld, remaining = %ld, buffer=%p\n", | ||
463 | req->rq_disk->disk_name, req->sector, req->nr_sectors, | ||
464 | req->buffer+512); | ||
465 | #endif | ||
466 | if (req->current_nr_sectors <= 0) | ||
467 | end_request(req, 1); | ||
468 | if (i > 0) { | ||
469 | SET_HANDLER(&read_intr); | ||
470 | return; | ||
471 | } | ||
472 | (void) inb_p(HD_STATUS); | ||
473 | #if (HD_DELAY > 0) | ||
474 | last_req = read_timer(); | ||
475 | #endif | ||
476 | if (elv_next_request(QUEUE)) | ||
477 | hd_request(); | ||
478 | return; | ||
479 | } | ||
480 | |||
481 | static void write_intr(void) | ||
482 | { | ||
483 | struct request *req = CURRENT; | ||
484 | int i; | ||
485 | int retries = 100000; | ||
486 | |||
487 | do { | ||
488 | i = (unsigned) inb_p(HD_STATUS); | ||
489 | if (i & BUSY_STAT) | ||
490 | continue; | ||
491 | if (!OK_STATUS(i)) | ||
492 | break; | ||
493 | if ((req->nr_sectors <= 1) || (i & DRQ_STAT)) | ||
494 | goto ok_to_write; | ||
495 | } while (--retries > 0); | ||
496 | dump_status("write_intr", i); | ||
497 | bad_rw_intr(); | ||
498 | hd_request(); | ||
499 | return; | ||
500 | ok_to_write: | ||
501 | req->sector++; | ||
502 | i = --req->nr_sectors; | ||
503 | --req->current_nr_sectors; | ||
504 | req->buffer += 512; | ||
505 | if (!i || (req->bio && req->current_nr_sectors <= 0)) | ||
506 | end_request(req, 1); | ||
507 | if (i > 0) { | ||
508 | SET_HANDLER(&write_intr); | ||
509 | outsw(HD_DATA, req->buffer, 256); | ||
510 | local_irq_enable(); | ||
511 | } else { | ||
512 | #if (HD_DELAY > 0) | ||
513 | last_req = read_timer(); | ||
514 | #endif | ||
515 | hd_request(); | ||
516 | } | ||
517 | return; | ||
518 | } | ||
519 | |||
520 | static void recal_intr(void) | ||
521 | { | ||
522 | check_status(); | ||
523 | #if (HD_DELAY > 0) | ||
524 | last_req = read_timer(); | ||
525 | #endif | ||
526 | hd_request(); | ||
527 | } | ||
528 | |||
529 | /* | ||
530 | * This is another of the error-routines I don't know what to do with. The | ||
531 | * best idea seems to just set reset, and start all over again. | ||
532 | */ | ||
533 | static void hd_times_out(unsigned long dummy) | ||
534 | { | ||
535 | char *name; | ||
536 | |||
537 | do_hd = NULL; | ||
538 | |||
539 | if (!CURRENT) | ||
540 | return; | ||
541 | |||
542 | disable_irq(HD_IRQ); | ||
543 | local_irq_enable(); | ||
544 | reset = 1; | ||
545 | name = CURRENT->rq_disk->disk_name; | ||
546 | printk("%s: timeout\n", name); | ||
547 | if (++CURRENT->errors >= MAX_ERRORS) { | ||
548 | #ifdef DEBUG | ||
549 | printk("%s: too many errors\n", name); | ||
550 | #endif | ||
551 | end_request(CURRENT, 0); | ||
552 | } | ||
553 | local_irq_disable(); | ||
554 | hd_request(); | ||
555 | enable_irq(HD_IRQ); | ||
556 | } | ||
557 | |||
558 | static int do_special_op(struct hd_i_struct *disk, struct request *req) | ||
559 | { | ||
560 | if (disk->recalibrate) { | ||
561 | disk->recalibrate = 0; | ||
562 | hd_out(disk, disk->sect, 0, 0, 0, WIN_RESTORE, &recal_intr); | ||
563 | return reset; | ||
564 | } | ||
565 | if (disk->head > 16) { | ||
566 | printk("%s: cannot handle device with more than 16 heads - giving up\n", req->rq_disk->disk_name); | ||
567 | end_request(req, 0); | ||
568 | } | ||
569 | disk->special_op = 0; | ||
570 | return 1; | ||
571 | } | ||
572 | |||
573 | /* | ||
574 | * The driver enables interrupts as much as possible. In order to do this, | ||
575 | * (a) the device-interrupt is disabled before entering hd_request(), | ||
576 | * and (b) the timeout-interrupt is disabled before the sti(). | ||
577 | * | ||
578 | * Interrupts are still masked (by default) whenever we are exchanging | ||
579 | * data/cmds with a drive, because some drives seem to have very poor | ||
580 | * tolerance for latency during I/O. The IDE driver has support to unmask | ||
581 | * interrupts for non-broken hardware, so use that driver if required. | ||
582 | */ | ||
583 | static void hd_request(void) | ||
584 | { | ||
585 | unsigned int block, nsect, sec, track, head, cyl; | ||
586 | struct hd_i_struct *disk; | ||
587 | struct request *req; | ||
588 | |||
589 | if (do_hd) | ||
590 | return; | ||
591 | repeat: | ||
592 | del_timer(&device_timer); | ||
593 | local_irq_enable(); | ||
594 | |||
595 | req = CURRENT; | ||
596 | if (!req) { | ||
597 | do_hd = NULL; | ||
598 | return; | ||
599 | } | ||
600 | |||
601 | if (reset) { | ||
602 | local_irq_disable(); | ||
603 | reset_hd(); | ||
604 | return; | ||
605 | } | ||
606 | disk = req->rq_disk->private_data; | ||
607 | block = req->sector; | ||
608 | nsect = req->nr_sectors; | ||
609 | if (block >= get_capacity(req->rq_disk) || | ||
610 | ((block+nsect) > get_capacity(req->rq_disk))) { | ||
611 | printk("%s: bad access: block=%d, count=%d\n", | ||
612 | req->rq_disk->disk_name, block, nsect); | ||
613 | end_request(req, 0); | ||
614 | goto repeat; | ||
615 | } | ||
616 | |||
617 | if (disk->special_op) { | ||
618 | if (do_special_op(disk, req)) | ||
619 | goto repeat; | ||
620 | return; | ||
621 | } | ||
622 | sec = block % disk->sect + 1; | ||
623 | track = block / disk->sect; | ||
624 | head = track % disk->head; | ||
625 | cyl = track / disk->head; | ||
626 | #ifdef DEBUG | ||
627 | printk("%s: %sing: CHS=%d/%d/%d, sectors=%d, buffer=%p\n", | ||
628 | req->rq_disk->disk_name, | ||
629 | req_data_dir(req) == READ ? "read" : "writ", | ||
630 | cyl, head, sec, nsect, req->buffer); | ||
631 | #endif | ||
632 | if (blk_fs_request(req)) { | ||
633 | switch (rq_data_dir(req)) { | ||
634 | case READ: | ||
635 | hd_out(disk, nsect, sec, head, cyl, WIN_READ, | ||
636 | &read_intr); | ||
637 | if (reset) | ||
638 | goto repeat; | ||
639 | break; | ||
640 | case WRITE: | ||
641 | hd_out(disk, nsect, sec, head, cyl, WIN_WRITE, | ||
642 | &write_intr); | ||
643 | if (reset) | ||
644 | goto repeat; | ||
645 | if (wait_DRQ()) { | ||
646 | bad_rw_intr(); | ||
647 | goto repeat; | ||
648 | } | ||
649 | outsw(HD_DATA, req->buffer, 256); | ||
650 | break; | ||
651 | default: | ||
652 | printk("unknown hd-command\n"); | ||
653 | end_request(req, 0); | ||
654 | break; | ||
655 | } | ||
656 | } | ||
657 | } | ||
658 | |||
659 | static void do_hd_request(struct request_queue *q) | ||
660 | { | ||
661 | disable_irq(HD_IRQ); | ||
662 | hd_request(); | ||
663 | enable_irq(HD_IRQ); | ||
664 | } | ||
665 | |||
666 | static int hd_getgeo(struct block_device *bdev, struct hd_geometry *geo) | ||
667 | { | ||
668 | struct hd_i_struct *disk = bdev->bd_disk->private_data; | ||
669 | |||
670 | geo->heads = disk->head; | ||
671 | geo->sectors = disk->sect; | ||
672 | geo->cylinders = disk->cyl; | ||
673 | return 0; | ||
674 | } | ||
675 | |||
676 | /* | ||
677 | * Releasing a block device means we sync() it, so that it can safely | ||
678 | * be forgotten about... | ||
679 | */ | ||
680 | |||
681 | static irqreturn_t hd_interrupt(int irq, void *dev_id) | ||
682 | { | ||
683 | void (*handler)(void) = do_hd; | ||
684 | |||
685 | do_hd = NULL; | ||
686 | del_timer(&device_timer); | ||
687 | if (!handler) | ||
688 | handler = unexpected_hd_interrupt; | ||
689 | handler(); | ||
690 | local_irq_enable(); | ||
691 | return IRQ_HANDLED; | ||
692 | } | ||
693 | |||
694 | static struct block_device_operations hd_fops = { | ||
695 | .getgeo = hd_getgeo, | ||
696 | }; | ||
697 | |||
698 | /* | ||
699 | * This is the hard disk IRQ description. The IRQF_DISABLED in sa_flags | ||
700 | * means we run the IRQ-handler with interrupts disabled: this is bad for | ||
701 | * interrupt latency, but anything else has led to problems on some | ||
702 | * machines. | ||
703 | * | ||
704 | * We enable interrupts in some of the routines after making sure it's | ||
705 | * safe. | ||
706 | */ | ||
707 | |||
708 | static int __init hd_init(void) | ||
709 | { | ||
710 | int drive; | ||
711 | |||
712 | if (register_blkdev(MAJOR_NR, "hd")) | ||
713 | return -1; | ||
714 | |||
715 | hd_queue = blk_init_queue(do_hd_request, &hd_lock); | ||
716 | if (!hd_queue) { | ||
717 | unregister_blkdev(MAJOR_NR, "hd"); | ||
718 | return -ENOMEM; | ||
719 | } | ||
720 | |||
721 | blk_queue_max_sectors(hd_queue, 255); | ||
722 | init_timer(&device_timer); | ||
723 | device_timer.function = hd_times_out; | ||
724 | blk_queue_hardsect_size(hd_queue, 512); | ||
725 | |||
726 | if (!NR_HD) { | ||
727 | /* | ||
728 | * We don't know anything about the drive. This means | ||
729 | * that you *MUST* specify the drive parameters to the | ||
730 | * kernel yourself. | ||
731 | * | ||
732 | * If we were on an i386, we used to read this info from | ||
733 | * the BIOS or CMOS. This doesn't work all that well, | ||
734 | * since this assumes that this is a primary or secondary | ||
735 | * drive, and if we're using this legacy driver, it's | ||
736 | * probably an auxilliary controller added to recover | ||
737 | * legacy data off an ST-506 drive. Either way, it's | ||
738 | * definitely safest to have the user explicitly specify | ||
739 | * the information. | ||
740 | */ | ||
741 | printk("hd: no drives specified - use hd=cyl,head,sectors" | ||
742 | " on kernel command line\n"); | ||
743 | goto out; | ||
744 | } | ||
745 | |||
746 | for (drive = 0 ; drive < NR_HD ; drive++) { | ||
747 | struct gendisk *disk = alloc_disk(64); | ||
748 | struct hd_i_struct *p = &hd_info[drive]; | ||
749 | if (!disk) | ||
750 | goto Enomem; | ||
751 | disk->major = MAJOR_NR; | ||
752 | disk->first_minor = drive << 6; | ||
753 | disk->fops = &hd_fops; | ||
754 | sprintf(disk->disk_name, "hd%c", 'a'+drive); | ||
755 | disk->private_data = p; | ||
756 | set_capacity(disk, p->head * p->sect * p->cyl); | ||
757 | disk->queue = hd_queue; | ||
758 | p->unit = drive; | ||
759 | hd_gendisk[drive] = disk; | ||
760 | printk("%s: %luMB, CHS=%d/%d/%d\n", | ||
761 | disk->disk_name, (unsigned long)get_capacity(disk)/2048, | ||
762 | p->cyl, p->head, p->sect); | ||
763 | } | ||
764 | |||
765 | if (request_irq(HD_IRQ, hd_interrupt, IRQF_DISABLED, "hd", NULL)) { | ||
766 | printk("hd: unable to get IRQ%d for the hard disk driver\n", | ||
767 | HD_IRQ); | ||
768 | goto out1; | ||
769 | } | ||
770 | if (!request_region(HD_DATA, 8, "hd")) { | ||
771 | printk(KERN_WARNING "hd: port 0x%x busy\n", HD_DATA); | ||
772 | goto out2; | ||
773 | } | ||
774 | if (!request_region(HD_CMD, 1, "hd(cmd)")) { | ||
775 | printk(KERN_WARNING "hd: port 0x%x busy\n", HD_CMD); | ||
776 | goto out3; | ||
777 | } | ||
778 | |||
779 | /* Let them fly */ | ||
780 | for (drive = 0; drive < NR_HD; drive++) | ||
781 | add_disk(hd_gendisk[drive]); | ||
782 | |||
783 | return 0; | ||
784 | |||
785 | out3: | ||
786 | release_region(HD_DATA, 8); | ||
787 | out2: | ||
788 | free_irq(HD_IRQ, NULL); | ||
789 | out1: | ||
790 | for (drive = 0; drive < NR_HD; drive++) | ||
791 | put_disk(hd_gendisk[drive]); | ||
792 | NR_HD = 0; | ||
793 | out: | ||
794 | del_timer(&device_timer); | ||
795 | unregister_blkdev(MAJOR_NR, "hd"); | ||
796 | blk_cleanup_queue(hd_queue); | ||
797 | return -1; | ||
798 | Enomem: | ||
799 | while (drive--) | ||
800 | put_disk(hd_gendisk[drive]); | ||
801 | goto out; | ||
802 | } | ||
803 | |||
804 | static int __init parse_hd_setup(char *line) | ||
805 | { | ||
806 | int ints[6]; | ||
807 | |||
808 | (void) get_options(line, ARRAY_SIZE(ints), ints); | ||
809 | hd_setup(NULL, ints); | ||
810 | |||
811 | return 1; | ||
812 | } | ||
813 | __setup("hd=", parse_hd_setup); | ||
814 | |||
815 | late_initcall(hd_init); | ||