xref: /reactos/drivers/network/dd/dc21x4/phy.c (revision 14d3b53c)
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