From 22cf14cfbeb75dfc24947cb395368050f919b127 Mon Sep 17 00:00:00 2001 From: Marcus Overhagen Date: Mon, 24 Sep 2007 21:32:32 +0000 Subject: [PATCH] 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 --- .../busses/scsi/ahci/ahci_controller.cpp | 48 ++++-- .../kernel/busses/scsi/ahci/ahci_controller.h | 4 +- .../kernel/busses/scsi/ahci/ahci_defs.h | 6 +- .../kernel/busses/scsi/ahci/ahci_port.cpp | 140 +++++++++++++++--- .../kernel/busses/scsi/ahci/ahci_port.h | 20 ++- 5 files changed, 170 insertions(+), 48 deletions(-) diff --git a/src/add-ons/kernel/busses/scsi/ahci/ahci_controller.cpp b/src/add-ons/kernel/busses/scsi/ahci/ahci_controller.cpp index 82843c98dd..b6de06a145 100644 --- a/src/add-ons/kernel/busses/scsi/ahci/ahci_controller.cpp +++ b/src/add-ons/kernel/busses/scsi/ahci/ahci_controller.cpp @@ -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(); } diff --git a/src/add-ons/kernel/busses/scsi/ahci/ahci_controller.h b/src/add-ons/kernel/busses/scsi/ahci/ahci_controller.h index bbded5f248..059e6540b5 100644 --- a/src/add-ons/kernel/busses/scsi/ahci/ahci_controller.h +++ b/src/add-ons/kernel/busses/scsi/ahci/ahci_controller.h @@ -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; diff --git a/src/add-ons/kernel/busses/scsi/ahci/ahci_defs.h b/src/add-ons/kernel/busses/scsi/ahci/ahci_defs.h index 97e50d6bbe..ec4987d4d1 100644 --- a/src/add-ons/kernel/busses/scsi/ahci/ahci_defs.h +++ b/src/add-ons/kernel/busses/scsi/ahci/ahci_defs.h @@ -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); } diff --git a/src/add-ons/kernel/busses/scsi/ahci/ahci_port.cpp b/src/add-ons/kernel/busses/scsi/ahci/ahci_port.cpp index a5af786964..51671af823 100644 --- a/src/add-ons/kernel/busses/scsi/ahci/ahci_port.cpp +++ b/src/add-ons/kernel/busses/scsi/ahci/ahci_port.cpp @@ -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; } - diff --git a/src/add-ons/kernel/busses/scsi/ahci/ahci_port.h b/src/add-ons/kernel/busses/scsi/ahci/ahci_port.h index 631d6384e8..0d9b926dea 100644 --- a/src/add-ons/kernel/busses/scsi/ahci/ahci_port.h +++ b/src/add-ons/kernel/busses/scsi/ahci/ahci_port.h @@ -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