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
MiiMdioPacket(_In_ PDC21X4_ADAPTER Adapter,_In_ ULONG Sequence,_In_ ULONG BitCount)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
MiiMdioShiftIn(_In_ PDC21X4_ADAPTER Adapter)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
MiiMdioClearExtraBits(_In_ PDC21X4_ADAPTER Adapter)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
MiiWrite(_In_ PDC21X4_ADAPTER Adapter,_In_ ULONG PhyAddress,_In_ ULONG RegAddress,_In_ ULONG Data)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
MiiRead(_In_ PDC21X4_ADAPTER Adapter,_In_ ULONG PhyAddress,_In_ ULONG RegAddress,_Out_ PULONG Data)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
HpnaSpiClose(_In_ PDC21X4_ADAPTER Adapter)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
HpnaSpiShiftOut(_In_ PDC21X4_ADAPTER Adapter,_In_ ULONG Sequence,_In_ ULONG BitCount)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
HpnaWrite(_In_ PDC21X4_ADAPTER Adapter,_In_ ULONG RegAddress,_In_ ULONG Data)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
HpnaPhyInit(_In_ PDC21X4_ADAPTER Adapter)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
DcFindMiiPhy(_In_ PDC21X4_ADAPTER Adapter)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