4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Note: mostly ripped from atapi.c
19 * Some of this code was based on knowledge and/or code developed
20 * by the Xbox Linux group: http://www.xbox-linux.org
28 DBG_DEFAULT_CHANNEL(DISK
);
30 #define XBOX_IDE_COMMAND_PORT 0x1f0
31 #define XBOX_IDE_CONTROL_PORT 0x170
33 #define IDE_SECTOR_BUF_SZ 512
34 #define IDE_MAX_POLL_RETRIES 100000
35 #define IDE_MAX_BUSY_RETRIES 50000
37 /* Control Block offsets and masks */
38 #define IDE_REG_ALT_STATUS 0x0000
39 #define IDE_REG_DEV_CNTRL 0x0000 /* device control register */
40 #define IDE_DC_SRST 0x04 /* drive reset (both drives) */
41 #define IDE_DC_nIEN 0x02 /* IRQ enable (active low) */
42 #define IDE_REG_DRV_ADDR 0x0001
44 /* Command Block offsets and masks */
45 #define IDE_REG_DATA_PORT 0x0000
46 #define IDE_REG_ERROR 0x0001 /* error register */
47 #define IDE_ER_AMNF 0x01 /* addr mark not found */
48 #define IDE_ER_TK0NF 0x02 /* track 0 not found */
49 #define IDE_ER_ABRT 0x04 /* command aborted */
50 #define IDE_ER_MCR 0x08 /* media change requested */
51 #define IDE_ER_IDNF 0x10 /* ID not found */
52 #define IDE_ER_MC 0x20 /* Media changed */
53 #define IDE_ER_UNC 0x40 /* Uncorrectable data error */
54 #define IDE_REG_PRECOMP 0x0001
55 #define IDE_REG_SECTOR_CNT 0x0002
56 #define IDE_REG_SECTOR_NUM 0x0003
57 #define IDE_REG_CYL_LOW 0x0004
58 #define IDE_REG_CYL_HIGH 0x0005
59 #define IDE_REG_DRV_HEAD 0x0006
60 #define IDE_DH_FIXED 0xA0
61 #define IDE_DH_LBA 0x40
62 #define IDE_DH_HDMASK 0x0F
63 #define IDE_DH_DRV0 0x00
64 #define IDE_DH_DRV1 0x10
65 #define IDE_REG_STATUS 0x0007
66 #define IDE_SR_BUSY 0x80
67 #define IDE_SR_DRDY 0x40
68 #define IDE_SR_WERR 0x20
69 #define IDE_SR_DRQ 0x08
70 #define IDE_SR_ERR 0x01
71 #define IDE_REG_COMMAND 0x0007
73 /* IDE/ATA commands */
74 #define IDE_CMD_RESET 0x08
75 #define IDE_CMD_READ 0x20
76 #define IDE_CMD_READ_RETRY 0x21
77 #define IDE_CMD_WRITE 0x30
78 #define IDE_CMD_WRITE_RETRY 0x31
79 #define IDE_CMD_PACKET 0xA0
80 #define IDE_CMD_READ_MULTIPLE 0xC4
81 #define IDE_CMD_WRITE_MULTIPLE 0xC5
82 #define IDE_CMD_READ_DMA 0xC8
83 #define IDE_CMD_WRITE_DMA 0xCA
84 #define IDE_CMD_FLUSH_CACHE 0xE7
85 #define IDE_CMD_FLUSH_CACHE_EXT 0xEA
86 #define IDE_CMD_IDENT_ATA_DRV 0xEC
87 #define IDE_CMD_IDENT_ATAPI_DRV 0xA1
88 #define IDE_CMD_GET_MEDIA_STATUS 0xDA
91 * Access macros for command registers
92 * Each macro takes an address of the command port block, and data
94 #define IDEReadError(Address) \
95 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_ERROR)))
96 #define IDEWritePrecomp(Address, Data) \
97 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_PRECOMP), (Data)))
98 #define IDEReadSectorCount(Address) \
99 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_CNT)))
100 #define IDEWriteSectorCount(Address, Data) \
101 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_CNT), (Data)))
102 #define IDEReadSectorNum(Address) \
103 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_NUM)))
104 #define IDEWriteSectorNum(Address, Data) \
105 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_SECTOR_NUM), (Data)))
106 #define IDEReadCylinderLow(Address) \
107 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_LOW)))
108 #define IDEWriteCylinderLow(Address, Data) \
109 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_LOW), (Data)))
110 #define IDEReadCylinderHigh(Address) \
111 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_HIGH)))
112 #define IDEWriteCylinderHigh(Address, Data) \
113 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_CYL_HIGH), (Data)))
114 #define IDEReadDriveHead(Address) \
115 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DRV_HEAD)))
116 #define IDEWriteDriveHead(Address, Data) \
117 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DRV_HEAD), (Data)))
118 #define IDEReadStatus(Address) \
119 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_STATUS)))
120 #define IDEWriteCommand(Address, Data) \
121 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_COMMAND), (Data)))
122 #define IDEReadDMACommand(Address) \
123 (READ_PORT_UCHAR((PUCHAR)((Address))))
124 #define IDEWriteDMACommand(Address, Data) \
125 (WRITE_PORT_UCHAR((PUCHAR)((Address)), (Data)))
126 #define IDEReadDMAStatus(Address) \
127 (READ_PORT_UCHAR((PUCHAR)((Address) + 2)))
128 #define IDEWriteDMAStatus(Address, Data) \
129 (WRITE_PORT_UCHAR((PUCHAR)((Address) + 2), (Data)))
130 #define IDEWritePRDTable(Address, Data) \
131 (WRITE_PORT_ULONG((PULONG)((Address) + 4), (Data)))
134 * Data block read and write commands
136 #define IDEReadBlock(Address, Buffer, Count) \
137 (READ_PORT_BUFFER_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT), (PUSHORT)(Buffer), (Count) / 2))
138 #define IDEWriteBlock(Address, Buffer, Count) \
139 (WRITE_PORT_BUFFER_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT), (PUSHORT)(Buffer), (Count) / 2))
141 #define IDEReadBlock32(Address, Buffer, Count) \
142 (READ_PORT_BUFFER_ULONG((PULONG)((Address) + IDE_REG_DATA_PORT), (PULONG)(Buffer), (Count) / 4))
143 #define IDEWriteBlock32(Address, Buffer, Count) \
144 (WRITE_PORT_BUFFER_ULONG((PULONG)((Address) + IDE_REG_DATA_PORT), (PULONG)(Buffer), (Count) / 4))
146 #define IDEReadWord(Address) \
147 (READ_PORT_USHORT((PUSHORT)((Address) + IDE_REG_DATA_PORT)))
150 * Access macros for control registers
151 * Each macro takes an address of the control port blank and data
153 #define IDEReadAltStatus(Address) \
154 (READ_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_ALT_STATUS)))
155 #define IDEWriteDriveControl(Address, Data) \
156 (WRITE_PORT_UCHAR((PUCHAR)((Address) + IDE_REG_DEV_CNTRL), (Data)))
158 /* IDE_DRIVE_IDENTIFY */
159 typedef struct _IDE_DRIVE_IDENTIFY
161 USHORT ConfigBits
; /*00*/
162 USHORT LogicalCyls
; /*01*/
163 USHORT Reserved02
; /*02*/
164 USHORT LogicalHeads
; /*03*/
165 USHORT BytesPerTrack
; /*04*/
166 USHORT BytesPerSector
; /*05*/
167 USHORT SectorsPerTrack
; /*06*/
168 UCHAR InterSectorGap
; /*07*/
169 UCHAR InterSectorGapSize
;
170 UCHAR Reserved08H
; /*08*/
172 USHORT VendorUniqueCnt
; /*09*/
173 UCHAR SerialNumber
[20]; /*10*/
174 USHORT ControllerType
; /*20*/
175 USHORT BufferSize
; /*21*/
176 USHORT ECCByteCnt
; /*22*/
177 UCHAR FirmwareRev
[8]; /*23*/
178 UCHAR ModelNumber
[40]; /*27*/
179 USHORT RWMultImplemented
; /*47*/
180 USHORT DWordIo
; /*48*/
181 USHORT Capabilities
; /*49*/
182 #define IDE_DRID_STBY_SUPPORTED 0x2000
183 #define IDE_DRID_IORDY_SUPPORTED 0x0800
184 #define IDE_DRID_IORDY_DISABLE 0x0400
185 #define IDE_DRID_LBA_SUPPORTED 0x0200
186 #define IDE_DRID_DMA_SUPPORTED 0x0100
187 USHORT Reserved50
; /*50*/
188 USHORT MinPIOTransTime
; /*51*/
189 USHORT MinDMATransTime
; /*52*/
190 USHORT TMFieldsValid
; /*53*/
191 USHORT TMCylinders
; /*54*/
192 USHORT TMHeads
; /*55*/
193 USHORT TMSectorsPerTrk
; /*56*/
194 USHORT TMCapacityLo
; /*57*/
195 USHORT TMCapacityHi
; /*58*/
196 USHORT RWMultCurrent
; /*59*/
197 USHORT TMSectorCountLo
; /*60*/
198 USHORT TMSectorCountHi
; /*61*/
199 USHORT DmaModes
; /*62*/
200 USHORT MultiDmaModes
; /*63*/
201 USHORT Reserved64
[5]; /*64*/
202 USHORT Reserved69
[2]; /*69*/
203 USHORT Reserved71
[4]; /*71*/
204 USHORT MaxQueueDepth
; /*75*/
205 USHORT Reserved76
[4]; /*76*/
206 USHORT MajorRevision
; /*80*/
207 USHORT MinorRevision
; /*81*/
208 USHORT SupportedFeatures82
; /*82*/
209 USHORT SupportedFeatures83
; /*83*/
210 USHORT SupportedFeatures84
; /*84*/
211 USHORT EnabledFeatures85
; /*85*/
212 USHORT EnabledFeatures86
; /*86*/
213 USHORT EnabledFeatures87
; /*87*/
214 USHORT UltraDmaModes
; /*88*/
215 USHORT Reserved89
[11]; /*89*/
216 USHORT Max48BitAddress
[4]; /*100*/
217 USHORT Reserved104
[151]; /*104*/
218 USHORT Checksum
; /*255*/
219 } IDE_DRIVE_IDENTIFY
, *PIDE_DRIVE_IDENTIFY
;
221 /* XboxDiskPolledRead
224 * Read a sector of data from the drive in a polled fashion.
230 * ULONG CommandPort Address of command port for drive
231 * ULONG ControlPort Address of control port for drive
232 * UCHAR PreComp Value to write to precomp register
233 * UCHAR SectorCnt Value to write to sectorCnt register
234 * UCHAR SectorNum Value to write to sectorNum register
235 * UCHAR CylinderLow Value to write to CylinderLow register
236 * UCHAR CylinderHigh Value to write to CylinderHigh register
237 * UCHAR DrvHead Value to write to Drive/Head register
238 * UCHAR Command Value to write to Command register
239 * PVOID Buffer Buffer for output data
242 * BOOLEAN: TRUE success, FALSE error
245 XboxDiskPolledRead(ULONG CommandPort
,
256 ULONG SectorCount
= 0;
258 BOOLEAN Junk
= FALSE
;
261 /* Wait for BUSY to clear */
262 for (RetryCount
= 0; RetryCount
< IDE_MAX_BUSY_RETRIES
; RetryCount
++)
264 Status
= IDEReadStatus(CommandPort
);
265 if (!(Status
& IDE_SR_BUSY
))
268 StallExecutionProcessor(10);
270 TRACE("status=0x%x\n", Status
);
271 TRACE("waited %d usecs for busy to clear\n", RetryCount
* 10);
272 if (RetryCount
>= IDE_MAX_BUSY_RETRIES
)
274 WARN("Drive is BUSY for too long\n");
278 /* Write Drive/Head to select drive */
279 IDEWriteDriveHead(CommandPort
, IDE_DH_FIXED
| DrvHead
);
280 StallExecutionProcessor(500);
282 /* Disable interrupts */
283 IDEWriteDriveControl(ControlPort
, IDE_DC_nIEN
);
284 StallExecutionProcessor(500);
286 /* Issue command to drive */
287 if (DrvHead
& IDE_DH_LBA
)
289 TRACE("READ:DRV=%d:LBA=1:BLK=%d:SC=0x%x:CM=0x%x\n",
290 DrvHead
& IDE_DH_DRV1
? 1 : 0,
291 ((DrvHead
& 0x0f) << 24) + (CylinderHigh
<< 16) + (CylinderLow
<< 8) + SectorNum
,
297 TRACE("READ:DRV=%d:LBA=0:CH=0x%x:CL=0x%x:HD=0x%x:SN=0x%x:SC=0x%x:CM=0x%x\n",
298 DrvHead
& IDE_DH_DRV1
? 1 : 0,
307 /* Setup command parameters */
308 IDEWritePrecomp(CommandPort
, PreComp
);
309 IDEWriteSectorCount(CommandPort
, SectorCnt
);
310 IDEWriteSectorNum(CommandPort
, SectorNum
);
311 IDEWriteCylinderHigh(CommandPort
, CylinderHigh
);
312 IDEWriteCylinderLow(CommandPort
, CylinderLow
);
313 IDEWriteDriveHead(CommandPort
, IDE_DH_FIXED
| DrvHead
);
315 /* Issue the command */
316 IDEWriteCommand(CommandPort
, Command
);
317 StallExecutionProcessor(50);
319 /* Wait for DRQ or error */
320 for (RetryCount
= 0; RetryCount
< IDE_MAX_POLL_RETRIES
; RetryCount
++)
322 Status
= IDEReadStatus(CommandPort
);
323 if (!(Status
& IDE_SR_BUSY
))
325 if (Status
& IDE_SR_ERR
)
327 IDEWriteDriveControl(ControlPort
, 0);
328 StallExecutionProcessor(50);
329 IDEReadStatus(CommandPort
);
333 if (Status
& IDE_SR_DRQ
)
339 IDEWriteDriveControl(ControlPort
, 0);
340 StallExecutionProcessor(50);
341 IDEReadStatus(CommandPort
);
345 StallExecutionProcessor(10);
349 if (RetryCount
>= IDE_MAX_POLL_RETRIES
)
351 IDEWriteDriveControl(ControlPort
, 0);
352 StallExecutionProcessor(50);
353 IDEReadStatus(CommandPort
);
359 /* Read data into buffer */
362 IDEReadBlock(CommandPort
, Buffer
, IDE_SECTOR_BUF_SZ
);
363 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ IDE_SECTOR_BUF_SZ
);
367 UCHAR JunkBuffer
[IDE_SECTOR_BUF_SZ
];
368 IDEReadBlock(CommandPort
, JunkBuffer
, IDE_SECTOR_BUF_SZ
);
372 /* Check for error or more sectors to read */
373 for (RetryCount
= 0; RetryCount
< IDE_MAX_BUSY_RETRIES
; RetryCount
++)
375 Status
= IDEReadStatus(CommandPort
);
376 if (!(Status
& IDE_SR_BUSY
))
378 if (Status
& IDE_SR_ERR
)
380 IDEWriteDriveControl(ControlPort
, 0);
381 StallExecutionProcessor(50);
382 IDEReadStatus(CommandPort
);
385 if (Status
& IDE_SR_DRQ
)
387 if (SectorCount
>= SectorCnt
)
389 TRACE("Buffer size exceeded!\n");
396 if (SectorCount
> SectorCnt
)
398 TRACE("Read %lu sectors of junk!\n",
399 SectorCount
- SectorCnt
);
402 IDEWriteDriveControl(ControlPort
, 0);
403 StallExecutionProcessor(50);
404 IDEReadStatus(CommandPort
);
413 XboxDiskReadLogicalSectors(UCHAR DriveNumber
, ULONGLONG SectorNumber
, ULONG SectorCount
, PVOID Buffer
)
418 if (DriveNumber
< 0x80 || (DriveNumber
& 0x0f) >= 2)
420 /* Xbox has only 1 IDE controller and no floppy */
421 WARN("Invalid drive number\n");
425 if (((SectorNumber
+ SectorCount
) & UINT64_C(0xfffffffff0000000)) != UINT64_C(0))
427 FIXME("48bit LBA required but not implemented\n");
431 StartSector
= (ULONG
) SectorNumber
;
432 while (SectorCount
> 0)
434 Count
= (SectorCount
<= 255 ? (UCHAR
)SectorCount
: 255);
435 if (!XboxDiskPolledRead(XBOX_IDE_COMMAND_PORT
,
436 XBOX_IDE_CONTROL_PORT
,
439 (StartSector
>> 8) & 0xff,
440 (StartSector
>> 16) & 0xff,
441 ((StartSector
>> 24) & 0x0f) | IDE_DH_LBA
|
442 ((DriveNumber
& 0x0f) == 0 ? IDE_DH_DRV0
: IDE_DH_DRV1
),
448 SectorCount
-= Count
;
449 Buffer
= (PVOID
) ((PCHAR
) Buffer
+ Count
* IDE_SECTOR_BUF_SZ
);
456 XboxDiskGetDriveGeometry(UCHAR DriveNumber
, PGEOMETRY Geometry
)
458 IDE_DRIVE_IDENTIFY DrvParms
;
462 Atapi
= FALSE
; /* FIXME */
463 /* Get the Drive Identify block from drive or die */
464 if (!XboxDiskPolledRead(XBOX_IDE_COMMAND_PORT
,
465 XBOX_IDE_CONTROL_PORT
,
471 ((DriveNumber
& 0x0f) == 0 ? IDE_DH_DRV0
: IDE_DH_DRV1
),
472 (Atapi
? IDE_CMD_IDENT_ATAPI_DRV
: IDE_CMD_IDENT_ATA_DRV
),
475 ERR("XboxDiskPolledRead() failed\n");
479 Geometry
->Cylinders
= DrvParms
.LogicalCyls
;
480 Geometry
->Heads
= DrvParms
.LogicalHeads
;
481 Geometry
->Sectors
= DrvParms
.SectorsPerTrack
;
483 if (!Atapi
&& (DrvParms
.Capabilities
& IDE_DRID_LBA_SUPPORTED
) != 0)
485 /* LBA ATA drives always have a sector size of 512 */
486 Geometry
->BytesPerSector
= 512;
490 TRACE("BytesPerSector %d\n", DrvParms
.BytesPerSector
);
491 if (DrvParms
.BytesPerSector
== 0)
493 Geometry
->BytesPerSector
= 512;
497 for (i
= 1 << 15; i
; i
/= 2)
499 if ((DrvParms
.BytesPerSector
& i
) != 0)
501 Geometry
->BytesPerSector
= i
;
507 TRACE("Cylinders %d\n", Geometry
->Cylinders
);
508 TRACE("Heads %d\n", Geometry
->Heads
);
509 TRACE("Sectors %d\n", Geometry
->Sectors
);
510 TRACE("BytesPerSector %d\n", Geometry
->BytesPerSector
);
516 XboxDiskGetCacheableBlockCount(UCHAR DriveNumber
)
518 /* 64 seems a nice number, it is used by the machpc code for LBA devices */