os-x-intel-network/IntelMausiEthernet/IntelMausiHardware.cpp

1768 lines
55 KiB
C++
Raw Normal View History

/* IntelMausiHardware.cpp -- IntelMausi hardware specific routines.
*
* Copyright (c) 2014 Laura Müller <laura-mueller@uni-duesseldorf.de>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* Driver for Intel PCIe gigabit ethernet controllers.
*
* This driver is based on Intel's E1000e driver for Linux.
*/
#include "IntelMausiEthernet.h"
#pragma mark --- hardware initialization methods ---
bool IntelMausi::initPCIConfigSpace(IOPCIDevice *provider)
{
UInt32 lat1, lat2;
bool result = false;
/* Get vendor and device info. */
pciDeviceData.vendor = provider->extendedConfigRead16(kIOPCIConfigVendorID);
pciDeviceData.device = provider->extendedConfigRead16(kIOPCIConfigDeviceID);
pciDeviceData.subsystem_vendor = provider->extendedConfigRead16(kIOPCIConfigSubSystemVendorID);
pciDeviceData.subsystem_device = provider->extendedConfigRead16(kIOPCIConfigSubSystemID);
pciDeviceData.revision = provider->extendedConfigRead8(kIOPCIConfigRevisionID);
/* Identify the chipset. */
if (!intelIdentifyChip())
goto done;
if (chipType >= board_pch_lpt) {
pciDeviceData.maxSnoop = provider->extendedConfigRead16(E1000_PCI_LTR_CAP_LPT);
pciDeviceData.maxNoSnoop = provider->extendedConfigRead16(E1000_PCI_LTR_CAP_LPT + 2);
lat1 = (pciDeviceData.maxSnoop & 0x3ff) << (((pciDeviceData.maxSnoop >> 10) & 0x7) * 5);
lat2 = (pciDeviceData.maxNoSnoop & 0x3ff) << (((pciDeviceData.maxNoSnoop >> 10) & 0x7) * 5);
maxLatency = (lat1 > lat2) ? lat1 : lat2;
if (maxLatency > kMaxDmaLatency)
maxLatency = kMaxDmaLatency;
DebugLog("Ethernet [IntelMausi]: maxSnoop: 0x%04x (%uns), maxNoSnoop: 0x%04x (%uns).\n", pciDeviceData.maxSnoop, lat1, pciDeviceData.maxNoSnoop, lat2);
}
/* Get the bus information. */
adapterData.hw.bus.func = pciDevice->getFunctionNumber();
adapterData.hw.bus.width = e1000_bus_width_pcie_x1;
/* Enable the device. */
intelEnablePCIDevice(provider);
baseMap = provider->mapDeviceMemoryWithRegister(kIOPCIConfigBaseAddress0, kIOMapInhibitCache);
if (!baseMap) {
IOLog("Ethernet [IntelMausi]: region #0 not an MMIO resource, aborting.\n");
goto done;
}
baseAddr = reinterpret_cast<volatile void *>(baseMap->getVirtualAddress());
adapterData.hw.hw_addr = (u8 __iomem *)baseAddr;
adapterData.hw.flash_address = NULL;
if ((adapterData.flags & FLAG_HAS_FLASH) && (adapterData.hw.mac.type < e1000_pch_spt)) {
flashMap = provider->mapDeviceMemoryWithRegister(kIOPCIConfigBaseAddress1, kIOMapInhibitCache);
if (!flashMap) {
IOLog("Ethernet [IntelMausi]: region #1 not an MMIO resource, aborting.\n");
goto error;
}
flashAddr = reinterpret_cast<volatile void *>(flashMap->getVirtualAddress());
adapterData.hw.flash_address = (u8 __iomem *)flashAddr;
}
result = true;
done:
return result;
error:
RELEASE(baseMap);
baseMap = NULL;
adapterData.hw.hw_addr = NULL;
goto done;
}
void IntelMausi::initPCIPowerManagment(IOPCIDevice *provider, const struct e1000_info *ei)
{
UInt32 pcieLinkCap;
UInt16 pcieLinkCtl;
UInt16 aspmDisable;
UInt16 pmCap;
UInt8 pmCapOffset;
/* Setup power management. */
if (provider->findPCICapability(kIOPCIPowerManagementCapability, &pmCapOffset)) {
pmCap = provider->extendedConfigRead16(pmCapOffset + kIOPCIPMCapability);
DebugLog("Ethernet [IntelMausi]: PCI power management capabilities: 0x%x.\n", pmCap);
if (pmCap & (kPCIPMCPMESupportFromD3Cold | kPCIPMCPMESupportFromD3Hot)) {
wolCapable = true;
DebugLog("Ethernet [IntelMausi]: PME# from D3 (cold/hot) supported.\n");
}
pciPMCtrlOffset = pmCapOffset + kIOPCIPMControl;
} else {
IOLog("Ethernet [IntelMausi]: PCI power management unsupported.\n");
}
provider->enablePCIPowerManagement();
/* Get PCIe link information. */
if (provider->findPCICapability(kIOPCIPCIExpressCapability, &pcieCapOffset)) {
pcieLinkCap = provider->extendedConfigRead32(pcieCapOffset + kIOPCIELinkCapability);
pcieLinkCtl = provider->extendedConfigRead16(pcieCapOffset + kIOPCIELinkControl);
DebugLog("Ethernet [IntelMausi]: PCIe link capabilities: 0x%08x, link control: 0x%04x.\n", pcieLinkCap, pcieLinkCtl);
aspmDisable = 0;
if (ei->flags2 & FLAG2_DISABLE_ASPM_L0S)
aspmDisable |= kIOPCIELinkCtlL0s;
if (ei->flags2 & FLAG2_DISABLE_ASPM_L1)
aspmDisable |= kIOPCIELinkCtlL1;
if (aspmDisable)
provider->extendedConfigWrite16(pcieCapOffset + kIOPCIELinkControl, (pcieLinkCtl & ~aspmDisable));
#ifdef DEBUG
pcieLinkCtl = provider->extendedConfigRead16(pcieCapOffset + kIOPCIELinkControl);
if (pcieLinkCtl & (kIOPCIELinkCtlASPM | kIOPCIELinkCtlClkReqEn))
IOLog("Ethernet [IntelMausi]: PCIe ASPM enabled. link control: 0x%04x.\n", pcieLinkCtl);
else
IOLog("Ethernet [IntelMausi]: PCIe ASPM disabled. link control: 0x%04x.\n", pcieLinkCtl);
#endif /* DEBUG */
}
}
IOReturn IntelMausi::setPowerStateWakeAction(OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4)
{
IntelMausi *ethCtlr = OSDynamicCast(IntelMausi, owner);
IOPCIDevice *dev;
UInt16 val16;
UInt8 offset;
if (ethCtlr) {
dev = ethCtlr->pciDevice;
offset = ethCtlr->pciPMCtrlOffset;
val16 = dev->extendedConfigRead16(offset);
val16 &= ~(kPCIPMCSPowerStateMask | kPCIPMCSPMEStatus | kPCIPMCSPMEEnable);
val16 |= kPCIPMCSPowerStateD0;
dev->extendedConfigWrite16(offset, val16);
/* Restore the PCI Command register. */
ethCtlr->intelEnablePCIDevice(dev);
}
return kIOReturnSuccess;
}
IOReturn IntelMausi::setPowerStateSleepAction(OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4)
{
IntelMausi *ethCtlr = OSDynamicCast(IntelMausi, owner);
IOPCIDevice *dev;
UInt16 val16;
UInt8 offset;
if (ethCtlr) {
dev = ethCtlr->pciDevice;
offset = ethCtlr->pciPMCtrlOffset;
val16 = dev->extendedConfigRead16(offset);
val16 &= ~(kPCIPMCSPowerStateMask | kPCIPMCSPMEStatus | kPCIPMCSPMEEnable);
if (ethCtlr->wolActive)
val16 |= (kPCIPMCSPMEStatus | kPCIPMCSPMEEnable | kPCIPMCSPowerStateD3);
else
val16 |= kPCIPMCSPowerStateD3;
dev->extendedConfigWrite16(offset, val16);
}
return kIOReturnSuccess;
}
void IntelMausi::intelEEPROMChecks(struct e1000_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
int ret_val;
u16 buf = 0;
if (hw->mac.type != e1000_82573)
return;
ret_val = e1000_read_nvm(hw, NVM_INIT_CONTROL2_REG, 1, &buf);
le16_to_cpus(&buf);
if (!ret_val && (!(buf & (1 << 0)))) {
/* Deep Smart Power Down (DSPD) */
IOLog("Ethernet [IntelMausi]: Warning: detected DSPD enabled in EEPROM.\n");
}
}
void IntelMausi::intelEnableIRQ(UInt32 newMask)
{
intelWriteMem32(E1000_IMC, ~newMask);
intelWriteMem32(E1000_IMS, newMask);
intelFlush();
}
void IntelMausi::intelDisableIRQ()
{
intelWriteMem32(E1000_IMC, 0xFFFFFFFF);
intelFlush();
}
void IntelMausi::intelEnable()
{
struct e1000_hw *hw = &adapterData.hw;
const IONetworkMedium *selectedMedium;
selectedMedium = getSelectedMedium();
if (!selectedMedium) {
DebugLog("Ethernet [IntelMausi]: No medium selected. Falling back to autonegotiation.\n");
selectedMedium = mediumTable[MEDIUM_INDEX_AUTO];
setCurrentMedium(selectedMedium);
}
/* Check if we re waking up from sleep with WoL enabled and still have a valid link. */
if (!linkOpts || !intelCheckLink(&adapterData)) {
linkOpts = 0;
setLinkStatus(kIONetworkLinkValid);
}
polling = false;
intelSetupAdvForMedium(selectedMedium);
e1000_phy_hw_reset(hw);
if (hw->mac.type >= e1000_pch2lan)
e1000_resume_workarounds_pchlan(hw);
e1000e_power_up_phy(&adapterData);
/* If AMT is enabled, let the firmware know that the network
* interface is now open and reset the part to a known state.
*/
if (adapterData.flags & FLAG_HAS_AMT) {
e1000e_get_hw_control(&adapterData);
}
intelReset(&adapterData);
#if DISABLED_CODE
/* DMA latency requirement to workaround jumbo issue */
pm_qos_add_request(&adapter->netdev->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
PM_QOS_DEFAULT_VALUE);
#endif /* DISABLED_CODE */
intelConfigure(&adapterData);
/* From here on the code is the same as e1000e_up() */
clear_bit(__E1000_DOWN, &adapterData.state);
intelEnableIRQ(intrMask);
hw->mac.get_link_status = true;
}
void IntelMausi::intelDisable()
{
struct e1000_hw *hw = &adapterData.hw;
UInt32 linkStatus = kIONetworkLinkValid;
UInt32 wufc = adapterData.wol;
UInt32 ctrl, ctrlExt, rctl, status;
int retval;
polling = false;
linkOpts = 0;
/* Flush LPIC. */
intelFlushLPIC();
status = intelReadMem32(E1000_STATUS);
if (status & E1000_STATUS_LU)
wufc &= ~E1000_WUFC_LNKC;
if (wolActive && wufc) {
intelDown(&adapterData, false);
intelSetupRxControl(&adapterData);
rctl = intelReadMem32(E1000_RCTL);
rctl &= ~(E1000_RCTL_UPE | E1000_RCTL_MPE);
/* turn on all-multi mode if wake on multicast is enabled */
if (wufc & E1000_WUFC_MC)
rctl |= E1000_RCTL_MPE;
intelWriteMem32(E1000_RCTL, rctl);
ctrl = intelReadMem32(E1000_CTRL);
ctrl |= E1000_CTRL_ADVD3WUC;
if (!(adapterData.flags2 & FLAG2_HAS_PHY_WAKEUP))
ctrl |= E1000_CTRL_EN_PHY_PWR_MGMT;
intelWriteMem32(E1000_CTRL, ctrl);
if (adapterData.hw.phy.media_type == e1000_media_type_fiber ||
adapterData.hw.phy.media_type == e1000_media_type_internal_serdes) {
/* keep the laser running in D3 */
ctrlExt = intelReadMem32(E1000_CTRL_EXT);
ctrlExt |= E1000_CTRL_EXT_SDP3_DATA;
intelWriteMem32(E1000_CTRL_EXT, ctrlExt);
}
if (adapterData.flags & FLAG_IS_ICH)
e1000_suspend_workarounds_ich8lan(hw);
if (adapterData.flags2 & FLAG2_HAS_PHY_WAKEUP) {
/* enable wakeup by the PHY */
intelInitPhyWakeup(wufc);
} else {
/* enable wakeup by the MAC */
intelWriteMem32(E1000_WUFC, wufc);
intelWriteMem32(E1000_WUC, E1000_WUC_PME_EN);
}
DebugLog("Ethernet [IntelMausi]: WUFC=0x%08x.\n", wufc);
linkOpts = kIONetworkLinkNoNetworkChange;
linkStatus |= linkOpts;
} else {
intelDown(&adapterData, true);
intelWriteMem32(E1000_WUC, 0);
intelWriteMem32(E1000_WUFC, 0);
intelPowerDownPhy(&adapterData);
/* ULP seems to cause trouble on I218LM3 and I218V3. Enable it only for I219V/LM. */
if (hw->mac.type >= e1000_pch_spt) {
if (!(wufc & (E1000_WUFC_EX | E1000_WUFC_MC | E1000_WUFC_BC)))
/* ULP does not support wake from unicast, multicast
* or broadcast.
*/
e1000_enable_ulp_lpt_lp(hw, false);
}
}
/* Ensure that the appropriate bits are set in LPI_CTRL
* for EEE in Sx
*/
if ((hw->phy.type >= e1000_phy_i217) && adapterData.eee_advert && hw->dev_spec.ich8lan.eee_lp_ability) {
UInt16 lpi_ctrl = 0;
retval = hw->phy.ops.acquire(hw);
if (!retval) {
retval = e1e_rphy_locked(hw, I82579_LPI_CTRL, &lpi_ctrl);
if (!retval) {
if (adapterData.eee_advert &
hw->dev_spec.ich8lan.eee_lp_ability &
I82579_EEE_100_SUPPORTED)
lpi_ctrl |= I82579_LPI_CTRL_100_ENABLE;
if (adapterData.eee_advert &
hw->dev_spec.ich8lan.eee_lp_ability &
I82579_EEE_1000_SUPPORTED)
lpi_ctrl |= I82579_LPI_CTRL_1000_ENABLE;
retval = e1e_wphy_locked(hw, I82579_LPI_CTRL,
lpi_ctrl);
}
}
hw->phy.ops.release(hw);
}
/* If AMT is enabled, let the firmware know that the network
* interface is now closed
*/
if (adapterData.flags & FLAG_HAS_AMT)
e1000e_release_hw_control(&adapterData);
if (linkUp) {
linkUp = false;
setLinkStatus(linkStatus);
IOLog("Ethernet [IntelMausi]: Link down on en%u\n", netif->getUnitNumber());
}
}
/**
* intelConfigure - configure the hardware for Rx and Tx
* @adapter: private board structure
**/
void IntelMausi::intelConfigure(struct e1000_adapter *adapter)
{
setMulticastMode(true);
intelInitManageabilityPt(adapter);
intelSetupRssHash(adapter);
intelVlanStripEnable(adapter);
intelConfigureTx(adapter);
intelSetupRxControl(adapter);
intelConfigureRx(adapter);
}
/**
* intelConfigureTx - Configure Transmit Unit after Reset
* @adapter: board private structure
*
* Configure the Tx unit of the MAC after a reset.
**/
void IntelMausi::intelConfigureTx(struct e1000_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
UInt32 tctl, tarc;
UInt32 txdctl;
/* Setup the HW Tx Head and Tail descriptor pointers */
intelInitTxRing();
/* Set the Tx Interrupt Delay register */
intelWriteMem32(E1000_TIDV, adapter->tx_int_delay);
/* Tx irq moderation */
intelWriteMem32(E1000_TADV, adapter->tx_abs_int_delay);
txdctl = intelReadMem32(E1000_TXDCTL(0));
/* Fix TXDCTL to disable descriptor prefetch for 82579, I217, I218 and I219. */
if (chipType >= board_pch2lan) {
txdctl = 0x01410000;
intelWriteMem32(E1000_TXDCTL(0), txdctl);
}
/* erratum work around: set txdctl the same for both queues */
intelWriteMem32(E1000_TXDCTL(1), txdctl);
/* Program the Transmit Control Register */
tctl = intelReadMem32(E1000_TCTL);
tctl &= ~E1000_TCTL_CT;
tctl |= E1000_TCTL_PSP | E1000_TCTL_RTLC | (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT);
/* errata: program both queues to unweighted RR */
if (adapter->flags & FLAG_TARC_SET_BIT_ZERO) {
tarc = intelReadMem32(E1000_TARC(0));
tarc |= 1;
intelWriteMem32(E1000_TARC(0), tarc);
tarc = intelReadMem32(E1000_TARC(1));
tarc |= 1;
intelWriteMem32(E1000_TARC(1), tarc);
}
intelWriteMem32(E1000_TCTL, tctl);
hw->mac.ops.config_collision_dist(hw);
/* SPT Si errata workaround to avoid data corruption */
if (hw->mac.type == e1000_pch_spt) {
UInt32 val;
val = intelReadMem32(E1000_IOSFPC);
val |= E1000_RCTL_RDMTS_HEX;
intelWriteMem32(E1000_IOSFPC, val);
/*
* SPT and KBL Si errata workaround to avoid Tx hang.
* Dropping the number of outstanding requests from
* 3 to 2 in order to avoid a buffer overrun.
*/
val = intelReadMem32(E1000_TARC(0));
val &= ~E1000_TARC0_CB_MULTIQ_3_REQ;
val |= E1000_TARC0_CB_MULTIQ_2_REQ;
intelWriteMem32(E1000_TARC(0), val);
}
}
/**
* intelSetupRxControl - configure the receive control registers
* @adapter: Board private structure
**/
void IntelMausi::intelSetupRxControl(struct e1000_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
u32 rctl, rfctl;
/* Workaround Si errata on PCHx - configure jumbo frame flow.
* If jumbo frames not set, program related MAC/PHY registers
* to h/w defaults
*/
if (hw->mac.type >= e1000_pch2lan) {
s32 ret_val;
if (mtu > ETH_DATA_LEN)
ret_val = e1000_lv_jumbo_workaround_ich8lan(hw, true);
else
ret_val = e1000_lv_jumbo_workaround_ich8lan(hw, false);
if (ret_val)
DebugLog("Ethernet [IntelMausi]: failed to enable/disable jumbo frame workaround mode.\n");
}
/* Program MC offset vector base */
rctl = intelReadMem32(E1000_RCTL);
rctl &= ~(3 << E1000_RCTL_MO_SHIFT);
rctl |= E1000_RCTL_BAM | E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF | (adapter->hw.mac.mc_filter_type << E1000_RCTL_MO_SHIFT);
/* Do not Store bad packets */
rctl &= ~E1000_RCTL_SBP;
/* Enable Long Packet receive */
if (mtu <= ETH_DATA_LEN)
rctl &= ~E1000_RCTL_LPE;
else
rctl |= E1000_RCTL_LPE;
/* Some systems expect that the CRC is included in SMBUS traffic. The
* hardware strips the CRC before sending to both SMBUS (BMC) and to
* host memory when this is enabled
*
* We setup hardware to always strip CRC as we want jumbo frame support
* anyway even if it breaks management SMBUS traffic on some machines.
*/
rctl |= E1000_RCTL_SECRC;
/* Workaround Si errata on 82577 PHY - configure IPG for jumbos */
if ((hw->phy.type == e1000_phy_82577) && (rctl & E1000_RCTL_LPE)) {
u16 phy_data;
e1e_rphy(hw, PHY_REG(770, 26), &phy_data);
phy_data &= 0xfff8;
phy_data |= (1 << 2);
e1e_wphy(hw, PHY_REG(770, 26), phy_data);
e1e_rphy(hw, 22, &phy_data);
phy_data &= 0x0fff;
phy_data |= (1 << 14);
e1e_wphy(hw, 0x10, 0x2823);
e1e_wphy(hw, 0x11, 0x0003);
e1e_wphy(hw, 22, phy_data);
}
/* Set buffer sizes to 2048 */
//rctl |= (0x2 << E1000_RCTL_FLXB_SHIFT);
rctl &= ~(E1000_RCTL_SZ_256 | E1000_RCTL_BSEX);
/* Enable Extended Status in all Receive Descriptors */
rfctl = intelReadMem32(E1000_RFCTL);
rfctl |= (E1000_RFCTL_NEW_IPV6_EXT_DIS | E1000_RFCTL_IPV6_EX_DIS | E1000_RFCTL_EXTEN | E1000_RFCTL_NFSW_DIS | E1000_RFCTL_NFSR_DIS);
intelWriteMem32(E1000_RFCTL, rfctl);
intelWriteMem32(E1000_RCTL, rctl);
}
/**
* intelConfigureRx - Configure Receive Unit after Reset
* @adapter: board private structure
*
* Configure the Rx unit of the MAC after a reset.
**/
void IntelMausi::intelConfigureRx(struct e1000_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
u32 rctl, rxcsum, ctrl_ext;
/* disable receives while setting up the descriptors */
rctl = intelReadMem32(E1000_RCTL);
intelFlush();
usleep_range(10000, 20000);
/* set the Receive Delay Timer Register */
intelWriteMem32(E1000_RDTR, adapter->rx_int_delay);
/* irq moderation */
intelWriteMem32(E1000_RADV, adapter->rx_abs_int_delay);
/* Set interrupt throttle value. */
intelWriteMem32(E1000_ITR, intrThrValue);
/* Auto-Mask interrupts upon ICR access. */
ctrl_ext = intelReadMem32(E1000_CTRL_EXT);
ctrl_ext |= E1000_CTRL_EXT_IAME;
intelWriteMem32(E1000_IAM, 0xffffffff);
intelWriteMem32(E1000_CTRL_EXT, ctrl_ext);
e1e_flush();
/* Setup the HW Rx Head and Tail Descriptor Pointers and
* the Base and Length of the Rx Descriptor Ring
*/
intelInitRxRing();
/* Enable Receive Checksum Offload for TCP and UDP */
rxcsum = intelReadMem32(E1000_RXCSUM);
rxcsum |= E1000_RXCSUM_TUOFL;
intelWriteMem32(E1000_RXCSUM, rxcsum);
/* With jumbo frames, excessive C-state transition latencies result
* in dropped transactions. Latency requirements will be adjusted
* when the link has been established and speed is known.
*/
if ((mtu > ETH_DATA_LEN) && (adapter->flags & FLAG_IS_ICH)) {
u32 rxdctl = intelReadMem32(E1000_RXDCTL(0));
intelWriteMem32(E1000_RXDCTL(0), rxdctl | 0x3);
}
intelWriteMem32(E1000_RCTL, rctl);
}
/**
* intelDown - quiesce the device and optionally reset the hardware
* @adapter: board private structure
* @reset: boolean flag to reset the hardware or not
*/
void IntelMausi::intelDown(struct e1000_adapter *adapter, bool reset)
{
struct e1000_hw *hw = &adapter->hw;
UInt32 tctl, rctl;
/* signal that we're down so the interrupt handler does not
* reschedule our watchdog timer
*/
set_bit(__E1000_DOWN, &adapter->state);
/* disable receives in the hardware */
rctl = intelReadMem32(E1000_RCTL);
rctl &= ~E1000_RCTL_EN;
intelWriteMem32(E1000_RCTL, rctl);
/* disable transmits in the hardware */
tctl = intelReadMem32(E1000_TCTL);
tctl &= ~E1000_TCTL_EN;
intelWriteMem32(E1000_TCTL, tctl);
/* flush both disables and wait for them to finish */
intelFlush();
usleep_range(10000, 20000);
intelDisableIRQ();
updateStatistics(adapter);
adapter->link_speed = 0;
adapter->link_duplex = 0;
/* Disable Si errata workaround on PCHx for jumbo frame flow */
if ((hw->mac.type >= e1000_pch2lan) && (mtu > ETH_DATA_LEN) && e1000_lv_jumbo_workaround_ich8lan(hw, false))
DebugLog("Ethernet [IntelMausi]: failed to disable jumbo frame workaround mode\n");
if (reset)
intelReset(adapter);
else if (hw->mac.type >= e1000_pch_spt)
intelFlushDescRings(adapter);
clearDescriptors();
if (chipType >= board_pch_lpt)
requireMaxBusStall(0);
}
void IntelMausi::intelInitManageabilityPt(struct e1000_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
u32 manc, manc2h, mdef, i, j;
if (!(adapter->flags & FLAG_MNG_PT_ENABLED))
return;
manc = intelReadMem32(E1000_MANC);
/* enable receiving management packets to the host. this will probably
* generate destination unreachable messages from the host OS, but
* the packets will be handled on SMBUS
*/
manc |= E1000_MANC_EN_MNG2HOST;
manc2h = intelReadMem32(E1000_MANC2H);
switch (hw->mac.type) {
default:
manc2h |= (E1000_MANC2H_PORT_623 | E1000_MANC2H_PORT_664);
break;
case e1000_82574:
case e1000_82583:
/* Check if IPMI pass-through decision filter already exists;
* if so, enable it.
*/
for (i = 0, j = 0; i < 8; i++) {
mdef = intelReadMem32(E1000_MDEF(i));
/* Ignore filters with anything other than IPMI ports */
if (mdef & ~(E1000_MDEF_PORT_623 | E1000_MDEF_PORT_664))
continue;
/* Enable this decision filter in MANC2H */
if (mdef)
manc2h |= (1 << i);
j |= mdef;
}
if (j == (E1000_MDEF_PORT_623 | E1000_MDEF_PORT_664))
break;
/* Create new decision filter in an empty filter */
for (i = 0, j = 0; i < 8; i++)
if (intelReadMem32(E1000_MDEF(i)) == 0) {
intelWriteMem32(E1000_MDEF(i), (E1000_MDEF_PORT_623 | E1000_MDEF_PORT_664));
manc2h |= (1 << 1);
j++;
break;
}
if (!j)
IOLog("Ethernet [IntelMausi]: Unable to create IPMI pass-through filter.\n");
break;
}
intelWriteMem32(E1000_MANC2H, manc2h);
intelWriteMem32(E1000_MANC, manc);
}
/**
* intelReset - bring the hardware into a known good state
*
* This function boots the hardware and enables some settings that
* require a configuration cycle of the hardware - those cannot be
* set/changed during runtime. After reset the device needs to be
* properly configured for Rx, Tx etc.
*/
void IntelMausi::intelReset(struct e1000_adapter *adapter)
{
struct e1000_mac_info *mac = &adapter->hw.mac;
struct e1000_fc_info *fc = &adapter->hw.fc;
struct e1000_hw *hw = &adapter->hw;
u32 tx_space, min_tx_space, min_rx_space;
u32 pba = adapter->pba;
u16 hwm;
/* reset Packet Buffer Allocation to default */
intelWriteMem32(E1000_PBA, pba);
if (adapter->max_frame_size > ETH_FRAME_LEN + ETH_FCS_LEN) {
/* To maintain wire speed transmits, the Tx FIFO should be
* large enough to accommodate two full transmit packets,
* rounded up to the next 1KB and expressed in KB. Likewise,
* the Rx FIFO should be large enough to accommodate at least
* one full receive packet and is similarly rounded up and
* expressed in KB.
*/
pba = intelReadMem32(E1000_PBA);
/* upper 16 bits has Tx packet buffer allocation size in KB */
tx_space = pba >> 16;
/* lower 16 bits has Rx packet buffer allocation size in KB */
pba &= 0xffff;
/* the Tx fifo also stores 16 bytes of information about the Tx
* but don't include ethernet FCS because hardware appends it
*/
min_tx_space = (adapter->max_frame_size +
sizeof(struct e1000_tx_desc) - ETH_FCS_LEN) * 2;
min_tx_space = ALIGN(min_tx_space, 1024);
min_tx_space >>= 10;
/* software strips receive CRC, so leave room for it */
min_rx_space = adapter->max_frame_size;
min_rx_space = ALIGN(min_rx_space, 1024);
min_rx_space >>= 10;
/* If current Tx allocation is less than the min Tx FIFO size,
* and the min Tx FIFO size is less than the current Rx FIFO
* allocation, take space away from current Rx allocation
*/
if ((tx_space < min_tx_space) &&
((min_tx_space - tx_space) < pba)) {
pba -= min_tx_space - tx_space;
/* if short on Rx space, Rx wins and must trump Tx
* adjustment
*/
if (pba < min_rx_space)
pba = min_rx_space;
}
intelWriteMem32(E1000_PBA, pba);
}
/* flow control settings
*
* The high water mark must be low enough to fit one full frame
* (or the size used for early receive) above it in the Rx FIFO.
* Set it to the lower of:
* - 90% of the Rx FIFO size, and
* - the full Rx FIFO size minus one full frame
*/
if (adapter->flags & FLAG_DISABLE_FC_PAUSE_TIME)
fc->pause_time = 0xFFFF;
else
fc->pause_time = E1000_FC_PAUSE_TIME;
fc->send_xon = true;
fc->current_mode = fc->requested_mode;
switch (hw->mac.type) {
case e1000_ich9lan:
case e1000_ich10lan:
if (mtu > ETH_DATA_LEN) {
pba = 14;
intelWriteMem32(E1000_PBA, pba);
fc->high_water = 0x2800;
fc->low_water = fc->high_water - 8;
break;
}
/* fall-through */
default:
hwm = min(((pba << 10) * 9 / 10),
((pba << 10) - adapter->max_frame_size));
fc->high_water = hwm & E1000_FCRTH_RTH; /* 8-byte granularity */
fc->low_water = fc->high_water - 8;
break;
case e1000_pchlan:
/* Workaround PCH LOM adapter hangs with certain network
* loads. If hangs persist, try disabling Tx flow control.
*/
if (mtu > ETH_DATA_LEN) {
fc->high_water = 0x3500;
fc->low_water = 0x1500;
} else {
fc->high_water = 0x5000;
fc->low_water = 0x3000;
}
fc->refresh_time = 0x1000;
break;
case e1000_pch2lan:
case e1000_pch_lpt:
case e1000_pch_spt:
case e1000_pch_cnp:
fc->refresh_time = 0x0400;
if (mtu <= ETH_DATA_LEN) {
fc->high_water = 0x05C20;
fc->low_water = 0x05048;
fc->pause_time = 0x0650;
break;
}
pba = 14;
intelWriteMem32(E1000_PBA, pba);
fc->high_water = ((pba << 10) * 9 / 10) & E1000_FCRTH_RTH;
fc->low_water = ((pba << 10) * 8 / 10) & E1000_FCRTL_RTL;
break;
}
/* Alignment of Tx data is on an arbitrary byte boundary with the
* maximum size per Tx descriptor limited only to the transmit
* allocation of the packet buffer minus 96 bytes with an upper
* limit of 24KB due to receive synchronization limitations.
*/
adapter->tx_fifo_limit = min_t(u32, ((intelReadMem32(E1000_PBA) >> 16) << 10) - 96,
24 << 10);
/* Set interrupt throttle value. */
intelWriteMem32(E1000_ITR, intrThrValue);
if (hw->mac.type >= e1000_pch_spt)
intelFlushDescRings(adapter);
/* Allow time for pending master requests to run */
mac->ops.reset_hw(hw);
/* For parts with AMT enabled, let the firmware know
* that the network interface is in control
*/
if (adapter->flags & FLAG_HAS_AMT)
e1000e_get_hw_control(adapter);
intelWriteMem32(E1000_WUC, 0);
if (mac->ops.init_hw(hw))
IOLog("Ethernet [IntelMausi]: Hardware Error.\n");
//e1000_update_mng_vlan(adapter);
/* Enable h/w to recognize an 802.1Q VLAN Ethernet packet */
intelWriteMem32(E1000_VET, ETH_P_8021Q);
intelResetAdaptive(hw);
/* initialize systim and reset the ns time counter */
//e1000e_config_hwtstamp(adapter, &adapter->hwtstamp_config);
/* Set EEE advertisement as appropriate */
if (adapter->flags2 & FLAG2_HAS_EEE) {
s32 ret_val;
u16 adv_addr;
switch (hw->phy.type) {
case e1000_phy_82579:
adv_addr = I82579_EEE_ADVERTISEMENT;
break;
case e1000_phy_i217:
adv_addr = I217_EEE_ADVERTISEMENT;
break;
default:
IOLog("Ethernet [IntelMausi]: Invalid PHY type setting EEE advertisement.\n");
return;
}
ret_val = hw->phy.ops.acquire(hw);
if (ret_val) {
IOLog("Ethernet [IntelMausi]: EEE advertisement - unable to acquire PHY.\n");
return;
}
e1000_write_emi_reg_locked(hw, adv_addr, hw->dev_spec.ich8lan.eee_disable ? 0 : adapter->eee_advert);
hw->phy.ops.release(hw);
}
e1000_get_phy_info(hw);
if ((adapter->flags & FLAG_HAS_SMART_POWER_DOWN) && !(adapter->flags & FLAG_SMART_POWER_DOWN)) {
u16 phy_data = 0;
/* speed up time to link by disabling smart power down, ignore
* the return value of this function because there is nothing
* different we would do if it failed
*/
e1e_rphy(hw, IGP02E1000_PHY_POWER_MGMT, &phy_data);
phy_data &= ~IGP02E1000_PM_SPD;
e1e_wphy(hw, IGP02E1000_PHY_POWER_MGMT, phy_data);
}
}
/**
* intelPowerDownPhy - Power down the PHY
*
* Power down the PHY so no link is implied when interface is down.
* The PHY cannot be powered down if management or WoL is active.
*/
void IntelMausi::intelPowerDownPhy(struct e1000_adapter *adapter)
{
if (adapter->hw.phy.ops.power_down)
adapter->hw.phy.ops.power_down(&adapter->hw);
}
/**
* intelEnableMngPassThru - Check if management passthrough is needed
* @hw: pointer to the HW structure
*
* Verifies the hardware needs to leave interface enabled so that frames can
* be directed to and from the management interface.
**/
bool IntelMausi::intelEnableMngPassThru(struct e1000_hw *hw)
{
u32 manc;
u32 fwsm, factps;
manc = intelReadMem32(E1000_MANC);
if (!(manc & E1000_MANC_RCV_TCO_EN))
return false;
if (hw->mac.has_fwsm) {
fwsm = intelReadMem32(E1000_FWSM);
factps = intelReadMem32(E1000_FACTPS);
if (!(factps & E1000_FACTPS_MNGCG) &&
((fwsm & E1000_FWSM_MODE_MASK) ==
(e1000_mng_mode_pt << E1000_FWSM_MODE_SHIFT)))
return true;
} else if ((manc & E1000_MANC_SMBUS_EN) &&
!(manc & E1000_MANC_ASF_EN)) {
return true;
}
return false;
}
/**
* intelResetAdaptive - Reset Adaptive Interframe Spacing
* @hw: pointer to the HW structure
*
* Reset the Adaptive Interframe Spacing throttle to default values.
**/
void IntelMausi::intelResetAdaptive(struct e1000_hw *hw)
{
struct e1000_mac_info *mac = &hw->mac;
if (!mac->adaptive_ifs) {
DebugLog("Ethernet [IntelMausi]: Not in Adaptive IFS mode!\n");
return;
}
mac->current_ifs_val = 0;
mac->ifs_min_val = IFS_MIN;
mac->ifs_max_val = IFS_MAX;
mac->ifs_step_size = IFS_STEP;
mac->ifs_ratio = IFS_RATIO;
mac->in_ifs_mode = false;
intelWriteMem32(E1000_AIT, 0);
}
/**
* intelUpdateAdaptive - Update Adaptive Interframe Spacing
* @hw: pointer to the HW structure
*
* Update the Adaptive Interframe Spacing Throttle value based on the
* time between transmitted packets and time between collisions.
**/
void IntelMausi::intelUpdateAdaptive(struct e1000_hw *hw)
{
struct e1000_mac_info *mac = &hw->mac;
if (!mac->adaptive_ifs) {
DebugLog("Ethernet [IntelMausi]: Not in Adaptive IFS mode!\n");
return;
}
if ((mac->collision_delta * mac->ifs_ratio) > mac->tx_packet_delta) {
if (mac->tx_packet_delta > MIN_NUM_XMITS) {
mac->in_ifs_mode = true;
if (mac->current_ifs_val < mac->ifs_max_val) {
if (!mac->current_ifs_val)
mac->current_ifs_val = mac->ifs_min_val;
else
mac->current_ifs_val += mac->ifs_step_size;
intelWriteMem32(E1000_AIT, mac->current_ifs_val);
}
}
} else {
if (mac->in_ifs_mode && (mac->tx_packet_delta <= MIN_NUM_XMITS)) {
mac->current_ifs_val = 0;
mac->in_ifs_mode = false;
intelWriteMem32(E1000_AIT, 0);
}
}
}
/**
* intelVlanStripDisable - helper to disable HW VLAN stripping
* @adapter: board private structure to initialize
**/
void IntelMausi::intelVlanStripDisable(struct e1000_adapter *adapter)
{
UInt32 ctrl;
/* disable VLAN tag insert/strip */
ctrl = intelReadMem32(E1000_CTRL);
ctrl &= ~E1000_CTRL_VME;
intelWriteMem32(E1000_CTRL, ctrl);
}
/**
* intelVlanStripEnable - helper to enable HW VLAN stripping
* @adapter: board private structure to initialize
**/
void IntelMausi::intelVlanStripEnable(struct e1000_adapter *adapter)
{
UInt32 ctrl;
/* enable VLAN tag insert/strip */
ctrl = intelReadMem32(E1000_CTRL);
ctrl |= E1000_CTRL_VME;
intelWriteMem32(E1000_CTRL, ctrl);
}
static const u32 rsskey[10] = {
0xda565a6d, 0xc20e5b25, 0x3d256741, 0xb08fa343, 0xcb2bcad0,
0xb4307bae, 0xa32dcb77, 0x0cf23080, 0x3bb7426a, 0xfa01acbe
};
void IntelMausi::intelSetupRssHash(struct e1000_adapter *adapter)
{
UInt32 mrqc, rxcsum;
int i;
for (i = 0; i < 10; i++)
intelWriteMem32(E1000_RSSRK(i), rsskey[i]);
/* Direct all traffic to queue 0 */
for (i = 0; i < 32; i++)
intelWriteMem32(E1000_RETA(i), 0);
/* Disable raw packet checksumming so that RSS hash is placed in
* descriptor on writeback.
*/
rxcsum = intelReadMem32(E1000_RXCSUM);
rxcsum |= E1000_RXCSUM_PCSD;
intelWriteMem32(E1000_RXCSUM, rxcsum);
mrqc = (E1000_MRQC_RSS_FIELD_IPV4 |
E1000_MRQC_RSS_FIELD_IPV4_TCP |
E1000_MRQC_RSS_FIELD_IPV6 |
E1000_MRQC_RSS_FIELD_IPV6_TCP |
E1000_MRQC_RSS_FIELD_IPV6_TCP_EX);
mrqc |= 0x01;
intelWriteMem32(E1000_MRQC, mrqc);
}
/* Reset the NIC in case a tx deadlock or a pci error occurred. timerSource and txQueue
* are stopped immediately but will be restarted by checkLinkStatus() when the link has
* been reestablished.
*/
void IntelMausi::intelRestart()
{
/* Stop output thread and flush txQueue */
netif->stopOutputThread();
netif->flushOutputQueue();
/* Also set the link status to down. */
if (linkUp)
IOLog("Ethernet [IntelMausi]: Link down on en%u\n", netif->getUnitNumber());
setLinkStatus(kIONetworkLinkValid);
linkUp = false;
/* Reset NIC and cleanup both descriptor rings. */
intelDisableIRQ();
intelReset(&adapterData);
clearDescriptors();
rxCleanedCount = rxNextDescIndex = 0;
deadlockWarn = 0;
forceReset = false;
eeeMode = 0;
/* Reinitialize NIC. */
intelConfigure(&adapterData);
/* From here on the code is the same as e1000e_up() */
clear_bit(__E1000_DOWN, &adapterData.state);
intelEnableIRQ(intrMask);
adapterData.hw.mac.get_link_status = true;
}
void IntelMausi::intelInitTxRing()
{
intelWriteMem32(E1000_TDBAL(0), (txPhyAddr & 0xffffffff));
intelWriteMem32(E1000_TDBAH(0), (txPhyAddr >> 32));
intelWriteMem32(E1000_TDLEN(0), kTxDescSize);
intelWriteMem32(E1000_TDH(0), 0);
intelWriteMem32(E1000_TDT(0), 0);
txNextDescIndex = txDirtyIndex = txCleanBarrierIndex = 0;
txNumFreeDesc = kNumTxDesc;
}
void IntelMausi::intelInitRxRing()
{
intelWriteMem32(E1000_RDBAL(0), (rxPhyAddr & 0xffffffff));
intelWriteMem32(E1000_RDBAH(0), (rxPhyAddr >> 32));
intelWriteMem32(E1000_RDLEN(0), kRxDescSize);
intelWriteMem32(E1000_RDH(0), 0);
if (adapterData.flags2 & FLAG2_PCIM2PCI_ARBITER_WA)
intelUpdateRxDescTail(kRxLastDesc);
else
intelWriteMem32(E1000_RDT(0), kRxLastDesc);
rxCleanedCount = rxNextDescIndex = 0;
}
void IntelMausi::intelUpdateTxDescTail(UInt32 index)
{
if (adapterData.flags2 & FLAG2_PCIM2PCI_ARBITER_WA) {
struct e1000_hw *hw = &adapterData.hw;
s32 ret = __ew32_prepare(hw);
intelWriteMem32(E1000_TDT(0),index);
if (!ret && (index != intelReadMem32(E1000_TDT(0)))) {
UInt32 tctl = intelReadMem32(E1000_TCTL);
intelWriteMem32(E1000_TCTL, tctl & ~E1000_TCTL_EN);
forceReset = true;
IOLog("Ethernet [IntelMausi]: ME firmware caused invalid TDT - resetting.\n");
}
} else {
intelWriteMem32(E1000_TDT(0), index);
}
txCleanBarrierIndex = txNextDescIndex;
}
void IntelMausi::intelUpdateRxDescTail(UInt32 index)
{
struct e1000_hw *hw = &adapterData.hw;
SInt32 ret = __ew32_prepare(hw);
intelWriteMem32(E1000_RDT(0),index);
if (!ret && (index != intelReadMem32(E1000_RDT(0)))) {
UInt32 rctl = intelReadMem32(E1000_RCTL);
intelWriteMem32(E1000_RCTL, rctl & ~E1000_RCTL_EN);
forceReset = true;
IOLog("Ethernet [IntelMausi]: ME firmware caused invalid RDT - resetting.\n");
}
}
inline void IntelMausi::intelEnablePCIDevice(IOPCIDevice *provider)
{
UInt16 cmdReg;
cmdReg = provider->extendedConfigRead16(kIOPCIConfigCommand);
cmdReg |= (kIOPCICommandBusMaster | kIOPCICommandMemorySpace);
cmdReg &= ~kIOPCICommandIOSpace;
provider->extendedConfigWrite16(kIOPCIConfigCommand, cmdReg);
}
void IntelMausi::intelFlushDescriptors()
{
/* flush pending descriptor writebacks to memory */
intelWriteMem32(E1000_TIDV, adapterData.tx_int_delay | E1000_TIDV_FPD);
//intelWriteMem32(E1000_RDTR, adapterData.rx_int_delay | E1000_RDTR_FPD);
/* execute the writes immediately */
intelFlush();
/* due to rare timing issues, write to TIDV/RDTR again to ensure the
* write is successful
*/
intelWriteMem32(E1000_TIDV, adapterData.tx_int_delay | E1000_TIDV_FPD);
//intelWriteMem32(E1000_RDTR, adapterData.rx_int_delay | E1000_RDTR_FPD);
/* execute the writes immediately */
intelFlush();
}
/**
* intelFlushTxRing - remove all descriptors from the tx_ring
*
* We want to clear all pending descriptors from the TX ring.
* zeroing happens when the HW reads the regs. We assign the ring itself as
* the data of the next descriptor. We don't care about the data we are about
* to reset the HW.
*/
void IntelMausi::intelFlushTxRing(struct e1000_adapter *adapter)
{
struct e1000_data_desc *desc = NULL;
UInt32 tdt, tctl, txdLower = E1000_TXD_CMD_IFCS;
UInt16 size = 512;
tctl = intelReadMem32(E1000_TCTL);
intelWriteMem32(E1000_TCTL, tctl | E1000_TCTL_EN);
tdt = intelReadMem32(E1000_TDT(0));
if (tdt != txNextDescIndex) {
IOLog("Ethernet [IntelMausi]: Failed to flush tx descriptor ring.\n");
goto done;
}
DebugLog("Ethernet [IntelMausi]: Flushing tx descriptor ring.\n");
OSAddAtomic(-1, &txNumFreeDesc);
desc = &txDescArray[txNextDescIndex++];
txNextDescIndex &= kTxDescMask;
desc->buffer_addr = OSSwapHostToLittleInt64(txPhyAddr);
desc->lower.data = OSSwapHostToLittleInt32(txdLower | size);
desc->upper.data = 0;
intelWriteMem32(E1000_TDT(0), txNextDescIndex);
intelFlush();
usleep_range(200, 250);
done:
return;
}
/**
* intelFlushRxRing - remove all descriptors from the rx_ring
*
* Mark all descriptors in the RX ring as consumed and disable the rx ring
*/
void IntelMausi::intelFlushRxRing(struct e1000_adapter *adapter)
{
UInt32 rctl, rxdctl;
DebugLog("Ethernet [IntelMausi]: Flushing rx descriptor ring.\n");
rctl = intelReadMem32(E1000_RCTL);
intelWriteMem32(E1000_RCTL, rctl & ~E1000_RCTL_EN);
intelFlush();
usleep_range(100, 150);
rxdctl = intelReadMem32(E1000_RXDCTL(0));
/* zero the lower 14 bits (prefetch and host thresholds) */
rxdctl &= 0xffffc000;
/* update thresholds: prefetch threshold to 31, host threshold to 1
* and make sure the granularity is "descriptors" and not "cache lines"
*/
rxdctl |= (0x1F | (1 << 8) | E1000_RXDCTL_THRESH_UNIT_DESC);
intelWriteMem32(E1000_RXDCTL(0), rxdctl);
/* momentarily enable the RX ring for the changes to take effect */
intelWriteMem32(E1000_RCTL, rctl | E1000_RCTL_EN);
intelFlush();
usleep_range(100, 150);
intelWriteMem32(E1000_RCTL, rctl & ~E1000_RCTL_EN);
}
/**
* intelFlushDescRings - remove all descriptors from the descriptor rings
*
* In i219, the descriptor rings must be emptied before resetting the HW
* or before changing the device state to D3 during runtime (runtime PM).
*
* Failure to do this will cause the HW to enter a unit hang state which can
* only be released by PCI reset on the device
*
*/
void IntelMausi::intelFlushDescRings(struct e1000_adapter *adapter)
{
UInt16 hangState;
u32 fext_nvm11, tdlen;
struct e1000_hw *hw = &adapter->hw;
/* First, disable MULR fix in FEXTNVM11 */
fext_nvm11 = intelReadMem32(E1000_FEXTNVM11);
fext_nvm11 |= E1000_FEXTNVM11_DISABLE_MULR_FIX;
ew32(FEXTNVM11, fext_nvm11);
/* do nothing if we're not in faulty state, or if the queue is empty */
tdlen = er32(TDLEN(0));
hangState = pciDevice->extendedConfigRead16(PCICFG_DESC_RING_STATUS);
if (!(hangState & FLUSH_DESC_REQUIRED) || !tdlen)
goto done;
intelFlushTxRing(adapter);
/* recheck, maybe the fault is caused by the rx ring */
hangState = pciDevice->extendedConfigRead16(PCICFG_DESC_RING_STATUS);
if (hangState & FLUSH_DESC_REQUIRED)
intelFlushRxRing(adapter);
done:
return;
}
bool IntelMausi::intelCheckLink(struct e1000_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
bool link_active = false;
s32 ret_val = 0;
/* get_link_status is set on LSC (link status) interrupt or
* Rx sequence error interrupt. get_link_status will stay
* false until the check_for_link establishes link
* for copper adapters ONLY
*/
switch (hw->phy.media_type) {
case e1000_media_type_copper:
if (hw->mac.get_link_status) {
ret_val = hw->mac.ops.check_for_link(hw);
link_active = !hw->mac.get_link_status;
} else {
link_active = true;
}
break;
case e1000_media_type_fiber:
ret_val = hw->mac.ops.check_for_link(hw);
link_active = !!(intelReadMem32(E1000_STATUS) & E1000_STATUS_LU);
break;
case e1000_media_type_internal_serdes:
ret_val = hw->mac.ops.check_for_link(hw);
link_active = adapter->hw.mac.serdes_has_link;
break;
default:
case e1000_media_type_unknown:
break;
}
if ((ret_val == E1000_ERR_PHY) && (hw->phy.type == e1000_phy_igp_3) &&
(intelReadMem32(E1000_CTRL) & E1000_PHY_CTRL_GBE_DISABLE)) {
/* See e1000_kmrn_lock_loss_workaround_ich8lan() */
IOLog("Ethernet [IntelMausi]: Gigabit has been disabled, downgrading speed.\n");
}
return link_active;
}
/**
* intelPhyReadStatus - Update the PHY register status snapshot
* @adapter: board private structure
**/
void IntelMausi::intelPhyReadStatus(struct e1000_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
struct e1000_phy_regs *phy = &adapter->phy_regs;
if ((intelReadMem32(E1000_STATUS) & E1000_STATUS_LU) &&
(adapter->hw.phy.media_type == e1000_media_type_copper)) {
int ret_val;
ret_val = e1e_rphy(hw, MII_BMCR, &phy->bmcr);
ret_val |= e1e_rphy(hw, MII_BMSR, &phy->bmsr);
ret_val |= e1e_rphy(hw, MII_ADVERTISE, &phy->advertise);
ret_val |= e1e_rphy(hw, MII_LPA, &phy->lpa);
ret_val |= e1e_rphy(hw, MII_EXPANSION, &phy->expansion);
ret_val |= e1e_rphy(hw, MII_CTRL1000, &phy->ctrl1000);
ret_val |= e1e_rphy(hw, MII_STAT1000, &phy->stat1000);
ret_val |= e1e_rphy(hw, MII_ESTATUS, &phy->estatus);
if (ret_val)
IOLog("Ethernet [IntelMausi]: Error reading PHY register.\n");
} else {
/* Do not read PHY registers if link is not up
* Set values to typical power-on defaults
*/
phy->bmcr = (BMCR_SPEED1000 | BMCR_ANENABLE | BMCR_FULLDPLX);
phy->bmsr = (BMSR_100FULL | BMSR_100HALF | BMSR_10FULL |
BMSR_10HALF | BMSR_ESTATEN | BMSR_ANEGCAPABLE |
BMSR_ERCAP);
phy->advertise = (ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP |
ADVERTISE_ALL | ADVERTISE_CSMA);
phy->lpa = 0;
phy->expansion = EXPANSION_ENABLENPAGE;
phy->ctrl1000 = ADVERTISE_1000FULL;
phy->stat1000 = 0;
phy->estatus = (ESTATUS_1000_TFULL | ESTATUS_1000_THALF);
}
}
void IntelMausi::intelInitPhyWakeup(UInt32 wufc)
{
struct e1000_hw *hw = &adapterData.hw;
u32 i, mac_reg, wuc;
u16 phy_reg, wuc_enable;
int error;
/* copy MAC RARs to PHY RARs */
e1000_copy_rx_addrs_to_phy_ich8lan(hw);
error = hw->phy.ops.acquire(hw);
if (error) {
DebugLog("Ethernet [IntelMausi]: Failed to acquire PHY.\n");
return;
}
/* Enable access to wakeup registers on and set page to BM_WUC_PAGE */
error = e1000_enable_phy_wakeup_reg_access_bm(hw, &wuc_enable);
if (error) {
DebugLog("Ethernet [IntelMausi]: Failed to access PHY wakeup registers.\n");
goto release;
}
/* copy MAC MTA to PHY MTA - only needed for pchlan */
for (i = 0; i < hw->mac.mta_reg_count; i++) {
mac_reg = E1000_READ_REG_ARRAY(hw, E1000_MTA, i);
hw->phy.ops.write_reg_page(hw, BM_MTA(i),
(u16)(mac_reg & 0xFFFF));
hw->phy.ops.write_reg_page(hw, BM_MTA(i) + 1,
(u16)((mac_reg >> 16) & 0xFFFF));
}
/* configure PHY Rx Control register */
hw->phy.ops.read_reg_page(hw, BM_RCTL, &phy_reg);
mac_reg = er32(RCTL);
if (mac_reg & E1000_RCTL_UPE)
phy_reg |= BM_RCTL_UPE;
if (mac_reg & E1000_RCTL_MPE)
phy_reg |= BM_RCTL_MPE;
phy_reg &= ~(BM_RCTL_MO_MASK);
if (mac_reg & E1000_RCTL_MO_3)
phy_reg |= (((mac_reg & E1000_RCTL_MO_3) >> E1000_RCTL_MO_SHIFT) << BM_RCTL_MO_SHIFT);
if (mac_reg & E1000_RCTL_BAM)
phy_reg |= BM_RCTL_BAM;
if (mac_reg & E1000_RCTL_PMCF)
phy_reg |= BM_RCTL_PMCF;
mac_reg = er32(CTRL);
if (mac_reg & E1000_CTRL_RFCE)
phy_reg |= BM_RCTL_RFCE;
hw->phy.ops.write_reg_page(hw, BM_RCTL, phy_reg);
wuc = E1000_WUC_PME_EN;
if (wufc & (E1000_WUFC_MAG | E1000_WUFC_LNKC))
wuc |= E1000_WUC_APME;
/* enable PHY wakeup in MAC register */
ew32(WUFC, wufc);
ew32(WUC, (E1000_WUC_PHY_WAKE | E1000_WUC_APMPME | E1000_WUC_PME_STATUS | wuc));
/* configure and enable PHY wakeup in PHY registers */
hw->phy.ops.write_reg_page(hw, BM_WUFC, wufc);
hw->phy.ops.write_reg_page(hw, BM_WUC, wuc);
/* activate PHY wakeup */
wuc_enable |= BM_WUC_ENABLE_BIT | BM_WUC_HOST_WU_BIT;
error = e1000_disable_phy_wakeup_reg_access_bm(hw, &wuc_enable);
if (error)
DebugLog("Ethernet [IntelMausi]: Failed to set PHY Host Wakeup bit.\n");
release:
hw->phy.ops.release(hw);
}
void IntelMausi::intelSetupAdvForMedium(const IONetworkMedium *medium)
{
struct e1000_hw *hw = &adapterData.hw;
struct e1000_mac_info *mac = &hw->mac;
if (adapterData.flags2 & FLAG2_HAS_EEE)
hw->dev_spec.ich8lan.eee_disable = true;
switch (medium->getIndex()) {
case MEDIUM_INDEX_10HD:
mac->forced_speed_duplex = ADVERTISE_10_HALF;
hw->mac.autoneg = 0;
break;
case MEDIUM_INDEX_10FD:
mac->forced_speed_duplex = ADVERTISE_10_FULL;
hw->mac.autoneg = 0;
break;
case MEDIUM_INDEX_100HD:
hw->phy.autoneg_advertised = ADVERTISED_100baseT_Half;
hw->mac.autoneg = 1;
hw->fc.requested_mode = e1000_fc_none;
break;
case MEDIUM_INDEX_100FD:
hw->phy.autoneg_advertised = ADVERTISED_100baseT_Full;
hw->mac.autoneg = 1;
hw->fc.requested_mode = e1000_fc_none;
break;
case MEDIUM_INDEX_100FDFC:
hw->phy.autoneg_advertised = ADVERTISED_100baseT_Full;
hw->mac.autoneg = 1;
hw->fc.requested_mode = e1000_fc_full;
break;
case MEDIUM_INDEX_1000FD:
hw->phy.autoneg_advertised = ADVERTISED_1000baseT_Full;
hw->mac.autoneg = 1;
hw->fc.requested_mode = e1000_fc_none;
break;
case MEDIUM_INDEX_1000FDFC:
hw->phy.autoneg_advertised = ADVERTISED_1000baseT_Full;
hw->mac.autoneg = 1;
hw->fc.requested_mode = e1000_fc_full;
break;
case MEDIUM_INDEX_1000FDEEE:
hw->phy.autoneg_advertised = ADVERTISED_1000baseT_Full;
hw->mac.autoneg = 1;
hw->fc.requested_mode = e1000_fc_none;
hw->dev_spec.ich8lan.eee_disable = false;
break;
case MEDIUM_INDEX_1000FDFCEEE:
hw->phy.autoneg_advertised = ADVERTISED_1000baseT_Full;
hw->mac.autoneg = 1;
hw->fc.requested_mode = e1000_fc_full;
hw->dev_spec.ich8lan.eee_disable = false;
break;
case MEDIUM_INDEX_100FDEEE:
hw->phy.autoneg_advertised = ADVERTISED_100baseT_Full;
hw->mac.autoneg = 1;
hw->fc.requested_mode = e1000_fc_none;
hw->dev_spec.ich8lan.eee_disable = false;
break;
case MEDIUM_INDEX_100FDFCEEE:
hw->phy.autoneg_advertised = ADVERTISED_100baseT_Full;
hw->mac.autoneg = 1;
hw->fc.requested_mode = e1000_fc_full;
hw->dev_spec.ich8lan.eee_disable = false;
break;
default:
if (hw->phy.media_type == e1000_media_type_fiber) {
hw->phy.autoneg_advertised = ADVERTISED_1000baseT_Full | ADVERTISED_FIBRE | ADVERTISED_Autoneg;
} else {
hw->phy.autoneg_advertised = (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
ADVERTISED_100baseT_Full | ADVERTISED_100baseT_Half |
ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg |
ADVERTISED_TP | ADVERTISED_MII);
if (adapterData.fc_autoneg)
hw->fc.requested_mode = e1000_fc_default;
if (adapterData.flags2 & FLAG2_HAS_EEE)
hw->dev_spec.ich8lan.eee_disable = false;
}
hw->mac.autoneg = 1;
break;
}
/* clear MDI, MDI(-X) override is only allowed when autoneg enabled */
hw->phy.mdix = AUTO_ALL_MODES;
}
void IntelMausi::intelFlushLPIC()
{
struct e1000_hw *hw = &adapterData.hw;
UInt32 error, lpic;
/* Flush LPIC. */
error = hw->phy.ops.acquire(hw);
if (error)
return;
lpic = intelReadMem32(E1000_LPIC);
DebugLog("Ethernet [IntelMausi]: LPIC=0x%08x.\n", lpic);
hw->phy.ops.release(hw);
}
void IntelMausi::setMaxLatency(UInt32 linkSpeed)
{
struct e1000_hw *hw = &adapterData.hw;
UInt32 rxa = intelReadMem32(E1000_PBA) & E1000_PBA_RXA_MASK;
UInt32 latency;
rxa = rxa << 9;
latency = (rxa > hw->adapter->max_frame_size) ? (rxa - hw->adapter->max_frame_size) * (16000 / linkSpeed) : 0;
if (maxLatency && (latency > maxLatency))
latency = maxLatency;
requireMaxBusStall(latency);
DebugLog("Ethernet [IntelMausi]: requireMaxBusStall(%uns).\n", latency);
}
UInt16 IntelMausi::intelSupportsEEE(struct e1000_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
struct e1000_dev_spec_ich8lan *dev_spec;
SInt32 error;
UInt16 result = 0;
UInt16 lpa, adv, advAddr;
if (!(adapter->flags2 & FLAG2_HAS_EEE))
goto done;
dev_spec = &hw->dev_spec.ich8lan;
if (hw->dev_spec.ich8lan.eee_disable)
goto done;
switch (hw->phy.type) {
case e1000_phy_82579:
lpa = I82579_EEE_LP_ABILITY;
advAddr = I82579_EEE_ADVERTISEMENT;
break;
case e1000_phy_i217:
lpa = I217_EEE_LP_ABILITY;
advAddr = I217_EEE_ADVERTISEMENT;
break;
default:
goto done;
}
error = hw->phy.ops.acquire(hw);
if (error)
goto done;
/* Save off link partner's EEE ability */
error = e1000_read_emi_reg_locked(hw, lpa, &dev_spec->eee_lp_ability);
if (error)
goto release;
/* Read EEE advertisement */
error = e1000_read_emi_reg_locked(hw, advAddr, &adv);
if (error)
goto release;
/* Enable EEE only for speeds in which the link partner is
* EEE capable and for which we advertise EEE.
*/
if (adv & dev_spec->eee_lp_ability & I82579_EEE_1000_SUPPORTED)
result |= I82579_LPI_CTRL_1000_ENABLE;
if (adv & dev_spec->eee_lp_ability & I82579_EEE_100_SUPPORTED)
result |= I82579_LPI_CTRL_100_ENABLE;
DebugLog("Ethernet [IntelMausi]: EEE mode = 0x%04x, adv=0x%04x, lpa=0x%04x\n", result, adv, dev_spec->eee_lp_ability);
release:
hw->phy.ops.release(hw);
done:
return result;
}
SInt32 IntelMausi::intelEnableEEE(struct e1000_hw *hw, UInt16 mode)
{
SInt32 error = 0;
UInt16 pcsStatus, lpiCtrl, data;
switch (hw->phy.type) {
case e1000_phy_82579:
pcsStatus = I82579_EEE_PCS_STATUS;
break;
case e1000_phy_i217:
pcsStatus = I217_EEE_PCS_STATUS;
break;
default:
goto done;
}
error = hw->phy.ops.acquire(hw);
if (error)
goto done;
error = e1e_rphy_locked(hw, I82579_LPI_CTRL, &lpiCtrl);
if (error)
goto release;
/* Clear bits that enable EEE in various speeds */
lpiCtrl &= ~I82579_LPI_CTRL_ENABLE_MASK;
/* Set the new EEE mode. */
lpiCtrl |= mode;
if (hw->phy.type == e1000_phy_82579) {
error = e1000_read_emi_reg_locked(hw, I82579_LPI_PLL_SHUT, &data);
if (error)
goto release;
data &= ~I82579_LPI_100_PLL_SHUT;
e1000_write_emi_reg_locked(hw, I82579_LPI_PLL_SHUT, data);
}
/* R/Clr IEEE MMD 3.1 bits 11:10 - Tx/Rx LPI Received */
error = e1000_read_emi_reg_locked(hw, pcsStatus, &data);
if (error)
goto release;
error = e1e_wphy_locked(hw, I82579_LPI_CTRL, lpiCtrl);
release:
hw->phy.ops.release(hw);
done:
return error;
}