/* * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include "AppleIntelPIIXATARoot.h" #include "AppleIntelPIIXATAChannel.h" #include "AppleIntelPIIXATAKeys.h" #include "AppleIntelPIIXATAHW.h" #define super IOService OSDefineMetaClassAndStructors( AppleIntelPIIXATARoot, IOService ) /* * Given the map value (Bit 2:0) in the 'Port Mapping Register' * for the Serial ATA controller, return the (logical) channel * configuration for both primary and secondary channels. */ static const UInt8 gICH5ChannelModeMap[8 /*map value*/][2 /*channel*/] = { /* Non-combined SATA only modes */ { kChannelModeSATAPort0, kChannelModeSATAPort1 }, /* 000 */ { kChannelModeSATAPort1, kChannelModeSATAPort0 }, /* 001 */ /* Undefined modes in ICH5 PRM (April 2003) */ { kChannelModeDisabled, kChannelModeDisabled }, /* 010 */ { kChannelModeDisabled, kChannelModeDisabled }, /* 011 */ /* Combined SATA/PATA modes */ { kChannelModeSATAPort01, kChannelModePATA }, /* 100 */ { kChannelModeSATAPort10, kChannelModePATA }, /* 101 */ { kChannelModePATA, kChannelModeSATAPort01 }, /* 110 */ { kChannelModePATA, kChannelModeSATAPort10 } /* 111 */ }; /* * ICH6 / ICH6-R */ static const UInt8 gICH6ChannelModeMap[4 /*map value*/][2 /*channel*/] = { /* Non-combined SATA only modes */ { kChannelModeSATAPort02, kChannelModeSATAPort13 }, /* 00 */ /* Combined SATA/PATA modes */ { kChannelModePATA, kChannelModeSATAPort13 }, /* 01 */ { kChannelModeSATAPort02, kChannelModePATA }, /* 10 */ /* Reserved mode */ { kChannelModeDisabled, kChannelModeDisabled } /* 11 */ }; /* * ICH6-M (does not implement SATA ports 1 and 3) */ static const UInt8 gICH6MChannelModeMap[4 /*map value*/][2 /*channel*/] = { /* Non-combined SATA only modes */ { kChannelModeSATAPort02, kChannelModeDisabled }, /* 00 */ /* Combined SATA/PATA modes */ { kChannelModePATA, kChannelModeDisabled }, /* 01 */ { kChannelModeSATAPort02, kChannelModePATA }, /* 10 */ /* Reserved mode */ { kChannelModeDisabled, kChannelModeDisabled } /* 11 */ }; /* * ICH7 / ICH7-R */ static const UInt8 gICH7ChannelModeMap[4 /*map value*/][2 /*channel*/] = { /* Non-combined SATA only modes */ { kChannelModeSATAPort02, kChannelModeSATAPort13 }, /* 00 */ /* Combined SATA/PATA modes */ { kChannelModePATA, kChannelModeSATAPort13 }, /* 01 */ { kChannelModeSATAPort02, kChannelModePATA }, /* 10 */ /* Reserved mode */ { kChannelModeDisabled, kChannelModeDisabled } /* 11 */ }; /* * ICH7-M (does not implement SATA ports 1 and 3) */ static const UInt8 gICH7MChannelModeMap[4 /*map value*/][2 /*channel*/] = { /* Non-combined SATA only modes */ { kChannelModeSATAPort02, kChannelModeDisabled }, /* 00 */ /* Combined SATA/PATA modes */ { kChannelModePATA, kChannelModeDisabled }, /* 01 */ { kChannelModeSATAPort02, kChannelModePATA }, /* 10 */ /* Reserved mode */ { kChannelModeDisabled, kChannelModeDisabled } /* 11 */ }; //--------------------------------------------------------------------------- // // Probe for PCI device and verify that I/O space decoding is enabled. // IOService * AppleIntelPIIXATARoot::probe( IOService * provider, SInt32 * score ) { IOPCIDevice * pciDevice; // Let our superclass probe first. if ( super::probe( provider, score ) == 0 ) { return 0; } // Verify the provider type. pciDevice = OSDynamicCast( IOPCIDevice, provider ); if ( pciDevice == 0 ) { return 0; } // BIOS did not enable I/O space decoding. // For now assume the ATA controller is disabled. if ( (pciDevice->configRead16( kIOPCIConfigCommand ) & kIOPCICommandIOSpace) == 0 ) { return 0; } return this; } //--------------------------------------------------------------------------- // // Start the Root PIIX ATA driver. // static void registerClientApplier( IOService * service, void * context ) { if ( service ) service->registerService(); } bool AppleIntelPIIXATARoot::start( IOService * provider ) { if ( super::start(provider) != true ) return false; // Allocate a mutex to serialize access to PCI config space. _pciConfigLock = IOLockAlloc(); if ( _pciConfigLock == 0 ) return false; _provider = OSDynamicCast( IOPCIDevice, provider ); if ( _provider == 0 ) return false; _provider->retain(); _nubs = createATAChannelNubs(); if ( _nubs == 0 ) return false; _openNubs = OSSet::withCapacity( _nubs->getCount() ); if ( _openNubs == 0 ) return false; // Register channel nubs. applyToClients( registerClientApplier, 0 ); return true; } //--------------------------------------------------------------------------- // // Release allocated resources before this object is destroyed. // void AppleIntelPIIXATARoot::free( void ) { if ( _nubs ) { _nubs->release(); _nubs = 0; } if ( _openNubs ) { _openNubs->release(); _openNubs = 0; } if ( _provider ) { _provider->release(); _provider = 0; } if ( _pciConfigLock ) { IOLockFree( _pciConfigLock ); _pciConfigLock = 0; } super::free(); } //--------------------------------------------------------------------------- // // Locate an entry in the device tree that correspond to the channels // behind the ATA controller. This allows discovery of the ACPI entry // for ACPI method evaluation, and also uses the ACPI assigned device // name for a persistent path to the root device. // IORegistryEntry * AppleIntelPIIXATARoot::getDTChannelEntry( int channelID ) { IORegistryEntry * entry = 0; const char * location; OSIterator * iter = _provider->getChildIterator( gIODTPlane ); if (iter == 0) return 0; while (( entry = (IORegistryEntry *) iter->getNextObject() )) { location = entry->getLocation(); if ( location && strtol(location, 0, 10) == channelID ) { entry->retain(); break; } } iter->release(); return entry; // retain held on the entry } //--------------------------------------------------------------------------- // // Create nubs based on the channel information in the driver personality. // OSSet * AppleIntelPIIXATARoot::createATAChannelNubs( void ) { OSSet * nubSet; OSDictionary * channelInfo; IORegistryEntry * dtEntry; UInt32 priChannelMode; UInt32 secChannelMode; UInt8 mapValue = 0; do { nubSet = OSSet::withCapacity(2); if ( nubSet == 0 ) break; if ( _provider->open( this ) != true ) break; priChannelMode = kChannelModePATA; secChannelMode = kChannelModePATA; // Determine SATA channel mode based on Port Mapping Register. if ( getProperty( kSerialATAKey ) == kOSBooleanTrue ) { OSString * hwName; hwName = OSDynamicCast(OSString, getProperty(kControllerNameKey)); mapValue = _provider->configRead8(kPIIX_PCI_MAP); setProperty( kPortMappingKey, mapValue, 8 ); priChannelMode = kChannelModeDisabled; secChannelMode = kChannelModeDisabled; if (hwName) { if (hwName->isEqualTo("ICH7-M SATA")) { mapValue &= 0x3; priChannelMode = gICH7MChannelModeMap[mapValue][0]; secChannelMode = gICH7MChannelModeMap[mapValue][1]; } else if (hwName->isEqualTo("ICH6 SATA") || hwName->isEqualTo("ESB2 SATA")) { mapValue &= 0x3; priChannelMode = gICH6ChannelModeMap[mapValue][0]; secChannelMode = gICH6ChannelModeMap[mapValue][1]; } else if (hwName->isEqualTo("ICH6-M SATA")) { mapValue &= 0x3; priChannelMode = gICH6MChannelModeMap[mapValue][0]; secChannelMode = gICH6MChannelModeMap[mapValue][1]; } else if (hwName->isEqualTo("ICH5 SATA")) { mapValue &= 0x7; priChannelMode = gICH5ChannelModeMap[mapValue][0]; secChannelMode = gICH5ChannelModeMap[mapValue][1]; } else /* if (hwName->isEqualTo("ICH7 SATA")) */ { mapValue &= 0x3; priChannelMode = gICH7ChannelModeMap[mapValue][0]; secChannelMode = gICH7ChannelModeMap[mapValue][1]; } } } if ( priChannelMode == kChannelModeDisabled && secChannelMode == kChannelModeDisabled ) { IOLog("%s: bad value (%x) in Port Mapping register", getName(), mapValue); _provider->close( this ); break; } for ( UInt32 channelID = 0; channelID < 2; channelID++ ) { UInt32 channelMode = (channelID ? secChannelMode : priChannelMode); // Create a dictionary for the channel info. Use native mode // settings if possible, else default to legacy mode. channelInfo = createNativeModeChannelInfo( channelID, channelMode ); if (channelInfo == 0) channelInfo = createLegacyModeChannelInfo( channelID, channelMode ); if (channelInfo == 0) continue; // Create a nub for each ATA channel. AppleIntelPIIXATAChannel * nub = new AppleIntelPIIXATAChannel; if ( nub ) { dtEntry = getDTChannelEntry( channelID ); if ( nub->init( this, channelInfo, dtEntry ) && nub->attach( this ) ) { nubSet->setObject( nub ); } if ( dtEntry ) { dtEntry->release(); } else { // Platform did not create a device tree entry for // this ATA channel. Do it here. char channelName[5] = {'C','H','N','_','\0'}; channelName[3] = '0' + channelID; nub->setName( channelName ); if ( _provider->inPlane(gIODTPlane) ) { nub->attachToParent( _provider, gIODTPlane ); } } nub->release(); } channelInfo->release(); } _provider->close( this ); } while ( false ); // Release and invalidate an empty set. if ( nubSet && (nubSet->getCount() == 0) ) { nubSet->release(); nubSet = 0; } return nubSet; } //--------------------------------------------------------------------------- OSDictionary * AppleIntelPIIXATARoot::createNativeModeChannelInfo( UInt32 ataChannel, UInt32 channelMode ) { UInt8 pi = _provider->configRead8( kPIIX_PCI_PI ); UInt16 cmdPort = 0; UInt16 ctrPort = 0; switch ( ataChannel ) { case kPIIX_CHANNEL_PRIMARY: if ((pi & kPIIX_PCI_PRI_NATIVE_MASK) == kPIIX_PCI_PRI_NATIVE_MASK) { // Primary channel native mode supported and enabled. cmdPort = _provider->configRead16( kIOPCIConfigBaseAddress0 ); ctrPort = _provider->configRead16( kIOPCIConfigBaseAddress1 ); cmdPort &= ~0x1; // clear PCI I/O space indicator bit ctrPort &= ~0x1; // Programming interface byte indicate that native mode // is supported and active, but the controller has been // assigned legacy ranges. Force legacy mode configuration // which is safest. PCI INT# interrupts are not wired // properly for some machines in this state. if ( cmdPort == kPIIX_P_CMD_ADDR && ctrPort == kPIIX_P_CTL_ADDR ) { cmdPort = ctrPort = 0; } } break; case kPIIX_CHANNEL_SECONDARY: if ((pi & kPIIX_PCI_SEC_NATIVE_MASK) == kPIIX_PCI_SEC_NATIVE_MASK) { cmdPort = _provider->configRead16( kIOPCIConfigBaseAddress2 ); ctrPort = _provider->configRead16( kIOPCIConfigBaseAddress3 ); cmdPort &= ~0x1; // clear PCI I/O space indicator bit ctrPort &= ~0x1; if ( cmdPort == kPIIX_S_CMD_ADDR && ctrPort == kPIIX_S_CTL_ADDR ) { cmdPort = ctrPort = 0; } } break; } if ( cmdPort && ctrPort ) return createChannelInfo( ataChannel, channelMode, cmdPort, ctrPort, _provider->configRead8( kIOPCIConfigInterruptLine ) ); else return 0; } //--------------------------------------------------------------------------- OSDictionary * AppleIntelPIIXATARoot::createLegacyModeChannelInfo( UInt32 ataChannel, UInt32 channelMode ) { UInt16 cmdPort = 0; UInt16 ctrPort = 0; UInt8 irq = 0; switch ( ataChannel ) { case kPIIX_CHANNEL_PRIMARY: cmdPort = kPIIX_P_CMD_ADDR; ctrPort = kPIIX_P_CTL_ADDR; irq = kPIIX_P_IRQ; break; case kPIIX_CHANNEL_SECONDARY: cmdPort = kPIIX_S_CMD_ADDR; ctrPort = kPIIX_S_CTL_ADDR; irq = kPIIX_S_IRQ; break; } return createChannelInfo( ataChannel, channelMode, cmdPort, ctrPort, irq ); } //--------------------------------------------------------------------------- OSDictionary * AppleIntelPIIXATARoot::createChannelInfo( UInt32 ataChannel, UInt32 channelMode, UInt16 commandPort, UInt16 controlPort, UInt8 interruptVector ) { OSDictionary * dict = OSDictionary::withCapacity( 4 ); OSNumber * num; if ( dict == 0 || commandPort == 0 || controlPort == 0 || interruptVector == 0 || interruptVector == 0xFF ) { if ( dict ) dict->release(); return 0; } num = OSNumber::withNumber( ataChannel, 32 ); if (num) { dict->setObject( kChannelNumberKey, num ); num->release(); } num = OSNumber::withNumber( commandPort, 16 ); if (num) { dict->setObject( kCommandBlockAddressKey, num ); num->release(); } num = OSNumber::withNumber( controlPort, 16 ); if (num) { dict->setObject( kControlBlockAddressKey, num ); num->release(); } num = OSNumber::withNumber( interruptVector, 8 ); if (num) { dict->setObject( kInterruptVectorKey, num ); num->release(); } num = OSNumber::withNumber( channelMode, 32 ); if (num) { dict->setObject( kChannelModeKey, num ); num->release(); } return dict; } //--------------------------------------------------------------------------- // // Handle an open request from a client. Several clients can call this // function and hold an open on us. // bool AppleIntelPIIXATARoot::handleOpen( IOService * client, IOOptionBits options, void * arg ) { bool ret = true; // Reject open request from unknown clients, or if the client // already holds an open. if ( ( _nubs->containsObject( client ) == false ) || ( _openNubs->containsObject( client ) == true ) ) return false; // First client open will trigger an open to our provider. if ( _openNubs->getCount() == 0 ) ret = _provider->open( this ); if ( ret ) { _openNubs->setObject( client ); if ( arg ) *((IOService **) arg) = _provider; } return ret; } //--------------------------------------------------------------------------- // // Handle a close request from a client. // void AppleIntelPIIXATARoot::handleClose( IOService * client, IOOptionBits options ) { // Reject close request from clients that do not hold an open. if ( _openNubs->containsObject( client ) == false ) return; _openNubs->removeObject( client ); // Last client close will trigger a close to our provider. if ( _openNubs->getCount() == 0 ) _provider->close( this ); } //--------------------------------------------------------------------------- // // Report if the specified client has an open on us. // bool AppleIntelPIIXATARoot::handleIsOpen( const IOService * client ) const { if ( client ) return _openNubs->containsObject( client ); else return ( _openNubs->getCount() != 0 ); } //--------------------------------------------------------------------------- // // Helpers for non 4-byte aligned PCI config space writes. // WARNING: These will not work on a big-endian machine. // void AppleIntelPIIXATARoot::pciConfigWrite8( UInt8 offset, UInt8 data, UInt8 mask ) { UInt8 u8; IOLockLock( _pciConfigLock ); u8 = _provider->configRead8( offset ); u8 &= ~mask; u8 |= (mask & data); _provider->configWrite8( offset, u8 ); IOLockUnlock( _pciConfigLock ); } void AppleIntelPIIXATARoot::pciConfigWrite16( UInt8 offset, UInt16 data, UInt16 mask ) { UInt16 u16; IOLockLock( _pciConfigLock ); u16 = _provider->configRead16( offset ); u16 &= ~mask; u16 |= (mask & data); _provider->configWrite16( offset, u16 ); IOLockUnlock( _pciConfigLock ); } //--------------------------------------------------------------------------- bool AppleIntelPIIXATARoot::serializeProperties( OSSerialize * s ) const { AppleIntelPIIXATARoot * self; char timingString[80]; if ( _provider ) { // Dump the timing registers for debugging. sprintf( timingString, "0x40=%08lx 0x44=%08lx 0x48=%08lx 0x54=%04x", _provider->configRead32( 0x40 ), _provider->configRead32( 0x44 ), _provider->configRead32( 0x48 ), _provider->configRead16( 0x54 ) ); self = (AppleIntelPIIXATARoot *) this; self->setProperty( "PCI Timing Registers", timingString ); } return super::serializeProperties(s); } //--------------------------------------------------------------------------- struct PCSPortMap { UInt8 enableOffset; UInt8 enableMask; UInt8 presenceOffset; UInt8 presenceMask; }; static const PCSPortMap gDefaultPortMap[4] = { { kPIIX_PCI_PCS, kPIIX_PCI_PCS_P0E, kPIIX_PCI_PCS, kPIIX_PCI_PCS_P0P }, { kPIIX_PCI_PCS, kPIIX_PCI_PCS_P1E, kPIIX_PCI_PCS, kPIIX_PCI_PCS_P1P }, { kPIIX_PCI_PCS, kPIIX_PCI_PCS_P2E, kPIIX_PCI_PCS, kPIIX_PCI_PCS_P2P }, { kPIIX_PCI_PCS, kPIIX_PCI_PCS_P3E, kPIIX_PCI_PCS, kPIIX_PCI_PCS_P3P } }; static bool getPCSPortMapping( IORegistryEntry * root, UInt32 portNum, UInt8 * enableOffset, UInt8 * enableMask, UInt8 * presenceOffset, UInt8 * presenceMask ) { OSObject * prop; OSData * data; const PCSPortMap * portMap = gDefaultPortMap; if (!root || (portNum > kSerialATAPort3) || root->getProperty( kSerialATAKey ) != kOSBooleanTrue) return false; prop = root->copyProperty( kPCSPortMapKey ); data = OSDynamicCast(OSData, prop); if (data && (data->getLength() == sizeof(gDefaultPortMap))) { portMap = (const PCSPortMap *) data->getBytesNoCopy(); } if (enableOffset) *enableOffset = portMap[portNum].enableOffset; if (enableMask) *enableMask = portMap[portNum].enableMask; if (presenceOffset) *presenceOffset = portMap[portNum].presenceOffset; if (presenceMask) *presenceMask = portMap[portNum].presenceMask; if (prop) prop->release(); return true; } void AppleIntelPIIXATARoot::setSerialATAPortEnable( UInt32 port, bool enable ) { UInt8 offset; UInt8 mask; if (getPCSPortMapping( this, port, &offset, &mask, NULL, NULL )) { pciConfigWrite8( offset, enable ? mask : 0, mask ); } } bool AppleIntelPIIXATARoot::getSerialATAPortPresentStatus( UInt32 port ) { UInt8 offset; UInt8 mask = 0; UInt8 pcs = 0; if (getPCSPortMapping( this, port, NULL, NULL, &offset, &mask )) { pcs = _provider->configRead8( offset ); } return ( pcs & mask ); }