File: /usr/src/linux/drivers/ide/ide-geometry.c

1     /*
2      * linux/drivers/ide/ide-geometry.c
3      */
4     #include <linux/config.h>
5     #include <linux/ide.h>
6     #include <linux/mc146818rtc.h>
7     #include <asm/io.h>
8     
9     /*
10      * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc
11      * controller that is BIOS compatible with ST-506, and thus showing up in our
12      * BIOS table, but not register compatible, and therefore not present in CMOS.
13      *
14      * Furthermore, we will assume that our ST-506 drives <if any> are the primary
15      * drives in the system -- the ones reflected as drive 1 or 2.  The first
16      * drive is stored in the high nibble of CMOS byte 0x12, the second in the low
17      * nibble.  This will be either a 4 bit drive type or 0xf indicating use byte
18      * 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.  A non-zero value
19      * means we have an AT controller hard disk for that drive.
20      *
21      * Of course, there is no guarantee that either drive is actually on the
22      * "primary" IDE interface, but we don't bother trying to sort that out here.
23      * If a drive is not actually on the primary interface, then these parameters
24      * will be ignored.  This results in the user having to supply the logical
25      * drive geometry as a boot parameter for each drive not on the primary i/f.
26      */
27     /*
28      * The only "perfect" way to handle this would be to modify the setup.[cS] code
29      * to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info
30      * for us during initialization.  I have the necessary docs -- any takers?  -ml
31      */
32     /*
33      * I did this, but it doesnt work - there is no reasonable way to find the
34      * correspondence between the BIOS numbering of the disks and the Linux
35      * numbering. -aeb
36      *
37      * The code below is bad. One of the problems is that drives 1 and 2
38      * may be SCSI disks (even when IDE disks are present), so that
39      * the geometry we read here from BIOS is attributed to the wrong disks.
40      * Consequently, also the former "drive->present = 1" below was a mistake.
41      *
42      * Eventually the entire routine below should be removed.
43      */
44     void probe_cmos_for_drives (ide_hwif_t *hwif)
45     {
46     #ifdef __i386__
47     	extern struct drive_info_struct drive_info;
48     	byte cmos_disks, *BIOS = (byte *) &drive_info;
49     	int unit;
50     	unsigned long flags;
51     
52     #ifdef CONFIG_BLK_DEV_PDC4030
53     	if (hwif->chipset == ide_pdc4030 && hwif->channel != 0)
54     		return;
55     #endif /* CONFIG_BLK_DEV_PDC4030 */
56     	spin_lock_irqsave(&rtc_lock, flags);
57     	cmos_disks = CMOS_READ(0x12);
58     	spin_unlock_irqrestore(&rtc_lock, flags);
59     	/* Extract drive geometry from CMOS+BIOS if not already setup */
60     	for (unit = 0; unit < MAX_DRIVES; ++unit) {
61     		ide_drive_t *drive = &hwif->drives[unit];
62     
63     		if ((cmos_disks & (0xf0 >> (unit*4)))
64     		   && !drive->present && !drive->nobios) {
65     			unsigned short cyl = *(unsigned short *)BIOS;
66     			unsigned char head = *(BIOS+2);
67     			unsigned char sect = *(BIOS+14);
68     			if (cyl > 0 && head > 0 && sect > 0 && sect < 64) {
69     				drive->cyl   = drive->bios_cyl  = cyl;
70     				drive->head  = drive->bios_head = head;
71     				drive->sect  = drive->bios_sect = sect;
72     				drive->ctl   = *(BIOS+8);
73     			} else {
74     				printk("hd%d: C/H/S=%d/%d/%d from BIOS ignored\n",
75     				       unit, cyl, head, sect);
76     			}
77     		}
78     
79     		BIOS += 16;
80     	}
81     #endif
82     }
83     
84     
85     #ifdef CONFIG_BLK_DEV_IDE
86     
87     extern ide_drive_t * get_info_ptr(kdev_t);
88     extern unsigned long current_capacity (ide_drive_t *);
89     
90     /*
91      * If heads is nonzero: find a translation with this many heads and S=63.
92      * Otherwise: find out how OnTrack Disk Manager would translate the disk.
93      */
94     static void
95     ontrack(ide_drive_t *drive, int heads, unsigned int *c, int *h, int *s) {
96     	static const byte dm_head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
97     	const byte *headp = dm_head_vals;
98     	unsigned long total;
99     
100     	/*
101     	 * The specs say: take geometry as obtained from Identify,
102     	 * compute total capacity C*H*S from that, and truncate to
103     	 * 1024*255*63. Now take S=63, H the first in the sequence
104     	 * 4, 8, 16, 32, 64, 128, 255 such that 63*H*1024 >= total.
105     	 * [Please tell aeb@cwi.nl in case this computes a
106     	 * geometry different from what OnTrack uses.]
107     	 */
108     	total = DRIVER(drive)->capacity(drive);
109     
110     	*s = 63;
111     
112     	if (heads) {
113     		*h = heads;
114     		*c = total / (63 * heads);
115     		return;
116     	}
117     
118     	while (63 * headp[0] * 1024 < total && headp[1] != 0)
119     		 headp++;
120     	*h = headp[0];
121     	*c = total / (63 * headp[0]);
122     }
123     
124     /*
125      * This routine is called from the partition-table code in pt/msdos.c.
126      * It has two tasks:
127      * (i) to handle Ontrack DiskManager by offsetting everything by 63 sectors,
128      *  or to handle EZdrive by remapping sector 0 to sector 1.
129      * (ii) to invent a translated geometry.
130      * Part (i) is suppressed if the user specifies the "noremap" option
131      * on the command line.
132      * Part (ii) is suppressed if the user specifies an explicit geometry.
133      *
134      * The ptheads parameter is either 0 or tells about the number of
135      * heads shown by the end of the first nonempty partition.
136      * If this is either 16, 32, 64, 128, 240 or 255 we'll believe it.
137      *
138      * The xparm parameter has the following meaning:
139      *	 0 = convert to CHS with fewer than 1024 cyls
140      *	     using the same method as Ontrack DiskManager.
141      *	 1 = same as "0", plus offset everything by 63 sectors.
142      *	-1 = similar to "0", plus redirect sector 0 to sector 1.
143      *	 2 = convert to a CHS geometry with "ptheads" heads.
144      *
145      * Returns 0 if the translation was not possible, if the device was not 
146      * an IDE disk drive, or if a geometry was "forced" on the commandline.
147      * Returns 1 if the geometry translation was successful.
148      */
149     int ide_xlate_1024 (kdev_t i_rdev, int xparm, int ptheads, const char *msg)
150     {
151     	ide_drive_t *drive;
152     	const char *msg1 = "";
153     	int heads = 0;
154     	int c, h, s;
155     	int transl = 1;		/* try translation */
156     	int ret = 0;
157     
158     	drive = get_info_ptr(i_rdev);
159     	if (!drive)
160     		return 0;
161     
162     	/* remap? */
163     	if (drive->remap_0_to_1 != 2) {
164     		if (xparm == 1) {		/* DM */
165     			drive->sect0 = 63;
166     			msg1 = " [remap +63]";
167     			ret = 1;
168     		} else if (xparm == -1) {	/* EZ-Drive */
169     			if (drive->remap_0_to_1 == 0) {
170     				drive->remap_0_to_1 = 1;
171     				msg1 = " [remap 0->1]";
172     				ret = 1;
173     			}
174     		}
175     	}
176     
177     	/* There used to be code here that assigned drive->id->CHS
178     	   to drive->CHS and that to drive->bios_CHS. However,
179     	   some disks have id->C/H/S = 4092/16/63 but are larger than 2.1 GB.
180     	   In such cases that code was wrong.  Moreover,
181     	   there seems to be no reason to do any of these things. */
182     
183     	/* translate? */
184     	if (drive->forced_geom)
185     		transl = 0;
186     
187     	/* does ptheads look reasonable? */
188     	if (ptheads == 32 || ptheads == 64 || ptheads == 128 ||
189     	    ptheads == 240 || ptheads == 255)
190     		heads = ptheads;
191     
192     	if (xparm == 2) {
193     		if (!heads ||
194     		   (drive->bios_head >= heads && drive->bios_sect == 63))
195     			transl = 0;
196     	}
197     	if (xparm == -1) {
198     		if (drive->bios_head > 16)
199     			transl = 0;     /* we already have a translation */
200     	}
201     
202     	if (transl) {
203     		ontrack(drive, heads, &c, &h, &s);
204     		drive->bios_cyl = c;
205     		drive->bios_head = h;
206     		drive->bios_sect = s;
207     		ret = 1;
208     	}
209     
210     	drive->part[0].nr_sects = current_capacity(drive);
211     
212     	if (ret)
213     		printk("%s%s [%d/%d/%d]", msg, msg1,
214     		       drive->bios_cyl, drive->bios_head, drive->bios_sect);
215     	return ret;
216     }
217     #endif /* CONFIG_BLK_DEV_IDE */
218