Introduce workaround for the (documented) bug with ATI/AMD SB600 and early SB700

periodic list cache. This workaround is based on similar patches in NetBSD and
Linux and disables the (Advanced) Periodic List Cache on the affected devices
using ATI/AMD specifc registers (as documented in the corresponding AMD
register reference guide).
Also remove a ton of stray whitespace introduced by the isochronous patches.
Please take more care the next time.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41512 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2011-05-15 14:36:19 +00:00
parent 84464c202e
commit 3bb5704280
2 changed files with 81 additions and 31 deletions

View File

@ -141,6 +141,48 @@ EHCI::EHCI(pci_info *info, Stack *stack)
TRACE("constructing new EHCI host controller driver\n");
fInitOK = false;
// ATI/AMD SB600/SB700 periodic list cache workaround
// Logic kindly borrowed from NetBSD PR 40056
if (fPCIInfo->vendor_id == AMD_SBX00_VENDOR) {
bool applyWorkaround = false;
if (fPCIInfo->device_id == AMD_SB600_EHCI_CONTROLLER) {
// always apply on SB600
applyWorkaround = true;
} else if (fPCIInfo->device_id == AMD_SB700_SB800_EHCI_CONTROLLER) {
// only apply on certain chipsets, determined by SMBus revision
pci_info smbus;
int32 index = 0;
while (sPCIModule->get_nth_pci_info(index++, &smbus) >= B_OK) {
if (smbus.vendor_id == AMD_SBX00_VENDOR
&& smbus.device_id == AMD_SBX00_SMBUS_CONTROLLER) {
// Only applies to chipsets < SB710 (rev A14)
if (smbus.revision == 0x3a || smbus.revision == 0x3b)
applyWorkaround = true;
break;
}
}
}
if (applyWorkaround) {
// According to AMD errata of SB700 and SB600 register documentation
// this disables the Periodic List Cache on SB600 and the Advanced
// Periodic List Cache on early SB700. Both the BSDs and Linux use
// this workaround.
TRACE_ALWAYS("disabling SB600/SB700 periodic list cache\n");
uint32 workaround = sPCIModule->read_pci_config(fPCIInfo->bus,
fPCIInfo->device, fPCIInfo->function,
AMD_SBX00_EHCI_MISC_REGISTER, 4);
sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device,
fPCIInfo->function, AMD_SBX00_EHCI_MISC_REGISTER, 4,
workaround | AMD_SBX00_EHCI_MISC_DISABLE_PERIODIC_LIST_CACHE);
}
}
// enable busmaster and memory mapped access
uint16 command = sPCIModule->read_pci_config(fPCIInfo->bus,
fPCIInfo->device, fPCIInfo->function, PCI_command, 2);
@ -285,7 +327,7 @@ EHCI::EHCI(pci_info *info, Stack *stack)
WriteOpReg(EHCI_USBINTR, fEnabledInterrupts);
// structures don't span page boundaries
size_t itdListSize = EHCI_VFRAMELIST_ENTRIES_COUNT
size_t itdListSize = EHCI_VFRAMELIST_ENTRIES_COUNT
/ (B_PAGE_SIZE / sizeof(itd_entry)) * B_PAGE_SIZE;
size_t sitdListSize = EHCI_VFRAMELIST_ENTRIES_COUNT
/ (B_PAGE_SIZE / sizeof(sitd_entry)) * B_PAGE_SIZE;
@ -351,13 +393,13 @@ EHCI::EHCI(pci_info *info, Stack *stack)
sitd->back_phy = EHCI_ITEM_TERMINATE;
fSitdEntries[i] = sitd;
TRACE("sitd entry %ld %p 0x%lx\n", i, sitd, sitd->this_phy);
ehci_itd *itd = &itds[i].itd;
itd->this_phy = itdPhysicalBase | EHCI_ITEM_TYPE_ITD;
itd->next_phy = sitd->this_phy;
fItdEntries[i] = itd;
TRACE("itd entry %ld %p 0x%lx\n", i, itd, itd->this_phy);
sitdPhysicalBase += sizeof(sitd_entry);
itdPhysicalBase += sizeof(itd_entry);
if ((sitdPhysicalBase & 0x10) != 0 || (itdPhysicalBase & 0x10) != 0)
@ -399,7 +441,7 @@ EHCI::EHCI(pci_info *info, Stack *stack)
fItdEntries[i & (EHCI_VFRAMELIST_ENTRIES_COUNT - 1)]->this_phy;
TRACE("periodic entry %ld linked to 0x%lx\n", i, fPeriodicFrameList[i]);
}
// Create the array that will keep bandwidth information
fFrameBandwidth = new(std::nothrow) uint16[EHCI_VFRAMELIST_ENTRIES_COUNT];
for (int32 i = 0; i < EHCI_VFRAMELIST_ENTRIES_COUNT; i++) {
@ -642,7 +684,7 @@ EHCI::SubmitIsochronous(Transfer *transfer)
// Find the entry where to start inserting the first Isochronous descriptor
if (isochronousData->flags & USB_ISO_ASAP ||
isochronousData->starting_frame_number == NULL) {
uint32 threshold = (ReadCapReg32(EHCI_HCCPARAMS)
>> EHCI_HCCPARAMS_IPT_SHIFT) & EHCI_HCCPARAMS_IPT_MASK;
TRACE("threshold: %ld\n", threshold);
@ -677,7 +719,7 @@ EHCI::SubmitIsochronous(Transfer *transfer)
TRACE_ERROR("unable to allocate itd buffer\n");
return B_NO_MEMORY;
}
memset(bufferLog, 0, dataLength);
addr_t currentPhy = bufferPhy;
@ -713,16 +755,16 @@ EHCI::SubmitIsochronous(Transfer *transfer)
itd->buffer_phy[1] |= (pipe->MaxPacketSize() & EHCI_ITD_MAXPACKETSIZE_MASK)
| (directionIn << EHCI_ITD_DIR_SHIFT);
itd->buffer_phy[2] |= (1 << EHCI_ITD_MUL_SHIFT);
TRACE("isochronous filled itd buffer_phy[0,1,2] 0x%lx, 0x%lx 0x%lx\n",
itd->buffer_phy[0], itd->buffer_phy[1], itd->buffer_phy[2]);
itd->buffer_phy[0], itd->buffer_phy[1], itd->buffer_phy[2]);
LinkITDescriptors(itd, &fItdEntries[currentFrame]);
fFrameBandwidth[currentFrame] -= bandwidth;
currentFrame = (currentFrame + 1) & (EHCI_VFRAMELIST_ENTRIES_COUNT - 1);
frameCount++;
}
TRACE("isochronous filled itds count %d\n", itdIndex);
// Add transfer to the list
@ -1190,7 +1232,7 @@ EHCI::AddPendingIsochronousTransfer(Transfer *transfer, ehci_itd **isoRequest,
data->buffer_phy = bufferPhy;
data->buffer_log = bufferLog;
data->buffer_size = bufferSize;
// Put in the isochronous transfer list
if (!LockIsochronous()) {
delete data;
@ -1561,7 +1603,7 @@ EHCI::FinishIsochronousTransfers()
return;
bool transferDone = false;
uint32 frame = (ReadOpReg(EHCI_FRINDEX) / 8 )
& (EHCI_FRAMELIST_ENTRIES_COUNT - 1);
uint32 currentFrame = (frame + EHCI_VFRAMELIST_ENTRIES_COUNT - 5)
@ -1578,7 +1620,7 @@ EHCI::FinishIsochronousTransfers()
}
ehci_itd *itd = fItdEntries[currentFrame];
TRACE("FinishIsochronousTransfers itd %p phy 0x%lx prev (%p/0x%lx)"
" at frame %ld\n", itd, itd->this_phy, itd->prev,
itd->prev != NULL ? itd->prev->this_phy : 0, currentFrame);
@ -1596,7 +1638,7 @@ EHCI::FinishIsochronousTransfers()
TRACE("FinishIsochronousTransfers unprocessed active itd\n");
}
UnlinkITDescriptors(itd, &fItdEntries[currentFrame]);
// Process the transfer if we found the last descriptor
isochronous_transfer_data *transfer
= FindIsochronousTransfer(itd);
@ -1639,7 +1681,7 @@ EHCI::FinishIsochronousTransfers()
FreeDescriptor(transfer->descriptors[i]);
TRACE("FinishIsochronousTransfers descriptors freed\n");
delete [] transfer->descriptors;
delete transfer->transfer;
fStack->FreeChunk(transfer->buffer_log,
@ -1651,13 +1693,12 @@ EHCI::FinishIsochronousTransfers()
}
itd = itd->prev;
}
TRACE("FinishIsochronousTransfers next frame\n");
// Make sure to reset the frame bandwidth
fFrameBandwidth[currentFrame] = MAX_AVAILABLE_BANDWIDTH;
currentFrame = (currentFrame + 1) % EHCI_VFRAMELIST_ENTRIES_COUNT;
}
}
}
@ -2070,7 +2111,7 @@ EHCI::CreateItdDescriptor()
TRACE_ERROR("failed to allocate a itd\n");
return NULL;
}
memset(result, 0, sizeof(ehci_itd));
result->this_phy = (addr_t)physicalAddress;
result->next_phy = EHCI_ITEM_TERMINATE;
@ -2093,7 +2134,7 @@ EHCI::CreateSitdDescriptor()
memset(result, 0, sizeof(ehci_sitd));
result->this_phy = (addr_t)physicalAddress | EHCI_ITEM_TYPE_SITD;
result->next_phy = EHCI_ITEM_TERMINATE;
return result;
}
@ -2178,7 +2219,7 @@ EHCI::UnlinkSITDescriptors(ehci_sitd *sitd, ehci_sitd **last)
sitd->prev->next = sitd->next;
if (sitd->next != NULL)
sitd->next->prev = sitd->prev;
if (sitd == *last)
if (sitd == *last)
*last = sitd->prev;
}
@ -2333,7 +2374,7 @@ EHCI::ReadIsochronousDescriptorChain(isochronous_transfer_data *transfer)
{
iovec *vector = transfer->transfer->Vector();
size_t vectorCount = transfer->transfer->VectorCount();
size_t vectorOffset = 0;
size_t vectorIndex = 0;
usb_isochronous_data *isochronousData
@ -2341,7 +2382,7 @@ EHCI::ReadIsochronousDescriptorChain(isochronous_transfer_data *transfer)
uint32 packet = 0;
size_t totalLength = 0;
size_t bufferOffset = 0;
size_t packetSize = transfer->transfer->DataLength();
packetSize /= isochronousData->packet_count;
@ -2349,19 +2390,19 @@ EHCI::ReadIsochronousDescriptorChain(isochronous_transfer_data *transfer)
ehci_itd *itd = transfer->descriptors[i];
for (uint32 j = 0; j <= itd->last_token
&& packet < isochronousData->packet_count; j++) {
size_t bufferSize = (itd->token[j] >> EHCI_ITD_TLENGTH_SHIFT)
& EHCI_ITD_TLENGTH_MASK;
isochronousData->packet_descriptors[packet].actual_length =
bufferSize;
if (bufferSize > 0)
isochronousData->packet_descriptors[packet].status = B_OK;
else
isochronousData->packet_descriptors[packet].status = B_ERROR;
totalLength += bufferSize;
size_t offset = bufferOffset;
while (bufferSize > 0) {
size_t length = min_c(bufferSize,
@ -2371,7 +2412,7 @@ EHCI::ReadIsochronousDescriptorChain(isochronous_transfer_data *transfer)
offset += length;
vectorOffset += length;
bufferSize -= length;
if (vectorOffset >= vector[vectorIndex].iov_len) {
if (++vectorIndex >= vectorCount) {
TRACE("read isodescriptor chain (%ld bytes, no more "
@ -2382,17 +2423,17 @@ EHCI::ReadIsochronousDescriptorChain(isochronous_transfer_data *transfer)
vectorOffset = 0;
}
}
bufferOffset += packetSize;
bufferOffset += packetSize;
if (bufferOffset >= transfer->buffer_size)
return totalLength;
packet++;
}
}
TRACE("ReadIsochronousDescriptorChain packet count %ld\n", packet);
return totalLength;
}

View File

@ -335,4 +335,13 @@ typedef struct {
// ToDo: Periodic Frame Span Traversal Node (FSTN, EHCI Spec 3.7)
// Quirk registers and values
#define AMD_SBX00_VENDOR 0x1002
#define AMD_SBX00_SMBUS_CONTROLLER 0x4385
#define AMD_SB600_EHCI_CONTROLLER 0x4386
#define AMD_SB700_SB800_EHCI_CONTROLLER 0x4396
#define AMD_SBX00_EHCI_MISC_REGISTER 0x50 // Advanced config register
#define AMD_SBX00_EHCI_MISC_DISABLE_PERIODIC_LIST_CACHE (1 << 27)
#endif // !EHCI_HARDWARE_H