1 /* 2 * PROJECT: ReactOS DC21x4 Driver 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: PHY layer setup and management 5 * COPYRIGHT: Copyright 2023 Dmitry Borisov <di.sean@protonmail.com> 6 */ 7 8 /* INCLUDES *******************************************************************/ 9 10 #include "dc21x4.h" 11 12 #include <debug.h> 13 14 /* GLOBALS ********************************************************************/ 15 16 #define MII_READ(Adapter, Data) \ 17 do { \ 18 *Data = DC_READ((Adapter), DcCsr9_SerialInterface); \ 19 NdisStallExecution(2); \ 20 } while (0) 21 22 #define MII_WRITE(Adapter, Value) \ 23 do { \ 24 DC_WRITE((Adapter), DcCsr9_SerialInterface, Value); \ 25 NdisStallExecution(2); \ 26 } while (0) 27 28 /* FUNCTIONS ******************************************************************/ 29 30 static 31 VOID 32 MiiMdioPacket( 33 _In_ PDC21X4_ADAPTER Adapter, 34 _In_ ULONG Sequence, 35 _In_ ULONG BitCount) 36 { 37 LONG i; 38 39 for (i = BitCount - 1; i >= 0; --i) 40 { 41 ULONG Mdo = ((Sequence >> i) & 1) << DC_SERIAL_MII_MDO_SHIFT; 42 43 MII_WRITE(Adapter, Mdo); 44 MII_WRITE(Adapter, Mdo | DC_SERIAL_MII_MDC); 45 } 46 } 47 48 static 49 ULONG 50 MiiMdioShiftIn( 51 _In_ PDC21X4_ADAPTER Adapter) 52 { 53 ULONG i, Csr; 54 ULONG Result = 0; 55 56 for (i = 0; i < RTL_BITS_OF(USHORT); ++i) 57 { 58 MII_WRITE(Adapter, DC_SERIAL_MII_MII); 59 MII_WRITE(Adapter, DC_SERIAL_MII_MII | DC_SERIAL_MII_MDC); 60 61 MII_READ(Adapter, &Csr); 62 Result = (Result << 1) | ((Csr >> DC_SERIAL_MII_MDI_SHIFT) & 1); 63 } 64 65 return Result; 66 } 67 68 static 69 VOID 70 MiiMdioClearExtraBits( 71 _In_ PDC21X4_ADAPTER Adapter) 72 { 73 MII_WRITE(Adapter, DC_SERIAL_MII_MII); 74 MII_WRITE(Adapter, DC_SERIAL_MII_MII | DC_SERIAL_MII_MDC); 75 } 76 77 BOOLEAN 78 MiiWrite( 79 _In_ PDC21X4_ADAPTER Adapter, 80 _In_ ULONG PhyAddress, 81 _In_ ULONG RegAddress, 82 _In_ ULONG Data) 83 { 84 MiiMdioPacket(Adapter, MDIO_PREAMBLE, 32); 85 MiiMdioPacket(Adapter, 86 (MDIO_START << 30) | 87 (MDIO_WRITE << 28) | 88 (PhyAddress << 23) | 89 (RegAddress << 18) | 90 (MDIO_TA << 16) | 91 Data, 92 32); 93 94 /* Idle state */ 95 MiiMdioClearExtraBits(Adapter); 96 97 return TRUE; 98 } 99 100 BOOLEAN 101 MiiRead( 102 _In_ PDC21X4_ADAPTER Adapter, 103 _In_ ULONG PhyAddress, 104 _In_ ULONG RegAddress, 105 _Out_ PULONG Data) 106 { 107 ULONG Csr; 108 BOOLEAN Success; 109 110 MiiMdioPacket(Adapter, MDIO_PREAMBLE, 32); 111 MiiMdioPacket(Adapter, 112 (MDIO_START << 12) | 113 (MDIO_READ << 10) | 114 (PhyAddress << 5) | 115 RegAddress, 116 14); 117 118 /* Turnaround */ 119 MiiMdioClearExtraBits(Adapter); 120 121 Csr = DC_READ(Adapter, DcCsr9_SerialInterface); 122 Success = !(Csr & DC_SERIAL_MII_MDI); 123 124 *Data = MiiMdioShiftIn(Adapter); 125 126 /* Idle state */ 127 MiiMdioClearExtraBits(Adapter); 128 129 return Success; 130 } 131 132 static 133 CODE_SEG("PAGE") 134 VOID 135 HpnaSpiClose( 136 _In_ PDC21X4_ADAPTER Adapter) 137 { 138 PAGED_CODE(); 139 140 DC_WRITE(Adapter, DcCsr9_SerialInterface, 0); 141 DC_WRITE(Adapter, DcCsr9_SerialInterface, DC_SERIAL_SPI_SK); 142 } 143 144 static 145 CODE_SEG("PAGE") 146 VOID 147 HpnaSpiShiftOut( 148 _In_ PDC21X4_ADAPTER Adapter, 149 _In_ ULONG Sequence, 150 _In_ ULONG BitCount) 151 { 152 LONG i; 153 154 PAGED_CODE(); 155 156 DC_WRITE(Adapter, DcCsr9_SerialInterface, 0); 157 DC_WRITE(Adapter, DcCsr9_SerialInterface, DC_SERIAL_SPI_CS); 158 159 for (i = BitCount - 1; i >= 0; --i) 160 { 161 ULONG DataIn = ((Sequence >> i) & 1) << DC_SERIAL_SPI_DI_SHIFT; 162 163 DC_WRITE(Adapter, DcCsr9_SerialInterface, DataIn | DC_SERIAL_SPI_CS); 164 DC_WRITE(Adapter, DcCsr9_SerialInterface, DataIn | DC_SERIAL_SPI_CS | DC_SERIAL_SPI_SK); 165 } 166 167 DC_WRITE(Adapter, DcCsr9_SerialInterface, 0); 168 DC_WRITE(Adapter, DcCsr9_SerialInterface, DC_SERIAL_SPI_SK); 169 } 170 171 static 172 CODE_SEG("PAGE") 173 VOID 174 HpnaWrite( 175 _In_ PDC21X4_ADAPTER Adapter, 176 _In_ ULONG RegAddress, 177 _In_ ULONG Data) 178 { 179 PAGED_CODE(); 180 181 HpnaSpiShiftOut(Adapter, DC_SPI_SET_WRITE_ENABLE, 8); 182 HpnaSpiClose(Adapter); 183 184 HpnaSpiShiftOut(Adapter, 185 (Data << 16) | 186 (RegAddress << 8) | 187 DC_SPI_BYTE_WRITE_OPERATION, 188 RTL_BITS_OF(UCHAR) * 3); 189 HpnaSpiClose(Adapter); 190 } 191 192 CODE_SEG("PAGE") 193 VOID 194 HpnaPhyInit( 195 _In_ PDC21X4_ADAPTER Adapter) 196 { 197 ULONG SiaConn, i; 198 199 PAGED_CODE(); 200 201 /* Select the HPNA interface */ 202 SiaConn = DC_READ(Adapter, DcCsr13_SiaConnectivity); 203 SiaConn |= DC_SIA_CONN_HPNA; 204 DC_WRITE(Adapter, DcCsr13_SiaConnectivity, SiaConn); 205 206 for (i = 0; i < RTL_NUMBER_OF(Adapter->HpnaRegister); ++i) 207 { 208 if (Adapter->HpnaInitBitmap & (1 << i)) 209 { 210 HpnaWrite(Adapter, i, Adapter->HpnaRegister[i]); 211 } 212 } 213 } 214 215 CODE_SEG("PAGE") 216 BOOLEAN 217 DcFindMiiPhy( 218 _In_ PDC21X4_ADAPTER Adapter) 219 { 220 ULONG Phy; 221 222 PAGED_CODE(); 223 224 /* Look for the first connected PHY */ 225 for (Phy = 1; Phy <= MII_MAX_PHY_ADDRESSES; ++Phy) 226 { 227 ULONG PhyAddress = Phy % MII_MAX_PHY_ADDRESSES; /* Check the PHY 0 last */ 228 ULONG MiiStatus; 229 #if DBG 230 ULONG PhyIdLow, PhyIdHigh, MiiControl, MiiAdvertise; 231 #endif 232 233 /* 234 * Read the status register. Some PHYs, such as the ML6692, 235 * don't implement the IEEE ID registers. 236 */ 237 if (!MiiRead(Adapter, PhyAddress, MII_STATUS, &MiiStatus)) 238 continue; 239 if (MiiStatus == 0xFFFF || MiiStatus == 0) 240 continue; 241 242 #if DBG 243 MiiRead(Adapter, PhyAddress, MII_PHY_ID1, &PhyIdLow); 244 MiiRead(Adapter, PhyAddress, MII_PHY_ID2, &PhyIdHigh); 245 MiiRead(Adapter, PhyAddress, MII_CONTROL, &MiiControl); 246 MiiRead(Adapter, PhyAddress, MII_AUTONEG_ADVERTISE, &MiiAdvertise); 247 248 INFO_VERB("Found PHY at address %u: ID %04lx:%04lx, Ctrl %04lx, Status %04lx, Adv %04lx\n", 249 PhyAddress, 250 PhyIdLow, 251 PhyIdHigh, 252 MiiControl, 253 MiiStatus, 254 MiiAdvertise); 255 #endif 256 257 Adapter->PhyAddress = PhyAddress; 258 259 return TRUE; 260 } 261 262 return FALSE; 263 } 264