perform port reset and wait for devices to be detected

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22293 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Marcus Overhagen 2007-09-24 21:32:32 +00:00
parent be30f13184
commit 22cf14cfbe
5 changed files with 170 additions and 48 deletions

View File

@ -28,8 +28,8 @@ AHCIController::AHCIController(device_node_handle node, pci_device_info *device)
{
memset(fPort, 0, sizeof(fPort));
ASSERT(sizeof(ahci_port) == 120);
ASSERT(sizeof(ahci_hba) == 4096);
ASSERT(sizeof(ahci_port) == 128);
ASSERT(sizeof(ahci_hba) == 4352);
ASSERT(sizeof(fis) == 256);
ASSERT(sizeof(command_list_entry) == 32);
ASSERT(sizeof(command_table) == 128);
@ -144,19 +144,31 @@ AHCIController::Init()
TRACE("out of memory creating port %d", i);
break;
}
status_t status = fPort[i]->Init();
status_t status = fPort[i]->Init1();
if (status < B_OK) {
TRACE("init port %d failed", i);
TRACE("init-1 port %d failed", i);
delete fPort[i];
fPort[i] = NULL;
break;
}
}
}
// enable interrupts
fRegs->ghc |= GHC_IE;
RegsFlush();
FlushPostedWrites();
for (int i = 0; i <= fPortCountMax; i++) {
if (fPort[i]) {
status_t status = fPort[i]->Init2();
if (status < B_OK) {
TRACE("init-2 port %d failed", i);
fPort[i]->Uninit();
delete fPort[i];
fPort[i] = NULL;
}
}
}
return B_OK;
@ -180,11 +192,11 @@ AHCIController::Uninit()
// disable interrupts
fRegs->ghc &= ~GHC_IE;
RegsFlush();
FlushPostedWrites();
// clear pending interrupts
fRegs->is = 0xffffffff;
RegsFlush();
FlushPostedWrites();
// well...
remove_io_interrupt_handler(fIRQ, Interrupt, this);
@ -204,8 +216,9 @@ AHCIController::ResetController()
uint32 saveCaps = fRegs->cap & (CAP_SMPS | CAP_SSS | CAP_SPM | CAP_EMS | CAP_SXS);
uint32 savePI = fRegs->pi;
#if 1
fRegs->ghc |= GHC_HR;
RegsFlush();
FlushPostedWrites();
for (int i = 0; i < 20; i++) {
snooze(50000);
if ((fRegs->ghc & GHC_HR) == 0)
@ -213,12 +226,17 @@ AHCIController::ResetController()
}
if (fRegs->ghc & GHC_HR)
return B_TIMED_OUT;
#else
fRegs->ghc &= ~GHC_AE;
fRegs->is = 0xffffffff;
FlushPostedWrites();
#endif
fRegs->ghc |= GHC_AE;
RegsFlush();
FlushPostedWrites();
fRegs->cap |= saveCaps;
fRegs->pi = savePI;
RegsFlush();
FlushPostedWrites();
if (fPCIVendorID == 0x8086) {
// Intel PCS—Port Control and Status
@ -264,7 +282,7 @@ AHCIController::ExecuteRequest(scsi_ccb *request)
return;
}
fPort[request->target_id]->ExecuteRequest(request);
fPort[request->target_id]->ScsiExecuteRequest(request);
}
@ -274,7 +292,7 @@ AHCIController::AbortRequest(scsi_ccb *request)
if (request->target_lun || !fPort[request->target_id])
return SCSI_DEV_NOT_THERE;
return fPort[request->target_id]->AbortRequest(request);
return fPort[request->target_id]->ScsiAbortRequest(request);
}
@ -284,7 +302,7 @@ AHCIController::TerminateRequest(scsi_ccb *request)
if (request->target_lun || !fPort[request->target_id])
return SCSI_DEV_NOT_THERE;
return fPort[request->target_id]->TerminateRequest(request);
return fPort[request->target_id]->ScsiTerminateRequest(request);
}
@ -294,6 +312,6 @@ AHCIController::ResetDevice(uchar targetID, uchar targetLUN)
if (targetLUN || !fPort[targetID])
return SCSI_DEV_NOT_THERE;
return fPort[targetID]->ResetDevice();
return fPort[targetID]->ScsiResetDevice();
}

View File

@ -29,7 +29,7 @@ public:
private:
bool IsDevicePresent(uint device);
status_t ResetController();
void RegsFlush();
void FlushPostedWrites();
static int32 Interrupt(void *data);
@ -58,7 +58,7 @@ private:
inline void
AHCIController::RegsFlush()
AHCIController::FlushPostedWrites()
{
volatile uint32 dummy = fRegs->ghc;
dummy = dummy;

View File

@ -132,7 +132,7 @@ enum {
PORT_INT_SDB | PORT_INT_DS | PORT_INT_PS | PORT_INT_DHR)
enum {
ATA_BUSY = 0x80,
ATA_BSY = 0x80,
ATA_DRQ = 0x08,
ATA_ERR = 0x01,
};
@ -237,7 +237,7 @@ wait_until_set(volatile uint32 *reg, uint32 bits, bigtime_t timeout)
{
int trys = (timeout + 9999) / 10000;
while (trys--) {
if ((*reg & bits) == bits)
if (((*reg) & bits) == bits)
return B_OK;
snooze(10000);
}
@ -250,7 +250,7 @@ wait_until_clear(volatile uint32 *reg, uint32 bits, bigtime_t timeout)
{
int trys = (timeout + 9999) / 10000;
while (trys--) {
if ((*reg & bits) == 0)
if (((*reg) & bits) == 0)
return B_OK;
snooze(10000);
}

View File

@ -29,9 +29,9 @@ AHCIPort::~AHCIPort()
status_t
AHCIPort::Init()
AHCIPort::Init1()
{
TRACE("AHCIPort::Init port %d\n", fIndex);
TRACE("AHCIPort::Init1 port %d\n", fIndex);
size_t size = sizeof(command_list_entry) * COMMAND_LIST_ENTRY_COUNT + sizeof(fis) + sizeof(command_table) + sizeof(prd) * PRD_TABLE_ENTRY_COUNT;
@ -67,7 +67,8 @@ AHCIPort::Init()
fRegs->is = fRegs->is;
// clear error bits
fRegs->serr = fRegs->serr;
// fRegs->serr = fRegs->serr;
fRegs->serr = 0xffffffff;
// spin up device
fRegs->cmd |= PORT_CMD_SUD;
@ -78,12 +79,39 @@ AHCIPort::Init()
// enable FIS receive
fRegs->cmd |= PORT_CMD_FER;
FlushPostedWrites();
return B_OK;
}
// called with global interrupts enabled
status_t
AHCIPort::Init2()
{
TRACE("AHCIPort::Init2 port %d\n", fIndex);
// start DMA engine
fRegs->cmd |= PORT_CMD_ST;
// enable interrupts
fRegs->ie = PORT_INT_MASK;
FlushPostedWrites();
// if (fRegs->sig == 0xffffffff)
ResetDevice();
PostResetDevice();
TRACE("ie 0x%08lx\n", fRegs->ie);
TRACE("is 0x%08lx\n", fRegs->is);
TRACE("cmd 0x%08lx\n", fRegs->cmd);
TRACE("ssts 0x%08lx\n", fRegs->ssts);
TRACE("sctl 0x%08lx\n", fRegs->sctl);
TRACE("serr 0x%08lx\n", fRegs->serr);
TRACE("sact 0x%08lx\n", fRegs->sact);
return B_OK;
}
@ -97,13 +125,7 @@ AHCIPort::Uninit()
fRegs->cmd &= ~PORT_CMD_FER;
// wait for receive completition, up to 500ms
for (int i = 0; i < 15; i++) {
if (!(fRegs->cmd & PORT_CMD_FR))
break;
snooze(50000);
}
if (fRegs->cmd & PORT_CMD_FR) {
if (wait_until_clear(&fRegs->cmd, PORT_CMD_FR, 500000) < B_OK) {
TRACE("AHCIPort::Uninit port %d error FIS rx still running\n", fIndex);
}
@ -111,13 +133,7 @@ AHCIPort::Uninit()
fRegs->cmd &= ~PORT_CMD_ST;
// wait for DMA completition
for (int i = 0; i < 15; i++) {
if (!(fRegs->cmd & PORT_CMD_CR))
break;
snooze(50000);
}
if (fRegs->cmd & PORT_CMD_CR) {
if (wait_until_clear(&fRegs->cmd, PORT_CMD_CR, 500000) < B_OK) {
TRACE("AHCIPort::Uninit port %d error DMA engine still running\n", fIndex);
}
@ -137,6 +153,84 @@ AHCIPort::Uninit()
}
status_t
AHCIPort::ResetDevice()
{
TRACE("AHCIPort::ResetDevice port %d\n", fIndex);
// stop DMA engine
fRegs->cmd &= ~PORT_CMD_ST;
FlushPostedWrites();
if (wait_until_clear(&fRegs->cmd, PORT_CMD_CR, 500000) < B_OK) {
TRACE("AHCIPort::ResetDevice port %d error DMA engine doesn't stop\n", fIndex);
}
// perform a hard reset
fRegs->sctl = (fRegs->sctl & ~0xf) | 1;
FlushPostedWrites();
snooze(10000);
fRegs->sctl &= ~0xf;
FlushPostedWrites();
if (wait_until_set(&fRegs->ssts, 0x1, 6000000) < B_OK) {
TRACE("AHCIPort::ResetDevice port %d no device detected\n", fIndex);
}
// clear error bits
// fRegs->serr = fRegs->serr;
fRegs->serr = 0xffffffff;
FlushPostedWrites();
// start DMA engine
fRegs->cmd |= PORT_CMD_ST;
FlushPostedWrites();
return B_OK;
}
status_t
AHCIPort::PostResetDevice()
{
TRACE("AHCIPort::PostResetDevice port %d\n", fIndex);
TRACE("tfd 1 0x%08lx\n", fRegs->tfd);
// wait for DMA idle ?
// if (wait_until_clear(&fRegs->cmd, PORT_CMD_CR, 1000000) < B_OK) {
// TRACE("AHCIPort::PostResetDevice port %d error DMA engine doesn't stop\n", fIndex);
// }
switch (fRegs->tfd & 0xff) {
case 0x7f:
TRACE("no device present?\n");
break;
case 0xff:
TRACE("invalid task file status 0xff\n");
// fall through
default:
TRACE("waiting...\n");
wait_until_clear(&fRegs->tfd, ATA_BSY | ATA_DRQ, 31000000);
}
if (fRegs->sig == 0xeb140101)
fRegs->cmd |= PORT_CMD_ATAPI;
else
fRegs->cmd &= ~PORT_CMD_ATAPI;
FlushPostedWrites();
TRACE("device signature 0x%08lx (%s)\n", fRegs->sig,
(fRegs->sig == 0xeb140101) ? "ATAPI" : (fRegs->sig == 0x00000101) ? "ATA" : "unknown");
TRACE("tfd 0x%08lx\n", fRegs->tfd);
TRACE("device detection: 0x%lx\n", fRegs->ssts & 0xf);
return B_OK;
}
void
AHCIPort::Interrupt()
{
@ -149,10 +243,11 @@ AHCIPort::Interrupt()
void
AHCIPort::ExecuteRequest(scsi_ccb *request)
AHCIPort::ScsiExecuteRequest(scsi_ccb *request)
{
TRACE("AHCIPort::ExecuteRequest port %d, opcode %u, length %u\n", fIndex, request->cdb[0], request->cdb_length);
TRACE("AHCIPort::ScsiExecuteRequest port %d, opcode %u, length %u\n", fIndex, request->cdb[0], request->cdb_length);
request->subsys_status = SCSI_DEV_NOT_THERE;
gSCSI->finished(request, 1);
@ -164,7 +259,7 @@ AHCIPort::ExecuteRequest(scsi_ccb *request)
uchar
AHCIPort::AbortRequest(scsi_ccb *request)
AHCIPort::ScsiAbortRequest(scsi_ccb *request)
{
return SCSI_REQ_CMP;
@ -172,15 +267,14 @@ AHCIPort::AbortRequest(scsi_ccb *request)
uchar
AHCIPort::TerminateRequest(scsi_ccb *request)
AHCIPort::ScsiTerminateRequest(scsi_ccb *request)
{
return SCSI_REQ_CMP;
}
uchar
AHCIPort::ResetDevice()
AHCIPort::ScsiResetDevice()
{
return SCSI_REQ_CMP;
}

View File

@ -14,18 +14,22 @@ public:
AHCIPort(AHCIController *controller, int index);
~AHCIPort();
status_t Init();
status_t Init1();
status_t Init2();
void Uninit();
void Interrupt();
void ExecuteRequest(scsi_ccb *request);
uchar AbortRequest(scsi_ccb *request);
uchar TerminateRequest(scsi_ccb *request);
uchar ResetDevice();
void ScsiExecuteRequest(scsi_ccb *request);
uchar ScsiAbortRequest(scsi_ccb *request);
uchar ScsiTerminateRequest(scsi_ccb *request);
uchar ScsiResetDevice();
private:
status_t ResetDevice();
status_t PostResetDevice();
void FlushPostedWrites();
private:
int fIndex;
@ -38,4 +42,10 @@ private:
volatile prd * fPRDTable;
};
inline void
AHCIPort::FlushPostedWrites()
{
volatile uint32 dummy = fRegs->cmd;
dummy = dummy;
}
#endif // _AHCI_PORT_H