AppleIntelPIIXATADriver.cpp [plain text]
#include <sys/systm.h> // snprintf
#include <IOKit/assert.h>
#include <IOKit/IOMessage.h>
#include <IOKit/ata/IOATATypes.h>
#include <IOKit/ata/IOATAController.h>
#include <IOKit/ata/ATADeviceNub.h>
#include "AppleIntelPIIXATAController.h"
#include "AppleIntelPIIXATADriver.h"
#include "AppleIntelPIIXATAHW.h"
#include "AppleIntelPIIXATAKeys.h"
#define super IOPCIATA
OSDefineMetaClassAndStructors( AppleIntelPIIXATADriver, IOPCIATA )
#ifdef PIIX_DEBUG
#define DLOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#define PIOModes \
(_provider->getPIOModes() & ((1 << piixPIOTimingCount) - 1))
#define DMAModes \
(_provider->getDMAModes() & ((1 << piixDMATimingCount) - 1))
#define UDMAModes \
(_provider->getUltraDMAModes() & ((1 << piixUDMATimingCount) - 1))
bool
AppleIntelPIIXATADriver::start( IOService * provider )
{
DLOG("%s::%s( %p )\n", getName(), __FUNCTION__, provider);
_provider = OSDynamicCast( AppleIntelPIIXATAController, 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;
}
_ioPorts = _provider->getIOBaseAddress();
_channel = _provider->getChannelNumber();
if ( ( _ioPorts != kPIIX_P_CMD_ADDR ) && ( _ioPorts != kPIIX_S_CMD_ADDR ) )
{
IOLog("%s: invalid ATA port address 0x%x\n", getName(), _ioPorts);
goto fail;
}
if ( _channel > kPIIX_CHANNEL_SECONDARY )
{
IOLog("%s: invalid ATA channel number %d\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 )
{
IOLog("%s: super start failed\n", getName());
goto fail;
}
_intSrc = IOInterruptEventSource::interruptEventSource(
(OSObject *) this,
(IOInterruptEventAction) &interruptOccurred,
_provider, 0 );
if ( !_intSrc || !_workLoop ||
(_workLoop->addEventSource(_intSrc) != kIOReturnSuccess) )
{
IOLog("%s: interrupt event source 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 (Port 0x%x, IRQ %d, BM 0x%x)\n", getName(),
_provider->getDeviceName(),
_ioPorts, _provider->getInterruptLine(), _ioBMOffset);
return true;
fail:
if ( _provider )
{
_provider->close( this );
}
return false;
}
void
AppleIntelPIIXATADriver::free()
{
#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 *
AppleIntelPIIXATADriver::getWorkLoop() const
{
DLOG("%s::%s()\n", getName(), __FUNCTION__);
return ( _workLoop ) ? _workLoop :
IOWorkLoop::workLoop();
}
bool
AppleIntelPIIXATADriver::configurePCIDevice( IOPCIDevice * device,
UInt16 channel )
{
UInt32 reg;
DLOG("%s::%s( %p, %d )\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 AppleIntelPIIXATADriver::getBMBaseAddress( IOPCIDevice * provider,
UInt16 channel,
UInt16 * addrOut )
{
UInt32 bmiba;
DLOG("%s::%s( %p, %d, %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
AppleIntelPIIXATADriver::resetTimingsForDevice( ataUnitID unit )
{
_pioTiming[unit] = &piixPIOTiming[ 0 ]; _dmaTiming[unit] = 0;
_udmaTiming[unit] = 0;
computeTimingRegisters( unit );
computeUDMATimingRegisters( unit );
writeTimingRegisters();
}
bool
AppleIntelPIIXATADriver::configureTFPointers()
{
DLOG("%s::%s()\n", getName(), __FUNCTION__);
_tfDataReg = IOATAIOReg16::withAddress( _ioPorts + 0 );
_tfFeatureReg = IOATAIOReg8::withAddress( _ioPorts + 1 );
_tfSCountReg = IOATAIOReg8::withAddress( _ioPorts + 2 );
_tfSectorNReg = IOATAIOReg8::withAddress( _ioPorts + 3 );
_tfCylLoReg = IOATAIOReg8::withAddress( _ioPorts + 4 );
_tfCylHiReg = IOATAIOReg8::withAddress( _ioPorts + 5 );
_tfSDHReg = IOATAIOReg8::withAddress( _ioPorts + 6 );
_tfStatusCmdReg = IOATAIOReg8::withAddress( _ioPorts + 7 );
_tfAltSDevCReg = IOATAIOReg8::withAddress( _ioPorts + 0x206 );
if ( !_tfDataReg || !_tfFeatureReg || !_tfSCountReg ||
!_tfSectorNReg || !_tfCylLoReg || !_tfCylHiReg ||
!_tfSDHReg || !_tfStatusCmdReg || !_tfAltSDevCReg )
{
return false;
}
return true;
}
void
AppleIntelPIIXATADriver::interruptOccurred( OSObject * owner,
IOInterruptEventSource * src,
int count )
{
AppleIntelPIIXATADriver * self = (AppleIntelPIIXATADriver *) owner;
self->handleDeviceInterrupt();
}
UInt32
AppleIntelPIIXATADriver::scanForDrives()
{
UInt32 unitsFound;
DLOG("%s::%s()\n", getName(), __FUNCTION__);
*_tfAltSDevCReg = mATADCRReset;
IODelay( 100 );
*_tfAltSDevCReg = 0x0;
IOSleep( 10 );
unitsFound = super::scanForDrives();
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++;
}
}
}
*_tfSDHReg = 0x00;
return unitsFound;
}
IOReturn
AppleIntelPIIXATADriver::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 );
UInt8 units = 0;
if ( _devInfo[0].type != kUnknownATADeviceType ) units++;
if ( _devInfo[1].type != kUnknownATADeviceType ) units++;
infoOut->setUnits( units );
return kATANoErr;
}
IOReturn
AppleIntelPIIXATADriver::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
AppleIntelPIIXATADriver::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
AppleIntelPIIXATADriver::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->hasPerChannelTimingSupport() == false )
{
_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);
}
#if 0
IOLog("%s: CH %d PCI 0x40: %08lx\n", getName(),
_channel, _pciDevice->configRead32( 0x40 ));
IOLog("%s: CH %d PCI 0x44: %08lx\n", getName(),
_channel, _pciDevice->configRead32( 0x44 ));
IOLog("%s: CH %d PCI 0x48: %08lx\n", getName(),
_channel, _pciDevice->configRead32( 0x48 ));
#endif
}
void
AppleIntelPIIXATADriver::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
AppleIntelPIIXATADriver::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->hasPerChannelTimingSupport() == false )
{
_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
AppleIntelPIIXATADriver::selectIOTiming( ataUnitID unit )
{
if ( _provider->hasPerChannelTimingSupport() == false )
{
DLOG("%s::%s( %d )\n", getName(), __FUNCTION__, unit);
writeTimingRegisters( unit );
}
}
IOReturn
AppleIntelPIIXATADriver::handleQueueFlush()
{
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
AppleIntelPIIXATADriver::message( UInt32 type,
IOService * provider,
void * argument )
{
if ( ( provider == _provider ) &&
( type == kIOMessageServiceIsTerminated ) )
{
_provider->close( this );
return kIOReturnSuccess;
}
return super::message( type, provider, argument );
}
bool
AppleIntelPIIXATADriver::setDriveProperty( UInt8 driveUnit,
const char * key,
UInt64 value,
UInt numberOfBits)
{
char keyString[40];
snprintf(keyString, 40, "Drive %d %s", driveUnit, key);
return super::setProperty( keyString, value, numberOfBits );
}