cvmx-helper-board.c revision 242104
1/***********************license start***************
2 * Copyright (c) 2003-2011  Cavium Inc. (support@cavium.com). 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  control
24 * laws, including the U.S. Export Administration Act and its  associated
25 * regulations, and may be subject to export or import  regulations in other
26 * 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 TO
31 * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32 * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33 * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34 * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35 * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36 * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37 * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
39
40
41
42
43
44
45
46/**
47 * @file
48 *
49 * Helper functions to abstract board specific data about
50 * network ports from the rest of the cvmx-helper files.
51 *
52 * <hr>$Revision: 70030 $<hr>
53 */
54#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
55#include <linux/module.h>
56#include <asm/octeon/cvmx.h>
57#include <asm/octeon/cvmx-bootinfo.h>
58#include <asm/octeon/cvmx-smix-defs.h>
59#include <asm/octeon/cvmx-gmxx-defs.h>
60#include <asm/octeon/cvmx-asxx-defs.h>
61#include <asm/octeon/cvmx-mdio.h>
62#include <asm/octeon/cvmx-helper.h>
63#include <asm/octeon/cvmx-helper-util.h>
64#include <asm/octeon/cvmx-helper-board.h>
65#include <asm/octeon/cvmx-twsi.h>
66#else
67#include "cvmx.h"
68#include "cvmx-app-init.h"
69#include "cvmx-sysinfo.h"
70#include "cvmx-twsi.h"
71#include "cvmx-mdio.h"
72#include "cvmx-helper.h"
73#include "cvmx-helper-util.h"
74#include "cvmx-helper-board.h"
75#include "cvmx-gpio.h"
76#if !defined(__FreeBSD__) || !defined(_KERNEL)
77#ifdef __U_BOOT__
78# include <libfdt.h>
79#else
80# include "libfdt/libfdt.h"
81#endif
82#endif
83#include "cvmx-swap.h"
84#endif
85
86/**
87 * cvmx_override_board_link_get(int ipd_port) is a function
88 * pointer. It is meant to allow customization of the process of
89 * talking to a PHY to determine link speed. It is called every
90 * time a PHY must be polled for link status. Users should set
91 * this pointer to a function before calling any cvmx-helper
92 * operations.
93 */
94CVMX_SHARED cvmx_helper_link_info_t (*cvmx_override_board_link_get)(int ipd_port) = NULL;
95
96#if !defined(CVMX_BUILD_FOR_LINUX_KERNEL) && (!defined(__FreeBSD__) || !defined(_KERNEL))
97
98static void cvmx_retry_i2c_write(int twsi_id, uint8_t dev_addr, uint16_t internal_addr, int num_bytes, int ia_width_bytes, uint64_t data)
99{
100    int tries = 3;
101    int r;
102    do {
103        r = cvmx_twsix_write_ia(twsi_id, dev_addr, internal_addr, num_bytes, ia_width_bytes, data);
104    } while (tries-- > 0 && r < 0);
105}
106
107static int __pip_eth_node(const void *fdt_addr, int aliases, int ipd_port)
108{
109    char name_buffer[20];
110    const char*pip_path;
111    int pip, iface, eth;
112    int interface_num    = cvmx_helper_get_interface_num(ipd_port);
113    int interface_index  = cvmx_helper_get_interface_index_num(ipd_port);
114
115    pip_path = fdt_getprop(fdt_addr, aliases, "pip", NULL);
116    if (!pip_path)
117    {
118        cvmx_dprintf("ERROR: pip path not found in device tree\n");
119        return -1;
120    }
121    pip = fdt_path_offset(fdt_addr, pip_path);
122    if (pip < 0)
123    {
124        cvmx_dprintf("ERROR: pip not found in device tree\n");
125        return -1;
126    }
127#ifdef __U_BOOT__
128    sprintf(name_buffer, "interface@%d", interface_num);
129#else
130    snprintf(name_buffer, sizeof(name_buffer), "interface@%d", interface_num);
131#endif
132    iface =  fdt_subnode_offset(fdt_addr, pip, name_buffer);
133    if (iface < 0)
134    {
135        cvmx_dprintf("ERROR : pip intf %d not found in device tree \n",
136                     interface_num);
137        return -1;
138    }
139#ifdef __U_BOOT__
140    sprintf(name_buffer, "ethernet@%x", interface_index);
141#else
142    snprintf(name_buffer, sizeof(name_buffer), "ethernet@%x", interface_index);
143#endif
144    eth = fdt_subnode_offset(fdt_addr, iface, name_buffer);
145    if (eth < 0)
146    {
147        cvmx_dprintf("ERROR : pip interface@%d ethernet@%d not found in device "
148                     "tree\n", interface_num, interface_index);
149        return -1;
150    }
151    return eth;
152}
153
154static int __mix_eth_node(const void *fdt_addr, int aliases, int interface_index)
155{
156    char name_buffer[20];
157    const char*mix_path;
158    int mix;
159
160#ifdef __U_BOOT__
161    sprintf(name_buffer, "mix%d", interface_index);
162#else
163    snprintf(name_buffer, sizeof(name_buffer), "mix%d", interface_index);
164#endif
165    mix_path = fdt_getprop(fdt_addr, aliases, name_buffer, NULL);
166    if (!mix_path)
167    {
168        cvmx_dprintf("ERROR: mix%d path not found in device tree\n",interface_index);
169    }
170    mix = fdt_path_offset(fdt_addr, mix_path);
171    if (mix < 0)
172    {
173        cvmx_dprintf("ERROR: %s not found in device tree\n", mix_path);
174        return -1;
175    }
176    return mix;
177}
178
179typedef struct cvmx_phy_info
180{
181    int phy_addr;
182    int direct_connect;
183    cvmx_phy_type_t phy_type;
184}cvmx_phy_info_t;
185
186
187static int __mdiobus_addr_to_unit(uint32_t addr)
188{
189    int unit = (addr >> 7) & 3;
190    if (!OCTEON_IS_MODEL(OCTEON_CN68XX))
191        unit >>= 1;
192    return unit;
193}
194/**
195 * Return the MII PHY address associated with the given IPD
196 * port. The phy address is obtained from the device tree.
197 *
198 * @param ipd_port Octeon IPD port to get the MII address for.
199 *
200 * @return MII PHY address and bus number or -1.
201 */
202
203static cvmx_phy_info_t __get_phy_info_from_dt(int ipd_port)
204{
205    const void *fdt_addr = CASTPTR(const void *, cvmx_sysinfo_get()->fdt_addr);
206    uint32_t *phy_handle;
207    int aliases, eth, phy, phy_parent, phandle, ret;
208    cvmx_phy_info_t phy_info;
209    int mdio_unit=-1;
210    const char *phy_comaptible_str;
211    uint32_t *phy_addr_ptr;
212
213    phy_info.phy_addr = -1;
214    phy_info.direct_connect = -1;
215    phy_info.phy_type = (cvmx_phy_type_t) -1;
216
217    if (!fdt_addr)
218    {
219        cvmx_dprintf("No device tree found.\n");
220        return phy_info;
221    }
222    aliases = fdt_path_offset(fdt_addr, "/aliases");
223    if (aliases < 0) {
224        cvmx_dprintf("Error: No /aliases node in device tree.\n");
225        return phy_info;
226    }
227    if (ipd_port < 0)
228    {
229        int interface_index = ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT;
230        eth = __mix_eth_node(fdt_addr, aliases, interface_index) ;
231    }
232    else
233    {
234        eth = __pip_eth_node(fdt_addr, aliases, ipd_port);
235    }
236    if (eth < 0 )
237    {
238        cvmx_dprintf("ERROR : cannot find interface for ipd_port=%d\n", ipd_port);
239        return phy_info;
240    }
241    /* Get handle to phy */
242    phy_handle = (uint32_t *) fdt_getprop(fdt_addr, eth, "phy-handle", NULL);
243    if (!phy_handle)
244    {
245        cvmx_dprintf("ERROR : phy handle not found in device tree ipd_port=%d"
246                     "\n", ipd_port);
247        return phy_info;
248    }
249    phandle = cvmx_be32_to_cpu(*phy_handle);
250    phy = fdt_node_offset_by_phandle(fdt_addr, phandle);
251    if (phy < 0)
252    {
253        cvmx_dprintf("ERROR : cannot find phy for ipd_port=%d ret=%d\n",
254                     ipd_port, phy);
255        return phy_info;
256    }
257    phy_comaptible_str = (const char *) fdt_getprop(fdt_addr, phy,
258                                                    "compatible", NULL);
259    if (!phy_comaptible_str)
260    {
261        cvmx_dprintf("ERROR : no compatible prop in phy\n");
262        return phy_info;
263    }
264    if (memcmp("marvell", phy_comaptible_str, strlen("marvell")) == 0)
265    {
266        phy_info.phy_type = MARVELL_GENERIC_PHY;
267    }
268    else if (memcmp("broadcom", phy_comaptible_str, strlen("broadcom")) == 0)
269    {
270        phy_info.phy_type = BROADCOM_GENERIC_PHY;
271    }
272    else
273    {
274        phy_info.phy_type = -1;
275    }
276
277    /* Check if PHY parent is the octeon MDIO bus. Some boards are connected
278       though a MUX and for them direct_connect_to_phy will be 0 */
279    phy_parent = fdt_parent_offset(fdt_addr, phy);
280    if (phy_parent < 0)
281    {
282        cvmx_dprintf("ERROR : cannot find phy parent for ipd_port=%d ret=%d\n",
283                     ipd_port, phy_parent);
284        return phy_info;
285    }
286    ret = fdt_node_check_compatible(fdt_addr, phy_parent,
287                                    "cavium,octeon-3860-mdio");
288    if (ret == 0)
289    {
290        phy_info.direct_connect = 1 ;
291        uint32_t *mdio_reg_base = (uint32_t *) fdt_getprop(fdt_addr, phy_parent,"reg",0);
292        if (mdio_reg_base == 0)
293        {
294            cvmx_dprintf("ERROR : unable to get reg property in phy mdio\n");
295            return phy_info;
296        }
297        mdio_unit = __mdiobus_addr_to_unit(mdio_reg_base[1]);
298        //cvmx_dprintf("phy parent=%s reg_base=%08x unit=%d \n",
299        //             fdt_get_name(fdt_addr,phy_parent, NULL), mdio_reg_base[1], mdio_unit);
300    }
301    else
302    {
303        phy_info.direct_connect = 0;
304        /* The PHY is not directly connected to the Octeon MDIO bus.
305           SE doesn't  have abstractions for MDIO MUX or MDIO MUX drivers and
306           hence for the non direct cases code will be needed which is
307           board specific.
308           For now the the MDIO Unit is defaulted to 1.
309        */
310        mdio_unit = 1;
311    }
312
313    phy_addr_ptr = (uint32_t *) fdt_getprop(fdt_addr, phy, "reg", NULL);
314    phy_info.phy_addr = cvmx_be32_to_cpu(*phy_addr_ptr) | mdio_unit << 8;
315    return phy_info;
316
317}
318
319/**
320 * Return the MII PHY address associated with the given IPD
321 * port. The phy address is obtained from the device tree.
322 *
323 * @param ipd_port Octeon IPD port to get the MII address for.
324 *
325 * @return MII PHY address and bus number or -1.
326 */
327
328int cvmx_helper_board_get_mii_address_from_dt(int ipd_port)
329{
330        cvmx_phy_info_t phy_info = __get_phy_info_from_dt(ipd_port);
331        return phy_info.phy_addr;
332}
333#endif
334
335/**
336 * Return the MII PHY address associated with the given IPD
337 * port. A result of -1 means there isn't a MII capable PHY
338 * connected to this port. On chips supporting multiple MII
339 * busses the bus number is encoded in bits <15:8>.
340 *
341 * This function must be modified for every new Octeon board.
342 * Internally it uses switch statements based on the cvmx_sysinfo
343 * data to determine board types and revisions. It replies on the
344 * fact that every Octeon board receives a unique board type
345 * enumeration from the bootloader.
346 *
347 * @param ipd_port Octeon IPD port to get the MII address for.
348 *
349 * @return MII PHY address and bus number or -1.
350 */
351int cvmx_helper_board_get_mii_address(int ipd_port)
352{
353    /*
354     * Board types we have to know at compile-time.
355     */
356#ifdef OCTEON_BOARD_CAPK_0100ND
357    switch (ipd_port) {
358    case 0:
359	return 2;
360    case 1:
361	return 3;
362    case 2:
363	/* XXX Switch PHY?  */
364	return -1;
365    default:
366	return -1;
367    }
368#endif
369
370    /*
371     * For board types we can determine at runtime.
372     */
373    if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
374        return -1;
375#if !defined(CVMX_BUILD_FOR_LINUX_KERNEL) && (!defined(__FreeBSD__) || !defined(_KERNEL))
376    if (cvmx_sysinfo_get()->fdt_addr)
377    {
378        cvmx_phy_info_t phy_info = __get_phy_info_from_dt(ipd_port);
379        //cvmx_dprintf("ipd_port=%d phy_addr=%d\n", ipd_port, phy_info.phy_addr);
380        if (phy_info.phy_addr >= 0) return phy_info.phy_addr;
381    }
382#endif
383    switch (cvmx_sysinfo_get()->board_type)
384    {
385        case CVMX_BOARD_TYPE_SIM:
386            /* Simulator doesn't have MII */
387            return -1;
388        case CVMX_BOARD_TYPE_EBT3000:
389        case CVMX_BOARD_TYPE_EBT5800:
390        case CVMX_BOARD_TYPE_THUNDER:
391        case CVMX_BOARD_TYPE_NICPRO2:
392            /* Interface 0 is SPI4, interface 1 is RGMII */
393            if ((ipd_port >= 16) && (ipd_port < 20))
394                return ipd_port - 16;
395            else
396                return -1;
397        case CVMX_BOARD_TYPE_LANAI2_A:
398            if (ipd_port == 0)
399                return 0;
400            else
401                return -1;
402        case CVMX_BOARD_TYPE_LANAI2_U:
403        case CVMX_BOARD_TYPE_LANAI2_G:
404            if (ipd_port == 0)
405                return 0x1c;
406            else
407                return -1;
408        case CVMX_BOARD_TYPE_KODAMA:
409        case CVMX_BOARD_TYPE_EBH3100:
410        case CVMX_BOARD_TYPE_HIKARI:
411        case CVMX_BOARD_TYPE_CN3010_EVB_HS5:
412        case CVMX_BOARD_TYPE_CN3005_EVB_HS5:
413        case CVMX_BOARD_TYPE_CN3020_EVB_HS5:
414            /* Port 0 is WAN connected to a PHY, Port 1 is GMII connected to a
415                switch */
416            if (ipd_port == 0)
417                return 4;
418            else if (ipd_port == 1)
419                return 9;
420            else
421                return -1;
422        case CVMX_BOARD_TYPE_EBH3000:
423            /* Board has dual SPI4 and no PHYs */
424            return -1;
425        case CVMX_BOARD_TYPE_EBT5810:
426            /* Board has 10g PHYs hooked up to the MII controller on the
427            ** IXF18201 MAC.  The 10G PHYS use clause 45 MDIO which the CN58XX
428            ** does not support. All MII accesses go through the IXF part. */
429            return -1;
430        case CVMX_BOARD_TYPE_EBH5200:
431        case CVMX_BOARD_TYPE_EBH5201:
432        case CVMX_BOARD_TYPE_EBT5200:
433            /* Board has 2 management ports */
434            if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) && (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2)))
435                return ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT;
436            /* Board has 4 SGMII ports. The PHYs start right after the MII
437                ports MII0 = 0, MII1 = 1, SGMII = 2-5 */
438            if ((ipd_port >= 0) && (ipd_port < 4))
439                return ipd_port+2;
440            else
441                return -1;
442        case CVMX_BOARD_TYPE_EBH5600:
443        case CVMX_BOARD_TYPE_EBH5601:
444        case CVMX_BOARD_TYPE_EBH5610:
445            /* Board has 1 management port */
446            if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT)
447                return 0;
448            /* Board has 8 SGMII ports. 4 connect out, two connect to a switch,
449                and 2 loop to each other */
450            if ((ipd_port >= 0) && (ipd_port < 4))
451                return ipd_port+1;
452            else
453                return -1;
454        case CVMX_BOARD_TYPE_EBB5600:
455            {
456                static unsigned char qlm_switch_addr = 0;
457
458                /* Board has 1 management port */
459                if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT)
460                    return 0;
461
462                /* Board has 8 SGMII ports. 4 connected QLM1, 4 connected QLM3 */
463                if ((ipd_port >= 0) && (ipd_port < 4))
464                {
465                    if (qlm_switch_addr != 0x3)
466                    {
467                        qlm_switch_addr = 0x3;  /* QLM1 */
468                        cvmx_twsix_write_ia(0, 0x71, 0, 1, 1, qlm_switch_addr);
469                        cvmx_wait_usec(11000); /* Let the write complete */
470                    }
471                    return ipd_port+1 + (1<<8);
472                }
473                else if ((ipd_port >= 16) && (ipd_port < 20))
474                {
475                    if (qlm_switch_addr != 0xC)
476                    {
477                        qlm_switch_addr = 0xC;  /* QLM3 */
478                        cvmx_twsix_write_ia(0, 0x71, 0, 1, 1, qlm_switch_addr);
479                        cvmx_wait_usec(11000); /* Let the write complete */
480                    }
481                    return ipd_port-16+1 + (1<<8);
482                }
483                else
484                    return -1;
485            }
486        case CVMX_BOARD_TYPE_EBB6300:
487            /* Board has 2 management ports */
488            if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) && (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2)))
489                return ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT + 4;
490            if ((ipd_port >= 0) && (ipd_port < 4))
491                return ipd_port + 1 + (1<<8);
492            else
493                return -1;
494        case CVMX_BOARD_TYPE_EBB6800:
495            /* Board has 1 management ports */
496            if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT)
497                return 6;
498            if (ipd_port >= 0x800 && ipd_port < 0x900) /* QLM 0*/
499                return 0x101 + ((ipd_port >> 4) & 3); /* SMI 1*/
500            if (ipd_port >= 0xa00 && ipd_port < 0xb00) /* QLM 2*/
501                return 0x201 + ((ipd_port >> 4) & 3); /* SMI 2*/
502            if (ipd_port >= 0xb00 && ipd_port < 0xc00) /* QLM 3*/
503                return 0x301 + ((ipd_port >> 4) & 3); /* SMI 3*/
504            if (ipd_port >= 0xc00 && ipd_port < 0xd00) /* QLM 4*/
505                return 0x001 + ((ipd_port >> 4) & 3); /* SMI 0*/
506            return -1;
507        case CVMX_BOARD_TYPE_EP6300C:
508            if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT)
509                return 0x01;
510            if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT+1)
511                return 0x02;
512#ifdef CVMX_ENABLE_PKO_FUNCTIONS
513            {
514                int interface = cvmx_helper_get_interface_num(ipd_port);
515                int mode = cvmx_helper_interface_get_mode(interface);
516                if (mode == CVMX_HELPER_INTERFACE_MODE_XAUI)
517                    return ipd_port;
518                else if ((ipd_port >= 0) && (ipd_port < 4))
519                    return ipd_port + 3;
520                else
521                    return -1;
522            }
523#endif
524            break;
525        case CVMX_BOARD_TYPE_CUST_NB5:
526            if (ipd_port == 2)
527                return 4;
528            else
529                return -1;
530        case CVMX_BOARD_TYPE_NIC_XLE_4G:
531            /* Board has 4 SGMII ports. connected QLM3(interface 1) */
532            if ((ipd_port >= 16) && (ipd_port < 20))
533                return ipd_port - 16 + 1;
534            else
535                return -1;
536        case CVMX_BOARD_TYPE_NIC_XLE_10G:
537        case CVMX_BOARD_TYPE_NIC10E:
538            return -1;  /* We don't use clause 45 MDIO for anything */
539        case CVMX_BOARD_TYPE_NIC4E:
540            if (ipd_port >= 0 && ipd_port <= 3)
541                return (ipd_port + 0x1f) & 0x1f;
542            else
543                return -1;
544        case CVMX_BOARD_TYPE_NIC2E:
545            if (ipd_port >= 0 && ipd_port <= 1)
546                return (ipd_port + 1);
547            else
548                return -1;
549        case CVMX_BOARD_TYPE_REDWING:
550	    return -1;  /* No PHYs connected to Octeon */
551        case CVMX_BOARD_TYPE_BBGW_REF:
552            return -1;  /* No PHYs are connected to Octeon, everything is through switch */
553	case CVMX_BOARD_TYPE_CUST_WSX16:
554		if (ipd_port >= 0 && ipd_port <= 3)
555			return ipd_port;
556		else if (ipd_port >= 16 && ipd_port <= 19)
557			return ipd_port - 16 + 4;
558		else
559			return -1;
560
561	/* Private vendor-defined boards.  */
562#if defined(OCTEON_VENDOR_LANNER)
563	case CVMX_BOARD_TYPE_CUST_LANNER_MR955:
564	    /* Interface 1 is 12 BCM5482S PHYs.  */
565            if ((ipd_port >= 16) && (ipd_port < 28))
566                return ipd_port - 16;
567	    return -1;
568	case CVMX_BOARD_TYPE_CUST_LANNER_MR730:
569            if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) && (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2)))
570		return (ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT) + 0x81;
571            if ((ipd_port >= 0) && (ipd_port < 4))
572                return ipd_port;
573	    return -1;
574	case CVMX_BOARD_TYPE_CUST_LANNER_MR320:
575	case CVMX_BOARD_TYPE_CUST_LANNER_MR321X:
576	    /* Port 0 is a Marvell 88E6161 switch, ports 1 and 2 are Marvell
577	       88E1111 interfaces.  */
578	    switch (ipd_port) {
579	    case 0:
580		return 16;
581	    case 1:
582		return 1;
583	    case 2:
584		return 2;
585	    default:
586		return -1;
587	    }
588#endif
589    }
590
591    /* Some unknown board. Somebody forgot to update this function... */
592    cvmx_dprintf("%s: Unknown board type %d\n",
593                 __FUNCTION__, cvmx_sysinfo_get()->board_type);
594    return -1;
595}
596#ifdef CVMX_BUILD_FOR_LINUX_KERNEL
597EXPORT_SYMBOL(cvmx_helper_board_get_mii_address);
598#endif
599
600/**
601 * @INTERNAL
602 * Get link state of marvell PHY
603 */
604static cvmx_helper_link_info_t __get_marvell_phy_link_state(int phy_addr)
605{
606    cvmx_helper_link_info_t  result;
607    int phy_status;
608
609    result.u64 = 0;
610    /*All the speed information can be read from register 17 in one go.*/
611    phy_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 17);
612
613    /* If the resolve bit 11 isn't set, see if autoneg is turned off
614       (bit 12, reg 0). The resolve bit doesn't get set properly when
615       autoneg is off, so force it */
616    if ((phy_status & (1<<11)) == 0)
617    {
618        int auto_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0);
619        if ((auto_status & (1<<12)) == 0)
620            phy_status |= 1<<11;
621    }
622
623    /* Only return a link if the PHY has finished auto negotiation
624       and set the resolved bit (bit 11) */
625    if (phy_status & (1<<11))
626    {
627        result.s.link_up = 1;
628        result.s.full_duplex = ((phy_status>>13)&1);
629        switch ((phy_status>>14)&3)
630        {
631            case 0: /* 10 Mbps */
632                result.s.speed = 10;
633                break;
634            case 1: /* 100 Mbps */
635                result.s.speed = 100;
636                break;
637            case 2: /* 1 Gbps */
638                result.s.speed = 1000;
639                break;
640            case 3: /* Illegal */
641                result.u64 = 0;
642                break;
643        }
644    }
645    return result;
646}
647
648/**
649 * @INTERNAL
650 * Get link state of broadcom PHY
651 */
652static cvmx_helper_link_info_t __get_broadcom_phy_link_state(int phy_addr)
653{
654    cvmx_helper_link_info_t  result;
655    int phy_status;
656
657    result.u64 = 0;
658    /* Below we are going to read SMI/MDIO register 0x19 which works
659       on Broadcom parts */
660    phy_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x19);
661    switch ((phy_status>>8) & 0x7)
662    {
663        case 0:
664            result.u64 = 0;
665            break;
666        case 1:
667            result.s.link_up = 1;
668            result.s.full_duplex = 0;
669            result.s.speed = 10;
670            break;
671        case 2:
672            result.s.link_up = 1;
673            result.s.full_duplex = 1;
674            result.s.speed = 10;
675            break;
676        case 3:
677            result.s.link_up = 1;
678            result.s.full_duplex = 0;
679            result.s.speed = 100;
680            break;
681        case 4:
682            result.s.link_up = 1;
683            result.s.full_duplex = 1;
684            result.s.speed = 100;
685            break;
686        case 5:
687            result.s.link_up = 1;
688            result.s.full_duplex = 1;
689            result.s.speed = 100;
690            break;
691        case 6:
692            result.s.link_up = 1;
693            result.s.full_duplex = 0;
694            result.s.speed = 1000;
695            break;
696        case 7:
697            result.s.link_up = 1;
698            result.s.full_duplex = 1;
699            result.s.speed = 1000;
700            break;
701    }
702    return result;
703}
704
705
706/**
707 * @INTERNAL
708 * Get link state using inband status
709 */
710static cvmx_helper_link_info_t __get_inband_link_state(int ipd_port)
711{
712    cvmx_helper_link_info_t  result;
713    cvmx_gmxx_rxx_rx_inbnd_t inband_status;
714    int interface = cvmx_helper_get_interface_num(ipd_port);
715    int index = cvmx_helper_get_interface_index_num(ipd_port);
716
717    result.u64 = 0;
718    inband_status.u64 = cvmx_read_csr(CVMX_GMXX_RXX_RX_INBND(index, interface));
719    result.s.link_up = inband_status.s.status;
720    result.s.full_duplex = inband_status.s.duplex;
721    switch (inband_status.s.speed)
722    {
723        case 0: /* 10 Mbps */
724            result.s.speed = 10;
725            break;
726        case 1: /* 100 Mbps */
727            result.s.speed = 100;
728            break;
729        case 2: /* 1 Gbps */
730            result.s.speed = 1000;
731            break;
732        case 3: /* Illegal */
733            result.u64 = 0;
734            break;
735    }
736    return result;
737}
738
739#if !defined(CVMX_BUILD_FOR_LINUX_KERNEL) && (!defined(__FreeBSD__) || !defined(_KERNEL))
740/**
741 * @INTERNAL
742 * Switch MDIO mux to the specified port.
743 */
744static int __switch_mdio_mux(int ipd_port)
745{
746    /* This method is board specific and doesn't use the device tree
747       information as SE doesn't implement MDIO MUX abstration */
748    switch (cvmx_sysinfo_get()->board_type)
749    {
750        case CVMX_BOARD_TYPE_EBB5600:
751        {
752            static unsigned char qlm_switch_addr = 0;
753            /* Board has 1 management port */
754            if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT)
755                return 0;
756            /* Board has 8 SGMII ports. 4 connected QLM1, 4 connected QLM3 */
757            if ((ipd_port >= 0) && (ipd_port < 4))
758            {
759                if (qlm_switch_addr != 0x3)
760                {
761                    qlm_switch_addr = 0x3;  /* QLM1 */
762                    cvmx_twsix_write_ia(0, 0x71, 0, 1, 1, qlm_switch_addr);
763                    cvmx_wait_usec(11000); /* Let the write complete */
764                }
765                return ipd_port+1 + (1<<8);
766            }
767            else if ((ipd_port >= 16) && (ipd_port < 20))
768            {
769                if (qlm_switch_addr != 0xC)
770                {
771                    qlm_switch_addr = 0xC;  /* QLM3 */
772                    cvmx_twsix_write_ia(0, 0x71, 0, 1, 1, qlm_switch_addr);
773                    cvmx_wait_usec(11000); /* Let the write complete */
774                }
775                return ipd_port-16+1 + (1<<8);
776            }
777            else
778                return -1;
779        }
780        case CVMX_BOARD_TYPE_EBB6600:
781        {
782            static unsigned char qlm_switch_addr = 0;
783            int old_twsi_switch_reg;
784            /* Board has 2 management ports */
785            if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) &&
786                (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2)))
787                return ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT + 4;
788            if ((ipd_port >= 0) && (ipd_port < 4)) /* QLM 2 */
789            {
790                if (qlm_switch_addr != 2)
791                {
792                    int tries;
793                    qlm_switch_addr = 2;
794                    tries = 3;
795                    do {
796                        old_twsi_switch_reg = cvmx_twsix_read8(0, 0x70, 0);
797                    } while (tries-- > 0 && old_twsi_switch_reg < 0);
798                    /* Set I2C MUX to enable port expander */
799                    cvmx_retry_i2c_write(0, 0x70, 0, 1, 0, 8);
800                    /* Set selecter to QLM 1 */
801                    cvmx_retry_i2c_write(0, 0x38, 0, 1, 0, 0xff);
802                    /* disable port expander */
803                    cvmx_retry_i2c_write(0, 0x70, 0, 1, 0, old_twsi_switch_reg);
804                }
805                return 0x101 + ipd_port;
806            }
807            else if ((ipd_port >= 16) && (ipd_port < 20)) /* QLM 1 */
808            {
809                if (qlm_switch_addr != 1)
810                {
811                    int tries;
812                    qlm_switch_addr = 1;
813                    tries = 3;
814                    do {
815                            old_twsi_switch_reg = cvmx_twsix_read8(0, 0x70, 0);
816                    } while (tries-- > 0 && old_twsi_switch_reg < 0);
817                    /* Set I2C MUX to enable port expander */
818                    cvmx_retry_i2c_write(0, 0x70, 0, 1, 0, 8);
819                    /* Set selecter to QLM 2 */
820                    cvmx_retry_i2c_write(0, 0x38, 0, 1, 0, 0xf7);
821                    /* disable port expander */
822                    cvmx_retry_i2c_write(0, 0x70, 0, 1, 0, old_twsi_switch_reg);
823                }
824                return 0x101 + (ipd_port - 16);
825            } else
826                return -1;
827        }
828        case CVMX_BOARD_TYPE_EBB6100:
829        {
830            static char gpio_configured = 0;
831
832            if (!gpio_configured)
833            {
834                cvmx_gpio_cfg(3, 1);
835                gpio_configured = 1;
836            }
837            /* Board has 2 management ports */
838            if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) &&
839                (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2)))
840                return ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT + 4;
841            if ((ipd_port >= 0) && (ipd_port < 4)) /* QLM 2 */
842            {
843                cvmx_gpio_set(1ull << 3);
844                return 0x101 + ipd_port;
845            }
846            else if ((ipd_port >= 16) && (ipd_port < 20)) /* QLM 0 */
847            {
848                cvmx_gpio_clear(1ull << 3);
849                return 0x101 + (ipd_port - 16);
850            }
851            else
852            {
853                printf("%s: Unknown ipd port 0x%x\n", __func__, ipd_port);
854                return -1;
855            }
856        }
857        default:
858        {
859            cvmx_dprintf("ERROR : unexpected mdio switch for board=%08x\n",
860                         cvmx_sysinfo_get()->board_type);
861            return -1;
862        }
863    }
864    /* should never get here */
865    return -1;
866}
867
868/**
869 * @INTERNAL
870 * This function is used ethernet ports link speed. This functions uses the
871 * device tree information to determine the phy address and type of PHY.
872 * The only supproted PHYs are Marvell and Broadcom.
873 *
874 * @param ipd_port IPD input port associated with the port we want to get link
875 *                 status for.
876 *
877 * @return The ports link status. If the link isn't fully resolved, this must
878 *         return zero.
879 */
880
881cvmx_helper_link_info_t __cvmx_helper_board_link_get_from_dt(int ipd_port)
882{
883    cvmx_helper_link_info_t  result;
884    cvmx_phy_info_t phy_info;
885
886    result.u64 = 0;
887    if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM)
888    {
889        /* The simulator gives you a simulated 1Gbps full duplex link */
890        result.s.link_up = 1;
891        result.s.full_duplex = 1;
892        result.s.speed = 1000;
893        return result;
894    }
895    phy_info = __get_phy_info_from_dt(ipd_port);
896    //cvmx_dprintf("ipd_port=%d phy_addr=%d dc=%d type=%d \n", ipd_port,
897    //             phy_info.phy_addr, phy_info.direct_connect, phy_info.phy_type);
898    if (phy_info.phy_addr < 0) return result;
899
900    if (phy_info.direct_connect == 0)
901        __switch_mdio_mux(ipd_port);
902    switch(phy_info.phy_type)
903    {
904        case BROADCOM_GENERIC_PHY:
905            result = __get_broadcom_phy_link_state(phy_info.phy_addr);
906            break;
907        case MARVELL_GENERIC_PHY:
908            result = __get_marvell_phy_link_state(phy_info.phy_addr);
909            break;
910        default:
911            result = __get_inband_link_state(ipd_port);
912    }
913    return result;
914
915}
916#endif
917
918/**
919 * @INTERNAL
920 * This function invokes  __cvmx_helper_board_link_get_from_dt when device tree
921 * info is available. When the device tree information is not available then
922 * this function is the board specific method of determining an
923 * ethernet ports link speed. Most Octeon boards have Marvell PHYs
924 * and are handled by the fall through case. This function must be
925 * updated for boards that don't have the normal Marvell PHYs.
926 *
927 * This function must be modified for every new Octeon board.
928 * Internally it uses switch statements based on the cvmx_sysinfo
929 * data to determine board types and revisions. It relies on the
930 * fact that every Octeon board receives a unique board type
931 * enumeration from the bootloader.
932 *
933 * @param ipd_port IPD input port associated with the port we want to get link
934 *                 status for.
935 *
936 * @return The ports link status. If the link isn't fully resolved, this must
937 *         return zero.
938 */
939cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port)
940{
941    cvmx_helper_link_info_t result;
942    int phy_addr;
943    int is_broadcom_phy = 0;
944
945#if !defined(CVMX_BUILD_FOR_LINUX_KERNEL) && (!defined(__FreeBSD__) || !defined(_KERNEL))
946    if (cvmx_sysinfo_get()->fdt_addr)
947    {
948        return __cvmx_helper_board_link_get_from_dt(ipd_port);
949    }
950#endif
951
952    /* Give the user a chance to override the processing of this function */
953    if (cvmx_override_board_link_get)
954        return cvmx_override_board_link_get(ipd_port);
955
956    /* Unless we fix it later, all links are defaulted to down */
957    result.u64 = 0;
958
959#if !defined(OCTEON_BOARD_CAPK_0100ND)
960    /* This switch statement should handle all ports that either don't use
961        Marvell PHYS, or don't support in-band status */
962    switch (cvmx_sysinfo_get()->board_type)
963    {
964        case CVMX_BOARD_TYPE_SIM:
965            /* The simulator gives you a simulated 1Gbps full duplex link */
966            result.s.link_up = 1;
967            result.s.full_duplex = 1;
968            result.s.speed = 1000;
969            return result;
970        case CVMX_BOARD_TYPE_LANAI2_A:
971        case CVMX_BOARD_TYPE_LANAI2_U:
972        case CVMX_BOARD_TYPE_LANAI2_G:
973            break;
974        case CVMX_BOARD_TYPE_EBH3100:
975        case CVMX_BOARD_TYPE_CN3010_EVB_HS5:
976        case CVMX_BOARD_TYPE_CN3005_EVB_HS5:
977        case CVMX_BOARD_TYPE_CN3020_EVB_HS5:
978            /* Port 1 on these boards is always Gigabit */
979            if (ipd_port == 1)
980            {
981                result.s.link_up = 1;
982                result.s.full_duplex = 1;
983                result.s.speed = 1000;
984                return result;
985            }
986            /* Fall through to the generic code below */
987            break;
988        case CVMX_BOARD_TYPE_EBH5600:
989        case CVMX_BOARD_TYPE_EBH5601:
990        case CVMX_BOARD_TYPE_EBH5610:
991            /* Board has 1 management ports */
992            if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT)
993                is_broadcom_phy = 1;
994            break;
995        case CVMX_BOARD_TYPE_EBH5200:
996        case CVMX_BOARD_TYPE_EBH5201:
997        case CVMX_BOARD_TYPE_EBT5200:
998            /* Board has 2 management ports */
999            if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) && (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2)))
1000                is_broadcom_phy = 1;
1001            break;
1002        case CVMX_BOARD_TYPE_EBB6100:
1003        case CVMX_BOARD_TYPE_EBB6300:   /* Only for MII mode, with PHY addresses 0/1. Default is RGMII*/
1004        case CVMX_BOARD_TYPE_EBB6600:   /* Only for MII mode, with PHY addresses 0/1. Default is RGMII*/
1005            if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) && (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2))
1006                && cvmx_helper_board_get_mii_address(ipd_port) >= 0 && cvmx_helper_board_get_mii_address(ipd_port) <= 1)
1007                is_broadcom_phy = 1;
1008            break;
1009        case CVMX_BOARD_TYPE_EP6300C:
1010            is_broadcom_phy = 1;
1011            break;
1012        case CVMX_BOARD_TYPE_CUST_NB5:
1013            /* Port 1 on these boards is always Gigabit */
1014            if (ipd_port == 1)
1015            {
1016                result.s.link_up = 1;
1017                result.s.full_duplex = 1;
1018                result.s.speed = 1000;
1019                return result;
1020            }
1021            else /* The other port uses a broadcom PHY */
1022                is_broadcom_phy = 1;
1023            break;
1024        case CVMX_BOARD_TYPE_BBGW_REF:
1025            /* Port 1 on these boards is always Gigabit */
1026            if (ipd_port == 2)
1027            {
1028                /* Port 2 is not hooked up */
1029                result.u64 = 0;
1030                return result;
1031            }
1032            else
1033            {
1034                /* Ports 0 and 1 connect to the switch */
1035                result.s.link_up = 1;
1036                result.s.full_duplex = 1;
1037                result.s.speed = 1000;
1038                return result;
1039            }
1040        case CVMX_BOARD_TYPE_NIC4E:
1041        case CVMX_BOARD_TYPE_NIC2E:
1042            is_broadcom_phy = 1;
1043            break;
1044	/* Private vendor-defined boards.  */
1045#if defined(OCTEON_VENDOR_LANNER)
1046	case CVMX_BOARD_TYPE_CUST_LANNER_MR730:
1047	    /* Ports are BCM5482S */
1048	    is_broadcom_phy = 1;
1049	    break;
1050	case CVMX_BOARD_TYPE_CUST_LANNER_MR320:
1051	case CVMX_BOARD_TYPE_CUST_LANNER_MR321X:
1052	    /* Port 0 connects to the switch */
1053	    if (ipd_port == 0)
1054	    {
1055                result.s.link_up = 1;
1056                result.s.full_duplex = 1;
1057                result.s.speed = 1000;
1058		return result;
1059	    }
1060	    break;
1061#endif
1062    }
1063#endif
1064
1065    phy_addr = cvmx_helper_board_get_mii_address(ipd_port);
1066    //cvmx_dprintf("ipd_port=%d phy_addr=%d broadcom=%d\n",
1067    //             ipd_port, phy_addr, is_broadcom_phy);
1068    if (phy_addr != -1)
1069    {
1070        if (is_broadcom_phy)
1071        {
1072            result =  __get_broadcom_phy_link_state(phy_addr);
1073        }
1074        else
1075        {
1076            /* This code assumes we are using a Marvell Gigabit PHY. */
1077            result = __get_marvell_phy_link_state(phy_addr);
1078        }
1079    }
1080    else if (OCTEON_IS_MODEL(OCTEON_CN3XXX) || OCTEON_IS_MODEL(OCTEON_CN58XX)
1081             || OCTEON_IS_MODEL(OCTEON_CN50XX))
1082    {
1083        /* We don't have a PHY address, so attempt to use in-band status. It is
1084            really important that boards not supporting in-band status never get
1085            here. Reading broken in-band status tends to do bad things */
1086        result = __get_inband_link_state(ipd_port);
1087    }
1088    else
1089    {
1090        /* We don't have a PHY address and we don't have in-band status. There
1091            is no way to determine the link speed. Return down assuming this
1092            port isn't wired */
1093        result.u64 = 0;
1094    }
1095
1096    /* If link is down, return all fields as zero. */
1097    if (!result.s.link_up)
1098        result.u64 = 0;
1099
1100    return result;
1101}
1102
1103
1104/**
1105 * This function as a board specific method of changing the PHY
1106 * speed, duplex, and autonegotiation. This programs the PHY and
1107 * not Octeon. This can be used to force Octeon's links to
1108 * specific settings.
1109 *
1110 * @param phy_addr  The address of the PHY to program
1111 * @param link_flags
1112 *                  Flags to control autonegotiation.  Bit 0 is autonegotiation
1113 *                  enable/disable to maintain backward compatibility.
1114 * @param link_info Link speed to program. If the speed is zero and autonegotiation
1115 *                  is enabled, all possible negotiation speeds are advertised.
1116 *
1117 * @return Zero on success, negative on failure
1118 */
1119int cvmx_helper_board_link_set_phy(int phy_addr, cvmx_helper_board_set_phy_link_flags_types_t link_flags,
1120                                   cvmx_helper_link_info_t link_info)
1121{
1122
1123    /* Set the flow control settings based on link_flags */
1124    if ((link_flags & set_phy_link_flags_flow_control_mask) != set_phy_link_flags_flow_control_dont_touch)
1125    {
1126        cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver;
1127        reg_autoneg_adver.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_AUTONEG_ADVER);
1128        reg_autoneg_adver.s.asymmetric_pause = (link_flags & set_phy_link_flags_flow_control_mask) == set_phy_link_flags_flow_control_enable;
1129        reg_autoneg_adver.s.pause = (link_flags & set_phy_link_flags_flow_control_mask) == set_phy_link_flags_flow_control_enable;
1130        cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_AUTONEG_ADVER, reg_autoneg_adver.u16);
1131    }
1132
1133    /* If speed isn't set and autoneg is on advertise all supported modes */
1134    if ((link_flags & set_phy_link_flags_autoneg) && (link_info.s.speed == 0))
1135    {
1136        cvmx_mdio_phy_reg_control_t reg_control;
1137        cvmx_mdio_phy_reg_status_t reg_status;
1138        cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver;
1139        cvmx_mdio_phy_reg_extended_status_t reg_extended_status;
1140        cvmx_mdio_phy_reg_control_1000_t reg_control_1000;
1141
1142        reg_status.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_STATUS);
1143        reg_autoneg_adver.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_AUTONEG_ADVER);
1144        reg_autoneg_adver.s.advert_100base_t4 = reg_status.s.capable_100base_t4;
1145        reg_autoneg_adver.s.advert_10base_tx_full = reg_status.s.capable_10_full;
1146        reg_autoneg_adver.s.advert_10base_tx_half = reg_status.s.capable_10_half;
1147        reg_autoneg_adver.s.advert_100base_tx_full = reg_status.s.capable_100base_x_full;
1148        reg_autoneg_adver.s.advert_100base_tx_half = reg_status.s.capable_100base_x_half;
1149        cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_AUTONEG_ADVER, reg_autoneg_adver.u16);
1150        if (reg_status.s.capable_extended_status)
1151        {
1152            reg_extended_status.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_EXTENDED_STATUS);
1153            reg_control_1000.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL_1000);
1154            reg_control_1000.s.advert_1000base_t_full = reg_extended_status.s.capable_1000base_t_full;
1155            reg_control_1000.s.advert_1000base_t_half = reg_extended_status.s.capable_1000base_t_half;
1156            cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL_1000, reg_control_1000.u16);
1157        }
1158        reg_control.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL);
1159        reg_control.s.autoneg_enable = 1;
1160        reg_control.s.restart_autoneg = 1;
1161        cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16);
1162    }
1163    else if ((link_flags & set_phy_link_flags_autoneg))
1164    {
1165        cvmx_mdio_phy_reg_control_t reg_control;
1166        cvmx_mdio_phy_reg_status_t reg_status;
1167        cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver;
1168        cvmx_mdio_phy_reg_control_1000_t reg_control_1000;
1169
1170        reg_status.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_STATUS);
1171        reg_autoneg_adver.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_AUTONEG_ADVER);
1172        reg_autoneg_adver.s.advert_100base_t4 = 0;
1173        reg_autoneg_adver.s.advert_10base_tx_full = 0;
1174        reg_autoneg_adver.s.advert_10base_tx_half = 0;
1175        reg_autoneg_adver.s.advert_100base_tx_full = 0;
1176        reg_autoneg_adver.s.advert_100base_tx_half = 0;
1177        if (reg_status.s.capable_extended_status)
1178        {
1179            reg_control_1000.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL_1000);
1180            reg_control_1000.s.advert_1000base_t_full = 0;
1181            reg_control_1000.s.advert_1000base_t_half = 0;
1182        }
1183        switch (link_info.s.speed)
1184        {
1185            case 10:
1186                reg_autoneg_adver.s.advert_10base_tx_full = link_info.s.full_duplex;
1187                reg_autoneg_adver.s.advert_10base_tx_half = !link_info.s.full_duplex;
1188                break;
1189            case 100:
1190                reg_autoneg_adver.s.advert_100base_tx_full = link_info.s.full_duplex;
1191                reg_autoneg_adver.s.advert_100base_tx_half = !link_info.s.full_duplex;
1192                break;
1193            case 1000:
1194                reg_control_1000.s.advert_1000base_t_full = link_info.s.full_duplex;
1195                reg_control_1000.s.advert_1000base_t_half = !link_info.s.full_duplex;
1196                break;
1197        }
1198        cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_AUTONEG_ADVER, reg_autoneg_adver.u16);
1199        if (reg_status.s.capable_extended_status)
1200            cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL_1000, reg_control_1000.u16);
1201        reg_control.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL);
1202        reg_control.s.autoneg_enable = 1;
1203        reg_control.s.restart_autoneg = 1;
1204        cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16);
1205    }
1206    else
1207    {
1208        cvmx_mdio_phy_reg_control_t reg_control;
1209        reg_control.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL);
1210        reg_control.s.autoneg_enable = 0;
1211        reg_control.s.restart_autoneg = 1;
1212        reg_control.s.duplex = link_info.s.full_duplex;
1213        if (link_info.s.speed == 1000)
1214        {
1215            reg_control.s.speed_msb = 1;
1216            reg_control.s.speed_lsb = 0;
1217        }
1218        else if (link_info.s.speed == 100)
1219        {
1220            reg_control.s.speed_msb = 0;
1221            reg_control.s.speed_lsb = 1;
1222        }
1223        else if (link_info.s.speed == 10)
1224        {
1225            reg_control.s.speed_msb = 0;
1226            reg_control.s.speed_lsb = 0;
1227        }
1228        cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16);
1229    }
1230    return 0;
1231}
1232
1233
1234/**
1235 * @INTERNAL
1236 * This function is called by cvmx_helper_interface_probe() after it
1237 * determines the number of ports Octeon can support on a specific
1238 * interface. This function is the per board location to override
1239 * this value. It is called with the number of ports Octeon might
1240 * support and should return the number of actual ports on the
1241 * board.
1242 *
1243 * This function must be modified for every new Octeon board.
1244 * Internally it uses switch statements based on the cvmx_sysinfo
1245 * data to determine board types and revisions. It relies on the
1246 * fact that every Octeon board receives a unique board type
1247 * enumeration from the bootloader.
1248 *
1249 * @param interface Interface to probe
1250 * @param supported_ports
1251 *                  Number of ports Octeon supports.
1252 *
1253 * @return Number of ports the actual board supports. Many times this will
1254 *         simple be "support_ports".
1255 */
1256int __cvmx_helper_board_interface_probe(int interface, int supported_ports)
1257{
1258    switch (cvmx_sysinfo_get()->board_type)
1259    {
1260        case CVMX_BOARD_TYPE_CN3005_EVB_HS5:
1261        case CVMX_BOARD_TYPE_LANAI2_A:
1262        case CVMX_BOARD_TYPE_LANAI2_U:
1263        case CVMX_BOARD_TYPE_LANAI2_G:
1264            if (interface == 0)
1265                return 2;
1266	    break;
1267        case CVMX_BOARD_TYPE_BBGW_REF:
1268            if (interface == 0)
1269                return 2;
1270	    break;
1271        case CVMX_BOARD_TYPE_NIC_XLE_4G:
1272            if (interface == 0)
1273                return 0;
1274	    break;
1275        /* The 2nd interface on the EBH5600 is connected to the Marvel switch,
1276            which we don't support. Disable ports connected to it */
1277        case CVMX_BOARD_TYPE_EBH5600:
1278            if (interface == 1)
1279                return 0;
1280	    break;
1281        case CVMX_BOARD_TYPE_EBB5600:
1282#ifdef CVMX_ENABLE_PKO_FUNCTIONS
1283            if (cvmx_helper_interface_get_mode(interface) == CVMX_HELPER_INTERFACE_MODE_PICMG)
1284                return 0;
1285#endif
1286	    break;
1287        case CVMX_BOARD_TYPE_EBT5810:
1288            return 1;  /* Two ports on each SPI: 1 hooked to MAC, 1 loopback
1289                       ** Loopback disabled by default. */
1290        case CVMX_BOARD_TYPE_NIC2E:
1291            if (interface == 0)
1292                return 2;
1293#if defined(OCTEON_VENDOR_LANNER)
1294	case CVMX_BOARD_TYPE_CUST_LANNER_MR955:
1295	    if (interface == 1)
1296	        return 12;
1297	    break;
1298#endif
1299#if defined(OCTEON_VENDOR_RADISYS)
1300	case CVMX_BOARD_TYPE_CUST_RADISYS_RSYS4GBE:
1301	    if (interface == 0)
1302		    return 13;
1303	    if (interface == 1)
1304		    return 8;
1305	    return 0;
1306#endif
1307    }
1308#ifdef CVMX_BUILD_FOR_UBOOT
1309    if (CVMX_HELPER_INTERFACE_MODE_SPI == cvmx_helper_interface_get_mode(interface) && getenv("disable_spi"))
1310        return 0;
1311#endif
1312    return supported_ports;
1313}
1314
1315
1316/**
1317 * @INTERNAL
1318 * Enable packet input/output from the hardware. This function is
1319 * called after by cvmx_helper_packet_hardware_enable() to
1320 * perform board specific initialization. For most boards
1321 * nothing is needed.
1322 *
1323 * @param interface Interface to enable
1324 *
1325 * @return Zero on success, negative on failure
1326 */
1327int __cvmx_helper_board_hardware_enable(int interface)
1328{
1329    if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_CN3005_EVB_HS5)
1330    {
1331        if (interface == 0)
1332        {
1333            /* Different config for switch port */
1334            cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(1, interface), 0);
1335            cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(1, interface), 0);
1336            /* Boards with gigabit WAN ports need a different setting that is
1337                compatible with 100 Mbit settings */
1338            cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(0, interface), 0xc);
1339            cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(0, interface), 0xc);
1340        }
1341    }
1342    else if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_LANAI2_U)
1343    {
1344        if (interface == 0)
1345        {
1346            cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(0, interface), 16);
1347            cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(0, interface), 16);
1348        }
1349    }
1350    else if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_CN3010_EVB_HS5)
1351    {
1352        /* Broadcom PHYs require different ASX clocks. Unfortunately
1353            many customer don't define a new board Id and simply
1354            mangle the CN3010_EVB_HS5 */
1355        if (interface == 0)
1356        {
1357            /* Some customers boards use a hacked up bootloader that identifies them as
1358            ** CN3010_EVB_HS5 evaluation boards.  This leads to all kinds of configuration
1359            ** problems.  Detect one case, and print warning, while trying to do the right thing.
1360            */
1361            int phy_addr = cvmx_helper_board_get_mii_address(0);
1362            if (phy_addr != -1)
1363            {
1364                int phy_identifier = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x2);
1365                /* Is it a Broadcom PHY? */
1366                if (phy_identifier == 0x0143)
1367                {
1368                    cvmx_dprintf("\n");
1369                    cvmx_dprintf("ERROR:\n");
1370                    cvmx_dprintf("ERROR: Board type is CVMX_BOARD_TYPE_CN3010_EVB_HS5, but Broadcom PHY found.\n");
1371                    cvmx_dprintf("ERROR: The board type is mis-configured, and software malfunctions are likely.\n");
1372                    cvmx_dprintf("ERROR: All boards require a unique board type to identify them.\n");
1373                    cvmx_dprintf("ERROR:\n");
1374                    cvmx_dprintf("\n");
1375                    cvmx_wait(1000000000);
1376                    cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(0, interface), 5);
1377                    cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(0, interface), 5);
1378                }
1379            }
1380        }
1381    }
1382    return 0;
1383}
1384
1385
1386/**
1387 * @INTERNAL
1388 * Gets the clock type used for the USB block based on board type.
1389 * Used by the USB code for auto configuration of clock type.
1390 *
1391 * @return USB clock type enumeration
1392 */
1393cvmx_helper_board_usb_clock_types_t __cvmx_helper_board_usb_get_clock_type(void)
1394{
1395#if !defined(CVMX_BUILD_FOR_LINUX_KERNEL) && (!defined(__FreeBSD__) || !defined(_KERNEL))
1396    const void *fdt_addr = CASTPTR(const void *, cvmx_sysinfo_get()->fdt_addr);
1397    int nodeoffset;
1398    const void *nodep;
1399    int len;
1400    uint32_t speed = 0;
1401    const char *type = NULL;
1402
1403    if (fdt_addr)
1404    {
1405        nodeoffset = fdt_path_offset(fdt_addr, "/soc/uctl");
1406        if (nodeoffset < 0)
1407            nodeoffset = fdt_path_offset(fdt_addr, "/soc/usbn");
1408
1409        if (nodeoffset >= 0)
1410        {
1411            nodep = fdt_getprop(fdt_addr, nodeoffset, "refclk-type", &len);
1412            if (nodep != NULL && len > 0)
1413                type = (const char *)nodep;
1414            else
1415                type = "unknown";
1416            nodep = fdt_getprop(fdt_addr, nodeoffset, "refclk-frequency", &len);
1417            if (nodep != NULL && len == sizeof(uint32_t))
1418                speed = fdt32_to_cpu(*(int *)nodep);
1419            else
1420                speed = 0;
1421            if (!strcmp(type, "crystal"))
1422            {
1423                if (speed == 0 || speed == 12000000)
1424                    return USB_CLOCK_TYPE_CRYSTAL_12;
1425                else
1426                    printf("Warning: invalid crystal speed for USB clock type in FDT\n");
1427            }
1428            else if (!strcmp(type, "external"))
1429            {
1430                switch (speed) {
1431                case 12000000:
1432                    return USB_CLOCK_TYPE_REF_12;
1433                case 24000000:
1434                    return USB_CLOCK_TYPE_REF_24;
1435                case 0:
1436                case 48000000:
1437                    return USB_CLOCK_TYPE_REF_48;
1438                default:
1439                    printf("Warning: invalid USB clock speed of %u hz in FDT\n", speed);
1440                }
1441            }
1442            else
1443                printf("Warning: invalid USB reference clock type \"%s\" in FDT\n", type ? type : "NULL");
1444        }
1445    }
1446#endif
1447    switch (cvmx_sysinfo_get()->board_type)
1448    {
1449        case CVMX_BOARD_TYPE_BBGW_REF:
1450        case CVMX_BOARD_TYPE_LANAI2_A:
1451        case CVMX_BOARD_TYPE_LANAI2_U:
1452        case CVMX_BOARD_TYPE_LANAI2_G:
1453#if defined(OCTEON_VENDOR_LANNER)
1454    case CVMX_BOARD_TYPE_CUST_LANNER_MR320:
1455    case CVMX_BOARD_TYPE_CUST_LANNER_MR321X:
1456#endif
1457#if defined(OCTEON_BOARD_CAPK_0100ND)
1458	case CVMX_BOARD_TYPE_CN3010_EVB_HS5:
1459#endif
1460        case CVMX_BOARD_TYPE_NIC10E_66:
1461            return USB_CLOCK_TYPE_CRYSTAL_12;
1462        case CVMX_BOARD_TYPE_NIC10E:
1463            return USB_CLOCK_TYPE_REF_12;
1464        default:
1465            break;
1466    }
1467    if (OCTEON_IS_MODEL(OCTEON_CN6XXX)	/* Most boards except NIC10e use a 12MHz crystal */
1468        || OCTEON_IS_MODEL(OCTEON_CNF7XXX))
1469        return USB_CLOCK_TYPE_CRYSTAL_12;
1470    return USB_CLOCK_TYPE_REF_48;
1471}
1472
1473
1474/**
1475 * @INTERNAL
1476 * Adjusts the number of available USB ports on Octeon based on board
1477 * specifics.
1478 *
1479 * @param supported_ports expected number of ports based on chip type;
1480 *
1481 *
1482 * @return number of available usb ports, based on board specifics.
1483 *         Return value is supported_ports if function does not
1484 *         override.
1485 */
1486int __cvmx_helper_board_usb_get_num_ports(int supported_ports)
1487{
1488    switch (cvmx_sysinfo_get()->board_type)
1489    {
1490        case CVMX_BOARD_TYPE_NIC_XLE_4G:
1491        case CVMX_BOARD_TYPE_NIC2E:
1492            return 0;
1493    }
1494
1495    return supported_ports;
1496}
1497
1498
1499