#include <architecture/i386/pio.h>
#include <IOKit/IOService.h>
#include <IOKit/assert.h>
#include "AppleATAPIIX.h"
#include "AppleATAPIIXTiming.h"
extern pmap_t kernel_pmap;
static IOLock * gPIIXLock = 0;
#define PIIX_LOCK IOLockLock(gPIIXLock)
#define PIIX_UNLOCK IOLockUnlock(gPIIXLock)
#define IOREG(x) (ioBMRange + PIIX_IO_ ## x)
#define CHECK_UNIT(drv) assert(drv < 2)
#ifdef DEBUG_XXX
#define DLOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#undef super
#define super IOATAStandardDriver
OSDefineMetaClassAndStructorsWithInit( AppleATAPIIX, IOATAStandardDriver,
AppleATAPIIX::initialize() )
void AppleATAPIIX::initialize()
{
gPIIXLock = IOLockAlloc();
assert(gPIIXLock);
}
static struct {
UInt32 CFID;
const char * name;
} piixDeviceTable[] = {{ PCI_ID_PIIX, "PIIX" },
{ PCI_ID_PIIX3, "PIIX3" },
{ PCI_ID_PIIX4, "PIIX4" },
{ PCI_ID_ICH, "ICH" },
{ PCI_ID_ICH0, "ICH0" },
{ PCI_ID_ICH2_M, "ICH2-M" },
{ PCI_ID_ICH2, "ICH2" },
{ PCI_ID_NONE, NULL }};
static const char *
PIIXGetName(UInt32 pciID)
{
for (int i = 0; piixDeviceTable[i].name; i++) {
if (piixDeviceTable[i].CFID == pciID)
return piixDeviceTable[i].name;
}
return 0;
}
static bool
PIIXVerifyID(UInt32 pciID)
{
return (PIIXGetName(pciID) == 0) ? false : true;
}
static void setupProviderInterrupts(IOPCIDevice * nub, long irq_p, long irq_s)
{
OSArray * controller;
OSArray * specifier;
OSData * tmpData;
extern OSSymbol * gIntelPICName;
do {
specifier = OSArray::withCapacity(2);
if (!specifier)
break;
tmpData = OSData::withBytes(&irq_p, sizeof(irq_p));
if (tmpData) {
specifier->setObject(tmpData);
tmpData->release();
}
tmpData = OSData::withBytes(&irq_s, sizeof(irq_s));
if (tmpData) {
specifier->setObject(tmpData);
tmpData->release();
}
controller = OSArray::withCapacity(2);
if (controller) {
controller->setObject(gIntelPICName);
controller->setObject(gIntelPICName);
nub->setProperty(gIOInterruptControllersKey, controller);
controller->release();
}
nub->setProperty(gIOInterruptSpecifiersKey, specifier);
specifier->release();
} while( false );
}
int AppleATAPIIX::PIIXGetChannel(IOPCIDevice * provider)
{
static bool primaryRegistered = false;
int rc;
extern OSSymbol * gIntelPICName;
PIIX_LOCK;
if (primaryRegistered == false) {
rc = PIIX_CHANNEL_PRIMARY;
primaryRegistered = true;
waitForService(resourceMatching(gIntelPICName));
setupProviderInterrupts(provider, PIIX_P_IRQ, PIIX_S_IRQ);
}
else {
rc = PIIX_CHANNEL_SECONDARY;
}
PIIX_UNLOCK;
if (rc == PIIX_CHANNEL_SECONDARY) IOSleep(20);
return rc;
}
bool AppleATAPIIX::_getIDERanges(IOPCIDevice * provider)
{
ioCmdRange = (channel == PIIX_CHANNEL_PRIMARY) ?
PIIX_P_CMD_ADDR : PIIX_S_CMD_ADDR;
ioCtlRange = (channel == PIIX_CHANNEL_PRIMARY) ?
PIIX_P_CTL_ADDR : PIIX_S_CTL_ADDR;
DLOG("%s: ioCmdRange - %04x\n", getName(), ioCmdRange);
DLOG("%s: ioCtlRange - %04x\n", getName(), ioCtlRange);
return true;
}
bool AppleATAPIIX::_getBMRange(IOPCIDevice * provider)
{
UInt32 bmiba;
bmiba = provider->configRead32(PIIX_PCI_BMIBA);
if ((bmiba & PIIX_PCI_BMIBA_RTE) == 0) {
IOLog("%s: PCI memory range 0x%02x (0x%08lx) is not an I/O range\n",
getName(), PIIX_PCI_BMIBA, bmiba);
return false;
}
bmiba &= PIIX_PCI_BMIBA_MASK;
if (bmiba == 0)
return false;
if (channel == PIIX_CHANNEL_SECONDARY)
bmiba += PIIX_IO_BM_OFFSET;
ioBMRange = (UInt16) bmiba;
DLOG("%s: ioBMRange - %04x\n", getName(), ioBMRange);
return true;
}
bool AppleATAPIIX::_resetTimings()
{
union {
UInt32 b32;
struct {
UInt16 pri;
UInt16 sec;
} b16;
} timing;
UInt32 udmaControl;
PIIX_LOCK;
timing.b32 = provider->configRead32(PIIX_PCI_IDETIM);
udmaControl = provider->configRead32(PIIX_PCI_UDMACTL);
switch (channel) {
case PIIX_CHANNEL_PRIMARY:
timing.b16.pri &= PIIX_PCI_IDETIM_IDE;
udmaControl &= ~(PIIX_PCI_UDMACTL_PSDE0 | PIIX_PCI_UDMACTL_PSDE1);
break;
case PIIX_CHANNEL_SECONDARY:
timing.b16.sec &= PIIX_PCI_IDETIM_IDE;
udmaControl &= ~(PIIX_PCI_UDMACTL_SSDE0 | PIIX_PCI_UDMACTL_SSDE1);
break;
}
provider->configWrite32(PIIX_PCI_UDMACTL, udmaControl);
provider->configWrite32(PIIX_PCI_IDETIM, timing.b32);
provider->configWrite32(PIIX_PCI_IDECONFIG, 0);
PIIX_UNLOCK;
return true;
}
bool AppleATAPIIX::_allocatePRDTable()
{
prdTable = (prdEntry_t *) IOMallocAligned(PRD_TABLE_SIZE, PAGE_SIZE);
if (!prdTable)
return false;
prdTablePhys = (UInt32) pmap_extract(kernel_pmap, (vm_offset_t) prdTable);
bzero(prdTable, PRD_TABLE_SIZE);
return true;
}
void AppleATAPIIX::_deallocatePRDTable()
{
IOFreeAligned(prdTable, PRD_TABLE_SIZE);
prdTable = NULL;
prdTablePhys = 0;
}
bool
AppleATAPIIX::configure(IOService * forProvider,
ATAControllerInfo * controllerInfo)
{
UInt32 reg;
provider = (IOPCIDevice *)forProvider->metaCast("IOPCIDevice");
if (!provider)
return false;
provider->close(this);
pciCFID = provider->configRead32(PIIX_PCI_CFID);
if (PIIXVerifyID(pciCFID) == false) {
IOLog("%s: Unknown PCI IDE controller (0x%08lx)\n",
getName(),
pciCFID);
return false;
}
channel = PIIXGetChannel(provider);
_getIDERanges(provider);
IOLog("%s: %s %s IDE controller, 0x%x, irq %d\n",
getName(),
(channel == PIIX_CHANNEL_PRIMARY) ? "Primary" : "Secondary",
PIIXGetName(pciCFID),
ioCmdRange,
(channel == PIIX_CHANNEL_PRIMARY) ? PIIX_P_IRQ : PIIX_S_IRQ);
reg = provider->configRead32(PIIX_PCI_PCICMD);
if ((reg & PIIX_PCI_PCICMD_IOSE) == 0) {
IOLog("%s: PCI IDE controller is not enabled\n", getName());
return false;
}
if ((reg & PIIX_PCI_PCICMD_BME) == 0) {
reg |= PIIX_PCI_PCICMD_BME;
PIIX_LOCK;
provider->configWrite32(PIIX_PCI_PCICMD, reg);
PIIX_UNLOCK;
}
reg = provider->configRead32(PIIX_PCI_IDETIM);
if (channel == PIIX_CHANNEL_SECONDARY)
reg >>= 16;
if ((reg & PIIX_PCI_IDETIM_IDE) == 0) {
IOLog("%s: %s PCI IDE channel is not enabled\n",
getName(),
(channel == PIIX_CHANNEL_PRIMARY) ? "Primary" : "Secondary");
return false;
}
if (_getBMRange(provider) == false) {
IOLog("%s: Bus master I/O range is invalid\n", getName());
return false;
}
if (_allocatePRDTable() == false) {
IOLog("%s: unable to allocate descriptor table\n", getName());
return false;
}
prdCursor = IOLittleMemoryCursor::withSpecification(64 * 1024, 0xffffffff);
if (prdCursor == 0)
return false;
interruptEventSource = IOInterruptEventSource::interruptEventSource(
(OSObject *) this,
(IOInterruptEventAction) &AppleATAPIIX::interruptOccurred,
(IOService *) provider,
(channel == PIIX_CHANNEL_PRIMARY) ? 0 : 1);
if (interruptEventSource == 0) {
IOLog("%s: unable to create an IOInterruptEventSource object\n",
getName());
return false;
}
disableControllerInterrupts();
getWorkLoop()->addEventSource(interruptEventSource);
_resetTimings();
controllerInfo->maxDevicesSupported = 2;
controllerInfo->devicePrivateDataSize = 0;
controllerInfo->commandPrivateDataSize = 0;
controllerInfo->disableCancelCommands = false;
DLOG("AppleATAPIIX::%s() completed successfully\n", __FUNCTION__);
return true;
}
bool AppleATAPIIX::provideProtocols(enum ATAProtocol * protocolsSupported)
{
return false;
}
bool AppleATAPIIX::provideTimings(UInt32 * numTimings,
ATATiming * timingsSupported)
{
return false;
}
bool AppleATAPIIX::calculateTiming(UInt32 unit, ATATiming * pTiming)
{
int i;
PIIXProtocol protocol = ataToPIIXProtocol(pTiming->timingProtocol);
DLOG("AppleATAPIIX::%s() - unit:%ld protocol:%d minCycles:%ld\n",
__FUNCTION__, unit, protocol, pTiming->minDataCycle);
CHECK_UNIT(unit);
timings[unit].validTimings[protocol] = 0;
switch (protocol) {
case kPIIXProtocolPIO:
for (i = 0; i < PIIXPIOTimingTableSize; i++)
{
if (PIIXPIOTimingTable[i].pioMode == _NVM_)
continue;
if (PIIXPIOTimingTable[i].cycle < pTiming->minDataCycle)
break;
timings[unit].validTimings[protocol] = i;
}
break;
case kPIIXProtocolDMA:
for (i = 0; i < PIIXPIOTimingTableSize; i++)
{
if (PIIXPIOTimingTable[i].mwDMAMode == _NVM_)
continue;
if (PIIXPIOTimingTable[i].cycle < pTiming->minDataCycle)
break;
timings[unit].validTimings[protocol] = i;
}
break;
case kPIIXProtocolUDMA33:
for (i = 0; i < PIIXUDMATimingTableSize; i++)
{
if (PIIXUDMATimingTable[i].strobe < pTiming->minDataCycle)
break;
timings[unit].validTimings[protocol] = i;
}
break;
default:
return false;
}
timings[unit].validFlag |= (1 << protocol);
return true;
}
bool AppleATAPIIX::selectTiming(UInt32 unit,
ATATimingProtocol timingProtocol)
{
bool ret = false;
UInt8 pciConfig[256];
PIIXProtocol protocol = ataToPIIXProtocol(timingProtocol);
DLOG("AppleATAPIIX::%s() - unit:%ld protocol:%d\n",
__FUNCTION__, unit, protocol);
CHECK_UNIT(unit);
PIIX_LOCK;
do {
if (protocol >= kPIIXProtocolLast)
break;
if (PIIX_PROTOCOL_IS_VALID(protocol) == 0) {
IOLog("%s: timing protocol selected is invalid\n", getName());
break;
}
if (!_readPCIConfigSpace(pciConfig) ||
!_selectTiming(unit, protocol, pciConfig) ||
!_writePCIConfigSpace(pciConfig))
break;
ret = true;
}
while (0);
PIIX_UNLOCK;
return ret;
}
bool AppleATAPIIX::_selectTiming(UInt32 unit,
PIIXProtocol protocol,
UInt8 * pciConfig)
{
UInt8 isp, rtc;
UInt8 index, dma, pio;
bool dmaActive;
bool pioActive;
bool useCompatiblePIOTiming = false;
bool ret = true;
UInt16 * idetim;
UInt8 * sidetim = (UInt8 *) &pciConfig[PIIX_PCI_SIDETIM];
UInt8 * udmactl = (UInt8 *) &pciConfig[PIIX_PCI_UDMACTL];
UInt16 * udmatim = (UInt16 *) &pciConfig[PIIX_PCI_UDMATIM];
idetim = (channel == PIIX_CHANNEL_PRIMARY) ?
(UInt16 *) &pciConfig[PIIX_PCI_IDETIM] :
(UInt16 *) &pciConfig[PIIX_PCI_IDETIM_S];
switch (protocol) {
case kPIIXProtocolUDMA66:
return false;
case kPIIXProtocolUDMA33:
if ((pciCFID == PCI_ID_PIIX) || (pciCFID == PCI_ID_PIIX3)) {
return false;
}
PIIX_DEACTIVATE_PROTOCOL(kPIIXProtocolDMA);
break;
case kPIIXProtocolDMA:
PIIX_DEACTIVATE_PROTOCOL(kPIIXProtocolUDMA33);
break;
case kPIIXProtocolPIO:
break;
default:
IOLog("%s: PIIX protocol not handled (%d)\n", getName(),
protocol);
return false;
}
PIIX_ACTIVATE_PROTOCOL(protocol);
if (PIIX_PROTOCOL_IS_ACTIVE(kPIIXProtocolUDMA33)) {
index = PIIX_GET_ACTIVE_TIMING(kPIIXProtocolUDMA33);
if (unit == 0) {
if (channel == PIIX_CHANNEL_PRIMARY) {
*udmactl |= PIIX_PCI_UDMACTL_PSDE0;
SET_REG_FIELD(*udmatim, PIIX_PCI_UDMATIM_PCT0,
PIIXUDMATimingTable[index].bits);
}
else {
*udmactl |= PIIX_PCI_UDMACTL_SSDE0;
SET_REG_FIELD(*udmatim, PIIX_PCI_UDMATIM_SCT0,
PIIXUDMATimingTable[index].bits);
}
}
else {
if (channel == PIIX_CHANNEL_PRIMARY) {
*udmactl |= PIIX_PCI_UDMACTL_PSDE1;
SET_REG_FIELD(*udmatim, PIIX_PCI_UDMATIM_PCT1,
PIIXUDMATimingTable[index].bits);
}
else {
*udmactl |= PIIX_PCI_UDMACTL_SSDE1;
SET_REG_FIELD(*udmatim, PIIX_PCI_UDMATIM_SCT1,
PIIXUDMATimingTable[index].bits);
}
}
}
else {
if (unit == 0) {
if (channel == PIIX_CHANNEL_PRIMARY) {
*udmactl &= ~PIIX_PCI_UDMACTL_PSDE0;
}
else {
*udmactl &= ~PIIX_PCI_UDMACTL_SSDE0;
}
}
else {
if (channel == PIIX_CHANNEL_PRIMARY) {
*udmactl &= ~PIIX_PCI_UDMACTL_PSDE1;
}
else {
*udmactl &= ~PIIX_PCI_UDMACTL_SSDE1;
}
}
}
dmaActive = PIIX_PROTOCOL_IS_ACTIVE(kPIIXProtocolDMA);
pioActive = PIIX_PROTOCOL_IS_ACTIVE(kPIIXProtocolPIO);
if (dmaActive || pioActive) {
dma = PIIX_GET_ACTIVE_TIMING(kPIIXProtocolDMA);
pio = PIIX_GET_ACTIVE_TIMING(kPIIXProtocolPIO);
if (pciCFID == PCI_ID_PIIX) {
unit = (unit ^ 1) & 1;
if (PIIX_PROTOCOL_IS_ACTIVE(kPIIXProtocolPIO)) {
if (!pioActive ||
(PIIX_GET_ACTIVE_TIMING(kPIIXProtocolPIO) < pio)) {
pio = PIIX_GET_ACTIVE_TIMING(kPIIXProtocolPIO);
}
pioActive = true;
}
if (PIIX_PROTOCOL_IS_ACTIVE(kPIIXProtocolDMA)) {
if (!dmaActive ||
(PIIX_GET_ACTIVE_TIMING(kPIIXProtocolDMA) < dma)) {
dma = PIIX_GET_ACTIVE_TIMING(kPIIXProtocolDMA);
}
dmaActive = true;
}
*idetim &= ~PIIX_PCI_IDETIM_SITRE; unit = 0;
}
else {
*idetim |= PIIX_PCI_IDETIM_SITRE; }
if (dmaActive && pioActive) {
index = dma;
if (pio < dma)
useCompatiblePIOTiming = true;
}
else if (dmaActive) {
index = dma;
}
else {
index = pio;
}
isp = PIIX_CLK_TO_ISP(PIIXPIOTimingTable[index].isp);
rtc = PIIX_CLK_TO_RTC(PIIXPIOTimingTable[index].rtc);
if (unit == 0) {
SET_REG_FIELD(*idetim, PIIX_PCI_IDETIM_ISP, isp);
SET_REG_FIELD(*idetim, PIIX_PCI_IDETIM_RTC, rtc);
if (useCompatiblePIOTiming)
*idetim |= PIIX_PCI_IDETIM_DTE0;
else
*idetim &= ~PIIX_PCI_IDETIM_DTE0;
if (pciCFID == PCI_ID_PIIX) {
if (useCompatiblePIOTiming)
*idetim |= PIIX_PCI_IDETIM_DTE1;
else
*idetim &= ~PIIX_PCI_IDETIM_DTE1;
}
}
else {
if (channel == PIIX_CHANNEL_PRIMARY) {
SET_REG_FIELD(*sidetim, PIIX_PCI_SIDETIM_PISP1, isp);
SET_REG_FIELD(*sidetim, PIIX_PCI_SIDETIM_PRTC1, rtc);
}
else {
SET_REG_FIELD(*sidetim, PIIX_PCI_SIDETIM_SISP1, isp);
SET_REG_FIELD(*sidetim, PIIX_PCI_SIDETIM_SRTC1, rtc);
}
if (useCompatiblePIOTiming)
*idetim |= PIIX_PCI_IDETIM_DTE1;
else
*idetim &= ~PIIX_PCI_IDETIM_DTE1;
}
*idetim |= (PIIX_PCI_IDETIM_TIME0 |
PIIX_PCI_IDETIM_PPE0 |
PIIX_PCI_IDETIM_IE0 |
PIIX_PCI_IDETIM_TIME1 |
PIIX_PCI_IDETIM_PPE1 |
PIIX_PCI_IDETIM_IE1);
}
#ifdef DEBUG_XXX
IOLog("\n%s: %s channel\n", getName(),
(channel == PIIX_CHANNEL_PRIMARY) ? "Primary" : "Secondary");
IOLog("%s: IDETIM : %04x\n", getName(), *idetim);
IOLog("%s: SIDETIM: %02x\n", getName(), *sidetim);
IOLog("%s: UDMACTL: %02x\n", getName(), *udmactl);
IOLog("%s: UDMATIM: %04x\n", getName(), *udmatim);
IOLog("%s: Active : %04lx\n", getName(), timings[unit].activeFlag);
IOLog("%s: Valid : %04lx\n", getName(), timings[unit].validFlag);
IOLog("%s: PIO:%d DMA:%d UDMA:%d\n\n", getName(),
timings[unit].activeTimings[kPIIXProtocolPIO],
timings[unit].activeTimings[kPIIXProtocolDMA],
timings[unit].activeTimings[kPIIXProtocolUDMA33]);
#endif
return ret;
}
bool AppleATAPIIX::programDma(IOATAStandardCommand * cmd)
{
IOPhysicalSegment physSeg;
IOByteCount offset = 0;
IOMemoryDescriptor * memDesc;
prdEntry_t * prd = prdTable;
UInt32 startSeg;
UInt32 endSeg;
UInt32 partialCount;
UInt32 bytesLeft;
cmd->getPointers(&memDesc, &dmaReqLength, &dmaIsWrite);
if (dmaReqLength == 0)
return true;
bytesLeft = dmaReqLength;
for (UInt32 i = 0; i < (PRD_ENTRIES - 1); i++, prd++)
{
if (prdCursor->getPhysicalSegments(memDesc, offset, &physSeg, 1) != 1)
break;
startSeg = (physSeg.location & ~0xffff);
endSeg = (physSeg.location + physSeg.length - 1) & ~0xffff;
prd->base = physSeg.location;
prd->flags = 0;
if (startSeg == endSeg) {
prd->count = PRD_COUNT(physSeg.length);
}
else {
partialCount = (-physSeg.location & 0xffff);
prd->count = PRD_COUNT(partialCount);
prd++;
i++;
prd->base = physSeg.location + partialCount;
prd->count = physSeg.length - partialCount;
prd->flags = 0;
}
bytesLeft -= physSeg.length;
offset += physSeg.length;
}
if (bytesLeft != 0)
return false;
prd--;
prd->flags = PRD_FLAG_EOT;
outl(IOREG(BMIDTPX), prdTablePhys);
return true;
}
bool AppleATAPIIX::startDma(IOATAStandardCommand * cmd)
{
outb(IOREG(BMISX), PIIX_IO_BMISX_ERROR |
PIIX_IO_BMISX_IDEINTS |
PIIX_IO_BMISX_DMA0CAP |
PIIX_IO_BMISX_DMA1CAP);
outb(IOREG(BMICX), (dmaIsWrite ? 0 : PIIX_IO_BMICX_RWCON) |
PIIX_IO_BMICX_SSBM);
return true;
}
bool AppleATAPIIX::stopDma(IOATAStandardCommand * cmd, UInt32 * transferCount)
{
UInt8 bmisx;
*transferCount = 0;
if (dmaReqLength == 0)
return true;
outb(IOREG(BMICX), 0);
bmisx = inb(IOREG(BMISX));
if ((bmisx & PIIX_IO_BMISX_STATUS) != PIIX_IO_BMISX_IDEINTS) {
IOLog("AppleATAPIIX::%s() DMA error (0x%02x)\n", __FUNCTION__, bmisx);
return false;
}
*transferCount = dmaReqLength;
return true;
}
void AppleATAPIIX::writeATAReg(UInt32 regIndex, UInt32 regValue)
{
if (regIndex == 0) {
outw(ioCmdRange, (UInt16) regValue);
}
else if (regIndex < kATARegDeviceControl) {
outb(ioCmdRange + regIndex, (UInt8) regValue);
}
else {
outb(ioCtlRange + regIndex - kATARegDeviceControl + 2,
(UInt8) regValue);
}
}
UInt32 AppleATAPIIX::readATAReg( UInt32 regIndex )
{
if (regIndex == 0) {
return inw(ioCmdRange);
}
else if (regIndex < kATARegDeviceControl) {
return inb(ioCmdRange + regIndex);
}
return inb(ioCtlRange + regIndex - kATARegDeviceControl + 2);
}
void AppleATAPIIX::free()
{
if (interruptEventSource) {
interruptEventSource->disable();
interruptEventSource->release();
}
if (prdCursor)
prdCursor->release();
if (prdTable != 0)
_deallocatePRDTable();
return super::free();
}
void AppleATAPIIX::interruptOccurred()
{
super::interruptOccurred();
}
void AppleATAPIIX::disableControllerInterrupts()
{
interruptEventSource->disable();
}
void AppleATAPIIX::enableControllerInterrupts()
{
interruptEventSource->enable();
}
bool AppleATAPIIX::_readPCIConfigSpace(UInt8 * configSpace)
{
UInt32 * dwordPtr = (UInt32 *) configSpace;
for (int i = 0; i < 64; i++, dwordPtr++)
*dwordPtr = provider->configRead32(i * 4);
return true;
}
bool AppleATAPIIX::_writePCIConfigSpace(UInt8 * configSpace)
{
UInt32 * dwordPtr = (UInt32 *) configSpace;
for (int i = 0; i < 64; i++, dwordPtr++)
provider->configWrite32(i * 4, *dwordPtr);
return true;
}