243 lines
10 KiB
C#
243 lines
10 KiB
C#
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Microsoft Research Singularity
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
// File: NvPciLpcBridge.cs
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
// Driver for Low-Pin Count devices. These are a mix of control
|
||
|
// registers for legacy and current devices. We currently use it to
|
||
|
// reroute integrated peripheral interrupts.
|
||
|
|
||
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.Diagnostics;
|
||
|
|
||
|
using Microsoft.Singularity.Drivers.Pci;
|
||
|
|
||
|
using Microsoft.Singularity.Channels;
|
||
|
using Microsoft.Singularity.Io;
|
||
|
using Microsoft.Singularity.Configuration;
|
||
|
|
||
|
namespace Microsoft.Singularity.Drivers
|
||
|
{
|
||
|
// create the resource object for CTR to fill in
|
||
|
[DriverCategory]
|
||
|
[Signature("pci/ven_10de&dev_0050&cc_0601")] // nForce4
|
||
|
[Signature("pci/ven_10de&dev_0030&cc_0601")] // nForce4 Intel Edition
|
||
|
[EnumeratesDevice("/pnp/PNP0103")]
|
||
|
internal class NvPciLpcResources : DriverCategoryDeclaration
|
||
|
{
|
||
|
// Provide to unify creation.
|
||
|
public static IDevice! DeviceCreate(IoConfig! config, string! name)
|
||
|
{
|
||
|
return new NvPciLpcBridge((PciDeviceConfig) config);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public sealed class NvPciLpcBridge : IBusDevice
|
||
|
{
|
||
|
private const ushort PCI_VENDOR_NVIDIA = 0x10de;
|
||
|
|
||
|
private const ushort PCI_DEVICE_NVIDIA_AMD_SATA0 = 0x0054;
|
||
|
private const ushort PCI_DEVICE_NVIDIA_AMD_SATA1 = 0x0055;
|
||
|
private const ushort PCI_DEVICE_NVIDIA_AMD_MCP04 = 0x0056;
|
||
|
|
||
|
private const ushort PCI_DEVICE_NVIDIA_INTEL_SATA0 = 0x0036;
|
||
|
private const ushort PCI_DEVICE_NVIDIA_INTEL_SATA1 = 0x003e;
|
||
|
private const ushort PCI_DEVICE_NVIDIA_INTEL_MCP04 = 0x0030;
|
||
|
|
||
|
private const int PCI_HPET_POINTER = 0x44;
|
||
|
|
||
|
// Generic PCI interrupt remapping
|
||
|
private const int PCI_INTERRUPT_MAPPING = 0x7c;
|
||
|
private const int SATA0_DEVICE_7_ROLL = 24; // Secondary SATA0 IRQ
|
||
|
private const int SATA1_DEVICE_8_ROLL = 20; // Secondary SATA1 IRQ
|
||
|
private const int PCI_INTR_Z_ROLL = 12;
|
||
|
private const int PCI_INTR_Y_ROLL = 8;
|
||
|
private const int PCI_INTR_X_ROLL = 4;
|
||
|
private const int PCI_INTR_W_ROLL = 0;
|
||
|
|
||
|
private const uint PINT_SEL_DISABLE_IRQ = 0x0;
|
||
|
private const uint PINT_SEL_APIC_IRQ17 = 0x1;
|
||
|
private const uint PINT_SEL_APIC_IRQ18 = 0x2;
|
||
|
private const uint PINT_SEL_IRQ3 = 0x3;
|
||
|
private const uint PINT_SEL_IRQ4 = 0x4;
|
||
|
private const uint PINT_SEL_IRQ5 = 0x5;
|
||
|
private const uint PINT_SEL_IRQ6 = 0x6;
|
||
|
private const uint PINT_SEL_IRQ7 = 0x7;
|
||
|
private const uint PINT_SEL_APIC_IRQ16 = 0x8;
|
||
|
private const uint PINT_SEL_IRQ9 = 0x9;
|
||
|
private const uint PINT_SEL_IRQ10 = 0xa;
|
||
|
private const uint PINT_SEL_IRQ11 = 0xb;
|
||
|
private const uint PINT_SEL_IRQ12 = 0xc;
|
||
|
private const uint PINT_SEL_APIC_IRQ19 = 0xd;
|
||
|
private const uint PINT_SEL_IRQ14 = 0xe;
|
||
|
private const uint PINT_SEL_IRQ15 = 0xf;
|
||
|
|
||
|
// Integrate peripheral mapping
|
||
|
private const int INTERNAL_IRQ0 = 0x80;
|
||
|
private const int INTERNAL_IRQ0_SATA0_ROLL = 28; // Primary SATA
|
||
|
private const int INTERNAL_IRQ0_SATA1_ROLL = 24;
|
||
|
private const int INTERNAL_IRQ0_P2P_ROLL = 16;
|
||
|
private const int INTERNAL_IRQ0_USB2_ROLL = 12;
|
||
|
private const int INTERNAL_IRQ0_SMBUS_ROLL = 8;
|
||
|
private const int INTERNAL_IRQ0_TCO_ROLL = 4;
|
||
|
private const int INTERNAL_IRQ0_SCI_ROLL = 0;
|
||
|
|
||
|
private const int INTERNAL_IRQ1 = 0x84;
|
||
|
private const int INTERNAL_IRQ1_IDE1_ROLL = 28;
|
||
|
private const int INTERNAL_IRQ1_IDE0_ROLL = 24;
|
||
|
private const int INTERNAL_IRQ1_MCI_ROLL = 20;
|
||
|
private const int INTERNAL_IRQ1_ACI_ROLL = 16;
|
||
|
private const int INTERNAL_IRQ1_MAC_ROLL = 8;
|
||
|
private const int INTERNAL_IRQ1_USB0_ROLL = 0;
|
||
|
|
||
|
private const uint INTERNAL_SEL_DISABLE_IRQ = 0x0;
|
||
|
private const uint INTERNAL_SEL_APIC_IRQ23 = 0x1;
|
||
|
private const uint INTERNAL_SEL_APIC_IRQ22 = 0x2;
|
||
|
private const uint INTERNAL_SEL_IRQ3 = 0x3;
|
||
|
private const uint INTERNAL_SEL_IRQ4 = 0x4;
|
||
|
private const uint INTERNAL_SEL_IRQ5 = 0x5;
|
||
|
private const uint INTERNAL_SEL_IRQ6 = 0x6;
|
||
|
private const uint INTERNAL_SEL_IRQ7 = 0x7;
|
||
|
private const uint INTERNAL_SEL_APIC_IRQ20 = 0x8;
|
||
|
private const uint INTERNAL_SEL_IRQ9 = 0x9;
|
||
|
private const uint INTERNAL_SEL_IRQ10 = 0xa;
|
||
|
private const uint INTERNAL_SEL_IRQ11 = 0xb;
|
||
|
private const uint INTERNAL_SEL_IRQ12 = 0xc;
|
||
|
private const uint INTERNAL_SEL_APIC_IRQ21 = 0xd;
|
||
|
private const uint INTERNAL_SEL_IRQ14 = 0xe;
|
||
|
private const uint INTERNAL_SEL_IRQ15 = 0xf;
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
PciDeviceConfig! config;
|
||
|
|
||
|
internal NvPciLpcBridge(PciDeviceConfig! pciDeviceConfig)
|
||
|
{
|
||
|
config = pciDeviceConfig;
|
||
|
}
|
||
|
|
||
|
void IDevice.Initialize()
|
||
|
{
|
||
|
DebugStub.Print("NvPciLpc.Initialize (maxirq={0})\n",
|
||
|
__arglist(IoSystem.GetMaximumIrq()));
|
||
|
// Reroute NVidia fixed device resources on APIC system
|
||
|
if (IoSystem.GetMaximumIrq() > 15)
|
||
|
{
|
||
|
ReroutePciInterrupts();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void IDevice.Finalize()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
private void SetRouteEntry(int register, int roll, uint value)
|
||
|
{
|
||
|
DebugStub.Print("NvPciLpc.SetRouteEntry(reg={0},rol={1},val={2})\n",
|
||
|
__arglist(register, roll, value));
|
||
|
uint r = config.Read32(register) & ~(0x0fu << roll);
|
||
|
r |= value << roll;
|
||
|
config.Write32(register, r);
|
||
|
}
|
||
|
|
||
|
private void ReroutePciInterrupts()
|
||
|
{
|
||
|
PciEnumerator enumerator = PciEnumerator.GetEnumerator(null);
|
||
|
ICollection! pciConfigs = enumerator.Enumerate().Values;
|
||
|
|
||
|
foreach (PciConfig! pc in pciConfigs)
|
||
|
{
|
||
|
if (pc.VendorId == PCI_VENDOR_NVIDIA)
|
||
|
{
|
||
|
// NB - if there are multiple devices on the bus with the
|
||
|
// same ID, we still break. Also note that the parameter
|
||
|
// "1" indicates that this method wants to renumber the 1st
|
||
|
// Irq in the IoConfig.DynamicRanges list, not the 1st
|
||
|
// object.
|
||
|
if ((pc.DeviceId == PCI_DEVICE_NVIDIA_AMD_MCP04 ||
|
||
|
pc.DeviceId == PCI_DEVICE_NVIDIA_INTEL_MCP04) &&
|
||
|
IoSystem.RequestRerouteIrq((!)pc.Id, 1, 23))
|
||
|
{
|
||
|
SetRouteEntry(INTERNAL_IRQ1, INTERNAL_IRQ1_MAC_ROLL,
|
||
|
INTERNAL_SEL_APIC_IRQ23);
|
||
|
pc.SetInterrupt(23);
|
||
|
}
|
||
|
else if ((pc.DeviceId == PCI_DEVICE_NVIDIA_AMD_SATA0 ||
|
||
|
pc.DeviceId == PCI_DEVICE_NVIDIA_INTEL_SATA0) &&
|
||
|
IoSystem.RequestRerouteIrq((!)pc.Id, 1, 14))
|
||
|
{
|
||
|
SetRouteEntry(INTERNAL_IRQ0,
|
||
|
INTERNAL_IRQ0_SATA0_ROLL,
|
||
|
INTERNAL_SEL_IRQ14);
|
||
|
pc.SetInterrupt(14);
|
||
|
|
||
|
// Map secondary same as primary
|
||
|
SetRouteEntry(PCI_INTERRUPT_MAPPING,
|
||
|
SATA0_DEVICE_7_ROLL,
|
||
|
PINT_SEL_IRQ14);
|
||
|
}
|
||
|
else if ((pc.DeviceId == PCI_DEVICE_NVIDIA_AMD_SATA1 ||
|
||
|
pc.DeviceId == PCI_DEVICE_NVIDIA_INTEL_SATA1) &&
|
||
|
IoSystem.RequestRerouteIrq((!)pc.Id, 1, 15))
|
||
|
{
|
||
|
SetRouteEntry(INTERNAL_IRQ0,
|
||
|
INTERNAL_IRQ0_SATA1_ROLL,
|
||
|
INTERNAL_SEL_IRQ15);
|
||
|
pc.SetInterrupt(15);
|
||
|
|
||
|
// Map secondary same as primary
|
||
|
SetRouteEntry(PCI_INTERRUPT_MAPPING,
|
||
|
SATA1_DEVICE_8_ROLL,
|
||
|
PINT_SEL_IRQ15);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public SortedList Enumerate()
|
||
|
{
|
||
|
SortedList devs = new SortedList();
|
||
|
|
||
|
uint hpetPointer = config.Read32(PCI_HPET_POINTER);
|
||
|
|
||
|
if (hpetPointer != 0)
|
||
|
{
|
||
|
IoMemoryRange hpetMemory =
|
||
|
new IoMemoryRange(hpetPointer, 0x1000,
|
||
|
Access.ReadWrite);
|
||
|
|
||
|
devs.Add("/hpet",
|
||
|
new PnpConfig(new string[] { "/pnp/PNP0103" },
|
||
|
new IoRange [1] { hpetMemory }
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
#if NO
|
||
|
//
|
||
|
// XXX sgc compiler does not accept the following preferred
|
||
|
// means of instantiating a PnpConfig object. It complains:
|
||
|
//
|
||
|
// NvPciLpc.cs(189,31): error CS1501:
|
||
|
// No overload for method 'PnpConfig' takes '2' arguments
|
||
|
//
|
||
|
// which is bogus - there are two constructors that take two
|
||
|
// arguments. They are both public and differ only in their
|
||
|
// second argument. This is visible in PnpConfig.cs and in
|
||
|
// the kernel ilasm.
|
||
|
//
|
||
|
ArrayList hpetResources = new ArrayList();
|
||
|
hpetResources.Add(hpetMemory);
|
||
|
devs.Add("/hpet", new PnpConfig("/PNP0103", hpResources));
|
||
|
#endif
|
||
|
return devs;
|
||
|
}
|
||
|
}
|
||
|
}
|