1 |
1275 |
phoenix |
/*
|
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 |
|
|
* The only "perfect" way to handle this would be to modify the setup.[cS] code
|
28 |
|
|
* to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info
|
29 |
|
|
* for us during initialization. I have the necessary docs -- any takers? -ml
|
30 |
|
|
*
|
31 |
|
|
* I did this, but it doesn't work - there is no reasonable way to find the
|
32 |
|
|
* correspondence between the BIOS numbering of the disks and the Linux
|
33 |
|
|
* numbering. -aeb
|
34 |
|
|
*
|
35 |
|
|
* The code below is bad. One of the problems is that drives 1 and 2
|
36 |
|
|
* may be SCSI disks (even when IDE disks are present), so that
|
37 |
|
|
* the geometry we read here from BIOS is attributed to the wrong disks.
|
38 |
|
|
* Consequently, also the former "drive->present = 1" below was a mistake.
|
39 |
|
|
*
|
40 |
|
|
* Eventually the entire routine below should be removed.
|
41 |
|
|
*
|
42 |
|
|
* 17-OCT-2000 rjohnson@analogic.com Added spin-locks for reading CMOS
|
43 |
|
|
* chip.
|
44 |
|
|
*/
|
45 |
|
|
|
46 |
|
|
void probe_cmos_for_drives (ide_hwif_t *hwif)
|
47 |
|
|
{
|
48 |
|
|
#ifdef __i386__
|
49 |
|
|
extern struct drive_info_struct drive_info;
|
50 |
|
|
u8 cmos_disks, *BIOS = (u8 *) &drive_info;
|
51 |
|
|
int unit;
|
52 |
|
|
unsigned long flags;
|
53 |
|
|
|
54 |
|
|
if (hwif->chipset == ide_pdc4030 && hwif->channel != 0)
|
55 |
|
|
return;
|
56 |
|
|
|
57 |
|
|
spin_lock_irqsave(&rtc_lock, flags);
|
58 |
|
|
cmos_disks = CMOS_READ(0x12);
|
59 |
|
|
spin_unlock_irqrestore(&rtc_lock, flags);
|
60 |
|
|
/* Extract drive geometry from CMOS+BIOS if not already setup */
|
61 |
|
|
for (unit = 0; unit < MAX_DRIVES; ++unit) {
|
62 |
|
|
ide_drive_t *drive = &hwif->drives[unit];
|
63 |
|
|
|
64 |
|
|
if ((cmos_disks & (0xf0 >> (unit*4)))
|
65 |
|
|
&& !drive->present && !drive->nobios) {
|
66 |
|
|
u16 cyl = *(u16 *)BIOS;
|
67 |
|
|
unsigned char head = *(BIOS+2);
|
68 |
|
|
unsigned char sect = *(BIOS+14);
|
69 |
|
|
if (cyl > 0 && head > 0 && sect > 0 && sect < 64) {
|
70 |
|
|
drive->cyl = drive->bios_cyl = cyl;
|
71 |
|
|
drive->head = drive->bios_head = head;
|
72 |
|
|
drive->sect = drive->bios_sect = sect;
|
73 |
|
|
drive->ctl = *(BIOS+8);
|
74 |
|
|
} else {
|
75 |
|
|
printk("hd%c: C/H/S=%d/%d/%d from BIOS ignored\n",
|
76 |
|
|
unit+'a', cyl, head, sect);
|
77 |
|
|
}
|
78 |
|
|
}
|
79 |
|
|
|
80 |
|
|
BIOS += 16;
|
81 |
|
|
}
|
82 |
|
|
#endif
|
83 |
|
|
}
|
84 |
|
|
|
85 |
|
|
|
86 |
|
|
extern unsigned long current_capacity (ide_drive_t *);
|
87 |
|
|
|
88 |
|
|
/*
|
89 |
|
|
* If heads is nonzero: find a translation with this many heads and S=63.
|
90 |
|
|
* Otherwise: find out how OnTrack Disk Manager would translate the disk.
|
91 |
|
|
*/
|
92 |
|
|
|
93 |
|
|
static void ontrack(ide_drive_t *drive, int heads, unsigned int *c, int *h, int *s)
|
94 |
|
|
{
|
95 |
|
|
static const u8 dm_head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
|
96 |
|
|
const u8 *headp = dm_head_vals;
|
97 |
|
|
unsigned long total;
|
98 |
|
|
|
99 |
|
|
/*
|
100 |
|
|
* The specs say: take geometry as obtained from Identify,
|
101 |
|
|
* compute total capacity C*H*S from that, and truncate to
|
102 |
|
|
* 1024*255*63. Now take S=63, H the first in the sequence
|
103 |
|
|
* 4, 8, 16, 32, 64, 128, 255 such that 63*H*1024 >= total.
|
104 |
|
|
* [Please tell aeb@cwi.nl in case this computes a
|
105 |
|
|
* geometry different from what OnTrack uses.]
|
106 |
|
|
*/
|
107 |
|
|
total = DRIVER(drive)->capacity(drive);
|
108 |
|
|
|
109 |
|
|
*s = 63;
|
110 |
|
|
|
111 |
|
|
if (heads) {
|
112 |
|
|
*h = heads;
|
113 |
|
|
*c = total / (63 * heads);
|
114 |
|
|
return;
|
115 |
|
|
}
|
116 |
|
|
|
117 |
|
|
while (63 * headp[0] * 1024 < total && headp[1] != 0)
|
118 |
|
|
headp++;
|
119 |
|
|
*h = headp[0];
|
120 |
|
|
*c = total / (63 * headp[0]);
|
121 |
|
|
}
|
122 |
|
|
|
123 |
|
|
/*
|
124 |
|
|
* This routine is called from the partition-table code in pt/msdos.c.
|
125 |
|
|
* It has two tasks:
|
126 |
|
|
* (i) to handle Ontrack DiskManager by offsetting everything by 63 sectors,
|
127 |
|
|
* or to handle EZdrive by remapping sector 0 to sector 1.
|
128 |
|
|
* (ii) to invent a translated geometry.
|
129 |
|
|
* Part (i) is suppressed if the user specifies the "noremap" option
|
130 |
|
|
* on the command line.
|
131 |
|
|
* Part (ii) is suppressed if the user specifies an explicit geometry.
|
132 |
|
|
*
|
133 |
|
|
* The ptheads parameter is either 0 or tells about the number of
|
134 |
|
|
* heads shown by the end of the first nonempty partition.
|
135 |
|
|
* If this is either 16, 32, 64, 128, 240 or 255 we'll believe it.
|
136 |
|
|
*
|
137 |
|
|
* The xparm parameter has the following meaning:
|
138 |
|
|
* 0 = convert to CHS with fewer than 1024 cyls
|
139 |
|
|
* using the same method as Ontrack DiskManager.
|
140 |
|
|
* 1 = same as "0", plus offset everything by 63 sectors.
|
141 |
|
|
* -1 = similar to "0", plus redirect sector 0 to sector 1.
|
142 |
|
|
* 2 = convert to a CHS geometry with "ptheads" heads.
|
143 |
|
|
*
|
144 |
|
|
* Returns 0 if the translation was not possible, if the device was not
|
145 |
|
|
* an IDE disk drive, or if a geometry was "forced" on the commandline.
|
146 |
|
|
* Returns 1 if the geometry translation was successful.
|
147 |
|
|
*/
|
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 = ide_info_ptr(i_rdev, 0);
|
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 |
|
|
}
|