Implement DMA transfers in the new ATA bus_manager.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30069 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz
2009-04-09 15:24:40 +00:00
parent c4005c4d29
commit 7997389772
4 changed files with 128 additions and 52 deletions
@@ -14,6 +14,7 @@ ATAChannel::ATAChannel(device_node *node)
fChannelID(0), fChannelID(0),
fController(NULL), fController(NULL),
fCookie(NULL), fCookie(NULL),
fExpectsDMATransfer(false),
fStatus(B_NO_INIT), fStatus(B_NO_INIT),
fSCSIBus(NULL), fSCSIBus(NULL),
fDeviceCount(0), fDeviceCount(0),
@@ -21,7 +22,9 @@ ATAChannel::ATAChannel(device_node *node)
fUseDMA(true), fUseDMA(true),
fRequest(NULL) fRequest(NULL)
{ {
mutex_init(&fLock, "ata channel"); mutex_init(&fExecutionLock, "ata io execution");
B_INITIALIZE_SPINLOCK(&fDMATransferLock);
fDMATransferCondition.Init(this, "ata dma transfer");
gDeviceManager->get_attr_uint32(node, ATA_CHANNEL_ID_ITEM, &fChannelID, gDeviceManager->get_attr_uint32(node, ATA_CHANNEL_ID_ITEM, &fChannelID,
true); true);
@@ -80,12 +83,14 @@ ATAChannel::ATAChannel(device_node *node)
fStatus = gDeviceManager->get_driver(parent, fStatus = gDeviceManager->get_driver(parent,
(driver_module_info **)&fController, &fCookie); (driver_module_info **)&fController, &fCookie);
gDeviceManager->put_node(parent); gDeviceManager->put_node(parent);
fController->set_channel(fCookie, this);
} }
ATAChannel::~ATAChannel() ATAChannel::~ATAChannel()
{ {
mutex_lock(&fLock); mutex_lock(&fExecutionLock);
if (fDevices) { if (fDevices) {
for (uint8 i = 0; i < fDeviceCount; i++) for (uint8 i = 0; i < fDeviceCount; i++)
@@ -94,7 +99,7 @@ ATAChannel::~ATAChannel()
} }
delete fRequest; delete fRequest;
mutex_destroy(&fLock); mutex_destroy(&fExecutionLock);
} }
@@ -217,10 +222,10 @@ status_t
ATAChannel::ExecuteIO(scsi_ccb *ccb) ATAChannel::ExecuteIO(scsi_ccb *ccb)
{ {
TRACE_FUNCTION("%p\n", ccb); TRACE_FUNCTION("%p\n", ccb);
if (mutex_trylock(&fLock) != B_OK) if (mutex_trylock(&fExecutionLock) != B_OK)
return B_BUSY; return B_BUSY;
MutexLocker _(&fLock, true); MutexLocker _(&fExecutionLock, true);
fRequest->SetCCB(ccb); fRequest->SetCCB(ccb);
if (ccb->cdb[0] == SCSI_OP_REQUEST_SENSE) { if (ccb->cdb[0] == SCSI_OP_REQUEST_SENSE) {
@@ -445,6 +450,10 @@ ATAChannel::WaitForIdle()
status_t status_t
ATAChannel::SendRequest(ATARequest *request, uint32 flags) ATAChannel::SendRequest(ATARequest *request, uint32 flags)
{ {
// disable interrupts for PIO transfers, enable them for DMA
_WriteControl((flags & ATA_DMA_TRANSFER) != 0 ? 0
: ATA_DEVICE_CONTROL_DISABLE_INTS);
ATADevice *device = request->Device(); ATADevice *device = request->Device();
if (device->Select() != B_OK || WaitForIdle() != B_OK) { if (device->Select() != B_OK || WaitForIdle() != B_OK) {
// resetting the device here will discard current configuration, // resetting the device here will discard current configuration,
@@ -479,7 +488,7 @@ ATAChannel::FinishRequest(ATARequest *request, uint32 flags, uint8 errorMask)
// wait for the device to finish current command (device no longer busy) // wait for the device to finish current command (device no longer busy)
status_t result = Wait(0, ATA_STATUS_BUSY, 0, request->Timeout()); status_t result = Wait(0, ATA_STATUS_BUSY, 0, request->Timeout());
if (result != B_OK) { if (result != B_OK) {
TRACE_ERROR("timeout\n"); TRACE_ERROR("timeout waiting for request finish\n");
request->SetStatus(SCSI_CMD_TIMEOUT); request->SetStatus(SCSI_CMD_TIMEOUT);
return result; return result;
} }
@@ -564,12 +573,45 @@ ATAChannel::FinishRequest(ATARequest *request, uint32 flags, uint8 errorMask)
} }
status_t
ATAChannel::PrepareDMA(ATARequest *request)
{
scsi_ccb *ccb = request->CCB();
return fController->prepare_dma(fCookie, ccb->sg_list, ccb->sg_count,
request->IsWrite());
}
status_t
ATAChannel::FinishDMA()
{
return fController->finish_dma(fCookie);
}
status_t status_t
ATAChannel::ExecuteDMATransfer(ATARequest *request) ATAChannel::ExecuteDMATransfer(ATARequest *request)
{ {
TRACE_ERROR("dma transfers unimplemented\n"); InterruptsSpinLocker locker(fDMATransferLock);
request->SetStatus(SCSI_SEQUENCE_FAIL); fExpectsDMATransfer = true;
return B_ERROR; ConditionVariableEntry entry;
fDMATransferCondition.Add(&entry);
locker.Unlock();
fController->start_dma(fCookie);
bigtime_t timeout = system_time() + request->Timeout();
status_t waitResult = entry.Wait(B_ABSOLUTE_TIMEOUT, timeout);
locker.Lock();
fExpectsDMATransfer = false;
locker.Unlock();
if (waitResult != B_OK) {
TRACE_ERROR("timeout waiting for DMA transfer\n");
return B_TIMED_OUT;
}
return B_OK;
} }
@@ -612,14 +654,6 @@ ATAChannel::ExecutePIOTransfer(ATARequest *request)
result = B_ERROR; result = B_ERROR;
} }
if (result == B_OK) {
result = FinishRequest(request, ATA_WAIT_FINISH
| ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ABORTED);
}
if (result != B_OK)
request->SetStatus(SCSI_SEQUENCE_FAIL);
return result; return result;
} }
@@ -632,6 +666,24 @@ ATAChannel::ReadPIO(uint8 *buffer, size_t length)
} }
void
ATAChannel::Interrupt(uint8 status)
{
SpinLocker locker(fDMATransferLock);
if (!fExpectsDMATransfer) {
TRACE_ERROR("interrupt when not expecting transfer\n");
return;
}
if ((status & ATA_STATUS_BUSY) != 0) {
TRACE_ERROR(("interrupt while device is busy\n"));
return;
}
fDMATransferCondition.NotifyAll();
}
status_t status_t
ATAChannel::_ReadRegs(ata_task_file *taskFile, ata_reg_mask mask) ATAChannel::_ReadRegs(ata_task_file *taskFile, ata_reg_mask mask)
{ {
@@ -17,6 +17,7 @@ ATADevice::ATADevice(ATAChannel *channel, uint8 index)
fUse48Bits(false), fUse48Bits(false),
fUseDMA(channel->UseDMA()), fUseDMA(channel->UseDMA()),
fDMAMode(0), fDMAMode(0),
fDMAFailures(0),
fTotalSectors(0), fTotalSectors(0),
fRegisterMask(0) fRegisterMask(0)
{ {
@@ -513,18 +514,15 @@ status_t
ATADevice::ExecuteReadWrite(ATARequest *request, uint64 address, ATADevice::ExecuteReadWrite(ATARequest *request, uint64 address,
uint32 sectorCount) uint32 sectorCount)
{ {
request->SetUseDMA(fUseDMA && _PrepareDMA(request) == B_OK); request->SetUseDMA(fUseDMA && fChannel->PrepareDMA(request) == B_OK);
if (!request->UseDMA()) { if (!request->UseDMA())
status_t result = _PreparePIO(request); request->PrepareSGInfo();
if (result != B_OK) {
TRACE_ERROR("failed to prepare pio transfer\n");
return result;
}
}
request->SetBlocksLeft(sectorCount); request->SetBlocksLeft(sectorCount);
if (_FillTaskFile(request, address) != B_OK) { if (_FillTaskFile(request, address) != B_OK) {
TRACE_ERROR("failed to setup transfer request\n"); TRACE_ERROR("failed to setup transfer request\n");
if (request->UseDMA())
fChannel->FinishDMA();
return B_ERROR; return B_ERROR;
} }
@@ -537,13 +535,39 @@ ATADevice::ExecuteReadWrite(ATARequest *request, uint64 address,
status_t result = fChannel->SendRequest(request, flags); status_t result = fChannel->SendRequest(request, flags);
if (result != B_OK) { if (result != B_OK) {
TRACE_ERROR("failed to send transfer request\n"); TRACE_ERROR("failed to send transfer request\n");
if (request->UseDMA())
fChannel->FinishDMA();
return result; return result;
} }
if (request->UseDMA()) if (request->UseDMA()) {
return fChannel->ExecuteDMATransfer(request); result = fChannel->ExecuteDMATransfer(request);
status_t dmaResult = fChannel->FinishDMA();
if (result == B_OK && dmaResult == B_OK) {
fDMAFailures = 0;
request->CCB()->data_resid = 0;
} else {
if (dmaResult != B_OK) {
request->SetSense(SCSIS_KEY_HARDWARE_ERROR,
SCSIS_ASC_LUN_COM_FAILURE);
fDMAFailures++;
if (fDMAFailures >= ATA_MAX_DMA_FAILURES) {
TRACE_ALWAYS("disabling DMA after %u failures\n",
fDMAFailures);
fUseDMA = false;
}
} else {
// timeout
request->SetStatus(SCSI_CMD_TIMEOUT);
}
}
} else {
if (fChannel->ExecutePIOTransfer(request) != B_OK)
request->SetStatus(SCSI_SEQUENCE_FAIL);
}
return fChannel->ExecutePIOTransfer(request); return fChannel->FinishRequest(request, ATA_WAIT_FINISH
| ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ABORTED);
} }
@@ -621,18 +645,3 @@ ATADevice::_FillTaskFile(ATARequest *request, uint64 address)
return B_OK; return B_OK;
} }
status_t
ATADevice::_PrepareDMA(ATARequest *request)
{
return B_ERROR;
}
status_t
ATADevice::_PreparePIO(ATARequest *request)
{
request->PrepareSGInfo();
return B_OK;
}
@@ -205,7 +205,13 @@ ata_channel_added(device_node *parent)
status_t status_t
ata_interrupt_handler(void *cookie, uint8 status) ata_interrupt_handler(void *cookie, uint8 status)
{ {
return B_UNHANDLED_INTERRUPT; ATAChannel *channel = (ATAChannel *)cookie;
channel->Interrupt(status);
// the controller driver already checks if its interrupt status indicates
// that the interrupt was for this device, so we are only here if that's
// the case.
return B_HANDLED_INTERRUPT;
} }
@@ -25,6 +25,7 @@
#include "ATATracing.h" #include "ATATracing.h"
#include "ata_device_infoblock.h" #include "ata_device_infoblock.h"
#define ATA_MAX_DMA_FAILURES 3
#define ATA_STANDARD_TIMEOUT 10 * 1000 * 1000 #define ATA_STANDARD_TIMEOUT 10 * 1000 * 1000
#define ATA_RELEASE_TIMEOUT 10 * 1000 * 1000 #define ATA_RELEASE_TIMEOUT 10 * 1000 * 1000
#define ATA_SIGNATURE_ATAPI 0xeb140101 #define ATA_SIGNATURE_ATAPI 0xeb140101
@@ -35,10 +36,10 @@
enum { enum {
ATA_DEVICE_READY_REQUIRED = 0x01, ATA_DEVICE_READY_REQUIRED = 0x01,
ATA_IS_WRITE = 0x02, ATA_IS_WRITE = 0x02,
ATA_DMA_TRANSFER = 0x03, ATA_DMA_TRANSFER = 0x04,
ATA_CHECK_ERROR_BIT = 0x04, ATA_CHECK_ERROR_BIT = 0x08,
ATA_WAIT_FINISH = 0x08, ATA_WAIT_FINISH = 0x10,
ATA_WAIT_ANY_BIT = 0x10 ATA_WAIT_ANY_BIT = 0x20
}; };
@@ -91,11 +92,16 @@ public:
uint32 flags, uint8 errorMask); uint32 flags, uint8 errorMask);
// data transfers // data transfers
status_t PrepareDMA(ATARequest *request);
status_t FinishDMA();
status_t ExecuteDMATransfer(ATARequest *request); status_t ExecuteDMATransfer(ATARequest *request);
status_t ExecutePIOTransfer(ATARequest *request); status_t ExecutePIOTransfer(ATARequest *request);
status_t ReadPIO(uint8 *buffer, size_t length); status_t ReadPIO(uint8 *buffer, size_t length);
void Interrupt(uint8 status);
private: private:
status_t _ReadRegs(ata_task_file *taskFile, status_t _ReadRegs(ata_task_file *taskFile,
ata_reg_mask mask); ata_reg_mask mask);
@@ -125,7 +131,12 @@ private:
uint32 fChannelID; uint32 fChannelID;
ata_controller_interface * fController; ata_controller_interface * fController;
void * fCookie; void * fCookie;
mutex fLock;
mutex fExecutionLock;
spinlock fDMATransferLock;
ConditionVariable fDMATransferCondition;
bool fExpectsDMATransfer;
status_t fStatus; status_t fStatus;
scsi_bus fSCSIBus; scsi_bus fSCSIBus;
uint8 fDeviceCount; uint8 fDeviceCount;
@@ -178,9 +189,6 @@ private:
status_t _FillTaskFile(ATARequest *request, status_t _FillTaskFile(ATARequest *request,
uint64 address); uint64 address);
status_t _PrepareDMA(ATARequest *request);
status_t _PreparePIO(ATARequest *request);
const char * _DebugContext() { return fDebugContext; }; const char * _DebugContext() { return fDebugContext; };
ATAChannel * fChannel; ATAChannel * fChannel;
@@ -189,6 +197,7 @@ private:
bool fUse48Bits; bool fUse48Bits;
bool fUseDMA; bool fUseDMA;
uint8 fDMAMode; uint8 fDMAMode;
uint8 fDMAFailures;
uint64 fTotalSectors; uint64 fTotalSectors;
ata_device_infoblock fInfoBlock; ata_device_infoblock fInfoBlock;
ata_task_file fTaskFile; ata_task_file fTaskFile;