AHCI: fixed constant mixup, minor cleanup.

* TRANSITION_... was incorrectly changed from the original patch.
* Divided it into two constants, and also prefixed the new constants with
  the register fields they are valid for.
* Fixed incorrect usage of |= and removed the corresponding TODO comments.
* Moved some reoccurring code into their own methods.
* Added check for the ST bit in the command register for the interrupt
  hard reset, too.
* This closes ticket #12295, thanks Anarchos!
This commit is contained in:
Axel Dörfler
2015-08-28 19:26:14 +02:00
parent 5584c22fdd
commit df5aeb6dda
3 changed files with 42 additions and 31 deletions
@@ -85,9 +85,10 @@ typedef struct {
uint8 det : 4; // Device Detection Initialization uint8 det : 4; // Device Detection Initialization
} _PACKED scontrol; } _PACKED scontrol;
#define TRANSITIONS_TO_PARTIAL_SLUMBER_DISABLED 0x300 #define IPM_TRANSITIONS_TO_PARTIAL_DISABLED 0x1
#define NO_INITIALIZATION 0 #define IPM_TRANSITIONS_TO_SLUMBER_DISABLED 0x2
#define INITIALIZATION 1 #define DET_NO_INITIALIZATION 0x0
#define DET_INITIALIZATION 0x1
typedef struct { typedef struct {
@@ -1,5 +1,5 @@
/* /*
* Copyright 2008-2014 Haiku, Inc. All rights reserved. * Copyright 2008-2015 Haiku, Inc. All rights reserved.
* Copyright 2007-2009, Marcus Overhagen. All rights reserved. * Copyright 2007-2009, Marcus Overhagen. All rights reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -110,7 +110,8 @@ AHCIPort::Init1()
// prdt follows after command table // prdt follows after command table
// disable transitions to partial or slumber state // disable transitions to partial or slumber state
fRegs->sctl.ipm |= TRANSITIONS_TO_PARTIAL_SLUMBER_DISABLED; /*TODO Why "|= and not "=" ??*/ fRegs->sctl.ipm = IPM_TRANSITIONS_TO_PARTIAL_DISABLED
| IPM_TRANSITIONS_TO_SLUMBER_DISABLED;
// clear IRQ status bits // clear IRQ status bits
fRegs->is = fRegs->is; fRegs->is = fRegs->is;
@@ -213,23 +214,13 @@ AHCIPort::Uninit()
void void
AHCIPort::ResetDevice() AHCIPort::ResetDevice()
{ {
if (fRegs->cmd & PORT_CMD_ST)
TRACE("AHCIPort::ResetDevice PORT_CMD_ST set, behaviour undefined\n");
// perform a hard reset // perform a hard reset
fRegs->sctl.det |= INITIALIZATION; //TODO Why "|=" instead of "=" ? _HardReset();
FlushPostedWrites();
spin(1100);
fRegs->sctl.det = NO_INITIALIZATION;
FlushPostedWrites();
if (wait_until_set(&fRegs->ssts, 0x1, 100000) < B_OK) { if (wait_until_set(&fRegs->ssts, 0x1, 100000) < B_OK)
TRACE("AHCIPort::ResetDevice port %d no device detected\n", fIndex); TRACE("AHCIPort::ResetDevice port %d no device detected\n", fIndex);
}
// clear error bits _ClearErrorRegister();
fRegs->serr = fRegs->serr;
FlushPostedWrites();
if (fRegs->ssts & 1) { if (fRegs->ssts & 1) {
if (wait_until_set(&fRegs->ssts, 0x3, 500000) < B_OK) { if (wait_until_set(&fRegs->ssts, 0x3, 500000) < B_OK) {
@@ -238,9 +229,7 @@ AHCIPort::ResetDevice()
} }
} }
// clear error bits _ClearErrorRegister();
fRegs->serr = fRegs->serr;
FlushPostedWrites();
} }
@@ -423,20 +412,13 @@ AHCIPort::InterruptErrorHandler(uint32 is)
} }
if (is & PORT_INT_PC) { if (is & PORT_INT_PC) {
TRACE("Port Connect Change\n"); TRACE("Port Connect Change\n");
/* spec v1.3, §6.2.2.3 Recovery of Unsolicited COMINIT (a COMINIT that is // Spec v1.3, §6.2.2.3 Recovery of Unsolicited COMINIT
* not received as a consequence of issuing a COMRESET to the device) */
// perform a hard reset // perform a hard reset
fRegs->sctl.det |= INITIALIZATION; //TODO Why "|=" instead of "=" ? _HardReset();
FlushPostedWrites();
spin(1100); // specification says you must wait 1ms
fRegs->sctl.det = NO_INITIALIZATION;
FlushPostedWrites();
// clear error bits to clear PxSERR.DIAG.X // clear error bits to clear PxSERR.DIAG.X
fRegs->serr = fRegs->serr; _ClearErrorRegister();
FlushPostedWrites();
// fResetPort = true;
} }
if (is & PORT_INT_UF) { if (is & PORT_INT_UF) {
TRACE("Unknown FIS\n"); TRACE("Unknown FIS\n");
@@ -1247,3 +1229,29 @@ AHCIPort::ScsiGetRestrictions(bool* isATAPI, bool* noAutoSense,
"maxBlocks %" B_PRIu32 "\n", fIndex, *isATAPI, *noAutoSense, "maxBlocks %" B_PRIu32 "\n", fIndex, *isATAPI, *noAutoSense,
*maxBlocks); *maxBlocks);
} }
void
AHCIPort::_HardReset()
{
if ((fRegs->cmd & PORT_CMD_ST) != 0) {
// We shouldn't perform a reset, but at least document it
TRACE("AHCIPort::_HardReset() PORT_CMD_ST set, behaviour undefined\n");
}
fRegs->sctl.det = DET_INITIALIZATION;
FlushPostedWrites();
spin(1100);
// You must wait 1ms at minimum
fRegs->sctl.det = DET_NO_INITIALIZATION;
FlushPostedWrites();
}
void
AHCIPort::_ClearErrorRegister()
{
// clear error bits
fRegs->serr = fRegs->serr;
FlushPostedWrites();
}
@@ -51,6 +51,8 @@ private:
status_t WaitForTransfer(int *tfd, bigtime_t timeout); status_t WaitForTransfer(int *tfd, bigtime_t timeout);
void FinishTransfer(); void FinishTransfer();
inline void _HardReset();
inline void _ClearErrorRegister();
// uint8 * SetCommandFis(volatile command_list_entry *cmd, volatile fis *fis, const void *data, size_t dataSize); // uint8 * SetCommandFis(volatile command_list_entry *cmd, volatile fis *fis, const void *data, size_t dataSize);
status_t FillPrdTable(volatile prd *prdTable, int *prdCount, int prdMax, const void *data, size_t dataSize); status_t FillPrdTable(volatile prd *prdTable, int *prdCount, int prdMax, const void *data, size_t dataSize);