AppleIntelPIIXPATA.cpp [plain text]
#include <sys/systm.h> // snprintf
#include <IOKit/assert.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOKitKeys.h>
#include "AppleIntelPIIXPATA.h"
#include <IOKit/IOLocksPrivate.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))
#define kATAXferDMADesc 512
#define kATAMaxDMADesc kATAXferDMADesc
#define kMaxATAXfer 512 * 2048
class AppleIntelPIIXPATAWorkLoop : public IOWorkLoop
{
OSDeclareDefaultStructors ( AppleIntelPIIXPATAWorkLoop );
public:
static AppleIntelPIIXPATAWorkLoop * workLoop ( void );
protected:
bool init ( void );
void free ( void );
lck_grp_t * fLockGroup;
};
OSDefineMetaClassAndStructors ( AppleIntelPIIXPATAWorkLoop, IOWorkLoop );
AppleIntelPIIXPATAWorkLoop * AppleIntelPIIXPATAWorkLoop::workLoop()
{
AppleIntelPIIXPATAWorkLoop *loop;
loop = new AppleIntelPIIXPATAWorkLoop;
if(!loop)
return loop;
if(!loop->init()) {
loop->release();
loop = NULL;
}
return loop;
}
bool
AppleIntelPIIXPATAWorkLoop::init ( void )
{
char name[64];
snprintf ( name, 64, "PATA" );
fLockGroup = lck_grp_alloc_init ( name, LCK_GRP_ATTR_NULL );
if ( fLockGroup )
{
gateLock = IORecursiveLockAllocWithLockGroup ( fLockGroup );
}
return IOWorkLoop::init ( );
}
void AppleIntelPIIXPATAWorkLoop::free ( void )
{
if ( fLockGroup )
{
lck_grp_free ( fLockGroup );
fLockGroup = NULL;
}
IOWorkLoop::free ( );
}
bool AppleIntelPIIXPATA::start( IOService * provider )
{
bool superStarted = false;
IOACPIPlatformDevice* myACPINode;
_drivePowerOn = true;
bool safeSleep = false;
polledPATAAdapter = NULL;
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;
}
superStarted = true;
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;
}
*_bmStatusReg = kPIIX_IO_BMISX_IDEINTS;
_intSrc->enable();
initForPM( provider );
myACPINode = getACPIParent();
if( myACPINode )
{
if( hasMediaNotify( myACPINode ) )
{
_pciACPIDevice = myACPINode;
_interestNotifier = myACPINode->registerInterest( gIOGeneralInterest ,
(IOServiceInterestHandler) AppleIntelPIIXPATA::mediaInterestHandler,
this);
if( _interestNotifier )
DLOG ("AppleIntellPIIX registered Interest.\n");
} else {
_pciACPIDevice = NULL;
}
}
if( _pciACPIDevice == NULL )
{
if( _provider->getProperty( "safe-sleep" ) )
{
safeSleep = true;
IOLog("Intel PATA has safe-sleep\n");
}
}
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 ( _devInfo[i].type == kATAPIDeviceType )
{
nub->setProperty( kIOMaximumSegmentCountReadKey,
kATAMaxDMADesc / 2, 64 );
nub->setProperty( kIOMaximumSegmentCountWriteKey,
kATAMaxDMADesc / 2, 64 );
nub->setProperty( kIOMaximumByteCountReadKey,
512*256, 64 );
nub->setProperty( kIOMaximumByteCountWriteKey,
512*256, 64 );
if( _pciACPIDevice )
{
nub->setProperty( kATANotifyOnChangeKey, 1, 32);
}
} else if ( _devInfo[i].type == kATADeviceType
&& safeSleep == true )
{
polledPATAAdapter = new AppleIntelICHxPATAPolledAdapter;
if( polledPATAAdapter)
{
polledPATAAdapter->setOwner( this );
setProperty ( kIOPolledInterfaceSupportKey, polledPATAAdapter );
polledPATAAdapter->release();
}
}
if ( nub->attach( this ) )
{
_nub[i] = (IOATADevice *) nub;
_nub[i]->retain();
_nub[i]->registerService();
}
nub->release();
}
}
}
DLOG("%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 );
}
if (superStarted)
super::stop( provider );
return false;
}
void AppleIntelPIIXPATA::stop( IOService * provider )
{
PMstop();
super::stop( provider );
}
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 :
AppleIntelPIIXPATAWorkLoop::workLoop();
}
IOReturn AppleIntelPIIXPATA::synchronousIO( void )
{
IOReturn ret;
if (_intSrc) _intSrc->disable();
ret = super::synchronousIO();
*_bmStatusReg = kPIIX_IO_BMISX_IDEINTS;
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 )
{
*(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->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;
}
IOATAController::transState
AppleIntelPIIXPATA::determineATAPIState(void)
{
IOATAController::transState drivePhase = super::determineATAPIState();
if( ( IOATAController::transState ) _currentCommand->state > drivePhase
|| _currentCommand->state == kATAStarted )
{
return (IOATAController::transState) _currentCommand->state;
}
return drivePhase;
}
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 );
infoOut->setMaxBlocksExtended( 0x0800 );
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 &&
_provider->getChannelMode() == kChannelModePATA )
{
UInt16 cableMask = kPIIX_PCI_IDECONFIG_PCR0;
if ( unit == kATADevice1DeviceID ) cableMask <<= 1;
if ( _channel == kPIIX_CHANNEL_SECONDARY ) cableMask <<= 2;
if ( ( cableMask & _ideConfig ) == 0 )
{
IOLog("%s: 80-conductor cable not detected on %s channel\n",
getName(), (_channel == kPIIX_CHANNEL_PRIMARY) ?
"primary" : "secondary" );
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 );
}
IOReturn AppleIntelPIIXPATA::createChannelCommands( void )
{
IOATABusCommand64* currentCommand64 = OSDynamicCast( IOATABusCommand64, _currentCommand );
IODMACommand* currentDMACmd = currentCommand64->GetDMACommand();
IODMACommand::Segment32 physSegment32;
UInt32 index = 0;
UInt8 *xferDataPtr, *ptr2EndData, *next64KBlock, *starting64KBlock;
UInt32 xferCount, count2Next64KBlock;
IOReturn DMAStatus = 0;
UInt32 numSegments = 1;
if ( NULL == currentDMACmd
|| currentDMACmd->getMemoryDescriptor() == NULL)
{
IOLog("%s: DMA buffer not set on command\n", getName());
return -1;
}
IOByteCount bytesRemaining = _currentCommand->getByteCount() ;
IOByteCount xfrPosition = _currentCommand->getPosition() ;
UInt64 transferSize = xfrPosition;
while ( bytesRemaining )
{
DMAStatus = currentDMACmd->gen32IOVMSegments( &transferSize, &physSegment32, &numSegments);
if ( ( DMAStatus != kIOReturnSuccess ) || ( numSegments != 1 ) || ( physSegment32.fLength == 0 ) )
{
panic ( "AppleIntelPIIXPATA::createChannelCommands [%d] status %x segs %d phys %x:%x \n", __LINE__, DMAStatus, ( int ) numSegments, ( int ) physSegment32.fIOVMAddr, ( int ) physSegment32.fLength );
break;
}
xferDataPtr = (UInt8 *) physSegment32.fIOVMAddr;
xferCount = physSegment32.fLength;
if ( (UInt32) xferDataPtr & 0x01 )
{
IOLog("%s: DMA buffer %p not 2 byte aligned\n",
getName(), xferDataPtr);
return kIOReturnNotAligned;
}
if ( xferCount & 0x01 )
{
IOLog("%s: DMA buffer length %ld is odd\n",
getName(), xferCount);
}
if( xferCount > bytesRemaining )
{
xferCount = bytesRemaining;
}
bytesRemaining -= xferCount;
xfrPosition += xferCount;
starting64KBlock = (UInt8*) ( (UInt32) xferDataPtr & 0xffff0000);
ptr2EndData = xferDataPtr + xferCount;
next64KBlock = starting64KBlock + 0x10000;
while ( xferCount > 0 )
{
if (ptr2EndData > next64KBlock)
{
count2Next64KBlock = next64KBlock - xferDataPtr;
if ( index < kATAMaxDMADesc )
{
setPRD( xferDataPtr, (UInt16)count2Next64KBlock,
&_prdTable[index], kContinue_PRD);
xferDataPtr = next64KBlock;
next64KBlock += 0x10000;
xferCount -= count2Next64KBlock;
index++;
}
else
{
IOLog("%s: PRD table exhausted error 1\n", getName());
_dmaState = kATADMAError;
return -1;
}
}
else
{
if (index < kATAMaxDMADesc)
{
setPRD( xferDataPtr, (UInt16) xferCount,
&_prdTable[index],
(bytesRemaining == 0) ? kLast_PRD : kContinue_PRD);
xferCount = 0;
index++;
}
else
{
IOLog("%s: PRD table exhausted error 2\n", getName());
_dmaState = kATADMAError;
return -1;
}
}
}
}
if (index == 0)
{
IOLog("%s: rejected command with zero PRD count (0x%lx bytes)\n",
getName(), _currentCommand->getByteCount());
return kATADeviceError;
}
_dmaState = kATADMAStatus;
return kATANoErr;
}
bool AppleIntelPIIXPATA::allocDMAChannel( void )
{
_prdTable = (PRD *) IOMallocContiguous(
sizeof(PRD) * kATAMaxDMADesc,
0x10000,
&_prdTablePhysical );
if ( !_prdTable )
{
IOLog("%s: PRD table allocation failed\n", getName());
return false;
}
_DMACursor = IONaturalMemoryCursor::withSpecification(
0x10000,
kMaxATAXfer );
if ( !_DMACursor )
{
freeDMAChannel();
IOLog("%s: Memory cursor allocation failed\n", getName());
return false;
}
initATADMAChains( _prdTable );
return true;
}
bool AppleIntelPIIXPATA::freeDMAChannel( void )
{
if ( _prdTable )
{
stopDMA();
IOFreeContiguous(_prdTable, sizeof(PRD) * kATAMaxDMADesc);
}
return true;
}
void AppleIntelPIIXPATA::initATADMAChains( PRD * descPtr )
{
UInt32 i;
for (i = 0; i < kATAMaxDMADesc; i++)
{
descPtr->bufferPtr = 0;
descPtr->byteCount = 1;
descPtr->flags = OSSwapHostToLittleConstInt16( kLast_PRD );
descPtr++;
}
}
void AppleIntelPIIXPATA::initForPM( IOService * provider )
{
static const IOPMPowerState powerStates[ kPIIXPowerStateCount ] =
{
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 0, IOPMSoftSleep, IOPMSoftSleep, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 0, IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 }
};
PMinit();
registerPowerDriver( this, (IOPMPowerState *) powerStates,
kPIIXPowerStateCount );
provider->joinPMtree( this );
}
IOReturn AppleIntelPIIXPATA::setPowerState( unsigned long stateIndex,
IOService * whatDevice )
{
if ( stateIndex == kPIIXPowerStateOff )
{
_initTimingRegisters = true;
}
else if ( _initTimingRegisters )
{
writeTimingRegisters();
_initTimingRegisters = false;
}
return IOPMAckImplied;
}
#pragma mark -
#pragma mark acpi code
IOACPIPlatformDevice*
AppleIntelPIIXPATA::getACPIParent( void )
{
OSObject* acpi_path_object = NULL;
OSString* acpi_path = NULL;
IORegistryEntry* acpi_device_entry = NULL;
IOACPIPlatformDevice* acpi_device = NULL;
acpi_path_object = _provider->getProvider()->getProvider()->getProperty("acpi-path");
if( acpi_path_object != NULL )
{
acpi_path = OSDynamicCast( OSString, acpi_path_object);
if( acpi_path )
{
acpi_device_entry = IORegistryEntry::fromPath( acpi_path->getCStringNoCopy() );
if( acpi_device_entry )
{
if( acpi_device_entry->metaCast( "IOACPIPlatformDevice") )
{
DLOG( "++++++--->>> PIIX got the ACPI device\n");
acpi_device = ( IOACPIPlatformDevice*) acpi_device_entry;
}
} else {
DLOG("****------> PIIXATA could not get registry acpi_path entry \n");
}
} else {
DLOG("****------> PIIXATA could not cast acpi_path\n");
}
} else {
DLOG("------> ** PIIX ATA no acpi-path\n");
}
return acpi_device;
}
bool
AppleIntelPIIXPATA::hasMediaNotify(IOACPIPlatformDevice* acpi_device)
{
OSData *compatibleEntry;
bool result = false;
compatibleEntry = OSDynamicCast( OSData, acpi_device->getProperty( "compatible") );
if( compatibleEntry )
{
if( compatibleEntry->isEqualTo( "media-notify" , 12 ) )
{
result = true;
DLOG( "PIIXATA has mediaNotify\n");
}
}
return result;
}
void
AppleIntelPIIXPATA::turnOffDrive( void )
{
if (_intSrc)
_intSrc->disable();
UInt32 maskBits = 0x3 << 16;
UInt32 disableBits = 0x02 << 16;
UInt32 ideConfigBits = _pciDevice->configRead32( kPIIX_PCI_IDECONFIG );
ideConfigBits &= ~maskBits;
_pciDevice->configWrite32( kPIIX_PCI_IDECONFIG , ( disableBits | ideConfigBits));
_pciACPIDevice->evaluateObject( "_PS3" );
_drivePowerOn = false;
*_bmStatusReg = kPIIX_IO_BMISX_IDEINTS;
if (_intSrc)
_intSrc->enable();
}
void
AppleIntelPIIXPATA::turnOnDrive( void )
{
if (_intSrc)
_intSrc->disable();
_drivePowerOn = true;
_pciACPIDevice->evaluateObject( "_PS0" );
UInt32 disableBits = 0x3 << 16;
UInt32 ideConfigBits = _pciDevice->configRead32( kPIIX_PCI_IDECONFIG );
_pciDevice->configWrite32( kPIIX_PCI_IDECONFIG , (~disableBits) & ideConfigBits);
IOSleep( 500 );
bool resetFailed = true;
for( int i = 0; i < 3100; i++)
{
UInt8 status = *_tfStatusCmdReg;
if( (status & 0x80) == 0x00 )
{
resetFailed = false;
DLOG(" AppleIntelPIIX cleared busy in %d seconds\n", i);
break;
}
IOSleep( 10 );
}
if( resetFailed )
{
DLOG("AppleIntelPIIX failed to clear busy on power control\n");
}
*_bmStatusReg = kPIIX_IO_BMISX_IDEINTS;
if (_intSrc)
_intSrc->enable();
executeEventCallouts( kATAResetEvent, kATAInvalidDeviceID);
}
IOReturn
AppleIntelPIIXPATA::mediaInterestHandler( void* target,
void* refCon,
UInt32 messageType,
IOService* provider,
void* messageArgument,
vm_size_t argSize)
{
IOReturn result = kIOReturnSuccess;
AppleIntelPIIXPATA* self = (AppleIntelPIIXPATA*) target;
if( messageType == kIOACPIMessageDeviceNotification )
{
UInt32 messageVal = * ((UInt32*)messageArgument);
switch( messageVal )
{
case 0x81:
DLOG( "PIIXPata got insert message\n");
self->_cmdGate->runAction(
OSMemberFunctionCast( IOCommandGate::Action, self, &AppleIntelPIIXPATA::handleInsert) );
break;
case 0x82:
DLOG( "PIIXPata got remove message \n");
break;
default:
DLOG("PIIXPata got unknown ACPI message value\n");
}
}
return result;
}
void
AppleIntelPIIXPATA::completeIO( IOReturn commandResult)
{
bool quiescePower = false;
if (_currentCommand->getFlags() & mATAFlagQuiesce)
{
quiescePower = true;
}
super::completeIO( commandResult );
if( quiescePower )
{
turnOffDrive();
}
}
IOReturn
AppleIntelPIIXPATA::dispatchNext( void )
{
if( ! _drivePowerOn )
{
turnOnDrive();
}
return super::dispatchNext();
}
IOReturn
AppleIntelPIIXPATA::handleInsert( void )
{
DLOG("AppleIntelPIIXPATA handling Insertion\n");
if( ! _drivePowerOn )
{
turnOnDrive();
}
return kIOReturnSuccess;
}
IOReturn
AppleIntelPIIXPATA::selectDevice( ataUnitID unit )
{
DLOG("AppleIntelPIIXPATA selectDevice\n");
IOReturn result = kIOReturnSuccess;
if (_intSrc) _intSrc->disable();
result = super::selectDevice( unit );
*_bmStatusReg = kPIIX_IO_BMISX_IDEINTS;
if (_intSrc) _intSrc->enable();
return result;
}
void
AppleIntelPIIXPATA::executeEventCallouts( ataEventCode event, ataUnitID unit )
{
if( polledPATAAdapter && polledPATAAdapter->isPolling())
{
return;
}
super::executeEventCallouts(event, unit);
}
IOReturn
AppleIntelPIIXPATA::startTimer( UInt32 inMS)
{
if( polledPATAAdapter && polledPATAAdapter->isPolling())
{
return kIOReturnSuccess;
}
return super::startTimer( inMS);
}
void
AppleIntelPIIXPATA::stopTimer(void)
{
if( polledPATAAdapter && polledPATAAdapter->isPolling())
{
return;
}
return super::stopTimer( );
}
void
AppleIntelPIIXPATA::pollEntry( void )
{
if( 0 == _currentCommand )
return;
if ( *(_bmStatusReg) & kPIIX_IO_BMISX_IDEINTS )
{
*(_bmStatusReg) = kPIIX_IO_BMISX_IDEINTS;
handleDeviceInterrupt();
}
}
void
AppleIntelPIIXPATA::transitionFixup( void )
{
_queueState = IOATAController::kQueueOpen;
_busState = IOATAController::kBusFree;
_currentCommand = 0L;
_selectedUnit = kATAInvalidDeviceID;
_queueState = IOATAController::kQueueOpen;
_immediateGate = IOATAController::kImmediateOK;
_pciDevice->restoreDeviceState();
}
#undef super
#define super IOPolledInterface
OSDefineMetaClassAndStructors( AppleIntelICHxPATAPolledAdapter, IOPolledInterface )
IOReturn
AppleIntelICHxPATAPolledAdapter::probe(IOService * target)
{
pollingActive = false;
return kIOReturnSuccess;
}
IOReturn
AppleIntelICHxPATAPolledAdapter::open( IOOptionBits state, IOMemoryDescriptor * buffer)
{
switch( state )
{
case kIOPolledPreflightState:
break;
case kIOPolledBeforeSleepState:
pollingActive = true;
break;
case kIOPolledAfterSleepState:
owner->transitionFixup();
pollingActive = true;
break;
case kIOPolledPostflightState:
default:
break;
}
return kIOReturnSuccess;
}
IOReturn
AppleIntelICHxPATAPolledAdapter::close(IOOptionBits state)
{
switch( state )
{
case kIOPolledPreflightState:
case kIOPolledBeforeSleepState:
case kIOPolledAfterSleepState:
case kIOPolledPostflightState:
default:
pollingActive = false;
break;
}
return kIOReturnSuccess;
}
IOReturn
AppleIntelICHxPATAPolledAdapter::startIO(uint32_t operation,
uint32_t bufferOffset,
uint64_t deviceOffset,
uint64_t length,
IOPolledCompletion completion)
{
return kIOReturnUnsupported;
}
IOReturn
AppleIntelICHxPATAPolledAdapter::checkForWork(void)
{
if( owner )
{
owner->pollEntry();
}
return kIOReturnSuccess;
}
bool
AppleIntelICHxPATAPolledAdapter::isPolling( void )
{
return pollingActive;
}
void
AppleIntelICHxPATAPolledAdapter::setOwner( AppleIntelPIIXPATA* myOwner )
{
owner = myOwner;
pollingActive = false;
}