AppleIntelPIIXPATA.cpp [plain text]
#include <sys/systm.h> // snprintf
#include <IOKit/assert.h>
#include <IOKit/IOMessage.h>
#include "AppleIntelPIIXPATA.h"
#define super IOPCIATA
OSDefineMetaClassAndStructors( AppleIntelPIIXPATA, IOPCIATA )
#ifdef PIIX_DEBUG
#define DLOG(fmt, args...) kprintf(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#define PIOModes \
(_provider->getPIOModeMask() & ((1 << piixPIOTimingCount) - 1))
#define DMAModes \
(_provider->getDMAModeMask() & ((1 << piixDMATimingCount) - 1))
#define UDMAModes \
(_provider->getUltraDMAModeMask() & ((1 << piixUDMATimingCount) - 1))
bool AppleIntelPIIXPATA::start( IOService * provider )
{
DLOG("%s::%s( %p )\n", getName(), __FUNCTION__, provider);
_provider = OSDynamicCast( AppleIntelPIIXATAChannel, provider );
if ( _provider == 0 )
goto fail;
_provider->retain();
if ( ( _provider->open( this, 0, &_pciDevice ) != true ) ||
( _pciDevice == 0 ) )
{
IOLog("%s: provider open failed\n", getName());
goto fail;
}
_cmdBlock = _provider->getCommandBlockAddress();
_ctrBlock = _provider->getControlBlockAddress();
_channel = _provider->getChannelNumber();
if ( _channel > kPIIX_CHANNEL_SECONDARY )
{
IOLog("%s: invalid ATA channel number %ld\n", getName(), _channel);
goto fail;
}
if ( configurePCIDevice( _pciDevice, _channel ) != true )
{
goto fail;
}
if ( getBMBaseAddress( _pciDevice, _channel, &_ioBMOffset ) != true )
{
IOLog("%s: get bus-master base address failed\n", getName());
goto fail;
}
_bmCommandReg = IOATAIOReg8::withAddress( _ioBMOffset + kPIIX_IO_BMICX );
_bmStatusReg = IOATAIOReg8::withAddress( _ioBMOffset + kPIIX_IO_BMISX );
_bmPRDAddresReg = IOATAIOReg32::withAddress( _ioBMOffset + kPIIX_IO_BMIDTPX );
resetTimingsForDevice( kATADevice0DeviceID );
resetTimingsForDevice( kATADevice1DeviceID );
if ( super::start(_provider) == false )
{
goto fail;
}
if ( _provider->getInterruptVector() == 14 ||
_provider->getInterruptVector() == 15 )
{
_intSrc = IOInterruptEventSource::interruptEventSource(
this, &interruptOccurred,
_provider, 0 );
}
else
{
_intSrc = IOFilterInterruptEventSource::filterInterruptEventSource(
this, &interruptOccurred, &interruptFilter,
_provider, 0 );
}
if ( !_intSrc || !_workLoop ||
(_workLoop->addEventSource(_intSrc) != kIOReturnSuccess) )
{
IOLog("%s: interrupt registration error\n", getName());
goto fail;
}
_intSrc->enable();
for ( UInt32 i = 0; i < kMaxDrives; i++ )
{
if ( _devInfo[i].type != kUnknownATADeviceType )
{
ATADeviceNub * nub;
nub = ATADeviceNub::ataDeviceNub( (IOATAController*) this,
(ataUnitID) i,
_devInfo[i].type );
if ( nub )
{
if ( nub->attach( this ) )
{
_nub[i] = (IOATADevice *) nub;
_nub[i]->retain();
_nub[i]->registerService();
}
nub->release();
}
}
}
IOLog("%s: %s (CMD 0x%x, CTR 0x%x, IRQ %d, BM 0x%x)\n", getName(),
_provider->getControllerName(), _cmdBlock, _ctrBlock,
_provider->getInterruptVector(), _ioBMOffset);
return true;
fail:
if ( _provider )
{
_provider->close( this );
}
return false;
}
void AppleIntelPIIXPATA::free( void )
{
#define RELEASE(x) do { if(x) { (x)->release(); (x) = 0; } } while(0)
DLOG("%s::%s()\n", getName(), __FUNCTION__);
RELEASE( _intSrc );
RELEASE( _nub[0] );
RELEASE( _nub[1] );
RELEASE( _provider );
RELEASE( _bmCommandReg );
RELEASE( _bmStatusReg );
RELEASE( _bmPRDAddresReg );
RELEASE( _tfDataReg );
RELEASE( _tfFeatureReg );
RELEASE( _tfSCountReg );
RELEASE( _tfSectorNReg );
RELEASE( _tfCylLoReg );
RELEASE( _tfCylHiReg );
RELEASE( _tfSDHReg );
RELEASE( _tfStatusCmdReg );
RELEASE( _tfAltSDevCReg );
if ( _doubleBuffer.logicalBuffer )
{
IOFree( (void *) _doubleBuffer.logicalBuffer,
_doubleBuffer.bufferSize );
_doubleBuffer.bufferSize = 0;
_doubleBuffer.logicalBuffer = 0;
_doubleBuffer.physicalBuffer = 0;
}
super::free();
}
IOWorkLoop * AppleIntelPIIXPATA::getWorkLoop( void ) const
{
DLOG("%s::%s()\n", getName(), __FUNCTION__);
return ( _workLoop ) ? _workLoop :
IOWorkLoop::workLoop();
}
IOReturn AppleIntelPIIXPATA::synchronousIO( void )
{
IOReturn ret;
if (_intSrc) _intSrc->disable();
ret = super::synchronousIO();
if (_intSrc) _intSrc->enable();
return ret;
}
bool AppleIntelPIIXPATA::configurePCIDevice( IOPCIDevice * device,
UInt32 channel )
{
UInt32 reg;
DLOG("%s::%s( %p, %ld )\n", getName(), __FUNCTION__,
device, channel);
reg = device->configRead32( kPIIX_PCI_IDETIM );
if ( channel == kPIIX_CHANNEL_SECONDARY )
reg >>= 16;
if ( (reg & kPIIX_PCI_IDETIM_IDE) == 0 )
{
IOLog("%s: %s PCI IDE channel is disabled\n", getName(),
(channel == kPIIX_CHANNEL_PRIMARY) ? "Primary" : "Secondary");
return false;
}
device->setBusMasterEnable( true );
_ideConfig = device->configRead16( kPIIX_PCI_IDECONFIG );
DLOG("%s: IDE_CONFIG = %04x\n", getName(), _ideConfig);
return true;
}
bool AppleIntelPIIXPATA::getBMBaseAddress( IOPCIDevice * provider,
UInt32 channel,
UInt16 * addrOut )
{
UInt32 bmiba;
DLOG("%s::%s( %p, %ld, %p )\n", getName(), __FUNCTION__,
provider, channel, addrOut);
bmiba = provider->configRead32( kPIIX_PCI_BMIBA );
if ( (bmiba & kPIIX_PCI_BMIBA_RTE) == 0 )
{
IOLog("%s: PCI memory range 0x%02x (0x%08lx) is not an I/O range\n",
getName(), kPIIX_PCI_BMIBA, bmiba);
return false;
}
bmiba &= kPIIX_PCI_BMIBA_MASK;
if ( bmiba == 0 )
return false;
if ( channel == kPIIX_CHANNEL_SECONDARY )
bmiba += kPIIX_IO_BM_OFFSET;
*addrOut = (UInt16) bmiba;
DLOG("%s::%s ioBMOffset = %04x\n", getName(), __FUNCTION__, *addrOut);
return true;
}
void AppleIntelPIIXPATA::resetTimingsForDevice( ataUnitID unit )
{
_pioTiming[unit] = &piixPIOTiming[ 0 ]; _dmaTiming[unit] = 0;
_udmaTiming[unit] = 0;
computeTimingRegisters( unit );
computeUDMATimingRegisters( unit );
writeTimingRegisters();
}
bool AppleIntelPIIXPATA::configureTFPointers( void )
{
DLOG("%s::%s()\n", getName(), __FUNCTION__);
_tfDataReg = IOATAIOReg16::withAddress( _cmdBlock + 0 );
_tfFeatureReg = IOATAIOReg8::withAddress( _cmdBlock + 1 );
_tfSCountReg = IOATAIOReg8::withAddress( _cmdBlock + 2 );
_tfSectorNReg = IOATAIOReg8::withAddress( _cmdBlock + 3 );
_tfCylLoReg = IOATAIOReg8::withAddress( _cmdBlock + 4 );
_tfCylHiReg = IOATAIOReg8::withAddress( _cmdBlock + 5 );
_tfSDHReg = IOATAIOReg8::withAddress( _cmdBlock + 6 );
_tfStatusCmdReg = IOATAIOReg8::withAddress( _cmdBlock + 7 );
_tfAltSDevCReg = IOATAIOReg8::withAddress( _ctrBlock + 2 );
if ( !_tfDataReg || !_tfFeatureReg || !_tfSCountReg ||
!_tfSectorNReg || !_tfCylLoReg || !_tfCylHiReg ||
!_tfSDHReg || !_tfStatusCmdReg || !_tfAltSDevCReg )
{
return false;
}
return true;
}
bool AppleIntelPIIXPATA::interruptFilter( OSObject * owner,
IOFilterInterruptEventSource * src )
{
AppleIntelPIIXPATA * self = (AppleIntelPIIXPATA *) owner;
if ( *(self->_bmStatusReg) & kPIIX_IO_BMISX_IDEINTS )
return true; else
return false; }
void AppleIntelPIIXPATA::interruptOccurred( OSObject * owner,
IOInterruptEventSource * src,
int count )
{
AppleIntelPIIXPATA * self = (AppleIntelPIIXPATA *) owner;
*(self->_bmStatusReg) = kPIIX_IO_BMISX_IDEINTS;
self->handleDeviceInterrupt();
}
UInt32 AppleIntelPIIXPATA::scanForDrives( void )
{
UInt32 unitsFound;
DLOG("%s::%s()\n", getName(), __FUNCTION__);
*_tfAltSDevCReg = mATADCRReset;
IODelay( 100 );
*_tfAltSDevCReg = 0x0;
IOSleep( 10 );
unitsFound = super::scanForDrives();
#if ENABLE_VPC4_DRIVESCAN_WORKAROUND
for ( int unit = 0; ((unit < 2) && (unitsFound < 2)); unit++ )
{
if ( _devInfo[unit].type == kUnknownATADeviceType )
{
UInt32 milsSpent;
for ( milsSpent = 0; milsSpent < 10000; )
{
*_tfSDHReg = ( unit << 4 );
IODelay( 10 );
if ( (*_tfStatusCmdReg & mATABusy) == 0x00 ) break;
IOSleep( 10 );
milsSpent += 10;
}
if ( milsSpent >= 10000 ) break;
if ( (*_tfCylLoReg == 0x00) && (*_tfCylHiReg == 0x00) &&
(*_tfSCountReg == 0x01) && (*_tfSectorNReg == 0x01) &&
( (*_tfAltSDevCReg & 0x50) == 0x50) )
{
_devInfo[unit].type = kATADeviceType;
_devInfo[unit].packetSend = kATAPIUnknown;
unitsFound++;
}
}
}
#endif
*_tfSDHReg = 0x00;
return unitsFound;
}
IOReturn AppleIntelPIIXPATA::provideBusInfo( IOATABusInfo * infoOut )
{
DLOG("%s::%s( %p )\n", getName(), __FUNCTION__, infoOut);
if ( infoOut == 0 )
{
IOLog("%s::%s bad argument\n", getName(), __FUNCTION__);
return -1;
}
infoOut->zeroData();
infoOut->setSocketType( kInternalATASocket );
infoOut->setPIOModes( PIOModes );
infoOut->setDMAModes( DMAModes );
infoOut->setUltraModes( UDMAModes );
infoOut->setExtendedLBA( true );
UInt8 units = 0;
if ( _devInfo[0].type != kUnknownATADeviceType ) units++;
if ( _devInfo[1].type != kUnknownATADeviceType ) units++;
infoOut->setUnits( units );
return kATANoErr;
}
IOReturn AppleIntelPIIXPATA::getConfig( IOATADevConfig * configOut,
UInt32 unit )
{
DLOG("%s::%s( %p, %ld )\n", getName(), __FUNCTION__,
configOut, unit);
if ( (configOut == 0) || (unit > kATADevice1DeviceID) )
{
IOLog("%s::%s bad argument\n", getName(), __FUNCTION__);
return -1;
}
configOut->setPIOMode( 0 );
configOut->setDMAMode( 0 );
configOut->setUltraMode( 0 );
if ( _pioTiming[unit] )
{
configOut->setPIOMode( 1 << _pioTiming[unit]->mode );
configOut->setPIOCycleTime( _pioTiming[unit]->cycleTime );
}
if ( _dmaTiming[unit] )
{
configOut->setDMAMode( 1 << _dmaTiming[unit]->mode );
configOut->setDMACycleTime( _dmaTiming[unit]->cycleTime );
}
if ( _udmaTiming[unit] )
{
configOut->setUltraMode( 1 << _udmaTiming[unit]->mode );
}
configOut->setPacketConfig( _devInfo[unit].packetSend );
return kATANoErr;
}
IOReturn AppleIntelPIIXPATA::selectConfig( IOATADevConfig * config,
UInt32 unit )
{
const PIIXTiming * pioTiming = 0;
const PIIXTiming * dmaTiming = 0;
const PIIXUDMATiming * udmaTiming = 0;
DLOG("%s::%s( %p, %ld )\n", getName(), __FUNCTION__,
config, unit);
if ( (config == 0) || (unit > kATADevice1DeviceID) )
{
IOLog("%s::%s bad argument\n", getName(), __FUNCTION__);
return -1;
}
removeProperty( kSelectedPIOModeKey );
removeProperty( kSelectedDMAModeKey );
removeProperty( kSelectedUltraDMAModeKey );
if ( ( config->getPIOMode() & PIOModes ) == 0 )
{
DLOG("%s: Unsupported PIO mode %x\n", getName(), config->getPIOMode());
pioTiming = &piixPIOTiming[0];
}
else
{
UInt8 pioModeNumber;
pioModeNumber = bitSigToNumeric( config->getPIOMode() );
DLOG("%s: pioModeNumber = %d\n", getName(), pioModeNumber);
for ( ; pioModeNumber > 0; pioModeNumber-- )
{
if ( ( ( (1 << pioModeNumber) & PIOModes ) ) &&
( config->getPIOCycleTime() <=
piixPIOTiming[ pioModeNumber ].cycleTime ) )
{
break;
}
DLOG("%s: pioModeNumber = %d\n", getName(), pioModeNumber - 1);
}
setDriveProperty( unit, kSelectedPIOModeKey, pioModeNumber, 8 );
pioTiming = &piixPIOTiming[ pioModeNumber ];
}
if ( config->getDMAMode() )
{
UInt8 dmaModeNumber;
if ( ( config->getDMAMode() & DMAModes ) == 0 )
{
DLOG("%s: Unsupported DMA mode\n", getName());
return kATAModeNotSupported;
}
dmaModeNumber = bitSigToNumeric( config->getDMAMode() );
DLOG("%s: dmaModeNumber = %d\n", getName(), dmaModeNumber);
for ( ; dmaModeNumber > 0; dmaModeNumber-- )
{
if ( ( ( (1 << dmaModeNumber) & DMAModes ) ) &&
( config->getDMACycleTime() <=
piixDMATiming[ dmaModeNumber ].cycleTime ) )
{
break;
}
DLOG("%s: dmaModeNumber = %d\n", getName(), dmaModeNumber - 1);
}
if ( (1 << dmaModeNumber) & DMAModes )
{
setDriveProperty( unit, kSelectedDMAModeKey, dmaModeNumber, 8 );
dmaTiming = &piixDMATiming[ dmaModeNumber ];
}
else
dmaTiming = 0; }
if ( config->getUltraMode() )
{
UInt8 udmaModeNumber;
if ( ( config->getUltraMode() & UDMAModes ) == 0 )
{
DLOG("%s: Unsupported U-DMA mode\n", getName());
return kATAModeNotSupported;
}
udmaModeNumber = bitSigToNumeric( config->getUltraMode() );
if ( udmaModeNumber > 2 )
{
UInt16 cableMask = kPIIX_PCI_IDECONFIG_PCR0;
if ( unit == kATADevice1DeviceID ) cableMask <<= 1;
if ( _channel == kPIIX_CHANNEL_SECONDARY ) cableMask <<= 2;
if ( ( cableMask & _ideConfig ) == 0 )
{
DLOG("%s: 80-conductor cable not detected\n", getName());
udmaModeNumber = 2; }
}
setDriveProperty( unit, kSelectedUltraDMAModeKey, udmaModeNumber, 8 );
DLOG("%s: udmaModeNumber = %d\n", getName(), udmaModeNumber);
udmaTiming = &piixUDMATiming[ udmaModeNumber ];
}
if ( dmaTiming && (pioTiming->cycleTime != dmaTiming->cycleTime) )
{
DLOG("%s: forcing PIO compatible timing\n", getName());
pioTiming = &piixPIOTiming[0];
}
_pioTiming[unit] = pioTiming;
_dmaTiming[unit] = dmaTiming;
_udmaTiming[unit] = udmaTiming;
computeTimingRegisters( (ataUnitID) unit );
computeUDMATimingRegisters( (ataUnitID) unit );
writeTimingRegisters();
_devInfo[unit].packetSend = config->getPacketConfig();
return getConfig( config, unit );
}
void AppleIntelPIIXPATA::writeTimingRegisters( ataUnitID unit )
{
UInt8 idetimOffset;
UInt8 sidetimMask;
UInt8 udmactlMask;
UInt16 udmatimMask;
UInt16 ideConfigMask;
DLOG("%s::%s()\n", getName(), __FUNCTION__);
if ( _channel == kPIIX_CHANNEL_PRIMARY )
{
idetimOffset = kPIIX_PCI_IDETIM;
sidetimMask = kPIIX_PCI_SIDETIM_PRTC1_MASK |
kPIIX_PCI_SIDETIM_PISP1_MASK;
udmactlMask = kPIIX_PCI_UDMACTL_PSDE0 |
kPIIX_PCI_UDMACTL_PSDE1;
udmatimMask = kPIIX_PCI_UDMATIM_PCT0_MASK |
kPIIX_PCI_UDMATIM_PCT1_MASK;
ideConfigMask = kPIIX_PCI_IDECONFIG_PCB0 |
kPIIX_PCI_IDECONFIG_PCB1 |
kPIIX_PCI_IDECONFIG_FAST_PCB0 |
kPIIX_PCI_IDECONFIG_FAST_PCB1 |
kPIIX_PCI_IDECONFIG_WR_PP_EN;
}
else
{
idetimOffset = kPIIX_PCI_IDETIM_S;
sidetimMask = kPIIX_PCI_SIDETIM_SRTC1_MASK |
kPIIX_PCI_SIDETIM_SISP1_MASK;
udmactlMask = kPIIX_PCI_UDMACTL_SSDE0 |
kPIIX_PCI_UDMACTL_SSDE1;
udmatimMask = kPIIX_PCI_UDMATIM_SCT0_MASK |
kPIIX_PCI_UDMATIM_SCT1_MASK;
ideConfigMask = kPIIX_PCI_IDECONFIG_SCB0 |
kPIIX_PCI_IDECONFIG_SCB1 |
kPIIX_PCI_IDECONFIG_FAST_SCB0 |
kPIIX_PCI_IDECONFIG_FAST_SCB1 |
kPIIX_PCI_IDECONFIG_WR_PP_EN;
}
if ( _provider->hasSharedDriveTimings() == true )
{
_provider->pciConfigWrite16( idetimOffset, _idetim[unit] );
DLOG("%s: IDETIM[%d] = %04x\n", getName(), unit, _idetim[unit]);
}
else
{
_provider->pciConfigWrite16( idetimOffset, _idetim[0] | _idetim[1]);
_provider->pciConfigWrite8( kPIIX_PCI_SIDETIM, _sidetim, sidetimMask );
_provider->pciConfigWrite8( kPIIX_PCI_UDMACTL, _udmactl, udmactlMask );
_provider->pciConfigWrite16( kPIIX_PCI_UDMATIM, _udmatim, udmatimMask );
_provider->pciConfigWrite16( kPIIX_PCI_IDECONFIG,
_ideConfig, ideConfigMask );
DLOG("%s: IDETIM = %04x SIDETIM = %02x UDMACTL = %02x UDMATIM = %04x\n",
getName(),
_idetim[0] | _idetim[1], _sidetim, _udmactl, _udmatim);
}
}
void AppleIntelPIIXPATA::computeUDMATimingRegisters( ataUnitID unit )
{
UInt8 udmaEnableBit = kPIIX_PCI_UDMACTL_PSDE0;
UInt8 udmatimShifts = kPIIX_PCI_UDMATIM_PCT0_SHIFT;
UInt8 udmaClockShifts = 0;
DLOG("%s::%s( %d )\n", getName(), __FUNCTION__, unit);
if ( _channel == kPIIX_CHANNEL_SECONDARY )
{
udmaEnableBit <<= 2;
udmatimShifts += 8;
udmaClockShifts += 2;
}
if ( unit == kATADevice1DeviceID )
{
udmaEnableBit <<= 1;
udmatimShifts += 4;
udmaClockShifts += 1;
}
_udmactl &= ~udmaEnableBit;
if ( _udmaTiming[unit] )
{
_udmatim &= ~( kPIIX_PCI_UDMATIM_PCT0_MASK << udmatimShifts );
_udmatim |= (( _udmaTiming[unit]->udmatim << udmatimShifts ) &
( kPIIX_PCI_UDMATIM_PCT0_MASK << udmatimShifts ));
_ideConfig &= ~(( kPIIX_PCI_IDECONFIG_PCB0 |
kPIIX_PCI_IDECONFIG_FAST_PCB0 ) << udmaClockShifts);
_ideConfig |= ( _udmaTiming[unit]->udmaClock << udmaClockShifts );
_udmactl |= udmaEnableBit;
}
}
void AppleIntelPIIXPATA::computeTimingRegisters( ataUnitID unit )
{
const PIIXTiming * timing;
UInt8 index;
bool slowPIO = false;
DLOG("%s::%s( %d )\n", getName(), __FUNCTION__, unit);
assert( _pioTiming[unit] != 0 );
timing = _dmaTiming[unit] ? _dmaTiming[unit] : _pioTiming[unit];
if ( timing->cycleTime != _pioTiming[unit]->cycleTime )
{
DLOG("%s: using PIO compatible timing\n", getName());
slowPIO = true;
}
index = timing->registerIndex;
if ( _provider->hasSharedDriveTimings() == true )
{
_idetim[unit] = piixIDETIM[index][unit];
}
else
{
_idetim[unit] = piix3IDETIM[index][unit];
if ( unit == kATADevice1DeviceID )
{
_sidetim &= ~piix3SIDETIM[0][_channel];
_sidetim |= piix3SIDETIM[index][_channel];
}
_ideConfig |= kPIIX_PCI_IDECONFIG_WR_PP_EN;
}
if ( slowPIO )
{
if ( unit == kATADevice1DeviceID )
_idetim[unit] &= ~kPIIX_PCI_IDETIM_DTE1;
else
_idetim[unit] &= ~kPIIX_PCI_IDETIM_DTE0;
}
}
void AppleIntelPIIXPATA::selectIOTiming( ataUnitID unit )
{
if ( _provider->hasSharedDriveTimings() == true )
{
DLOG("%s::%s( %d )\n", getName(), __FUNCTION__, unit);
writeTimingRegisters( unit );
}
}
IOReturn AppleIntelPIIXPATA::handleQueueFlush( void )
{
UInt32 savedQstate = _queueState;
DLOG("%s::%s()\n", getName(), __FUNCTION__);
_queueState = IOATAController::kQueueLocked;
IOATABusCommand * cmdPtr = 0;
while ( cmdPtr = dequeueFirstCommand() )
{
cmdPtr->setResult( kIOReturnError );
cmdPtr->executeCallback();
}
_queueState = savedQstate;
return kATANoErr;
}
IOReturn AppleIntelPIIXPATA::message( UInt32 type,
IOService * provider,
void * argument )
{
if ( ( provider == _provider ) &&
( type == kIOMessageServiceIsTerminated ) )
{
_provider->close( this );
return kIOReturnSuccess;
}
return super::message( type, provider, argument );
}
bool AppleIntelPIIXPATA::setDriveProperty( UInt32 driveUnit,
const char * key,
UInt32 value,
UInt32 numberOfBits)
{
char keyString[40];
snprintf(keyString, 40, "Drive %ld %s", driveUnit, key);
return super::setProperty( keyString, value, numberOfBits );
}