xref: /aosp_15_r20/external/coreboot/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /***********************license start***********************************
2 * Copyright (c) 2003-2017  Cavium Inc. ([email protected]). All rights
3 * reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *   * Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 *
13 *   * Redistributions in binary form must reproduce the above
14 *     copyright notice, this list of conditions and the following
15 *     disclaimer in the documentation and/or other materials provided
16 *     with the distribution.
17 *
18 *   * Neither the name of Cavium Inc. nor the names of
19 *     its contributors may be used to endorse or promote products
20 *     derived from this software without specific prior written
21 *     permission.
22 *
23 * This Software, including technical data, may be subject to U.S. export
24 * control laws, including the U.S. Export Administration Act and its
25 * associated regulations, and may be subject to export or import
26 * regulations in other countries.
27 *
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT
31 * TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
32 * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
33 * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
34 * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
35 * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT,
36 * QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK
37 * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
39 #include <bdk.h>
40 #include <libbdk-hal/if/bdk-if.h>
41 #include <libbdk-hal/bdk-config.h>
42 #include <libbdk-hal/bdk-mdio.h>
43 #include <libbdk-hal/bdk-qlm.h>
44 #include <libbdk-hal/bdk-twsi.h>
45 
46 /**
47  * Called when the PHY is connected through TWSI
48  *
49  * @param dev_node Node the ethernet device is on
50  * @param phy_addr Encoded address, see bdk-if.h for format
51  *
52  * @return Link status
53  */
__bdk_if_phy_get_twsi(bdk_node_t dev_node,int phy_addr)54 static bdk_if_link_t __bdk_if_phy_get_twsi(bdk_node_t dev_node, int phy_addr)
55 {
56     /* For TWSI:
57         Bits[31:24]: Node ID, 0xff for device node
58         Bits[23:16]: TWSI internal address width in bytes (0-2)
59         Bits[15:12]: 2=TWSI
60         Bits[11:8]: TWSI bus number
61         Bits[7:0]: TWSI address */
62     int node = (phy_addr >> 24) & 0xff;
63     int twsi_ia_width = (phy_addr >> 16) & 0xff;
64     int twsi_bus = (phy_addr >> 8) & 0xf;
65     int twsi_addr = phy_addr & 0xff;
66     if (node == 0xff)
67         node = dev_node;
68 
69     bdk_if_link_t result;
70     result.u64 = 0;
71 
72     /* This is from the Avago SFP 1G Module data sheet
73        Register 17 (Extended Status 1) */
74     int64_t phy_status = bdk_twsix_read_ia(node, twsi_bus, twsi_addr, 17, 2, twsi_ia_width);
75     if (phy_status != -1)
76     {
77         int speed = (phy_status >> 14)& 3;
78         int duplex = (phy_status >> 13)& 1;
79         int resolved = (phy_status >> 11)& 1;
80         int link = (phy_status >> 10)& 1;
81         if (resolved)
82         {
83             result.s.up = link;
84             result.s.full_duplex = duplex;
85             switch (speed)
86             {
87                 case 0: /* 10 Mbps */
88                     result.s.speed = 10;
89                     break;
90                 case 1: /* 100 Mbps */
91                     result.s.speed = 100;
92                     break;
93                 case 2: /* 1 Gbps */
94                     result.s.speed = 1000;
95                     break;
96                 case 3: /* Illegal */
97                     result.u64 = 0;
98                     break;
99             }
100         }
101     }
102 
103     return result;
104 }
105 
106 /**
107  * Read the status of a PHY
108  *
109  * @param dev_node Node the ethernet device is on
110  * @param phy_addr Encoded PHY address, see bdk-if.h for format
111  *
112  * @return Link status
113  */
__bdk_if_phy_get(bdk_node_t dev_node,int phy_addr)114 bdk_if_link_t __bdk_if_phy_get(bdk_node_t dev_node, int phy_addr)
115 {
116     int node = (phy_addr >> 24) & 0xff;
117     int mdio_bus = (phy_addr >> 8) & 0xff;
118     int mdio_addr = phy_addr & 0xff;
119     if (node == 0xff)
120         node = dev_node;
121     int phy_status;
122     bdk_if_link_t result;
123     result.u64 = 0;
124 
125     /* PHY address of -1 menas there is no PHY and we should have never
126         gotten here */
127     if (phy_addr == -1)
128         return result;
129 
130     /* A PHY address with the special value 0x1000 represents a PHY we can't
131         connect to through MDIO which is assumed to be at 1Gbps */
132     if (phy_addr == BDK_IF_PHY_FIXED_1GB)
133     {
134         result.s.up = 1;
135         result.s.full_duplex = 1;
136         result.s.speed = 1000;
137         return result;
138     }
139 
140     /* A PHY address with the special value 0x1001 represents a PHY we can't
141         connect to through MDIO which is assumed to be at 100Mbps */
142     if (phy_addr == BDK_IF_PHY_FIXED_100MB)
143     {
144         result.s.up = 1;
145         result.s.full_duplex = 1;
146         result.s.speed = 100;
147         return result;
148     }
149 
150     /* Check for a PHY connected through TWSI */
151     if ((phy_addr & BDK_IF_PHY_TYPE_MASK) == BDK_IF_PHY_TWSI)
152         return __bdk_if_phy_get_twsi(dev_node, phy_addr);
153 
154     phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, BDK_MDIO_PHY_REG_ID1);
155     if ((phy_status <= 0) || (phy_status == 0xffff))
156         return result;
157 
158     switch (phy_status)
159     {
160         case 0x0141: /* Marvell */
161         {
162 
163             /* This code assumes we are using a Marvell Gigabit PHY. All the
164                 speed information can be read from register 17 in one go. Somebody
165                 using a different PHY will need to handle it above in the board
166                 specific area */
167             phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 17);
168             if (phy_status < 0)
169                 return result;
170 
171             /* If the resolve bit 11 isn't set, see if autoneg is turned off
172                 (bit 12, reg 0). The resolve bit doesn't get set properly when
173                 autoneg is off, so force it */
174             if ((phy_status & (1<<11)) == 0)
175             {
176                 bdk_mdio_phy_reg_control_t control;
177                 int phy_c = bdk_mdio_read(node, mdio_bus, mdio_addr, BDK_MDIO_PHY_REG_CONTROL);
178                 if (phy_c < 0)
179                     return result;
180                 control.u16 = phy_c;
181                 if (control.s.autoneg_enable == 0)
182                     phy_status |= 1<<11;
183             }
184 
185             /* Only return a link if the PHY has finished auto negotiation
186                 and set the resolved bit (bit 11) */
187             if (phy_status & (1<<11))
188             {
189                 result.s.up = 1;
190                 result.s.full_duplex = ((phy_status>>13)&1);
191                 switch ((phy_status>>14)&3)
192                 {
193                     case 0: /* 10 Mbps */
194                         result.s.speed = 10;
195                         break;
196                     case 1: /* 100 Mbps */
197                         result.s.speed = 100;
198                         break;
199                     case 2: /* 1 Gbps */
200                         result.s.speed = 1000;
201                         break;
202                     case 3: /* Illegal */
203                         result.u64 = 0;
204                         break;
205                 }
206             }
207             break;
208         }
209         case 0x0022: /* Kendin */
210         {
211             /* Register 1Fh - PHY Control */
212             /* Micrel KSZ9031RNX, EBB8104 RGMII transceiver */
213             /* Reports as "Kendin" in BDK_MDIO_PHY_REG_ID1 */
214             phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x1F);
215             if (phy_status & (1 << 6)) // Speed Status - 1000Base-T
216             {
217                 result.s.up = 1;
218                 result.s.speed = 1000;
219             }
220             else if (phy_status & (1 << 5)) // Speed Status - 100Base-TX
221             {
222                 result.s.up = 1;
223                 result.s.speed = 100;
224             }
225             else if (phy_status & (1 << 4)) // Speed Status - 10Base-T
226             {
227                 result.s.up = 1;
228                 result.s.speed = 10;
229             }
230             if (phy_status & (1 << 3)) // Duplex Status
231             {
232                 result.s.full_duplex = 1;
233             }
234             break;
235         }
236         case 0x0007: /* Vitesse */
237         {
238             /* Auxiliary Control and Status, Address 28 (0x1C) */
239             phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x1c);
240             result.s.full_duplex = (phy_status>>5)&1;
241             switch ((phy_status>>3) & 3)
242             {
243                 case 0:
244                     result.s.speed = 10;
245                     result.s.up = 1;
246                     break;
247                 case 1:
248                     result.s.speed = 100;
249                     result.s.up = 1;
250                     break;
251                 default:
252                     result.s.speed = 1000;
253                     break;
254             }
255             phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x01);
256             result.s.up = (phy_status>>2)&1;
257             break;
258         }
259         default: /* Treat like Broadcom */
260         {
261             /* Below we are going to read SMI/MDIO register 0x19 which works
262                 on Broadcom parts */
263             phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x19);
264             if (phy_status < 0)
265                 return result;
266 
267             switch ((phy_status>>8) & 0x7)
268             {
269                 case 0:
270                     result.u64 = 0;
271                     break;
272                 case 1:
273                     result.s.up = 1;
274                     result.s.full_duplex = 0;
275                     result.s.speed = 10;
276                     break;
277                 case 2:
278                     result.s.up = 1;
279                     result.s.full_duplex = 1;
280                     result.s.speed = 10;
281                     break;
282                 case 3:
283                     result.s.up = 1;
284                     result.s.full_duplex = 0;
285                     result.s.speed = 100;
286                     break;
287                 case 4:
288                     result.s.up = 1;
289                     result.s.full_duplex = 1;
290                     result.s.speed = 100;
291                     break;
292                 case 5:
293                     result.s.up = 1;
294                     result.s.full_duplex = 1;
295                     result.s.speed = 100;
296                     break;
297                 case 6:
298                     result.s.up = 1;
299                     result.s.full_duplex = 0;
300                     result.s.speed = 1000;
301                     break;
302                 case 7:
303                     result.s.up = 1;
304                     result.s.full_duplex = 1;
305                     result.s.speed = 1000;
306                     break;
307             }
308             break;
309         }
310     }
311 
312     /* If link is down, return all fields as zero. */
313     if (!result.s.up)
314         result.u64 = 0;
315 
316     return result;
317 }
318 
319 /**
320  * PHY XS initialization, primarily for RXAUI
321  *
322  * @param dev_node Node the ethernet device is on
323  * @param phy_addr Encoded PHY address, see bdk-if.h for format
324  *
325  * @return none
326  */
__bdk_if_phy_xs_init(bdk_node_t dev_node,int phy_addr)327 void __bdk_if_phy_xs_init(bdk_node_t dev_node, int phy_addr)
328 {
329     /* This code only supports PHYs connected through MDIO */
330     if ((phy_addr & BDK_IF_PHY_TYPE_MASK) != BDK_IF_PHY_MDIO)
331         return;
332 
333     int node = (phy_addr >> 24) & 0xff;
334     int mdio_bus = (phy_addr >> 8) & 0xff;
335     int mdio_addr = phy_addr & 0xff;
336     if (node == 0xff)
337         node = dev_node;
338 
339     /* Read the PMA/PMD Device Identifier (1.2, 1.3)
340        OUI is spread across both registers */
341     int dev_addr = 1;
342     int reg_addr = 2;
343     int phy_id1 = bdk_mdio_45_read(node, mdio_bus, mdio_addr, dev_addr, reg_addr);
344     if (phy_id1 == -1)
345         return;
346     reg_addr = 3;
347     int phy_id2 = bdk_mdio_45_read(node, mdio_bus, mdio_addr, dev_addr, reg_addr);
348     if (phy_id2 == -1)
349         return;
350     int model_number = (phy_id2 >> 4) & 0x3F;
351     int oui = phy_id1;
352     oui <<= 6;
353     oui |= (phy_id2 >> 10) & 0x3F;
354     switch (oui)
355     {
356        case 0x5016:  /* Marvell */
357            if (model_number == 9) /* 88X3140/3120 */
358            {
359                BDK_TRACE(BGX, "N%d.MDIO%d.%d: Performing PHY reset on Marvell RXAUI PHY\n",
360                    node, mdio_bus, mdio_addr);
361                dev_addr = 4;
362                reg_addr = 0;
363                /* Write bit 15, Software Reset, in PHY XS Control 1 (4.0).  On CN78xx,
364                   sometimes the PHY/BGX gets stuck in local fault mode, link never comes up,
365                   and this appears to clear it up.  Haven't seen this on CN81xx or T88,
366                   but the reset seems like cheap insurance. */
367                if (bdk_mdio_45_write(node, mdio_bus, mdio_addr, dev_addr, reg_addr, (1 << 15)))
368                {
369                    bdk_error("PHY XS: MDIO write to (%d.%d) failed\n", dev_addr, reg_addr);
370                    return;
371                }
372 
373                int reset_pending = 1;
374                while (reset_pending)
375                {
376                    reset_pending = bdk_mdio_45_read(node, mdio_bus, mdio_addr, dev_addr, reg_addr);
377                    reset_pending &= (1 << 15);
378                }
379 
380                /* Adjust the RXAUI TX Level for Marvell PHY, per Brendan Metzner
381                   write 5 to register 4.49155 */
382                reg_addr = 49155;
383                if (bdk_mdio_45_write(node, mdio_bus, mdio_addr, dev_addr, reg_addr, 5))
384                {
385                    bdk_error("PHY XS: MDIO write to (%d.%d) failed\n", dev_addr, reg_addr);
386                    return;
387                }
388            }
389            break;
390 
391        default:  /* Unknown PHY, or no PHY present */
392            break;
393     }
394 }
395 
bdk_if_phy_setup(bdk_node_t dev_node)396 int bdk_if_phy_setup(bdk_node_t dev_node)
397 {
398     /* 81xx has only 2 BGX (BGX0-BGX1); BGX2 is RGMII */
399     for (int bgx = 0; bgx < 2; bgx++)
400     {
401         int port = 0;
402         int phy_addr = bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, dev_node, bgx, port);
403         if (phy_addr != -1)
404         {
405             int node = (phy_addr >> 24) & 0xff;
406             int mdio_bus = (phy_addr >> 8) & 0xff;
407             int mdio_addr = phy_addr & 0xff;
408             if (node == 0xff)
409                 node = bdk_numa_local();
410             if ((phy_addr & BDK_IF_PHY_TYPE_MASK) == BDK_IF_PHY_MDIO)
411             {
412                 int qlm = bdk_qlm_get_qlm_num(node, BDK_IF_BGX, bgx, port);
413                 if (qlm == -1)
414                     continue;
415 
416                 BDK_TRACE(PHY, "N%d.BGX%d.%d: Configuring ...\n", node, bgx, port);
417 
418                  /* Check PHY id */
419                 int phy_status_1 = bdk_mdio_read(node, mdio_bus, phy_addr, BDK_MDIO_PHY_REG_ID1);
420                 int phy_status_2 = bdk_mdio_read(node, mdio_bus, phy_addr, BDK_MDIO_PHY_REG_ID2);
421 
422                 /* Vitesse */
423                 if (phy_status_1 == 0x0007)
424                 {
425                     if (phy_status_2 == 0x0670)
426                     {
427                         bdk_if_phy_vsc8514_setup(node, qlm, mdio_bus, mdio_addr);
428                     }
429                     else
430                     {
431                         bdk_if_phy_vetesse_setup(node, qlm, mdio_bus, mdio_addr);
432                     }
433                 }
434 
435                 /* Marvell */
436                 else if (phy_status_1 == 0x0141)
437                     bdk_if_phy_marvell_setup(node, qlm, mdio_bus, mdio_addr);
438                 else
439                     BDK_TRACE(PHY, "N%d.BGX%d.%d: Unknown PHY %x\n", node, bgx, port, phy_status_1);
440             }
441         }
442     }
443     return 0;
444 }
445 
446