reactos/drivers/storage/ide/uniata/id_sata.cpp
Amine Khaldi c424146e2c Create a branch for cmake bringup.
svn path=/branches/cmake-bringup/; revision=48236
2010-07-24 18:52:44 +00:00

455 lines
16 KiB
C++

#include "stdafx.h"
UCHAR
NTAPI
UniataSataConnect(
IN PVOID HwDeviceExtension,
IN ULONG lChannel // logical channel
)
{
PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
//ULONG Channel = deviceExtension->Channel + lChannel;
PHW_CHANNEL chan = &deviceExtension->chan[lChannel];
SATA_SSTATUS_REG SStatus;
ULONG i;
/*
UCHAR signatureLow,
signatureHigh;
*/
UCHAR Status;
KdPrint2((PRINT_PREFIX "UniataSataConnect:\n"));
if(!UniataIsSATARangeAvailable(deviceExtension, lChannel)) {
KdPrint2((PRINT_PREFIX " no I/O range\n"));
return IDE_STATUS_IDLE;
}
/* clear SATA error register, some controllers need this */
AtapiWritePort4(chan, IDX_SATA_SError,
AtapiReadPort4(chan, IDX_SATA_SError));
/* wait up to 1 second for "connect well" */
for(i=0; i<100; i++) {
SStatus.Reg = AtapiReadPort4(chan, IDX_SATA_SStatus);
if(SStatus.SPD == SStatus_SPD_Gen1 ||
SStatus.SPD == SStatus_SPD_Gen2) {
deviceExtension->lun[lChannel*2].TransferMode = ATA_SA150 + (UCHAR)(SStatus.SPD - 1);
break;
}
AtapiStallExecution(10000);
}
if(i >= 100) {
KdPrint2((PRINT_PREFIX "UniataSataConnect: SStatus %8.8x\n", SStatus.Reg));
return 0xff;
}
/* clear SATA error register */
AtapiWritePort4(chan, IDX_SATA_SError,
AtapiReadPort4(chan, IDX_SATA_SError));
Status = WaitOnBaseBusyLong(chan);
if(Status & IDE_STATUS_BUSY) {
return Status;
}
/*
signatureLow = AtapiReadPort1(chan, &deviceExtension->BaseIoAddress1[lChannel].i.CylinderLow);
signatureHigh = AtapiReadPort1(chan, &deviceExtension->baseIoAddress1[lChannel].i.CylinderHigh);
if (signatureLow == ATAPI_MAGIC_LSB && signatureHigh == ATAPI_MAGIC_MSB) {
}
*/
KdPrint2((PRINT_PREFIX "UniataSataConnect: OK, ATA status %x\n", Status));
return IDE_STATUS_IDLE;
} // end UniataSataConnect()
UCHAR
NTAPI
UniataSataPhyEnable(
IN PVOID HwDeviceExtension,
IN ULONG lChannel // logical channel
)
{
PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
PHW_CHANNEL chan = &deviceExtension->chan[lChannel];
SATA_SCONTROL_REG SControl;
int loop, retry;
KdPrint2((PRINT_PREFIX "UniataSataPhyEnable:\n"));
if(!UniataIsSATARangeAvailable(deviceExtension, lChannel)) {
KdPrint2((PRINT_PREFIX " no I/O range\n"));
return IDE_STATUS_IDLE;
}
SControl.Reg = AtapiReadPort4(chan, IDX_SATA_SControl);
KdPrint2((PRINT_PREFIX "SControl %x\n", SControl.Reg));
if(SControl.DET == SControl_DET_Idle) {
return UniataSataConnect(HwDeviceExtension, lChannel);
}
for (retry = 0; retry < 10; retry++) {
KdPrint2((PRINT_PREFIX "UniataSataPhyEnable: retry init %d\n", retry));
for (loop = 0; loop < 10; loop++) {
SControl.Reg = 0;
SControl.DET = SControl_DET_Init;
AtapiWritePort4(chan, IDX_SATA_SControl, SControl.Reg);
AtapiStallExecution(100);
SControl.Reg = AtapiReadPort4(chan, IDX_SATA_SControl);
KdPrint2((PRINT_PREFIX " SControl %8.8%x\n", SControl.Reg));
if(SControl.DET == SControl_DET_Init) {
break;
}
}
AtapiStallExecution(5000);
KdPrint2((PRINT_PREFIX "UniataSataPhyEnable: retry idle %d\n", retry));
for (loop = 0; loop < 10; loop++) {
SControl.Reg = 0;
SControl.DET = SControl_DET_DoNothing;
SControl.IPM = SControl_IPM_NoPartialSlumber;
AtapiWritePort4(chan, IDX_SATA_SControl, SControl.Reg);
AtapiStallExecution(100);
SControl.Reg = AtapiReadPort4(chan, IDX_SATA_SControl);
KdPrint2((PRINT_PREFIX " SControl %8.8%x\n", SControl.Reg));
if(SControl.DET == SControl_DET_Idle) {
return UniataSataConnect(HwDeviceExtension, lChannel);
}
}
}
KdPrint2((PRINT_PREFIX "UniataSataPhyEnable: failed\n"));
return 0xff;
} // end UniataSataPhyEnable()
BOOLEAN
NTAPI
UniataSataClearErr(
IN PVOID HwDeviceExtension,
IN ULONG lChannel, // logical channel
IN BOOLEAN do_connect
)
{
PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
PHW_CHANNEL chan = &deviceExtension->chan[lChannel];
//ULONG ChipFlags = deviceExtension->HwFlags & CHIPFLAG_MASK;
SATA_SSTATUS_REG SStatus;
SATA_SERROR_REG SError;
if(UniataIsSATARangeAvailable(deviceExtension, lChannel)) {
//if(ChipFlags & UNIATA_SATA) {
SStatus.Reg = AtapiReadPort4(chan, IDX_SATA_SStatus);
SError.Reg = AtapiReadPort4(chan, IDX_SATA_SError);
if(SStatus.Reg) {
KdPrint2((PRINT_PREFIX " SStatus %x\n", SStatus.Reg));
}
if(SError.Reg) {
KdPrint2((PRINT_PREFIX " SError %x\n", SError.Reg));
/* clear error bits/interrupt */
AtapiWritePort4(chan, IDX_SATA_SError, SError.Reg);
if(do_connect) {
/* if we have a connection event deal with it */
if(SError.DIAG.N) {
KdPrint2((PRINT_PREFIX " catch SATA connect/disconnect\n"));
if(SStatus.SPD >= SStatus_SPD_Gen1) {
UniataSataEvent(deviceExtension, lChannel, UNIATA_SATA_EVENT_ATTACH);
} else {
UniataSataEvent(deviceExtension, lChannel, UNIATA_SATA_EVENT_DETACH);
}
return TRUE;
}
}
}
}
return FALSE;
} // end UniataSataClearErr()
BOOLEAN
NTAPI
UniataSataEvent(
IN PVOID HwDeviceExtension,
IN ULONG lChannel, // logical channel
IN ULONG Action
)
{
PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
UCHAR Status;
ULONG ldev = lChannel*2;
if(!UniataIsSATARangeAvailable(deviceExtension, lChannel)) {
return FALSE;
}
switch(Action) {
case UNIATA_SATA_EVENT_ATTACH:
KdPrint2((PRINT_PREFIX " CONNECTED\n"));
Status = UniataSataConnect(HwDeviceExtension, lChannel);
KdPrint2((PRINT_PREFIX " Status %x\n", Status));
if(Status != IDE_STATUS_IDLE) {
return FALSE;
}
CheckDevice(HwDeviceExtension, lChannel, 0 /*dev*/, FALSE);
return TRUE;
break;
case UNIATA_SATA_EVENT_DETACH:
KdPrint2((PRINT_PREFIX " DISCONNECTED\n"));
UniataForgetDevice(&(deviceExtension->lun[ldev]));
return TRUE;
break;
}
return FALSE;
} // end UniataSataEvent()
BOOLEAN
NTAPI
UniataAhciInit(
IN PVOID HwDeviceExtension
)
{
PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
ULONG version;
ULONG c, i, n;
PHW_CHANNEL chan;
ULONG offs;
ULONG BaseMemAddress;
ULONG PI;
ULONG CAP;
BOOLEAN MemIo;
ULONGLONG base;
/* reset AHCI controller */
AtapiWritePortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), IDX_AHCI_GHC,
AtapiReadPortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), IDX_AHCI_GHC) | AHCI_GHC_HR);
AtapiStallExecution(1000000);
if(AtapiReadPortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), IDX_AHCI_GHC) & AHCI_GHC_HR) {
KdPrint2((PRINT_PREFIX " AHCI reset failed\n"));
return FALSE;
}
/* enable AHCI mode */
AtapiWritePortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), IDX_AHCI_GHC,
AtapiReadPortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), IDX_AHCI_GHC) | AHCI_GHC_AE);
CAP = AtapiReadPortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), IDX_AHCI_CAP);
PI = AtapiReadPortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), IDX_AHCI_PI);
/* get the number of HW channels */
for(i=PI, n=0; i; n++, i=i>>1);
deviceExtension->NumberChannels =
max((CAP & AHCI_CAP_NOP_MASK)+1, n);
if(CAP & AHCI_CAP_S64A) {
KdPrint2((PRINT_PREFIX " AHCI 64bit\n"));
deviceExtension->Host64 = TRUE;
}
/* clear interrupts */
AtapiWritePortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), IDX_AHCI_IS,
AtapiReadPortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), IDX_AHCI_IS));
/* enable AHCI interrupts */
AtapiWritePortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), IDX_AHCI_GHC,
AtapiReadPortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), IDX_AHCI_GHC) | AHCI_GHC_IE);
version = AtapiReadPortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), IDX_AHCI_VS);
KdPrint2((PRINT_PREFIX " AHCI version %x%x.%x%x controller with %d ports (mask %x) detected\n",
(version >> 24) & 0xff, (version >> 16) & 0xff,
(version >> 8) & 0xff, version & 0xff, deviceExtension->NumberChannels, PI));
deviceExtension->HwFlags |= UNIATA_SATA;
deviceExtension->HwFlags |= UNIATA_AHCI;
BaseMemAddress = deviceExtension->BaseIoAHCI_0.Addr;
MemIo = deviceExtension->BaseIoAHCI_0.MemIo;
for(c=0; c<deviceExtension->NumberChannels; c++) {
chan = &deviceExtension->chan[c];
offs = sizeof(IDE_AHCI_REGISTERS) + c*sizeof(IDE_AHCI_PORT_REGISTERS);
chan->RegTranslation[IDX_IO1_i_Status ].Addr = BaseMemAddress + offs + FIELD_OFFSET(IDE_AHCI_PORT_REGISTERS, TFD.STS);
chan->RegTranslation[IDX_IO1_i_Status ].MemIo = MemIo;
chan->RegTranslation[IDX_IO2_AltStatus] = chan->RegTranslation[IDX_IO1_i_Status];
chan->RegTranslation[IDX_IO1_i_Error ].Addr = BaseMemAddress + offs + FIELD_OFFSET(IDE_AHCI_PORT_REGISTERS, TFD.ERR);
chan->RegTranslation[IDX_IO1_i_Error ].MemIo = MemIo;
chan->RegTranslation[IDX_IO1_i_CylinderLow ].Addr = BaseMemAddress + offs + FIELD_OFFSET(IDE_AHCI_PORT_REGISTERS, SIG.LbaLow);
chan->RegTranslation[IDX_IO1_i_CylinderLow ].MemIo = MemIo;
chan->RegTranslation[IDX_IO1_i_CylinderHigh].Addr = BaseMemAddress + offs + FIELD_OFFSET(IDE_AHCI_PORT_REGISTERS, SIG.LbaHigh);
chan->RegTranslation[IDX_IO1_i_CylinderHigh].MemIo = MemIo;
chan->RegTranslation[IDX_IO1_i_BlockCount ].Addr = BaseMemAddress + offs + FIELD_OFFSET(IDE_AHCI_PORT_REGISTERS, SIG.SectorCount);
chan->RegTranslation[IDX_IO1_i_BlockCount ].MemIo = MemIo;
UniataInitSyncBaseIO(chan);
chan->RegTranslation[IDX_SATA_SStatus].Addr = BaseMemAddress + offs + FIELD_OFFSET(IDE_AHCI_PORT_REGISTERS, SSTS);
chan->RegTranslation[IDX_SATA_SStatus].MemIo = MemIo;
chan->RegTranslation[IDX_SATA_SError].Addr = BaseMemAddress + offs + FIELD_OFFSET(IDE_AHCI_PORT_REGISTERS, SERR);
chan->RegTranslation[IDX_SATA_SError].MemIo = MemIo;
chan->RegTranslation[IDX_SATA_SControl].Addr = BaseMemAddress + offs + FIELD_OFFSET(IDE_AHCI_PORT_REGISTERS, SCTL);
chan->RegTranslation[IDX_SATA_SControl].MemIo = MemIo;
chan->RegTranslation[IDX_SATA_SActive].Addr = BaseMemAddress + offs + FIELD_OFFSET(IDE_AHCI_PORT_REGISTERS, SACT);
chan->RegTranslation[IDX_SATA_SActive].MemIo = MemIo;
AtapiDmaAlloc(HwDeviceExtension, NULL, c);
base = chan->AHCI_CL_PhAddr;
if(!base) {
KdPrint2((PRINT_PREFIX " AHCI buffer allocation failed\n"));
return FALSE;
}
AtapiWritePortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), offs + IDX_AHCI_P_CLB,
(ULONG)(base & 0xffffffff));
AtapiWritePortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), offs + IDX_AHCI_P_CLB + 4,
(ULONG)((base >> 32) & 0xffffffff));
base = chan->AHCI_CL_PhAddr + ATA_AHCI_MAX_TAGS;
AtapiWritePortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), offs + IDX_AHCI_P_FB,
(ULONG)(base & 0xffffffff));
AtapiWritePortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), offs + IDX_AHCI_P_FB + 4,
(ULONG)((base >> 32) & 0xffffffff));
chan->ChannelCtrlFlags |= CTRFLAGS_NO_SLAVE;
}
return TRUE;
} // end UniataAhciInit()
UCHAR
NTAPI
UniataAhciStatus(
IN PVOID HwDeviceExtension,
IN ULONG lChannel
)
{
PHW_DEVICE_EXTENSION deviceExtension = (PHW_DEVICE_EXTENSION)HwDeviceExtension;
PHW_CHANNEL chan = &deviceExtension->chan[lChannel];
ULONG Channel = deviceExtension->Channel + lChannel;
ULONG hIS;
ULONG CI;
AHCI_IS_REG IS;
SATA_SSTATUS_REG SStatus;
SATA_SERROR_REG SError;
ULONG offs = sizeof(IDE_AHCI_REGISTERS) + Channel*sizeof(IDE_AHCI_PORT_REGISTERS);
ULONG_PTR base;
ULONG tag=0;
KdPrint(("UniataAhciStatus:\n"));
hIS = AtapiReadPortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), IDX_AHCI_IS);
KdPrint((" hIS %x\n", hIS));
hIS &= (1 << Channel);
if(!hIS) {
return 0;
}
base = (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0 + offs);
IS.Reg = AtapiReadPort4(chan, base + IDX_AHCI_P_IS);
CI = AtapiReadPort4(chan, base + IDX_AHCI_P_CI);
SStatus.Reg = AtapiReadPort4(chan, IDX_SATA_SStatus);
SError.Reg = AtapiReadPort4(chan, IDX_SATA_SError);
/* clear interrupt(s) */
AtapiWritePortEx4(NULL, (ULONG_PTR)(&deviceExtension->BaseIoAHCI_0), IDX_AHCI_IS, hIS);
AtapiWritePort4(chan, base + IDX_AHCI_P_IS, IS.Reg);
AtapiWritePort4(chan, IDX_SATA_SError, SError.Reg);
KdPrint((" AHCI: status=%08x sstatus=%08x error=%08x CI=%08x\n",
IS.Reg, SStatus.Reg, SError.Reg, CI));
/* do we have cold connect surprise */
if(IS.CPDS) {
}
/* check for and handle connect events */
if(IS.PCS) {
UniataSataEvent(HwDeviceExtension, lChannel, UNIATA_SATA_EVENT_ATTACH);
}
if(IS.PRCS) {
UniataSataEvent(HwDeviceExtension, lChannel, UNIATA_SATA_EVENT_DETACH);
}
if(CI & (1 << tag)) {
return 1;
}
KdPrint((" AHCI: unexpected\n"));
return 2;
} // end UniataAhciStatus()
ULONG
NTAPI
UniataAhciSetupFIS(
IN PHW_DEVICE_EXTENSION deviceExtension,
IN ULONG DeviceNumber,
IN ULONG lChannel,
OUT PUCHAR fis,
IN UCHAR command,
IN ULONGLONG lba,
IN USHORT count,
IN USHORT feature,
IN ULONG flags
)
{
ULONG ldev = lChannel*2 + DeviceNumber;
ULONG i;
PUCHAR plba;
KdPrint2((PRINT_PREFIX " AHCI setup FIS\n" ));
i = 0;
plba = (PUCHAR)&lba;
if((AtaCommandFlags[command] & ATA_CMD_FLAG_LBAIOsupp) &&
CheckIfBadBlock(&(deviceExtension->lun[ldev]), lba, count)) {
KdPrint3((PRINT_PREFIX ": artificial bad block, lba %#I64x count %#x\n", lba, count));
return IDE_STATUS_ERROR;
//return SRB_STATUS_ERROR;
}
/* translate command into 48bit version */
if ((lba >= ATA_MAX_LBA28 || count > 256) &&
deviceExtension->lun[ldev].IdentifyData.FeaturesSupport.Address48) {
if(AtaCommandFlags[command] & ATA_CMD_FLAG_48supp) {
command = AtaCommands48[command];
} else {
KdPrint2((PRINT_PREFIX " unhandled LBA48 command\n"));
return 0;
}
}
fis[0] = 0x27; /* host to device */
fis[1] = 0x80; /* command FIS (note PM goes here) */
fis[2] = command;
fis[3] = (UCHAR)feature;
fis[4] = plba[0];
fis[5] = plba[1];
fis[6] = plba[2];
fis[7] = IDE_USE_LBA | (DeviceNumber ? IDE_DRIVE_2 : IDE_DRIVE_1);
if ((lba >= ATA_MAX_LBA28 || count > 256) &&
deviceExtension->lun[ldev].IdentifyData.FeaturesSupport.Address48) {
i++;
} else {
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4333) // right shift by too large amount, data loss
#endif
fis[7] |= (plba[3] >> 24) & 0x0f;
#ifdef _MSC_VER
#pragma warning(pop)
#endif
}
fis[8] = plba[3];
fis[9] = plba[4];
fis[10] = plba[5];
fis[11] = (UCHAR)(feature>>8) & 0xff;
fis[12] = (UCHAR)count & 0xff;
fis[13] = (UCHAR)(count>>8) & 0xff;
fis[14] = 0x00;
fis[15] = IDE_DC_A_4BIT;
fis[16] = 0x00;
fis[17] = 0x00;
fis[18] = 0x00;
fis[19] = 0x00;
return 20;
} // end UniataAhciSetupFIS()