blob: 624d30f7da3fc0ba125de8c46b5f9ec8d1f4efc8 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * This file contains the driver for an XT hard disk controller
3 * (at least the DTC 5150X) for Linux.
4 *
5 * Author: Pat Mackinlay, pat@it.com.au
6 * Date: 29/09/92
7 *
8 * Revised: 01/01/93, ...
9 *
10 * Ref: DTC 5150X Controller Specification (thanks to Kevin Fowler,
11 * kevinf@agora.rain.com)
12 * Also thanks to: Salvador Abreu, Dave Thaler, Risto Kankkunen and
13 * Wim Van Dorst.
14 *
15 * Revised: 04/04/94 by Risto Kankkunen
16 * Moved the detection code from xd_init() to xd_geninit() as it needed
17 * interrupts enabled and Linus didn't want to enable them in that first
18 * phase. xd_geninit() is the place to do these kinds of things anyway,
19 * he says.
20 *
21 * Modularized: 04/10/96 by Todd Fries, tfries@umr.edu
22 *
23 * Revised: 13/12/97 by Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl
24 * Fixed some problems with disk initialization and module initiation.
25 * Added support for manual geometry setting (except Seagate controllers)
26 * in form:
27 * xd_geo=<cyl_xda>,<head_xda>,<sec_xda>[,<cyl_xdb>,<head_xdb>,<sec_xdb>]
28 * Recovered DMA access. Abridged messages. Added support for DTC5051CX,
29 * WD1002-27X & XEBEC controllers. Driver uses now some jumper settings.
30 * Extended ioctl() support.
31 *
32 * Bugfix: 15/02/01, Paul G. - inform queue layer of tiny xd_maxsect.
33 *
34 */
35
36#include <linux/module.h>
37#include <linux/errno.h>
38#include <linux/interrupt.h>
39#include <linux/mm.h>
40#include <linux/fs.h>
41#include <linux/kernel.h>
42#include <linux/timer.h>
43#include <linux/genhd.h>
44#include <linux/hdreg.h>
45#include <linux/ioport.h>
46#include <linux/init.h>
47#include <linux/wait.h>
48#include <linux/blkdev.h>
49#include <linux/blkpg.h>
Nishanth Aravamudanf6a2f342005-09-10 00:27:14 -070050#include <linux/delay.h>
Matthew Wilcox53d5ed62006-10-11 01:22:01 -070051#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
53#include <asm/system.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#include <asm/uaccess.h>
55#include <asm/dma.h>
56
57#include "xd.h"
58
59static void __init do_xd_setup (int *integers);
60#ifdef MODULE
61static int xd[5] = { -1,-1,-1,-1, };
62#endif
63
64#define XD_DONT_USE_DMA 0 /* Initial value. may be overriden using
65 "nodma" module option */
Nishanth Aravamudanf6a2f342005-09-10 00:27:14 -070066#define XD_INIT_DISK_DELAY (30) /* 30 ms delay during disk initialization */
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
68/* Above may need to be increased if a problem with the 2nd drive detection
69 (ST11M controller) or resetting a controller (WD) appears */
70
71static XD_INFO xd_info[XD_MAXDRIVES];
72
73/* If you try this driver and find that your card is not detected by the driver at bootup, you need to add your BIOS
74 signature and details to the following list of signatures. A BIOS signature is a string embedded into the first
75 few bytes of your controller's on-board ROM BIOS. To find out what yours is, use something like MS-DOS's DEBUG
76 command. Run DEBUG, and then you can examine your BIOS signature with:
77
78 d xxxx:0000
79
80 where xxxx is the segment of your controller (like C800 or D000 or something). On the ASCII dump at the right, you should
81 be able to see a string mentioning the manufacturer's copyright etc. Add this string into the table below. The parameters
82 in the table are, in order:
83
84 offset ; this is the offset (in bytes) from the start of your ROM where the signature starts
85 signature ; this is the actual text of the signature
86 xd_?_init_controller ; this is the controller init routine used by your controller
87 xd_?_init_drive ; this is the drive init routine used by your controller
88
89 The controllers directly supported at the moment are: DTC 5150x, WD 1004A27X, ST11M/R and override. If your controller is
90 made by the same manufacturer as one of these, try using the same init routines as they do. If that doesn't work, your
91 best bet is to use the "override" routines. These routines use a "portable" method of getting the disk's geometry, and
92 may work with your card. If none of these seem to work, try sending me some email and I'll see what I can do <grin>.
93
94 NOTE: You can now specify your XT controller's parameters from the command line in the form xd=TYPE,IRQ,IO,DMA. The driver
95 should be able to detect your drive's geometry from this info. (eg: xd=0,5,0x320,3 is the "standard"). */
96
97#include <asm/page.h>
98#define xd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,get_order(size))
99#define xd_dma_mem_free(addr, size) free_pages(addr, get_order(size))
100static char *xd_dma_buffer;
101
102static XD_SIGNATURE xd_sigs[] __initdata = {
103 { 0x0000,"Override geometry handler",NULL,xd_override_init_drive,"n unknown" }, /* Pat Mackinlay, pat@it.com.au */
104 { 0x0008,"[BXD06 (C) DTC 17-MAY-1985]",xd_dtc_init_controller,xd_dtc5150cx_init_drive," DTC 5150CX" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */
105 { 0x000B,"CRD18A Not an IBM rom. (C) Copyright Data Technology Corp. 05/31/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Todd Fries, tfries@umr.edu */
106 { 0x000B,"CXD23A Not an IBM ROM (C)Copyright Data Technology Corp 12/03/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Pat Mackinlay, pat@it.com.au */
107 { 0x0008,"07/15/86(C) Copyright 1986 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Dig. 1002-27X" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */
108 { 0x0008,"06/24/88(C) Copyright 1988 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Dig. WDXT-GEN2" }, /* Dan Newcombe, newcombe@aa.csc.peachnet.edu */
109 { 0x0015,"SEAGATE ST11 BIOS REVISION",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Salvador Abreu, spa@fct.unl.pt */
110 { 0x0010,"ST11R BIOS",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Risto Kankkunen, risto.kankkunen@cs.helsinki.fi */
111 { 0x0010,"ST11 BIOS v1.7",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11R" }, /* Alan Hourihane, alanh@fairlite.demon.co.uk */
112 { 0x1000,"(c)Copyright 1987 SMS",xd_omti_init_controller,xd_omti_init_drive,"n OMTI 5520" }, /* Dirk Melchers, dirk@merlin.nbg.sub.org */
113 { 0x0006,"COPYRIGHT XEBEC (C) 1984",xd_xebec_init_controller,xd_xebec_init_drive," XEBEC" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */
114 { 0x0008,"(C) Copyright 1984 Western Digital Corp", xd_wd_init_controller, xd_wd_init_drive," Western Dig. 1002s-wx2" },
115 { 0x0008,"(C) Copyright 1986 Western Digital Corporation", xd_wd_init_controller, xd_wd_init_drive," 1986 Western Digital" }, /* jfree@sovereign.org */
116};
117
118static unsigned int xd_bases[] __initdata =
119{
120 0xC8000, 0xCA000, 0xCC000,
121 0xCE000, 0xD0000, 0xD2000,
122 0xD4000, 0xD6000, 0xD8000,
123 0xDA000, 0xDC000, 0xDE000,
124 0xE0000
125};
126
127static DEFINE_SPINLOCK(xd_lock);
128
129static struct gendisk *xd_gendisk[2];
130
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800131static int xd_getgeo(struct block_device *bdev, struct hd_geometry *geo);
132
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133static struct block_device_operations xd_fops = {
134 .owner = THIS_MODULE,
135 .ioctl = xd_ioctl,
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800136 .getgeo = xd_getgeo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137};
138static DECLARE_WAIT_QUEUE_HEAD(xd_wait_int);
139static u_char xd_drives, xd_irq = 5, xd_dma = 3, xd_maxsectors;
140static u_char xd_override __initdata = 0, xd_type __initdata = 0;
141static u_short xd_iobase = 0x320;
142static int xd_geo[XD_MAXDRIVES*3] __initdata = { 0, };
143
144static volatile int xdc_busy;
145static struct timer_list xd_watchdog_int;
146
147static volatile u_char xd_error;
148static int nodma = XD_DONT_USE_DMA;
149
150static struct request_queue *xd_queue;
151
152/* xd_init: register the block device number and set up pointer tables */
153static int __init xd_init(void)
154{
155 u_char i,controller;
156 unsigned int address;
157 int err;
158
159#ifdef MODULE
160 {
161 u_char count = 0;
162 for (i = 4; i > 0; i--)
163 if (((xd[i] = xd[i-1]) >= 0) && !count)
164 count = i;
165 if ((xd[0] = count))
166 do_xd_setup(xd);
167 }
168#endif
169
170 init_timer (&xd_watchdog_int); xd_watchdog_int.function = xd_watchdog;
171
172 if (!xd_dma_buffer)
173 xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200);
174 if (!xd_dma_buffer) {
175 printk(KERN_ERR "xd: Out of memory.\n");
176 return -ENOMEM;
177 }
178
179 err = -EBUSY;
180 if (register_blkdev(XT_DISK_MAJOR, "xd"))
181 goto out1;
182
183 err = -ENOMEM;
184 xd_queue = blk_init_queue(do_xd_request, &xd_lock);
185 if (!xd_queue)
186 goto out1a;
187
188 if (xd_detect(&controller,&address)) {
189
190 printk("Detected a%s controller (type %d) at address %06x\n",
191 xd_sigs[controller].name,controller,address);
192 if (!request_region(xd_iobase,4,"xd")) {
193 printk("xd: Ports at 0x%x are not available\n",
194 xd_iobase);
195 goto out2;
196 }
197 if (controller)
198 xd_sigs[controller].init_controller(address);
199 xd_drives = xd_initdrives(xd_sigs[controller].init_drive);
200
201 printk("Detected %d hard drive%s (using IRQ%d & DMA%d)\n",
202 xd_drives,xd_drives == 1 ? "" : "s",xd_irq,xd_dma);
203 }
204
205 err = -ENODEV;
206 if (!xd_drives)
207 goto out3;
208
209 for (i = 0; i < xd_drives; i++) {
210 XD_INFO *p = &xd_info[i];
211 struct gendisk *disk = alloc_disk(64);
212 if (!disk)
213 goto Enomem;
214 p->unit = i;
215 disk->major = XT_DISK_MAJOR;
216 disk->first_minor = i<<6;
217 sprintf(disk->disk_name, "xd%c", i+'a');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 disk->fops = &xd_fops;
219 disk->private_data = p;
220 disk->queue = xd_queue;
221 set_capacity(disk, p->heads * p->cylinders * p->sectors);
222 printk(" %s: CHS=%d/%d/%d\n", disk->disk_name,
223 p->cylinders, p->heads, p->sectors);
224 xd_gendisk[i] = disk;
225 }
226
227 err = -EBUSY;
228 if (request_irq(xd_irq,xd_interrupt_handler, 0, "XT hard disk", NULL)) {
229 printk("xd: unable to get IRQ%d\n",xd_irq);
230 goto out4;
231 }
232
233 if (request_dma(xd_dma,"xd")) {
234 printk("xd: unable to get DMA%d\n",xd_dma);
235 goto out5;
236 }
237
238 /* xd_maxsectors depends on controller - so set after detection */
239 blk_queue_max_sectors(xd_queue, xd_maxsectors);
240
241 for (i = 0; i < xd_drives; i++)
242 add_disk(xd_gendisk[i]);
243
244 return 0;
245
246out5:
247 free_irq(xd_irq, NULL);
248out4:
249 for (i = 0; i < xd_drives; i++)
250 put_disk(xd_gendisk[i]);
251out3:
252 release_region(xd_iobase,4);
253out2:
254 blk_cleanup_queue(xd_queue);
255out1a:
256 unregister_blkdev(XT_DISK_MAJOR, "xd");
257out1:
258 if (xd_dma_buffer)
259 xd_dma_mem_free((unsigned long)xd_dma_buffer,
260 xd_maxsectors * 0x200);
261 return err;
262Enomem:
263 err = -ENOMEM;
264 while (i--)
265 put_disk(xd_gendisk[i]);
266 goto out3;
267}
268
269/* xd_detect: scan the possible BIOS ROM locations for the signature strings */
270static u_char __init xd_detect (u_char *controller, unsigned int *address)
271{
272 int i, j;
273
274 if (xd_override)
275 {
276 *controller = xd_type;
277 *address = 0;
278 return(1);
279 }
280
Tobias Klauser945f3902006-01-08 01:05:11 -0800281 for (i = 0; i < ARRAY_SIZE(xd_bases); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 void __iomem *p = ioremap(xd_bases[i], 0x2000);
283 if (!p)
284 continue;
Tobias Klauser945f3902006-01-08 01:05:11 -0800285 for (j = 1; j < ARRAY_SIZE(xd_sigs); j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 const char *s = xd_sigs[j].string;
287 if (check_signature(p + xd_sigs[j].offset, s, strlen(s))) {
288 *controller = j;
289 xd_type = j;
290 *address = xd_bases[i];
291 iounmap(p);
292 return 1;
293 }
294 }
295 iounmap(p);
296 }
297 return 0;
298}
299
300/* do_xd_request: handle an incoming request */
Jens Axboe165125e2007-07-24 09:28:11 +0200301static void do_xd_request (struct request_queue * q)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302{
303 struct request *req;
304
305 if (xdc_busy)
306 return;
307
308 while ((req = elv_next_request(q)) != NULL) {
309 unsigned block = req->sector;
310 unsigned count = req->nr_sectors;
311 int rw = rq_data_dir(req);
312 XD_INFO *disk = req->rq_disk->private_data;
313 int res = 0;
314 int retry;
315
Jens Axboe4aff5e22006-08-10 08:44:47 +0200316 if (!blk_fs_request(req)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 end_request(req, 0);
318 continue;
319 }
320 if (block + count > get_capacity(req->rq_disk)) {
321 end_request(req, 0);
322 continue;
323 }
324 if (rw != READ && rw != WRITE) {
325 printk("do_xd_request: unknown request\n");
326 end_request(req, 0);
327 continue;
328 }
329 for (retry = 0; (retry < XD_RETRIES) && !res; retry++)
330 res = xd_readwrite(rw, disk, req->buffer, block, count);
331 end_request(req, res); /* wrap up, 0 = fail, 1 = success */
332 }
333}
334
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800335static int xd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
336{
337 XD_INFO *p = bdev->bd_disk->private_data;
338
339 geo->heads = p->heads;
340 geo->sectors = p->sectors;
341 geo->cylinders = p->cylinders;
342 return 0;
343}
344
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345/* xd_ioctl: handle device ioctl's */
346static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg)
347{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 case HDIO_SET_DMA:
350 if (!capable(CAP_SYS_ADMIN)) return -EACCES;
351 if (xdc_busy) return -EBUSY;
352 nodma = !arg;
353 if (nodma && xd_dma_buffer) {
354 xd_dma_mem_free((unsigned long)xd_dma_buffer,
355 xd_maxsectors * 0x200);
356 xd_dma_buffer = NULL;
357 } else if (!nodma && !xd_dma_buffer) {
358 xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200);
359 if (!xd_dma_buffer) {
360 nodma = XD_DONT_USE_DMA;
361 return -ENOMEM;
362 }
363 }
364 return 0;
365 case HDIO_GET_DMA:
366 return put_user(!nodma, (long __user *) arg);
367 case HDIO_GET_MULTCOUNT:
368 return put_user(xd_maxsectors, (long __user *) arg);
369 default:
370 return -EINVAL;
371 }
372}
373
374/* xd_readwrite: handle a read/write request */
375static int xd_readwrite (u_char operation,XD_INFO *p,char *buffer,u_int block,u_int count)
376{
377 int drive = p->unit;
378 u_char cmdblk[6],sense[4];
379 u_short track,cylinder;
380 u_char head,sector,control,mode = PIO_MODE,temp;
381 char **real_buffer;
382 register int i;
383
384#ifdef DEBUG_READWRITE
385 printk("xd_readwrite: operation = %s, drive = %d, buffer = 0x%X, block = %d, count = %d\n",operation == READ ? "read" : "write",drive,buffer,block,count);
386#endif /* DEBUG_READWRITE */
387
388 spin_unlock_irq(&xd_lock);
389
390 control = p->control;
391 if (!xd_dma_buffer)
392 xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200);
393 while (count) {
394 temp = count < xd_maxsectors ? count : xd_maxsectors;
395
396 track = block / p->sectors;
397 head = track % p->heads;
398 cylinder = track / p->heads;
399 sector = block % p->sectors;
400
401#ifdef DEBUG_READWRITE
402 printk("xd_readwrite: drive = %d, head = %d, cylinder = %d, sector = %d, count = %d\n",drive,head,cylinder,sector,temp);
403#endif /* DEBUG_READWRITE */
404
405 if (xd_dma_buffer) {
406 mode = xd_setup_dma(operation == READ ? DMA_MODE_READ : DMA_MODE_WRITE,(u_char *)(xd_dma_buffer),temp * 0x200);
407 real_buffer = &xd_dma_buffer;
408 for (i=0; i < (temp * 0x200); i++)
409 xd_dma_buffer[i] = buffer[i];
410 }
411 else
412 real_buffer = &buffer;
413
414 xd_build(cmdblk,operation == READ ? CMD_READ : CMD_WRITE,drive,head,cylinder,sector,temp & 0xFF,control);
415
416 switch (xd_command(cmdblk,mode,(u_char *)(*real_buffer),(u_char *)(*real_buffer),sense,XD_TIMEOUT)) {
417 case 1:
418 printk("xd%c: %s timeout, recalibrating drive\n",'a'+drive,(operation == READ ? "read" : "write"));
419 xd_recalibrate(drive);
420 spin_lock_irq(&xd_lock);
421 return (0);
422 case 2:
423 if (sense[0] & 0x30) {
424 printk("xd%c: %s - ",'a'+drive,(operation == READ ? "reading" : "writing"));
425 switch ((sense[0] & 0x30) >> 4) {
426 case 0: printk("drive error, code = 0x%X",sense[0] & 0x0F);
427 break;
428 case 1: printk("controller error, code = 0x%X",sense[0] & 0x0F);
429 break;
430 case 2: printk("command error, code = 0x%X",sense[0] & 0x0F);
431 break;
432 case 3: printk("miscellaneous error, code = 0x%X",sense[0] & 0x0F);
433 break;
434 }
435 }
436 if (sense[0] & 0x80)
437 printk(" - CHS = %d/%d/%d\n",((sense[2] & 0xC0) << 2) | sense[3],sense[1] & 0x1F,sense[2] & 0x3F);
438 /* reported drive number = (sense[1] & 0xE0) >> 5 */
439 else
440 printk(" - no valid disk address\n");
441 spin_lock_irq(&xd_lock);
442 return (0);
443 }
444 if (xd_dma_buffer)
445 for (i=0; i < (temp * 0x200); i++)
446 buffer[i] = xd_dma_buffer[i];
447
448 count -= temp, buffer += temp * 0x200, block += temp;
449 }
450 spin_lock_irq(&xd_lock);
451 return (1);
452}
453
454/* xd_recalibrate: recalibrate a given drive and reset controller if necessary */
455static void xd_recalibrate (u_char drive)
456{
457 u_char cmdblk[6];
458
459 xd_build(cmdblk,CMD_RECALIBRATE,drive,0,0,0,0,0);
460 if (xd_command(cmdblk,PIO_MODE,NULL,NULL,NULL,XD_TIMEOUT * 8))
461 printk("xd%c: warning! error recalibrating, controller may be unstable\n", 'a'+drive);
462}
463
464/* xd_interrupt_handler: interrupt service routine */
David Howells7d12e782006-10-05 14:55:46 +0100465static irqreturn_t xd_interrupt_handler(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466{
467 if (inb(XD_STATUS) & STAT_INTERRUPT) { /* check if it was our device */
468#ifdef DEBUG_OTHER
469 printk("xd_interrupt_handler: interrupt detected\n");
470#endif /* DEBUG_OTHER */
471 outb(0,XD_CONTROL); /* acknowledge interrupt */
472 wake_up(&xd_wait_int); /* and wake up sleeping processes */
473 return IRQ_HANDLED;
474 }
475 else
476 printk("xd: unexpected interrupt\n");
477 return IRQ_NONE;
478}
479
480/* xd_setup_dma: set up the DMA controller for a data transfer */
481static u_char xd_setup_dma (u_char mode,u_char *buffer,u_int count)
482{
483 unsigned long f;
484
485 if (nodma)
486 return (PIO_MODE);
487 if (((unsigned long) buffer & 0xFFFF0000) != (((unsigned long) buffer + count) & 0xFFFF0000)) {
488#ifdef DEBUG_OTHER
489 printk("xd_setup_dma: using PIO, transfer overlaps 64k boundary\n");
490#endif /* DEBUG_OTHER */
491 return (PIO_MODE);
492 }
493
494 f=claim_dma_lock();
495 disable_dma(xd_dma);
496 clear_dma_ff(xd_dma);
497 set_dma_mode(xd_dma,mode);
498 set_dma_addr(xd_dma, (unsigned long) buffer);
499 set_dma_count(xd_dma,count);
500
501 release_dma_lock(f);
502
503 return (DMA_MODE); /* use DMA and INT */
504}
505
506/* xd_build: put stuff into an array in a format suitable for the controller */
507static u_char *xd_build (u_char *cmdblk,u_char command,u_char drive,u_char head,u_short cylinder,u_char sector,u_char count,u_char control)
508{
509 cmdblk[0] = command;
510 cmdblk[1] = ((drive & 0x07) << 5) | (head & 0x1F);
511 cmdblk[2] = ((cylinder & 0x300) >> 2) | (sector & 0x3F);
512 cmdblk[3] = cylinder & 0xFF;
513 cmdblk[4] = count;
514 cmdblk[5] = control;
515
516 return (cmdblk);
517}
518
519static void xd_watchdog (unsigned long unused)
520{
521 xd_error = 1;
522 wake_up(&xd_wait_int);
523}
524
525/* xd_waitport: waits until port & mask == flags or a timeout occurs. return 1 for a timeout */
526static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout)
527{
528 u_long expiry = jiffies + timeout;
529 int success;
530
531 xdc_busy = 1;
Nishanth Aravamudan86e84862005-09-10 00:27:28 -0700532 while ((success = ((inb(port) & mask) != flags)) && time_before(jiffies, expiry))
533 schedule_timeout_uninterruptible(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 xdc_busy = 0;
535 return (success);
536}
537
538static inline u_int xd_wait_for_IRQ (void)
539{
540 unsigned long flags;
541 xd_watchdog_int.expires = jiffies + 8 * HZ;
542 add_timer(&xd_watchdog_int);
543
544 flags=claim_dma_lock();
545 enable_dma(xd_dma);
546 release_dma_lock(flags);
547
548 sleep_on(&xd_wait_int);
549 del_timer(&xd_watchdog_int);
550 xdc_busy = 0;
551
552 flags=claim_dma_lock();
553 disable_dma(xd_dma);
554 release_dma_lock(flags);
555
556 if (xd_error) {
557 printk("xd: missed IRQ - command aborted\n");
558 xd_error = 0;
559 return (1);
560 }
561 return (0);
562}
563
564/* xd_command: handle all data transfers necessary for a single command */
565static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outdata,u_char *sense,u_long timeout)
566{
567 u_char cmdblk[6],csb,complete = 0;
568
569#ifdef DEBUG_COMMAND
570 printk("xd_command: command = 0x%X, mode = 0x%X, indata = 0x%X, outdata = 0x%X, sense = 0x%X\n",command,mode,indata,outdata,sense);
571#endif /* DEBUG_COMMAND */
572
573 outb(0,XD_SELECT);
574 outb(mode,XD_CONTROL);
575
576 if (xd_waitport(XD_STATUS,STAT_SELECT,STAT_SELECT,timeout))
577 return (1);
578
579 while (!complete) {
580 if (xd_waitport(XD_STATUS,STAT_READY,STAT_READY,timeout))
581 return (1);
582
583 switch (inb(XD_STATUS) & (STAT_COMMAND | STAT_INPUT)) {
584 case 0:
585 if (mode == DMA_MODE) {
586 if (xd_wait_for_IRQ())
587 return (1);
588 } else
589 outb(outdata ? *outdata++ : 0,XD_DATA);
590 break;
591 case STAT_INPUT:
592 if (mode == DMA_MODE) {
593 if (xd_wait_for_IRQ())
594 return (1);
595 } else
596 if (indata)
597 *indata++ = inb(XD_DATA);
598 else
599 inb(XD_DATA);
600 break;
601 case STAT_COMMAND:
602 outb(command ? *command++ : 0,XD_DATA);
603 break;
604 case STAT_COMMAND | STAT_INPUT:
605 complete = 1;
606 break;
607 }
608 }
609 csb = inb(XD_DATA);
610
611 if (xd_waitport(XD_STATUS,0,STAT_SELECT,timeout)) /* wait until deselected */
612 return (1);
613
614 if (csb & CSB_ERROR) { /* read sense data if error */
615 xd_build(cmdblk,CMD_SENSE,(csb & CSB_LUN) >> 5,0,0,0,0,0);
616 if (xd_command(cmdblk,0,sense,NULL,NULL,XD_TIMEOUT))
617 printk("xd: warning! sense command failed!\n");
618 }
619
620#ifdef DEBUG_COMMAND
621 printk("xd_command: completed with csb = 0x%X\n",csb);
622#endif /* DEBUG_COMMAND */
623
624 return (csb & CSB_ERROR);
625}
626
627static u_char __init xd_initdrives (void (*init_drive)(u_char drive))
628{
629 u_char cmdblk[6],i,count = 0;
630
631 for (i = 0; i < XD_MAXDRIVES; i++) {
632 xd_build(cmdblk,CMD_TESTREADY,i,0,0,0,0,0);
633 if (!xd_command(cmdblk,PIO_MODE,NULL,NULL,NULL,XD_TIMEOUT*8)) {
Nishanth Aravamudanf6a2f342005-09-10 00:27:14 -0700634 msleep_interruptible(XD_INIT_DISK_DELAY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
636 init_drive(count);
637 count++;
638
Nishanth Aravamudanf6a2f342005-09-10 00:27:14 -0700639 msleep_interruptible(XD_INIT_DISK_DELAY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 }
641 }
642 return (count);
643}
644
645static void __init xd_manual_geo_set (u_char drive)
646{
647 xd_info[drive].heads = (u_char)(xd_geo[3 * drive + 1]);
648 xd_info[drive].cylinders = (u_short)(xd_geo[3 * drive]);
649 xd_info[drive].sectors = (u_char)(xd_geo[3 * drive + 2]);
650}
651
652static void __init xd_dtc_init_controller (unsigned int address)
653{
654 switch (address) {
655 case 0x00000:
656 case 0xC8000: break; /*initial: 0x320 */
657 case 0xCA000: xd_iobase = 0x324;
658 case 0xD0000: /*5150CX*/
659 case 0xD8000: break; /*5150CX & 5150XL*/
660 default: printk("xd_dtc_init_controller: unsupported BIOS address %06x\n",address);
661 break;
662 }
663 xd_maxsectors = 0x01; /* my card seems to have trouble doing multi-block transfers? */
664
665 outb(0,XD_RESET); /* reset the controller */
666}
667
668
669static void __init xd_dtc5150cx_init_drive (u_char drive)
670{
671 /* values from controller's BIOS - BIOS chip may be removed */
672 static u_short geometry_table[][4] = {
673 {0x200,8,0x200,0x100},
674 {0x267,2,0x267,0x267},
675 {0x264,4,0x264,0x80},
676 {0x132,4,0x132,0x0},
677 {0x132,2,0x80, 0x132},
678 {0x177,8,0x177,0x0},
679 {0x132,8,0x84, 0x0},
680 {}, /* not used */
681 {0x132,6,0x80, 0x100},
682 {0x200,6,0x100,0x100},
683 {0x264,2,0x264,0x80},
684 {0x280,4,0x280,0x100},
685 {0x2B9,3,0x2B9,0x2B9},
686 {0x2B9,5,0x2B9,0x2B9},
687 {0x280,6,0x280,0x100},
688 {0x132,4,0x132,0x0}};
689 u_char n;
690
691 n = inb(XD_JUMPER);
692 n = (drive ? n : (n >> 2)) & 0x33;
693 n = (n | (n >> 2)) & 0x0F;
694 if (xd_geo[3*drive])
695 xd_manual_geo_set(drive);
696 else
697 if (n != 7) {
698 xd_info[drive].heads = (u_char)(geometry_table[n][1]); /* heads */
699 xd_info[drive].cylinders = geometry_table[n][0]; /* cylinders */
700 xd_info[drive].sectors = 17; /* sectors */
701#if 0
702 xd_info[drive].rwrite = geometry_table[n][2]; /* reduced write */
703 xd_info[drive].precomp = geometry_table[n][3] /* write precomp */
704 xd_info[drive].ecc = 0x0B; /* ecc length */
705#endif /* 0 */
706 }
707 else {
708 printk("xd%c: undetermined drive geometry\n",'a'+drive);
709 return;
710 }
711 xd_info[drive].control = 5; /* control byte */
712 xd_setparam(CMD_DTCSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,geometry_table[n][2],geometry_table[n][3],0x0B);
713 xd_recalibrate(drive);
714}
715
716static void __init xd_dtc_init_drive (u_char drive)
717{
718 u_char cmdblk[6],buf[64];
719
720 xd_build(cmdblk,CMD_DTCGETGEOM,drive,0,0,0,0,0);
721 if (!xd_command(cmdblk,PIO_MODE,buf,NULL,NULL,XD_TIMEOUT * 2)) {
722 xd_info[drive].heads = buf[0x0A]; /* heads */
723 xd_info[drive].cylinders = ((u_short *) (buf))[0x04]; /* cylinders */
724 xd_info[drive].sectors = 17; /* sectors */
725 if (xd_geo[3*drive])
726 xd_manual_geo_set(drive);
727#if 0
728 xd_info[drive].rwrite = ((u_short *) (buf + 1))[0x05]; /* reduced write */
729 xd_info[drive].precomp = ((u_short *) (buf + 1))[0x06]; /* write precomp */
730 xd_info[drive].ecc = buf[0x0F]; /* ecc length */
731#endif /* 0 */
732 xd_info[drive].control = 0; /* control byte */
733
734 xd_setparam(CMD_DTCSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,((u_short *) (buf + 1))[0x05],((u_short *) (buf + 1))[0x06],buf[0x0F]);
735 xd_build(cmdblk,CMD_DTCSETSTEP,drive,0,0,0,0,7);
736 if (xd_command(cmdblk,PIO_MODE,NULL,NULL,NULL,XD_TIMEOUT * 2))
737 printk("xd_dtc_init_drive: error setting step rate for xd%c\n", 'a'+drive);
738 }
739 else
740 printk("xd_dtc_init_drive: error reading geometry for xd%c\n", 'a'+drive);
741}
742
743static void __init xd_wd_init_controller (unsigned int address)
744{
745 switch (address) {
746 case 0x00000:
747 case 0xC8000: break; /*initial: 0x320 */
748 case 0xCA000: xd_iobase = 0x324; break;
749 case 0xCC000: xd_iobase = 0x328; break;
750 case 0xCE000: xd_iobase = 0x32C; break;
751 case 0xD0000: xd_iobase = 0x328; break; /* ? */
752 case 0xD8000: xd_iobase = 0x32C; break; /* ? */
753 default: printk("xd_wd_init_controller: unsupported BIOS address %06x\n",address);
754 break;
755 }
756 xd_maxsectors = 0x01; /* this one doesn't wrap properly either... */
757
758 outb(0,XD_RESET); /* reset the controller */
759
Nishanth Aravamudanf6a2f342005-09-10 00:27:14 -0700760 msleep(XD_INIT_DISK_DELAY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761}
762
763static void __init xd_wd_init_drive (u_char drive)
764{
765 /* values from controller's BIOS - BIOS may be disabled */
766 static u_short geometry_table[][4] = {
767 {0x264,4,0x1C2,0x1C2}, /* common part */
768 {0x132,4,0x099,0x0},
769 {0x267,2,0x1C2,0x1C2},
770 {0x267,4,0x1C2,0x1C2},
771
772 {0x334,6,0x335,0x335}, /* 1004 series RLL */
773 {0x30E,4,0x30F,0x3DC},
774 {0x30E,2,0x30F,0x30F},
775 {0x267,4,0x268,0x268},
776
777 {0x3D5,5,0x3D6,0x3D6}, /* 1002 series RLL */
778 {0x3DB,7,0x3DC,0x3DC},
779 {0x264,4,0x265,0x265},
780 {0x267,4,0x268,0x268}};
781
782 u_char cmdblk[6],buf[0x200];
783 u_char n = 0,rll,jumper_state,use_jumper_geo;
784 u_char wd_1002 = (xd_sigs[xd_type].string[7] == '6');
785
786 jumper_state = ~(inb(0x322));
787 if (jumper_state & 0x40)
788 xd_irq = 9;
789 rll = (jumper_state & 0x30) ? (0x04 << wd_1002) : 0;
790 xd_build(cmdblk,CMD_READ,drive,0,0,0,1,0);
791 if (!xd_command(cmdblk,PIO_MODE,buf,NULL,NULL,XD_TIMEOUT * 2)) {
792 xd_info[drive].heads = buf[0x1AF]; /* heads */
793 xd_info[drive].cylinders = ((u_short *) (buf + 1))[0xD6]; /* cylinders */
794 xd_info[drive].sectors = 17; /* sectors */
795 if (xd_geo[3*drive])
796 xd_manual_geo_set(drive);
797#if 0
798 xd_info[drive].rwrite = ((u_short *) (buf))[0xD8]; /* reduced write */
799 xd_info[drive].wprecomp = ((u_short *) (buf))[0xDA]; /* write precomp */
800 xd_info[drive].ecc = buf[0x1B4]; /* ecc length */
801#endif /* 0 */
802 xd_info[drive].control = buf[0x1B5]; /* control byte */
803 use_jumper_geo = !(xd_info[drive].heads) || !(xd_info[drive].cylinders);
804 if (xd_geo[3*drive]) {
805 xd_manual_geo_set(drive);
806 xd_info[drive].control = rll ? 7 : 5;
807 }
808 else if (use_jumper_geo) {
809 n = (((jumper_state & 0x0F) >> (drive << 1)) & 0x03) | rll;
810 xd_info[drive].cylinders = geometry_table[n][0];
811 xd_info[drive].heads = (u_char)(geometry_table[n][1]);
812 xd_info[drive].control = rll ? 7 : 5;
813#if 0
814 xd_info[drive].rwrite = geometry_table[n][2];
815 xd_info[drive].wprecomp = geometry_table[n][3];
816 xd_info[drive].ecc = 0x0B;
817#endif /* 0 */
818 }
819 if (!wd_1002) {
820 if (use_jumper_geo)
821 xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,
822 geometry_table[n][2],geometry_table[n][3],0x0B);
823 else
824 xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,
825 ((u_short *) (buf))[0xD8],((u_short *) (buf))[0xDA],buf[0x1B4]);
826 }
827 /* 1002 based RLL controller requests converted addressing, but reports physical
828 (physical 26 sec., logical 17 sec.)
829 1004 based ???? */
830 if (rll & wd_1002) {
831 if ((xd_info[drive].cylinders *= 26,
832 xd_info[drive].cylinders /= 17) > 1023)
833 xd_info[drive].cylinders = 1023; /* 1024 ? */
834#if 0
835 xd_info[drive].rwrite *= 26;
836 xd_info[drive].rwrite /= 17;
837 xd_info[drive].wprecomp *= 26
838 xd_info[drive].wprecomp /= 17;
839#endif /* 0 */
840 }
841 }
842 else
843 printk("xd_wd_init_drive: error reading geometry for xd%c\n",'a'+drive);
844
845}
846
847static void __init xd_seagate_init_controller (unsigned int address)
848{
849 switch (address) {
850 case 0x00000:
851 case 0xC8000: break; /*initial: 0x320 */
852 case 0xD0000: xd_iobase = 0x324; break;
853 case 0xD8000: xd_iobase = 0x328; break;
854 case 0xE0000: xd_iobase = 0x32C; break;
855 default: printk("xd_seagate_init_controller: unsupported BIOS address %06x\n",address);
856 break;
857 }
858 xd_maxsectors = 0x40;
859
860 outb(0,XD_RESET); /* reset the controller */
861}
862
863static void __init xd_seagate_init_drive (u_char drive)
864{
865 u_char cmdblk[6],buf[0x200];
866
867 xd_build(cmdblk,CMD_ST11GETGEOM,drive,0,0,0,1,0);
868 if (!xd_command(cmdblk,PIO_MODE,buf,NULL,NULL,XD_TIMEOUT * 2)) {
869 xd_info[drive].heads = buf[0x04]; /* heads */
870 xd_info[drive].cylinders = (buf[0x02] << 8) | buf[0x03]; /* cylinders */
871 xd_info[drive].sectors = buf[0x05]; /* sectors */
872 xd_info[drive].control = 0; /* control byte */
873 }
874 else
875 printk("xd_seagate_init_drive: error reading geometry from xd%c\n", 'a'+drive);
876}
877
878/* Omti support courtesy Dirk Melchers */
879static void __init xd_omti_init_controller (unsigned int address)
880{
881 switch (address) {
882 case 0x00000:
883 case 0xC8000: break; /*initial: 0x320 */
884 case 0xD0000: xd_iobase = 0x324; break;
885 case 0xD8000: xd_iobase = 0x328; break;
886 case 0xE0000: xd_iobase = 0x32C; break;
887 default: printk("xd_omti_init_controller: unsupported BIOS address %06x\n",address);
888 break;
889 }
890
891 xd_maxsectors = 0x40;
892
893 outb(0,XD_RESET); /* reset the controller */
894}
895
896static void __init xd_omti_init_drive (u_char drive)
897{
898 /* gets infos from drive */
899 xd_override_init_drive(drive);
900
901 /* set other parameters, Hardcoded, not that nice :-) */
902 xd_info[drive].control = 2;
903}
904
905/* Xebec support (AK) */
906static void __init xd_xebec_init_controller (unsigned int address)
907{
908/* iobase may be set manually in range 0x300 - 0x33C
909 irq may be set manually to 2(9),3,4,5,6,7
910 dma may be set manually to 1,2,3
911 (How to detect them ???)
912BIOS address may be set manually in range 0x0 - 0xF8000
913If you need non-standard settings use the xd=... command */
914
915 switch (address) {
916 case 0x00000:
917 case 0xC8000: /* initially: xd_iobase==0x320 */
918 case 0xD0000:
919 case 0xD2000:
920 case 0xD4000:
921 case 0xD6000:
922 case 0xD8000:
923 case 0xDA000:
924 case 0xDC000:
925 case 0xDE000:
926 case 0xE0000: break;
927 default: printk("xd_xebec_init_controller: unsupported BIOS address %06x\n",address);
928 break;
929 }
930
931 xd_maxsectors = 0x01;
932 outb(0,XD_RESET); /* reset the controller */
933
Nishanth Aravamudanf6a2f342005-09-10 00:27:14 -0700934 msleep(XD_INIT_DISK_DELAY);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935}
936
937static void __init xd_xebec_init_drive (u_char drive)
938{
939 /* values from controller's BIOS - BIOS chip may be removed */
940 static u_short geometry_table[][5] = {
941 {0x132,4,0x080,0x080,0x7},
942 {0x132,4,0x080,0x080,0x17},
943 {0x264,2,0x100,0x100,0x7},
944 {0x264,2,0x100,0x100,0x17},
945 {0x132,8,0x080,0x080,0x7},
946 {0x132,8,0x080,0x080,0x17},
947 {0x264,4,0x100,0x100,0x6},
948 {0x264,4,0x100,0x100,0x17},
949 {0x2BC,5,0x2BC,0x12C,0x6},
950 {0x3A5,4,0x3A5,0x3A5,0x7},
951 {0x26C,6,0x26C,0x26C,0x7},
952 {0x200,8,0x200,0x100,0x17},
953 {0x400,5,0x400,0x400,0x7},
954 {0x400,6,0x400,0x400,0x7},
955 {0x264,8,0x264,0x200,0x17},
956 {0x33E,7,0x33E,0x200,0x7}};
957 u_char n;
958
959 n = inb(XD_JUMPER) & 0x0F; /* BIOS's drive number: same geometry
960 is assumed for BOTH drives */
961 if (xd_geo[3*drive])
962 xd_manual_geo_set(drive);
963 else {
964 xd_info[drive].heads = (u_char)(geometry_table[n][1]); /* heads */
965 xd_info[drive].cylinders = geometry_table[n][0]; /* cylinders */
966 xd_info[drive].sectors = 17; /* sectors */
967#if 0
968 xd_info[drive].rwrite = geometry_table[n][2]; /* reduced write */
969 xd_info[drive].precomp = geometry_table[n][3] /* write precomp */
970 xd_info[drive].ecc = 0x0B; /* ecc length */
971#endif /* 0 */
972 }
973 xd_info[drive].control = geometry_table[n][4]; /* control byte */
974 xd_setparam(CMD_XBSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,geometry_table[n][2],geometry_table[n][3],0x0B);
975 xd_recalibrate(drive);
976}
977
978/* xd_override_init_drive: this finds disk geometry in a "binary search" style, narrowing in on the "correct" number of heads
979 etc. by trying values until it gets the highest successful value. Idea courtesy Salvador Abreu (spa@fct.unl.pt). */
980static void __init xd_override_init_drive (u_char drive)
981{
982 u_short min[] = { 0,0,0 },max[] = { 16,1024,64 },test[] = { 0,0,0 };
983 u_char cmdblk[6],i;
984
985 if (xd_geo[3*drive])
986 xd_manual_geo_set(drive);
987 else {
988 for (i = 0; i < 3; i++) {
989 while (min[i] != max[i] - 1) {
990 test[i] = (min[i] + max[i]) / 2;
991 xd_build(cmdblk,CMD_SEEK,drive,(u_char) test[0],(u_short) test[1],(u_char) test[2],0,0);
992 if (!xd_command(cmdblk,PIO_MODE,NULL,NULL,NULL,XD_TIMEOUT * 2))
993 min[i] = test[i];
994 else
995 max[i] = test[i];
996 }
997 test[i] = min[i];
998 }
999 xd_info[drive].heads = (u_char) min[0] + 1;
1000 xd_info[drive].cylinders = (u_short) min[1] + 1;
1001 xd_info[drive].sectors = (u_char) min[2] + 1;
1002 }
1003 xd_info[drive].control = 0;
1004}
1005
1006/* xd_setup: initialise controller from command line parameters */
1007static void __init do_xd_setup (int *integers)
1008{
1009 switch (integers[0]) {
1010 case 4: if (integers[4] < 0)
1011 nodma = 1;
1012 else if (integers[4] < 8)
1013 xd_dma = integers[4];
1014 case 3: if ((integers[3] > 0) && (integers[3] <= 0x3FC))
1015 xd_iobase = integers[3];
1016 case 2: if ((integers[2] > 0) && (integers[2] < 16))
1017 xd_irq = integers[2];
1018 case 1: xd_override = 1;
Tobias Klauser945f3902006-01-08 01:05:11 -08001019 if ((integers[1] >= 0) && (integers[1] < ARRAY_SIZE(xd_sigs)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 xd_type = integers[1];
1021 case 0: break;
1022 default:printk("xd: too many parameters for xd\n");
1023 }
1024 xd_maxsectors = 0x01;
1025}
1026
1027/* xd_setparam: set the drive characteristics */
1028static void __init xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc)
1029{
1030 u_char cmdblk[14];
1031
1032 xd_build(cmdblk,command,drive,0,0,0,0,0);
1033 cmdblk[6] = (u_char) (cylinders >> 8) & 0x03;
1034 cmdblk[7] = (u_char) (cylinders & 0xFF);
1035 cmdblk[8] = heads & 0x1F;
1036 cmdblk[9] = (u_char) (rwrite >> 8) & 0x03;
1037 cmdblk[10] = (u_char) (rwrite & 0xFF);
1038 cmdblk[11] = (u_char) (wprecomp >> 8) & 0x03;
1039 cmdblk[12] = (u_char) (wprecomp & 0xFF);
1040 cmdblk[13] = ecc;
1041
1042 /* Some controllers require geometry info as data, not command */
1043
1044 if (xd_command(cmdblk,PIO_MODE,NULL,&cmdblk[6],NULL,XD_TIMEOUT * 2))
1045 printk("xd: error setting characteristics for xd%c\n", 'a'+drive);
1046}
1047
1048
1049#ifdef MODULE
1050
1051module_param_array(xd, int, NULL, 0);
1052module_param_array(xd_geo, int, NULL, 0);
1053module_param(nodma, bool, 0);
1054
1055MODULE_LICENSE("GPL");
1056
1057void cleanup_module(void)
1058{
1059 int i;
1060 unregister_blkdev(XT_DISK_MAJOR, "xd");
1061 for (i = 0; i < xd_drives; i++) {
1062 del_gendisk(xd_gendisk[i]);
1063 put_disk(xd_gendisk[i]);
1064 }
1065 blk_cleanup_queue(xd_queue);
1066 release_region(xd_iobase,4);
1067 if (xd_drives) {
1068 free_irq(xd_irq, NULL);
1069 free_dma(xd_dma);
1070 if (xd_dma_buffer)
1071 xd_dma_mem_free((unsigned long)xd_dma_buffer, xd_maxsectors * 0x200);
1072 }
1073}
1074#else
1075
1076static int __init xd_setup (char *str)
1077{
1078 int ints[5];
1079 get_options (str, ARRAY_SIZE (ints), ints);
1080 do_xd_setup (ints);
1081 return 1;
1082}
1083
1084/* xd_manual_geo_init: initialise drive geometry from command line parameters
1085 (used only for WD drives) */
1086static int __init xd_manual_geo_init (char *str)
1087{
1088 int i, integers[1 + 3*XD_MAXDRIVES];
1089
1090 get_options (str, ARRAY_SIZE (integers), integers);
1091 if (integers[0]%3 != 0) {
1092 printk("xd: incorrect number of parameters for xd_geo\n");
1093 return 1;
1094 }
1095 for (i = 0; (i < integers[0]) && (i < 3*XD_MAXDRIVES); i++)
1096 xd_geo[i] = integers[i+1];
1097 return 1;
1098}
1099
1100__setup ("xd=", xd_setup);
1101__setup ("xd_geo=", xd_manual_geo_init);
1102
1103#endif /* MODULE */
1104
1105module_init(xd_init);
1106MODULE_ALIAS_BLOCKDEV_MAJOR(XT_DISK_MAJOR);