/* * PROJECT: ReactOS DC21x4 Driver * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * PURPOSE: Media common code * COPYRIGHT: Copyright 2023 Dmitry Borisov */ /* INCLUDES *******************************************************************/ #include "dc21x4.h" #include /* FUNCTIONS ******************************************************************/ VOID MediaIndicateConnect( _In_ PDC21X4_ADAPTER Adapter, _In_ BOOLEAN LinkUp) { /* Nothing to do */ if (Adapter->LinkUp == LinkUp) return; Adapter->LinkUp = LinkUp; INFO_VERB("Link %sconnected, media is %s\n", LinkUp ? "" : "dis", MediaNumber2Str(Adapter, Adapter->MediaNumber)); NdisDprReleaseSpinLock(&Adapter->ModeLock); NdisMIndicateStatus(Adapter->AdapterHandle, LinkUp ? NDIS_STATUS_MEDIA_CONNECT : NDIS_STATUS_MEDIA_DISCONNECT, NULL, 0); NdisMIndicateStatusComplete(Adapter->AdapterHandle); NdisDprAcquireSpinLock(&Adapter->ModeLock); } static ULONG MediaMiiNextMedia( _In_ PDC21X4_ADAPTER Adapter) { Adapter->ModeFlags &= ~(DC_MODE_TEST_PACKET | DC_MODE_AUI_FAILED | DC_MODE_BNC_FAILED); Adapter->LastReceiveActivity = (ULONG)Adapter->Statistics.ReceiveOk; /* * In MII mode, we don't know exactly which port is active. * Switch to the media with a higher priority. */ if (Adapter->MediaBitmap & (1 << MEDIA_HMR)) return MEDIA_HMR; else if (Adapter->MediaBitmap & (1 << MEDIA_AUI)) return MEDIA_AUI; else return MEDIA_BNC; } static VOID MediaMiiSetSpeedAndDuplex( _In_ PDC21X4_ADAPTER Adapter, _In_ BOOLEAN FullDuplex, _In_ BOOLEAN Speed100) { ULONG OpMode = Adapter->OpMode; if (FullDuplex) OpMode |= DC_OPMODE_FULL_DUPLEX; else OpMode &= ~DC_OPMODE_FULL_DUPLEX; if (Speed100) OpMode &= ~DC_OPMODE_PORT_XMIT_10; else OpMode |= DC_OPMODE_PORT_XMIT_10; /* Nothing to do */ if (OpMode == Adapter->OpMode) return; INFO_VERB("Configuring MAC from %u %s-duplex to %u %s-duplex\n", Adapter->LinkSpeedMbps, (Adapter->OpMode & DC_OPMODE_FULL_DUPLEX) ? "full" : "half", Speed100 ? 100 : 10, FullDuplex ? "full" : "half"); Adapter->LinkSpeedMbps = Speed100 ? 100 : 10; DcStopTxRxProcess(Adapter); Adapter->OpMode = OpMode; DC_WRITE(Adapter, DcCsr6_OpMode, Adapter->OpMode); } static VOID MediaMiiGetSpeedAndDuplex( _In_ PDC21X4_ADAPTER Adapter, _Out_ PBOOLEAN FullDuplex, _Out_ PBOOLEAN Speed100) { ULONG MiiLinkPartnerAbility, AdvLpa; MiiRead(Adapter, Adapter->PhyAddress, MII_AUTONEG_LINK_PARTNER, &MiiLinkPartnerAbility); TRACE("MII LPA %04lx\n", MiiLinkPartnerAbility); AdvLpa = Adapter->MiiMedia.Advertising & MiiLinkPartnerAbility; if (AdvLpa & MII_LP_100T_FD) { *FullDuplex = TRUE; *Speed100 = TRUE; } else if (AdvLpa & MII_LP_100T4) { *FullDuplex = FALSE; *Speed100 = TRUE; } else if (AdvLpa & MII_LP_100T_HD) { *FullDuplex = FALSE; *Speed100 = TRUE; } else if (AdvLpa & MII_LP_10T_FD) { *FullDuplex = TRUE; *Speed100 = FALSE; } else { *FullDuplex = FALSE; *Speed100 = FALSE; } } BOOLEAN MediaMiiCheckLink( _In_ PDC21X4_ADAPTER Adapter) { ULONG MiiStatus; BOOLEAN FullDuplex, Speed100; /* The link status is a latched-low bit, read it twice */ if (!MiiRead(Adapter, Adapter->PhyAddress, MII_STATUS, &MiiStatus)) { goto NoLink; } if (!(MiiStatus & MII_SR_LINK_STATUS)) { MiiRead(Adapter, Adapter->PhyAddress, MII_STATUS, &MiiStatus); } TRACE("MII Status %04lx\n", MiiStatus); /* Check the link status */ if (!(MiiStatus & MII_SR_LINK_STATUS)) { NoLink: /* No link detected, check the other port */ if (Adapter->MediaBitmap & ((1 << MEDIA_HMR) | (1 << MEDIA_AUI) | (1 << MEDIA_BNC))) { if ((Adapter->Features & DC_MII_AUTOSENSE) && !MEDIA_IS_FIXED(Adapter)) { Adapter->MediaNumber = MediaMiiNextMedia(Adapter); MediaSiaSelect(Adapter); } } return FALSE; } /* If we are forcing speed and duplex */ if (MEDIA_IS_FIXED(Adapter)) { FullDuplex = !!(Adapter->MiiControl & MII_CR_FULL_DUPLEX); Speed100 = !!(Adapter->MiiControl & MII_CR_SPEED_SELECTION); } else { /* Check auto-negotiation is complete */ if (!(MiiStatus & MII_SR_AUTONEG_COMPLETE)) return FALSE; MediaMiiGetSpeedAndDuplex(Adapter, &FullDuplex, &Speed100); } /* Set the link speed and duplex */ MediaMiiSetSpeedAndDuplex(Adapter, FullDuplex, Speed100); return TRUE; } VOID MediaMiiSelect( _In_ PDC21X4_ADAPTER Adapter) { ULONG MiiControl, MiiAdvertise; MiiRead(Adapter, Adapter->PhyAddress, MII_CONTROL, &MiiControl); MiiControl &= ~(MII_CR_POWER_DOWN | MII_CR_ISOLATE | MII_CR_RESET | MII_CR_SPEED_SELECTION | MII_CR_FULL_DUPLEX | MII_CR_AUTONEG | MII_CR_AUTONEG_RESTART); MiiWrite(Adapter, Adapter->PhyAddress, MII_CONTROL, MiiControl); MiiControl |= Adapter->MiiControl; MiiAdvertise = Adapter->MiiAdvertising; MiiWrite(Adapter, Adapter->PhyAddress, MII_AUTONEG_ADVERTISE, MiiAdvertise | MII_ADV_CSMA); MiiWrite(Adapter, Adapter->PhyAddress, MII_CONTROL, MiiControl); } VOID MediaSelectMiiPort( _In_ PDC21X4_ADAPTER Adapter, _In_ BOOLEAN ResetPhy) { ULONG OpMode, i; if (Adapter->ChipType != DC21140) { DcWriteSia(Adapter, 0, 0, 0); } OpMode = Adapter->OpMode; OpMode &= ~DC_OPMODE_MEDIA_MASK; OpMode |= DC_OPMODE_PORT_SELECT | DC_OPMODE_PORT_HEARTBEAT_DISABLE; Adapter->OpMode = OpMode; DC_WRITE(Adapter, DcCsr6_OpMode, OpMode); NdisStallExecution(10); if (ResetPhy) { ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); /* Execute the GPIO reset sequence */ if (Adapter->MiiMedia.ResetStreamLength) { /* Set the GPIO direction */ DcWriteGpio(Adapter, Adapter->MiiMedia.SetupStream[0]); for (i = 0; i < Adapter->MiiMedia.ResetStreamLength; ++i) { NdisMSleep(100); DcWriteGpio(Adapter, Adapter->MiiMedia.ResetStream[i]); } /* Give the PHY some time to reset */ NdisMSleep(5000); } } /* Set the GPIO direction */ DcWriteGpio(Adapter, Adapter->MiiMedia.SetupStream[0]); /* Execute the GPIO setup sequence */ for (i = 1; i < Adapter->MiiMedia.SetupStreamLength; ++i) { NdisStallExecution(10); DcWriteGpio(Adapter, Adapter->MiiMedia.SetupStream[i]); } } VOID MediaSiaSelect( _In_ PDC21X4_ADAPTER Adapter) { ULONG OpMode; PDC_MEDIA Media; INFO_VERB("Selected media %s\n", MediaNumber2Str(Adapter, Adapter->MediaNumber)); Media = &Adapter->Media[Adapter->MediaNumber]; DcStopTxRxProcess(Adapter); if (Adapter->ChipType != DC21041) { /* Make sure the reset pulse is wide enough */ NdisStallExecution(100); DcWriteGpio(Adapter, Media->GpioCtrl); NdisStallExecution(100); DcWriteGpio(Adapter, Media->GpioData); } DcWriteSia(Adapter, Media->Csr13, Media->Csr14, Media->Csr15); NdisStallExecution(10); OpMode = Adapter->OpMode; OpMode &= ~DC_OPMODE_MEDIA_MASK; OpMode |= Media->OpMode; Adapter->OpMode = OpMode; DC_WRITE(Adapter, DcCsr6_OpMode, OpMode); } VOID MediaGprSelect( _In_ PDC21X4_ADAPTER Adapter) { ULONG OpMode; PDC_MEDIA Media; INFO("Selected media %s\n", MediaNumber2Str(Adapter, Adapter->MediaNumber)); Media = &Adapter->Media[Adapter->MediaNumber]; DC_WRITE(Adapter, DcCsr12_Gpio, Media->GpioData); OpMode = Adapter->OpMode; OpMode &= ~DC_OPMODE_MEDIA_MASK; OpMode |= Media->OpMode; Adapter->OpMode = OpMode; DC_WRITE(Adapter, DcCsr6_OpMode, OpMode); } CODE_SEG("PAGE") VOID MediaInitDefaultMedia( _In_ PDC21X4_ADAPTER Adapter, _In_ ULONG MediaNumber) { ULONG Csr14, i; BOOLEAN UseMii; PAGED_CODE(); /* Media auto-detection */ if (MediaNumber == MEDIA_AUTO) { Adapter->Flags |= DC_AUTOSENSE; /* Initial value for all boards */ Adapter->DefaultMedia = MEDIA_10T; Adapter->MiiAdvertising = Adapter->MiiMedia.Advertising; Adapter->MiiControl = MII_CR_AUTONEG | MII_CR_AUTONEG_RESTART; switch (Adapter->ChipType) { case DC21041: { /* Errata: don't enable auto-negotiation */ if (Adapter->RevisionId < 0x20) break; /* Advertise 10T HD and 10T FD. The chip chooses the 10T FD mode automatically */ Adapter->Media[MEDIA_10T].Csr14 |= DC_SIA_TXRX_AUTONEG | DC_SIA_TXRX_ADV_10T_HD; Adapter->Media[MEDIA_10T].OpMode |= DC_OPMODE_FULL_DUPLEX; break; } case DC21140: { /* Pick the default media */ if (Adapter->Features & DC_HAS_MII) { Adapter->DefaultMedia = MEDIA_MII; break; } /* The final entry in the media list should be checked first */ _BitScanReverse(&Adapter->DefaultMedia, Adapter->MediaBitmap); /* * Select the first half-duplex media. * If you want to be able to use 21140 boards without MII in full-duplex mode, * you have to manually select the media. */ for (i = Adapter->DefaultMedia; i > 0; --i) { if ((Adapter->MediaBitmap & (1 << i)) && !MEDIA_IS_FD(i)) break; } Adapter->DefaultMedia = i; break; } case DC21143: case DC21145: { /* Pick the default media */ if (Adapter->Features & DC_HAS_MII) { Adapter->DefaultMedia = MEDIA_MII; } else if (Adapter->MediaBitmap & (1 << MEDIA_10T)) { /* Start at 10mbps to do internal auto-negotiation */ Adapter->DefaultMedia = MEDIA_10T; } else { /* The final entry in the media list should be checked first */ _BitScanReverse(&Adapter->DefaultMedia, Adapter->MediaBitmap); } /* Enable the PCS function to do 100mbps parallel detection */ if (Adapter->SymAdvertising & MII_ADV_100) { Adapter->Media[MEDIA_10T].OpMode |= DC_OPMODE_PORT_PCS; Adapter->Media[MEDIA_10T_FD].OpMode |= DC_OPMODE_PORT_PCS; Adapter->Media[MEDIA_AUI].OpMode |= DC_OPMODE_PORT_PCS; Adapter->Media[MEDIA_BNC].OpMode |= DC_OPMODE_PORT_PCS; Adapter->Media[MEDIA_HMR].OpMode |= DC_OPMODE_PORT_PCS; } Csr14 = DC_SIA_TXRX_AUTONEG; if (Adapter->SymAdvertising & MII_ADV_10T_HD) Csr14 |= DC_SIA_TXRX_ADV_10T_HD; /* When NWay is turned on, the FDX bit advertises 10T FD */ if (Adapter->SymAdvertising & MII_ADV_10T_FD) Adapter->Media[MEDIA_10T].OpMode |= DC_OPMODE_FULL_DUPLEX; if (Adapter->SymAdvertising & MII_ADV_100T_HD) Csr14 |= DC_SIA_TXRX_ADV_100TX_HD; if (Adapter->SymAdvertising & MII_ADV_100T_FD) Csr14 |= DC_SIA_TXRX_ADV_100TX_FD; if (Adapter->SymAdvertising & MII_ADV_100T4) Csr14 |= DC_SIA_TXRX_ADV_100T4; /* Advertise the PHY capability */ Adapter->Media[MEDIA_10T].Csr14 |= Csr14; /* This media may use GPIO data different from the 10T HD */ Adapter->Media[MEDIA_10T_FD].Csr14 |= Csr14; break; } default: break; } } else /* Forced speed and duplex */ { UseMii = FALSE; if (Adapter->Features & DC_HAS_MII) { if (!MEDIA_MII_OVERRIDE(MediaNumber)) { UseMii = TRUE; } } if (!UseMii) { Adapter->DefaultMedia = MediaNumber; if (MEDIA_IS_10T(MediaNumber)) { Adapter->InterruptMask &= ~DC_IRQ_LINK_CHANGED; Adapter->LinkStateChangeMask &= ~DC_IRQ_LINK_CHANGED; } if (MEDIA_IS_100(MediaNumber)) { Adapter->InterruptMask &= ~(DC_IRQ_LINK_FAIL | DC_IRQ_LINK_PASS); Adapter->LinkStateChangeMask &= ~(DC_IRQ_LINK_FAIL | DC_IRQ_LINK_PASS); } } else { ULONG MiiAdvertising, MiiControl; Adapter->DefaultMedia = MEDIA_MII; switch (MediaNumber) { case MEDIA_10T: MiiAdvertising = MII_ADV_10T_HD; MiiControl = 0; break; case MEDIA_10T_FD: MiiAdvertising = MII_ADV_10T_FD; MiiControl = MII_CR_FULL_DUPLEX; break; case MEDIA_100TX_HD: MiiAdvertising = MII_ADV_100T_HD; MiiControl = MII_CR_SPEED_SELECTION; break; case MEDIA_100TX_FD: MiiAdvertising = MII_ADV_100T_FD; MiiControl = MII_CR_FULL_DUPLEX | MII_CR_SPEED_SELECTION; break; case MEDIA_100T4: MiiAdvertising = MII_ADV_100T4; MiiControl = MII_CR_SPEED_SELECTION; break; default: MiiAdvertising = 0; MiiControl = 0; break; } if (MiiControl & MII_CR_SPEED_SELECTION) Adapter->LinkSpeedMbps = 100; else Adapter->LinkSpeedMbps = 10; Adapter->MiiAdvertising = MiiAdvertising; Adapter->MiiControl = MiiControl; } } INFO("Default media is %s\n", MediaNumber2Str(Adapter, Adapter->DefaultMedia)); } static CODE_SEG("PAGE") VOID MediaInitOpMode2114x( _In_ PDC21X4_ADAPTER Adapter) { PAGED_CODE(); Adapter->Media[MEDIA_10T ].OpMode = 0; Adapter->Media[MEDIA_BNC ].OpMode = 0; Adapter->Media[MEDIA_AUI ].OpMode = 0; Adapter->Media[MEDIA_100TX_HD].OpMode = DC_OPMODE_PORT_SELECT | DC_OPMODE_PORT_HEARTBEAT_DISABLE; Adapter->Media[MEDIA_10T_FD ].OpMode = DC_OPMODE_FULL_DUPLEX; Adapter->Media[MEDIA_100TX_FD].OpMode = DC_OPMODE_PORT_SELECT | DC_OPMODE_FULL_DUPLEX | DC_OPMODE_PORT_HEARTBEAT_DISABLE; Adapter->Media[MEDIA_100T4 ].OpMode = DC_OPMODE_PORT_SELECT | DC_OPMODE_PORT_HEARTBEAT_DISABLE; Adapter->Media[MEDIA_100FX_HD].OpMode = DC_OPMODE_PORT_SELECT | DC_OPMODE_PORT_HEARTBEAT_DISABLE | DC_OPMODE_PORT_PCS; Adapter->Media[MEDIA_100FX_FD].OpMode = DC_OPMODE_PORT_SELECT | DC_OPMODE_FULL_DUPLEX | DC_OPMODE_PORT_HEARTBEAT_DISABLE | DC_OPMODE_PORT_PCS; Adapter->Media[MEDIA_HMR ].OpMode = DC_OPMODE_PORT_HEARTBEAT_DISABLE; } CODE_SEG("PAGE") VOID MediaInitMediaList( _In_ PDC21X4_ADAPTER Adapter) { PAGED_CODE(); /* * Set the default internal values for the SIA/SYM operating modes. * The SROM parsing code may later overwrite them. */ switch (Adapter->ChipType) { case DC21040: { Adapter->Media[MEDIA_10T].Csr13 = 0x8F01; Adapter->Media[MEDIA_10T].Csr14 = 0xFFFF; Adapter->Media[MEDIA_10T].Csr15 = 0x0000; Adapter->Media[MEDIA_BNC].Csr13 = 0x8F09; Adapter->Media[MEDIA_BNC].Csr14 = 0x0705; Adapter->Media[MEDIA_BNC].Csr15 = 0x0006; Adapter->Media[MEDIA_10T_FD].Csr13 = 0x8F01; Adapter->Media[MEDIA_10T_FD].Csr14 = 0xFFFD; Adapter->Media[MEDIA_10T_FD].Csr15 = 0x0000; Adapter->Media[MEDIA_10T_FD].OpMode = DC_OPMODE_FULL_DUPLEX; break; } case DC21041: { Adapter->Media[MEDIA_10T].Csr13 = 0xEF01; Adapter->Media[MEDIA_10T].Csr14 = 0xFF3F; Adapter->Media[MEDIA_10T].Csr15 = 0x0008; Adapter->Media[MEDIA_BNC].Csr13 = 0xEF09; Adapter->Media[MEDIA_BNC].Csr14 = 0xF7FD; Adapter->Media[MEDIA_BNC].Csr15 = 0x0006; Adapter->Media[MEDIA_AUI].Csr13 = 0xEF09; Adapter->Media[MEDIA_AUI].Csr14 = 0xF7FD; Adapter->Media[MEDIA_AUI].Csr15 = 0x000E; Adapter->Media[MEDIA_10T_HD].Csr13 = 0xEF01; Adapter->Media[MEDIA_10T_HD].Csr14 = 0x7F3F; Adapter->Media[MEDIA_10T_HD].Csr15 = 0x0008; Adapter->Media[MEDIA_10T_FD].Csr13 = 0xEF01; Adapter->Media[MEDIA_10T_FD].Csr14 = 0x7F3D; Adapter->Media[MEDIA_10T_FD].Csr15 = 0x0008; Adapter->Media[MEDIA_10T_FD].OpMode = DC_OPMODE_FULL_DUPLEX; break; } case DC21140: { MediaInitOpMode2114x(Adapter); break; } case DC21143: case DC21145: { Adapter->Media[MEDIA_10T].Csr13 = 0x0001; Adapter->Media[MEDIA_10T].Csr14 = 0x7F3F; Adapter->Media[MEDIA_10T].Csr15 = 0x0008; Adapter->Media[MEDIA_BNC].Csr13 = 0x0009; Adapter->Media[MEDIA_BNC].Csr14 = 0x0705; Adapter->Media[MEDIA_BNC].Csr15 = 0x0006; Adapter->Media[MEDIA_AUI].Csr13 = 0x0009; Adapter->Media[MEDIA_AUI].Csr14 = 0x0705; Adapter->Media[MEDIA_AUI].Csr15 = 0x000E; Adapter->Media[MEDIA_10T_FD].Csr13 = 0x0001; Adapter->Media[MEDIA_10T_FD].Csr14 = 0x7F3D; Adapter->Media[MEDIA_10T_FD].Csr15 = 0x0008; Adapter->Media[MEDIA_HMR].Csr13 = 0x0009; Adapter->Media[MEDIA_HMR].Csr14 = 0x0505; Adapter->Media[MEDIA_HMR].Csr15 = 0x0010; MediaInitOpMode2114x(Adapter); break; } default: break; } }