AppleNVIDIAnForceATA.cpp [plain text]
#include <IOKit/pci/IOPCIDevice.h>
#include "AppleNVIDIAnForceATA.h"
#define kPIOModeMask ((1 << kPIOModeCount) - 1)
#define kDMAModeMask ((1 << kDMAModeCount) - 1)
#define DRIVE_IS_PRESENT(u) \
(_devInfo[u].type != kUnknownATADeviceType)
#define TIMING_PARAM_IS_VALID(p) \
((p) != 0)
#define CLASS AppleNVIDIAnForceATA
#define super AppleOnboardPCATA
OSDefineMetaClassAndStructors( AppleNVIDIAnForceATA, AppleOnboardPCATA )
static const HardwareInfo * getHardwareInfo( UInt32 pciID )
{
const HardwareInfo * info = 0;
static const HardwareInfo hardwareTable[] =
{
{ 0x01bc10de, 5, "NVIDIA nForce" },
{ 0x006510de, 6, "NVIDIA nForce2" },
{ 0x00d510de, 6, "NVIDIA nForce3" },
};
for (UInt i = 0; i < sizeof(hardwareTable)/sizeof(hardwareTable[0]); i++)
{
if (hardwareTable[i].pciDeviceID == pciID)
{
info = &hardwareTable[i];
break;
}
}
return info;
}
bool CLASS::start( IOService * provider )
{
bool superStarted = false;
UInt8 cableDetect;
UInt32 udmaTiming;
DEBUG_LOG("%s: %s( %p, %p )\n", getName(), __FUNCTION__, this, provider);
if (openATAChannel(provider) == false)
goto fail;
fWorkLoop = IOWorkLoop::workLoop();
if (fWorkLoop == 0)
{
DEBUG_LOG("%s: new work loop failed\n", getName());
goto fail;
}
if (fChannelNumber > kSecondaryChannelID)
{
DEBUG_LOG("%s: bad ATA channel number %lu\n", getName(),
fChannelNumber);
goto fail;
}
fHWInfo = getHardwareInfo(
fChannelNub->pciConfigRead32(kIOPCIConfigVendorID));
if (!fHWInfo)
{
DEBUG_LOG("%s: unsupported hardware\n", getName());
goto fail;
}
fUltraModeMask = (1 << (fHWInfo->maxUltraMode + 1)) - 1;
cableDetect = fChannelNub->pciConfigRead8(PCI_CABLE_DETECT);
udmaTiming = fChannelNub->pciConfigRead32(PCI_ULTRA_TIMING);
if (cableDetect & (0x3 << (fChannelNumber * 2)))
f80PinCablePresent = true;
if (udmaTiming & (0x0404 << ((1 - fChannelNumber) * 16)))
f80PinCablePresent = true;
if (!getBMBaseAddress(&fBMBaseAddr))
{
DEBUG_LOG("%s: get bus-master base address failed\n", getName());
goto fail;
}
_bmCommandReg = IOATAIOReg8::withAddress( fBMBaseAddr + BM_COMMAND );
_bmStatusReg = IOATAIOReg8::withAddress( fBMBaseAddr + BM_STATUS );
_bmPRDAddresReg = IOATAIOReg32::withAddress( fBMBaseAddr + BM_PRD_PTR );
dumpHardwareRegisters();
initializeHardware();
resetBusTimings();
if (super::start(provider) == false)
{
goto fail;
}
superStarted = true;
if ( fChannelNub->getInterruptVector() == 14 ||
fChannelNub->getInterruptVector() == 15 )
{
fInterruptSource = IOInterruptEventSource::interruptEventSource(
this, &interruptOccurred,
fChannelNub, 0 );
}
else
{
fInterruptSource = IOFilterInterruptEventSource::filterInterruptEventSource(
this, &interruptOccurred, &interruptFilter,
fChannelNub, 0 );
}
if (!fInterruptSource ||
(fWorkLoop->addEventSource(fInterruptSource) != kIOReturnSuccess))
{
DEBUG_LOG("%s: interrupt event source error\n", getName());
goto fail;
}
fInterruptSource->enable();
attachATADeviceNubs();
IOLog("%s: %s (CMD 0x%lx, CTR 0x%lx, IRQ %ld, BM 0x%x)\n",
getName(),
fHWInfo->deviceName,
fChannelNub->getCommandBlockAddress(),
fChannelNub->getControlBlockAddress(),
fChannelNub->getInterruptVector(),
fBMBaseAddr);
return true;
fail:
if (fChannelNub)
closeATAChannel();
if (superStarted)
super::stop( provider );
return false;
}
void CLASS::free( void )
{
DEBUG_LOG("%s::%s( %p )\n", getName(), __FUNCTION__, this);
if (fInterruptSource && fWorkLoop)
{
fWorkLoop->removeEventSource(fInterruptSource);
}
RELEASE( fInterruptSource );
RELEASE( fWorkLoop );
super::free();
}
IOWorkLoop * CLASS::getWorkLoop( void ) const
{
return fWorkLoop;
}
bool CLASS::getBMBaseAddress( UInt16 * baseAddr )
{
UInt32 bmiba;
DEBUG_LOG("[CH%lu] %s\n", fChannelNumber, __FUNCTION__);
bmiba = fChannelNub->pciConfigRead32( kIOPCIConfigBaseAddress4 );
if ((bmiba & 0x01) == 0)
{
DEBUG_LOG(" PCI BAR 0x20 (0x%08lx) is not an I/O range\n", bmiba);
return false;
}
bmiba &= BM_ADDR_MASK; if (bmiba == 0)
{
DEBUG_LOG(" BMIBA is zero\n");
return false;
}
if (fChannelNumber == kSecondaryChannelID)
bmiba += BM_SEC_OFFSET;
*baseAddr = (UInt16) bmiba;
DEBUG_LOG(" BMBaseAddr = %04x\n", *baseAddr);
return true;
}
void CLASS::resetBusTimings( void )
{
DEBUG_LOG("[CH%lu] %s\n", fChannelNumber, __FUNCTION__);
memset(&fBusTimings[0], 0, sizeof(fBusTimings));
fBusTimings[0].pioTiming = &PIOTimingTable[0];
fBusTimings[1].pioTiming = &PIOTimingTable[0];
programTimingRegisters();
}
bool CLASS::interruptFilter( OSObject * owner,
IOFilterInterruptEventSource * src )
{
CLASS * driver = (CLASS *) owner;
if (*(driver->_bmStatusReg) & BM_STATUS_INT)
return true; else
return false; }
void CLASS::interruptOccurred( OSObject * owner,
IOInterruptEventSource * source,
int count )
{
CLASS * me = (CLASS *) owner;
*(me->_bmStatusReg) = BM_STATUS_INT;
me->handleDeviceInterrupt();
}
IOReturn CLASS::provideBusInfo( IOATABusInfo * infoOut )
{
DEBUG_LOG("[CH%lu] %s( %p )\n", fChannelNumber, __FUNCTION__, infoOut);
if (infoOut == 0)
{
return -1;
}
infoOut->zeroData();
infoOut->setSocketType( kInternalATASocket );
infoOut->setPIOModes( kPIOModeMask );
infoOut->setDMAModes( kDMAModeMask );
infoOut->setUltraModes( fUltraModeMask );
infoOut->setExtendedLBA( true );
infoOut->setMaxBlocksExtended( 0x0800 );
UInt8 units = 0;
if ( _devInfo[0].type != kUnknownATADeviceType ) units++;
if ( _devInfo[1].type != kUnknownATADeviceType ) units++;
infoOut->setUnits( units );
return kATANoErr;
}
IOReturn CLASS::getConfig( IOATADevConfig * configOut,
UInt32 unit )
{
DEBUG_LOG("[CH%lu D%lu] %s( %p )\n", fChannelNumber, unit, __FUNCTION__,
configOut);
if ((configOut == 0) || (unit > kATADevice1DeviceID))
{
return -1;
}
configOut->setPIOMode( 0 );
configOut->setDMAMode( 0 );
configOut->setUltraMode( 0 );
if (TIMING_PARAM_IS_VALID(fBusTimings[unit].pioTiming))
{
configOut->setPIOMode( 1 << fBusTimings[unit].pioModeNumber );
configOut->setPIOCycleTime( fBusTimings[unit].pioTiming->cycleTime );
DEBUG_LOG(" PIO mode %lu @ %u ns\n",
fBusTimings[unit].pioModeNumber,
fBusTimings[unit].pioTiming->cycleTime);
}
if (TIMING_PARAM_IS_VALID(fBusTimings[unit].dmaTiming))
{
configOut->setDMAMode( 1 << fBusTimings[unit].dmaModeNumber );
configOut->setDMACycleTime( fBusTimings[unit].dmaTiming->cycleTime );
DEBUG_LOG(" DMA mode %lu @ %u ns\n",
fBusTimings[unit].dmaModeNumber,
fBusTimings[unit].dmaTiming->cycleTime);
}
if (fBusTimings[unit].ultraEnabled)
{
configOut->setUltraMode( 1 << fBusTimings[unit].ultraModeNumber );
DEBUG_LOG(" Ultra mode %lu\n", fBusTimings[unit].ultraModeNumber);
}
configOut->setPacketConfig( _devInfo[unit].packetSend );
return kATANoErr;
}
IOReturn CLASS::selectConfig( IOATADevConfig * configRequest,
UInt32 unit )
{
DEBUG_LOG("[CH%lu D%lu] %s( %p )\n", fChannelNumber, unit, __FUNCTION__,
configRequest);
if ((configRequest == 0) || (unit > kATADevice1DeviceID))
{
return -1;
}
if ((configRequest->getPIOMode() & kPIOModeMask) == 0)
{
DEBUG_LOG(" missing PIO mode\n");
return kATAModeNotSupported;
}
if (configRequest->getDMAMode() & ~kDMAModeMask)
{
DEBUG_LOG(" DMA mode not supported\n");
return kATAModeNotSupported;
}
if (configRequest->getUltraMode() & ~fUltraModeMask)
{
DEBUG_LOG(" Ultra DMA mode not supported\n");
return kATAModeNotSupported;
}
if (configRequest->getDMAMode() && configRequest->getUltraMode())
{
DEBUG_LOG(" multiple DMA mode selection error\n");
return kATAModeNotSupported;
}
_devInfo[unit].packetSend = configRequest->getPacketConfig();
selectTimingParameter( configRequest, unit );
return getConfig( configRequest, unit );
}
void CLASS::selectTimingParameter( IOATADevConfig * configRequest,
UInt32 unit )
{
DEBUG_LOG("[CH%lu D%lu] %s( %p )\n", fChannelNumber, unit, __FUNCTION__,
configRequest);
fBusTimings[unit].pioTiming = &PIOTimingTable[0];
fBusTimings[unit].dmaTiming = 0;
fBusTimings[unit].ultraEnabled = false;
if (configRequest->getPIOMode())
{
UInt32 pioModeNumber;
UInt32 pioCycleTime;
UInt32 pioTimingEntry = 0;
pioModeNumber = bitSigToNumeric( configRequest->getPIOMode() );
pioModeNumber = min(pioModeNumber, kPIOModeCount - 1);
pioCycleTime = configRequest->getPIOCycleTime();
pioCycleTime = max(pioCycleTime,
PIOTimingTable[pioModeNumber].cycleTime);
for (int i = kPIOModeCount - 1; i > 0; i--)
{
if (PIOTimingTable[i].cycleTime >= pioCycleTime)
{
pioTimingEntry = i;
break;
}
}
fBusTimings[unit].pioTiming = &PIOTimingTable[pioTimingEntry];
fBusTimings[unit].pioModeNumber = pioModeNumber;
DEBUG_LOG(" selected PIO timing entry %d\n", pioTimingEntry);
publishDriveProperty(unit, kSelectedPIOModeKey, pioModeNumber);
}
if (configRequest->getDMAMode())
{
UInt32 dmaModeNumber;
UInt32 dmaCycleTime;
UInt32 dmaTimingEntry = 0;
dmaModeNumber = bitSigToNumeric(configRequest->getDMAMode());
dmaModeNumber = min(dmaModeNumber, kDMAModeCount - 1);
dmaCycleTime = configRequest->getDMACycleTime();
dmaCycleTime = max(dmaCycleTime,
DMATimingTable[dmaModeNumber].cycleTime);
for (int i = kDMAModeCount - 1; i > 0; i--)
{
if (DMATimingTable[i].cycleTime >= dmaCycleTime)
{
dmaTimingEntry = i;
break;
}
}
fBusTimings[unit].dmaTiming = &DMATimingTable[dmaTimingEntry];
fBusTimings[unit].dmaModeNumber = dmaModeNumber;
DEBUG_LOG(" selected DMA timing entry %d\n", dmaTimingEntry);
publishDriveProperty(unit, kSelectedDMAModeKey, dmaModeNumber);
}
if (configRequest->getUltraMode())
{
UInt32 ultraModeNumber;
ultraModeNumber = bitSigToNumeric(configRequest->getUltraMode());
ultraModeNumber = min(ultraModeNumber, fHWInfo->maxUltraMode);
if (ultraModeNumber > 2)
{
if ( f80PinCablePresent == false )
{
ERROR_LOG("%s: 80-conductor cable not detected on channel %u\n",
getName(), fChannelNumber);
ultraModeNumber = 2;
}
}
fBusTimings[unit].ultraEnabled = true;
fBusTimings[unit].ultraModeNumber = ultraModeNumber;
DEBUG_LOG(" selected Ultra mode %d\n", ultraModeNumber);
publishDriveProperty(unit, kSelectedUltraDMAModeKey, ultraModeNumber);
}
programTimingRegisters();
}
static void mergeTimings( TimingParameter * dst,
const TimingParameter * src )
{
if (TIMING_PARAM_IS_VALID(dst) == false ||
TIMING_PARAM_IS_VALID(src) == false)
return;
dst->cycleTime = max(dst->cycleTime, src->cycleTime);
dst->setupTime = max(dst->setupTime, src->setupTime);
dst->activeTime = max(dst->activeTime, src->activeTime);
dst->recoveryTime = max(dst->recoveryTime, src->recoveryTime);
}
void CLASS::programTimingRegisters( void )
{
TimingParameter timingCommand; TimingParameter timingData[2];
memset(&timingCommand, 0, sizeof(timingCommand));
memset(&timingData[0], 0, sizeof(timingData));
for (int unit = 0; unit < 2; unit++)
{
if (DRIVE_IS_PRESENT(unit) == false)
continue;
mergeTimings( &timingCommand, fBusTimings[unit].pioTiming );
mergeTimings( &timingData[unit], fBusTimings[unit].pioTiming );
mergeTimings( &timingData[unit], fBusTimings[unit].dmaTiming );
}
for (int unit = 0; unit < 2; unit++)
{
if (DRIVE_IS_PRESENT(unit) == false)
continue;
writeTimingIntervalNS( kTimingRegCommandActive,
unit, timingCommand.activeTime );
writeTimingIntervalNS( kTimingRegCommandRecovery,
unit, timingCommand.recoveryTime );
writeTimingIntervalNS( kTimingRegAddressSetup,
unit, timingData[unit].setupTime );
writeTimingIntervalNS( kTimingRegDataActive,
unit, timingData[unit].activeTime );
writeTimingIntervalNS( kTimingRegDataRecovery,
unit, timingData[unit].recoveryTime );
if (fBusTimings[unit].ultraEnabled)
{
UInt8 mode = fBusTimings[unit].ultraModeNumber;
writeTimingRegister( kTimingRegUltra, unit,
UltraTimingTable[mode]);
}
else
{
writeTimingRegister( kTimingRegUltra, unit, 0x8b );
}
}
dumpHardwareRegisters();
}
void CLASS::writeTimingIntervalNS( TimingReg reg, UInt32 drive, UInt32 timeNS )
{
const UInt32 clockPeriodPS = 30000; UInt32 clocks = ((timeNS * 1000) + clockPeriodPS - 1) / clockPeriodPS;
UInt32 shifts = TimingRegInfo[reg].bitnum;
if (reg == kTimingRegAddressSetup)
shifts -= ((drive << 1) + (fChannelNumber << 2));
clocks = min(clocks, TimingRegInfo[reg].maxValue);
clocks = max(clocks, TimingRegInfo[reg].minValue);
clocks -= TimingRegInfo[reg].minValue;
clocks &= TimingRegInfo[reg].bitmask;
clocks <<= shifts;
fChannelNub->pciConfigWrite8(TimingRegOffset[reg][fChannelNumber][drive],
clocks, TimingRegInfo[reg].bitmask << shifts);
DEBUG_LOG("%s: CH%d DRV%d wrote 0x%02x to offset 0x%02x\n",
getName(), fChannelNumber, drive, clocks,
TimingRegOffset[reg][fChannelNumber][drive]);
}
void CLASS::writeTimingRegister( TimingReg reg, UInt32 drive, UInt8 value )
{
fChannelNub->pciConfigWrite8(TimingRegOffset[reg][fChannelNumber][drive],
value, 0xFF);
DEBUG_LOG("%s: CH%d DRV%d wrote 0x%02x to offset 0x%02x\n",
getName(), fChannelNumber, drive, value,
TimingRegOffset[reg][fChannelNumber][drive]);
}
UInt32 CLASS::readTimingIntervalNS( TimingReg reg, UInt32 drive )
{
UInt32 timeNS;
UInt32 shifts = TimingRegInfo[reg].bitnum;
if (reg == kTimingRegAddressSetup)
shifts -= ((drive << 1) + (fChannelNumber << 2));
timeNS = readTimingRegister( reg, drive );
timeNS >>= shifts;
timeNS &= TimingRegInfo[reg].bitmask;
timeNS += TimingRegInfo[reg].minValue;
timeNS *= 30;
return timeNS;
}
UInt8 CLASS::readTimingRegister( TimingReg reg, UInt32 drive )
{
return fChannelNub->pciConfigRead8(
TimingRegOffset[reg][fChannelNumber][drive]);
}
void CLASS::initializeHardware( void )
{
fChannelNub->pciConfigWrite8( PCI_IDE_CONFIG, 0xF0, 0xF0 );
}
void CLASS::restoreHardwareState( void )
{
initializeHardware();
programTimingRegisters();
}
void CLASS::dumpHardwareRegisters( void )
{
DEBUG_LOG("PCI_IDE_ENABLE 0x%02x\n",
fChannelNub->pciConfigRead8(PCI_IDE_ENABLE));
DEBUG_LOG("PCI_IDE_CONFIG 0x%02x\n",
fChannelNub->pciConfigRead8(PCI_IDE_CONFIG));
DEBUG_LOG("PCI_CABLE_DETECT 0x%02x\n",
fChannelNub->pciConfigRead8(PCI_CABLE_DETECT));
DEBUG_LOG("PCI_FIFO_CONFIG 0x%02x\n",
fChannelNub->pciConfigRead8(PCI_FIFO_CONFIG));
for (int unit = 0; unit < kMaxDriveCount; unit++)
{
if (DRIVE_IS_PRESENT(unit) == false) continue;
DEBUG_LOG("[ Ch%ld Drive%ld ]\n",
fChannelNumber, unit);
DEBUG_LOG("Command Active %ld ns\n",
readTimingIntervalNS(kTimingRegCommandActive, unit));
DEBUG_LOG("Command Recovery %ld ns\n",
readTimingIntervalNS(kTimingRegCommandRecovery, unit));
DEBUG_LOG("Address Setup %ld ns\n",
readTimingIntervalNS(kTimingRegAddressSetup, unit));
DEBUG_LOG("Data Active %ld ns\n",
readTimingIntervalNS(kTimingRegDataActive, unit));
DEBUG_LOG("Data Recovery %ld ns\n",
readTimingIntervalNS(kTimingRegDataRecovery, unit));
if (fBusTimings[unit].ultraEnabled)
{
DEBUG_LOG("UDMA Timing 0x%02x\n",
readTimingRegister(kTimingRegUltra, unit));
}
}
}