1210284Sjmallett/***********************license start*************** 2232812Sjmallett * Copyright (c) 2003-2010 Cavium Inc. (support@cavium.com). All rights 3215990Sjmallett * reserved. 4210284Sjmallett * 5210284Sjmallett * 6215990Sjmallett * Redistribution and use in source and binary forms, with or without 7215990Sjmallett * modification, are permitted provided that the following conditions are 8215990Sjmallett * met: 9210284Sjmallett * 10215990Sjmallett * * Redistributions of source code must retain the above copyright 11215990Sjmallett * notice, this list of conditions and the following disclaimer. 12210284Sjmallett * 13215990Sjmallett * * Redistributions in binary form must reproduce the above 14215990Sjmallett * copyright notice, this list of conditions and the following 15215990Sjmallett * disclaimer in the documentation and/or other materials provided 16215990Sjmallett * with the distribution. 17215990Sjmallett 18232812Sjmallett * * Neither the name of Cavium Inc. nor the names of 19215990Sjmallett * its contributors may be used to endorse or promote products 20215990Sjmallett * derived from this software without specific prior written 21215990Sjmallett * permission. 22215990Sjmallett 23215990Sjmallett * This Software, including technical data, may be subject to U.S. export control 24215990Sjmallett * laws, including the U.S. Export Administration Act and its associated 25215990Sjmallett * regulations, and may be subject to export or import regulations in other 26215990Sjmallett * countries. 27215990Sjmallett 28215990Sjmallett * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 29232812Sjmallett * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR 30215990Sjmallett * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO 31215990Sjmallett * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR 32215990Sjmallett * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM 33215990Sjmallett * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, 34215990Sjmallett * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF 35215990Sjmallett * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR 36215990Sjmallett * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR 37215990Sjmallett * PERFORMANCE OF THE SOFTWARE LIES WITH YOU. 38210284Sjmallett ***********************license end**************************************/ 39210284Sjmallett 40210284Sjmallett 41210284Sjmallett 42210284Sjmallett 43210284Sjmallett 44210284Sjmallett 45215990Sjmallett 46210284Sjmallett/** 47210284Sjmallett * @file 48210284Sjmallett * 49210284Sjmallett * Support functions for managing the MII management port 50210284Sjmallett * 51232812Sjmallett * <hr>$Revision: 70030 $<hr> 52210284Sjmallett */ 53210284Sjmallett#include "cvmx.h" 54210284Sjmallett#include "cvmx-bootmem.h" 55210284Sjmallett#include "cvmx-spinlock.h" 56210284Sjmallett#include "cvmx-mdio.h" 57210284Sjmallett#include "cvmx-mgmt-port.h" 58210284Sjmallett#include "cvmx-sysinfo.h" 59232816Sjmallett#if !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL) 60215990Sjmallett#include "cvmx-error.h" 61232816Sjmallett#endif 62210284Sjmallett 63210284Sjmallett/** 64215990Sjmallett * Enum of MIX interface modes 65215990Sjmallett */ 66215990Sjmalletttypedef enum 67215990Sjmallett{ 68215990Sjmallett CVMX_MGMT_PORT_NONE = 0, 69215990Sjmallett CVMX_MGMT_PORT_MII_MODE, 70215990Sjmallett CVMX_MGMT_PORT_RGMII_MODE, 71215990Sjmallett} cvmx_mgmt_port_mode_t; 72215990Sjmallett 73215990Sjmallett/** 74210284Sjmallett * Format of the TX/RX ring buffer entries 75210284Sjmallett */ 76210284Sjmalletttypedef union 77210284Sjmallett{ 78210284Sjmallett uint64_t u64; 79210284Sjmallett struct 80210284Sjmallett { 81210284Sjmallett uint64_t reserved_62_63 : 2; 82210284Sjmallett uint64_t len : 14; /* Length of the buffer/packet in bytes */ 83215990Sjmallett uint64_t tstamp : 1; /* For TX, signals that the packet should be timestamped */ 84215990Sjmallett uint64_t code : 7; /* The RX error code */ 85210284Sjmallett uint64_t addr : 40; /* Physical address of the buffer */ 86210284Sjmallett } s; 87210284Sjmallett} cvmx_mgmt_port_ring_entry_t; 88210284Sjmallett 89210284Sjmallett/** 90210284Sjmallett * Per port state required for each mgmt port 91210284Sjmallett */ 92210284Sjmalletttypedef struct 93210284Sjmallett{ 94210284Sjmallett cvmx_spinlock_t lock; /* Used for exclusive access to this structure */ 95210284Sjmallett int tx_write_index; /* Where the next TX will write in the tx_ring and tx_buffers */ 96210284Sjmallett int rx_read_index; /* Where the next RX will be in the rx_ring and rx_buffers */ 97215990Sjmallett int port; /* Port to use. (This is the 'fake' IPD port number */ 98210284Sjmallett uint64_t mac; /* Our MAC address */ 99210284Sjmallett cvmx_mgmt_port_ring_entry_t tx_ring[CVMX_MGMT_PORT_NUM_TX_BUFFERS]; 100210284Sjmallett cvmx_mgmt_port_ring_entry_t rx_ring[CVMX_MGMT_PORT_NUM_RX_BUFFERS]; 101210284Sjmallett char tx_buffers[CVMX_MGMT_PORT_NUM_TX_BUFFERS][CVMX_MGMT_PORT_TX_BUFFER_SIZE]; 102210284Sjmallett char rx_buffers[CVMX_MGMT_PORT_NUM_RX_BUFFERS][CVMX_MGMT_PORT_RX_BUFFER_SIZE]; 103215990Sjmallett cvmx_mgmt_port_mode_t mode; /* Mode of the interface */ 104210284Sjmallett} cvmx_mgmt_port_state_t; 105210284Sjmallett 106210284Sjmallett/** 107210284Sjmallett * Pointers to each mgmt port's state 108210284Sjmallett */ 109210284SjmallettCVMX_SHARED cvmx_mgmt_port_state_t *cvmx_mgmt_port_state_ptr = NULL; 110210284Sjmallett 111210284Sjmallett 112210284Sjmallett/** 113210284Sjmallett * Return the number of management ports supported by this chip 114210284Sjmallett * 115210284Sjmallett * @return Number of ports 116210284Sjmallett */ 117217214Sjmallettstatic int __cvmx_mgmt_port_num_ports(void) 118210284Sjmallett{ 119250428Simp#if defined(OCTEON_VENDOR_GEFES) 120250428Simp return 0; /* none of the GEFES boards have mgmt ports */ 121250428Simp#else 122232812Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN68XX)) 123210284Sjmallett return 1; 124232812Sjmallett else if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN6XXX)) 125210284Sjmallett return 2; 126210284Sjmallett else 127210284Sjmallett return 0; 128250428Simp#endif 129210284Sjmallett} 130210284Sjmallett 131210284Sjmallett 132210284Sjmallett/** 133250191Simp * Return the number of management ports supported on this board. 134250191Simp * 135250191Simp * @return Number of ports 136250191Simp */ 137250191Simpint cvmx_mgmt_port_num_ports(void) 138250191Simp{ 139250191Simp return __cvmx_mgmt_port_num_ports(); 140250191Simp} 141250191Simp 142250191Simp 143250191Simp/** 144210284Sjmallett * Called to initialize a management port for use. Multiple calls 145215990Sjmallett * to this function across applications is safe. 146210284Sjmallett * 147210284Sjmallett * @param port Port to initialize 148210284Sjmallett * 149210284Sjmallett * @return CVMX_MGMT_PORT_SUCCESS or an error code 150210284Sjmallett */ 151210284Sjmallettcvmx_mgmt_port_result_t cvmx_mgmt_port_initialize(int port) 152210284Sjmallett{ 153210284Sjmallett char *alloc_name = "cvmx_mgmt_port"; 154210284Sjmallett cvmx_mixx_oring1_t oring1; 155210284Sjmallett cvmx_mixx_ctl_t mix_ctl; 156210284Sjmallett 157210284Sjmallett if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports())) 158210284Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 159210284Sjmallett 160232812Sjmallett cvmx_mgmt_port_state_ptr = cvmx_bootmem_alloc_named_flags(CVMX_MGMT_PORT_NUM_PORTS * sizeof(cvmx_mgmt_port_state_t), 128, alloc_name, CVMX_BOOTMEM_FLAG_END_ALLOC); 161210284Sjmallett if (cvmx_mgmt_port_state_ptr) 162210284Sjmallett { 163210284Sjmallett memset(cvmx_mgmt_port_state_ptr, 0, CVMX_MGMT_PORT_NUM_PORTS * sizeof(cvmx_mgmt_port_state_t)); 164210284Sjmallett } 165210284Sjmallett else 166210284Sjmallett { 167215990Sjmallett const cvmx_bootmem_named_block_desc_t *block_desc = cvmx_bootmem_find_named_block(alloc_name); 168210284Sjmallett if (block_desc) 169210284Sjmallett cvmx_mgmt_port_state_ptr = cvmx_phys_to_ptr(block_desc->base_addr); 170210284Sjmallett else 171210284Sjmallett { 172215990Sjmallett cvmx_dprintf("ERROR: cvmx_mgmt_port_initialize: Unable to get named block %s on MIX%d.\n", alloc_name, port); 173210284Sjmallett return CVMX_MGMT_PORT_NO_MEMORY; 174210284Sjmallett } 175210284Sjmallett } 176210284Sjmallett 177210284Sjmallett /* Reset the MIX block if the previous user had a different TX ring size, or if 178210284Sjmallett ** we allocated a new (and blank) state structure. */ 179210284Sjmallett mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port)); 180210284Sjmallett if (!mix_ctl.s.reset) 181210284Sjmallett { 182210284Sjmallett oring1.u64 = cvmx_read_csr(CVMX_MIXX_ORING1(port)); 183210284Sjmallett if (oring1.s.osize != CVMX_MGMT_PORT_NUM_TX_BUFFERS || cvmx_mgmt_port_state_ptr[port].tx_ring[0].u64 == 0) 184210284Sjmallett { 185210284Sjmallett mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port)); 186210284Sjmallett mix_ctl.s.en = 0; 187210284Sjmallett cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64); 188210284Sjmallett do 189210284Sjmallett { 190210284Sjmallett mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port)); 191210284Sjmallett } while (mix_ctl.s.busy); 192210284Sjmallett mix_ctl.s.reset = 1; 193210284Sjmallett cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64); 194210284Sjmallett cvmx_read_csr(CVMX_MIXX_CTL(port)); 195210284Sjmallett memset(cvmx_mgmt_port_state_ptr + port, 0, sizeof(cvmx_mgmt_port_state_t)); 196210284Sjmallett } 197210284Sjmallett } 198210284Sjmallett 199210284Sjmallett if (cvmx_mgmt_port_state_ptr[port].tx_ring[0].u64 == 0) 200210284Sjmallett { 201210284Sjmallett cvmx_mgmt_port_state_t *state = cvmx_mgmt_port_state_ptr + port; 202210284Sjmallett int i; 203210284Sjmallett cvmx_mixx_bist_t mix_bist; 204210284Sjmallett cvmx_agl_gmx_bist_t agl_gmx_bist; 205210284Sjmallett cvmx_mixx_oring1_t oring1; 206210284Sjmallett cvmx_mixx_iring1_t iring1; 207210284Sjmallett cvmx_mixx_ctl_t mix_ctl; 208215990Sjmallett cvmx_agl_prtx_ctl_t agl_prtx_ctl; 209210284Sjmallett 210210284Sjmallett /* Make sure BIST passed */ 211210284Sjmallett mix_bist.u64 = cvmx_read_csr(CVMX_MIXX_BIST(port)); 212210284Sjmallett if (mix_bist.u64) 213215990Sjmallett cvmx_dprintf("WARNING: cvmx_mgmt_port_initialize: Managment port MIX failed BIST (0x%016llx) on MIX%d\n", CAST64(mix_bist.u64), port); 214210284Sjmallett 215210284Sjmallett agl_gmx_bist.u64 = cvmx_read_csr(CVMX_AGL_GMX_BIST); 216210284Sjmallett if (agl_gmx_bist.u64) 217215990Sjmallett cvmx_dprintf("WARNING: cvmx_mgmt_port_initialize: Managment port AGL failed BIST (0x%016llx) on MIX%d\n", CAST64(agl_gmx_bist.u64), port); 218210284Sjmallett 219210284Sjmallett /* Clear all state information */ 220210284Sjmallett memset(state, 0, sizeof(*state)); 221210284Sjmallett 222210284Sjmallett /* Take the control logic out of reset */ 223210284Sjmallett mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port)); 224210284Sjmallett mix_ctl.s.reset = 0; 225210284Sjmallett cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64); 226210284Sjmallett 227215990Sjmallett /* Read until reset == 0. Timeout should never happen... */ 228215990Sjmallett if (CVMX_WAIT_FOR_FIELD64(CVMX_MIXX_CTL(port), cvmx_mixx_ctl_t, reset, ==, 0, 300000000)) 229215990Sjmallett { 230215990Sjmallett cvmx_dprintf("ERROR: cvmx_mgmt_port_initialize: Timeout waiting for MIX(%d) reset.\n", port); 231215990Sjmallett return CVMX_MGMT_PORT_INIT_ERROR; 232215990Sjmallett } 233215990Sjmallett 234215990Sjmallett /* Set the PHY address and mode of the interface (RGMII/MII mode). */ 235210284Sjmallett if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) 236215990Sjmallett { 237215990Sjmallett state->port = -1; 238215990Sjmallett state->mode = CVMX_MGMT_PORT_MII_MODE; 239215990Sjmallett } 240210284Sjmallett else 241215990Sjmallett { 242215990Sjmallett int port_num = CVMX_HELPER_BOARD_MGMT_IPD_PORT + port; 243215990Sjmallett int phy_addr = cvmx_helper_board_get_mii_address(port_num); 244215990Sjmallett if (phy_addr != -1) 245215990Sjmallett { 246215990Sjmallett cvmx_mdio_phy_reg_status_t phy_status; 247215990Sjmallett /* Read PHY status register to find the mode of the interface. */ 248215990Sjmallett phy_status.u16 = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, CVMX_MDIO_PHY_REG_STATUS); 249215990Sjmallett if (phy_status.s.capable_extended_status == 0) // MII mode 250215990Sjmallett state->mode = CVMX_MGMT_PORT_MII_MODE; 251215990Sjmallett else if (OCTEON_IS_MODEL(OCTEON_CN6XXX) 252215990Sjmallett && phy_status.s.capable_extended_status) // RGMII mode 253215990Sjmallett state->mode = CVMX_MGMT_PORT_RGMII_MODE; 254215990Sjmallett else 255215990Sjmallett state->mode = CVMX_MGMT_PORT_NONE; 256215990Sjmallett } 257215990Sjmallett else 258215990Sjmallett { 259215990Sjmallett cvmx_dprintf("ERROR: cvmx_mgmt_port_initialize: Not able to read the PHY on MIX%d\n", port); 260215990Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 261215990Sjmallett } 262215990Sjmallett state->port = port_num; 263215990Sjmallett } 264210284Sjmallett 265215990Sjmallett /* All interfaces should be configured in same mode */ 266215990Sjmallett for (i = 0; i < __cvmx_mgmt_port_num_ports(); i++) 267215990Sjmallett { 268215990Sjmallett if (i != port 269215990Sjmallett && cvmx_mgmt_port_state_ptr[i].mode != CVMX_MGMT_PORT_NONE 270215990Sjmallett && cvmx_mgmt_port_state_ptr[i].mode != state->mode) 271215990Sjmallett { 272215990Sjmallett cvmx_dprintf("ERROR: cvmx_mgmt_port_initialize: All ports in MIX interface are not configured in same mode.\n \ 273215990Sjmallett Port %d is configured as %d\n \ 274215990Sjmallett And Port %d is configured as %d\n", port, state->mode, i, cvmx_mgmt_port_state_ptr[i].mode); 275215990Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 276215990Sjmallett } 277215990Sjmallett } 278215990Sjmallett 279210284Sjmallett /* Create a default MAC address */ 280210284Sjmallett state->mac = 0x000000dead000000ull; 281210284Sjmallett state->mac += 0xffffff & CAST64(state); 282210284Sjmallett 283210284Sjmallett /* Setup the TX ring */ 284210284Sjmallett for (i=0; i<CVMX_MGMT_PORT_NUM_TX_BUFFERS; i++) 285210284Sjmallett { 286210284Sjmallett state->tx_ring[i].s.len = CVMX_MGMT_PORT_TX_BUFFER_SIZE; 287210284Sjmallett state->tx_ring[i].s.addr = cvmx_ptr_to_phys(state->tx_buffers[i]); 288210284Sjmallett } 289210284Sjmallett 290210284Sjmallett /* Tell the HW where the TX ring is */ 291210284Sjmallett oring1.u64 = 0; 292210284Sjmallett oring1.s.obase = cvmx_ptr_to_phys(state->tx_ring)>>3; 293210284Sjmallett oring1.s.osize = CVMX_MGMT_PORT_NUM_TX_BUFFERS; 294210284Sjmallett CVMX_SYNCWS; 295210284Sjmallett cvmx_write_csr(CVMX_MIXX_ORING1(port), oring1.u64); 296210284Sjmallett 297210284Sjmallett /* Setup the RX ring */ 298210284Sjmallett for (i=0; i<CVMX_MGMT_PORT_NUM_RX_BUFFERS; i++) 299210284Sjmallett { 300210284Sjmallett /* This size is -8 due to an errata for CN56XX pass 1 */ 301210284Sjmallett state->rx_ring[i].s.len = CVMX_MGMT_PORT_RX_BUFFER_SIZE - 8; 302210284Sjmallett state->rx_ring[i].s.addr = cvmx_ptr_to_phys(state->rx_buffers[i]); 303210284Sjmallett } 304210284Sjmallett 305210284Sjmallett /* Tell the HW where the RX ring is */ 306210284Sjmallett iring1.u64 = 0; 307210284Sjmallett iring1.s.ibase = cvmx_ptr_to_phys(state->rx_ring)>>3; 308210284Sjmallett iring1.s.isize = CVMX_MGMT_PORT_NUM_RX_BUFFERS; 309210284Sjmallett CVMX_SYNCWS; 310210284Sjmallett cvmx_write_csr(CVMX_MIXX_IRING1(port), iring1.u64); 311210284Sjmallett cvmx_write_csr(CVMX_MIXX_IRING2(port), CVMX_MGMT_PORT_NUM_RX_BUFFERS); 312210284Sjmallett 313210284Sjmallett /* Disable the external input/output */ 314210284Sjmallett cvmx_mgmt_port_disable(port); 315210284Sjmallett 316210284Sjmallett /* Set the MAC address filtering up */ 317210284Sjmallett cvmx_mgmt_port_set_mac(port, state->mac); 318210284Sjmallett 319210284Sjmallett /* Set the default max size to an MTU of 1500 with L2 and VLAN */ 320210284Sjmallett cvmx_mgmt_port_set_max_packet_size(port, 1518); 321210284Sjmallett 322210284Sjmallett /* Enable the port HW. Packets are not allowed until cvmx_mgmt_port_enable() is called */ 323210284Sjmallett mix_ctl.u64 = 0; 324210284Sjmallett mix_ctl.s.crc_strip = 1; /* Strip the ending CRC */ 325210284Sjmallett mix_ctl.s.en = 1; /* Enable the port */ 326210284Sjmallett mix_ctl.s.nbtarb = 0; /* Arbitration mode */ 327210284Sjmallett mix_ctl.s.mrq_hwm = 1; /* MII CB-request FIFO programmable high watermark */ 328210284Sjmallett cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64); 329210284Sjmallett 330215990Sjmallett /* Select the mode of operation for the interface. */ 331215990Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) 332210284Sjmallett { 333215990Sjmallett agl_prtx_ctl.u64 = cvmx_read_csr(CVMX_AGL_PRTX_CTL(port)); 334215990Sjmallett 335215990Sjmallett if (state->mode == CVMX_MGMT_PORT_RGMII_MODE) 336215990Sjmallett agl_prtx_ctl.s.mode = 0; 337215990Sjmallett else if (state->mode == CVMX_MGMT_PORT_MII_MODE) 338215990Sjmallett agl_prtx_ctl.s.mode = 1; 339215990Sjmallett else 340215990Sjmallett { 341215990Sjmallett cvmx_dprintf("ERROR: cvmx_mgmt_port_initialize: Invalid mode for MIX(%d)\n", port); 342215990Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 343215990Sjmallett } 344215990Sjmallett 345215990Sjmallett cvmx_write_csr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64); 346215990Sjmallett } 347215990Sjmallett 348215990Sjmallett /* Initialize the physical layer. */ 349215990Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) 350215990Sjmallett { 351215990Sjmallett /* MII clocks counts are based on the 125Mhz reference, so our 352215990Sjmallett delays need to be scaled to match the core clock rate. The 353215990Sjmallett "+1" is to make sure rounding always waits a little too 354215990Sjmallett long. */ 355215990Sjmallett uint64_t clock_scale = cvmx_clock_get_rate(CVMX_CLOCK_CORE) / 125000000 + 1; 356215990Sjmallett 357215990Sjmallett /* Take the DLL and clock tree out of reset */ 358215990Sjmallett agl_prtx_ctl.u64 = cvmx_read_csr(CVMX_AGL_PRTX_CTL(port)); 359215990Sjmallett agl_prtx_ctl.s.clkrst = 0; 360215990Sjmallett if (state->mode == CVMX_MGMT_PORT_RGMII_MODE) // RGMII Initialization 361215990Sjmallett { 362215990Sjmallett agl_prtx_ctl.s.dllrst = 0; 363215990Sjmallett agl_prtx_ctl.s.clktx_byp = 0; 364215990Sjmallett } 365215990Sjmallett cvmx_write_csr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64); 366215990Sjmallett cvmx_read_csr(CVMX_AGL_PRTX_CTL(port)); /* Force write out before wait */ 367215990Sjmallett 368215990Sjmallett /* Wait for the DLL to lock. External 125 MHz reference clock must be stable at this point. */ 369215990Sjmallett cvmx_wait(256 * clock_scale); 370215990Sjmallett 371215990Sjmallett /* The rest of the config is common between RGMII/MII */ 372215990Sjmallett 373215990Sjmallett /* Enable the interface */ 374215990Sjmallett agl_prtx_ctl.u64 = cvmx_read_csr(CVMX_AGL_PRTX_CTL(port)); 375215990Sjmallett agl_prtx_ctl.s.enable = 1; 376215990Sjmallett cvmx_write_csr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64); 377215990Sjmallett 378215990Sjmallett /* Read the value back to force the previous write */ 379215990Sjmallett agl_prtx_ctl.u64 = cvmx_read_csr(CVMX_AGL_PRTX_CTL(port)); 380215990Sjmallett 381215990Sjmallett /* Enable the componsation controller */ 382215990Sjmallett agl_prtx_ctl.s.comp = 1; 383232812Sjmallett agl_prtx_ctl.s.drv_byp = 0; 384215990Sjmallett cvmx_write_csr(CVMX_AGL_PRTX_CTL(port), agl_prtx_ctl.u64); 385215990Sjmallett cvmx_read_csr(CVMX_AGL_PRTX_CTL(port)); /* Force write out before wait */ 386215990Sjmallett cvmx_wait(1024 * clock_scale); // for componsation state to lock. 387215990Sjmallett } 388215990Sjmallett else if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X) || OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X)) 389215990Sjmallett { 390210284Sjmallett /* Force compensation values, as they are not determined properly by HW */ 391210284Sjmallett cvmx_agl_gmx_drv_ctl_t drv_ctl; 392210284Sjmallett 393210284Sjmallett drv_ctl.u64 = cvmx_read_csr(CVMX_AGL_GMX_DRV_CTL); 394210284Sjmallett if (port) 395210284Sjmallett { 396210284Sjmallett drv_ctl.s.byp_en1 = 1; 397210284Sjmallett drv_ctl.s.nctl1 = 6; 398210284Sjmallett drv_ctl.s.pctl1 = 6; 399210284Sjmallett } 400210284Sjmallett else 401210284Sjmallett { 402210284Sjmallett drv_ctl.s.byp_en = 1; 403210284Sjmallett drv_ctl.s.nctl = 6; 404210284Sjmallett drv_ctl.s.pctl = 6; 405210284Sjmallett } 406210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_DRV_CTL, drv_ctl.u64); 407210284Sjmallett } 408210284Sjmallett } 409232816Sjmallett#if !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL) 410215990Sjmallett cvmx_error_enable_group(CVMX_ERROR_GROUP_MGMT_PORT, port); 411232816Sjmallett#endif 412210284Sjmallett return CVMX_MGMT_PORT_SUCCESS; 413210284Sjmallett} 414210284Sjmallett 415210284Sjmallett 416210284Sjmallett/** 417210284Sjmallett * Shutdown a management port. This currently disables packet IO 418210284Sjmallett * but leaves all hardware and buffers. Another application can then 419210284Sjmallett * call initialize() without redoing the hardware setup. 420210284Sjmallett * 421210284Sjmallett * @param port Management port 422210284Sjmallett * 423210284Sjmallett * @return CVMX_MGMT_PORT_SUCCESS or an error code 424210284Sjmallett */ 425210284Sjmallettcvmx_mgmt_port_result_t cvmx_mgmt_port_shutdown(int port) 426210284Sjmallett{ 427210284Sjmallett if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports())) 428210284Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 429210284Sjmallett 430232816Sjmallett#if !defined(CVMX_BUILD_FOR_FREEBSD_KERNEL) 431215990Sjmallett cvmx_error_disable_group(CVMX_ERROR_GROUP_MGMT_PORT, port); 432232816Sjmallett#endif 433215990Sjmallett 434210284Sjmallett /* Stop packets from comming in */ 435210284Sjmallett cvmx_mgmt_port_disable(port); 436210284Sjmallett 437210284Sjmallett /* We don't free any memory so the next intialize can reuse the HW setup */ 438210284Sjmallett return CVMX_MGMT_PORT_SUCCESS; 439210284Sjmallett} 440210284Sjmallett 441210284Sjmallett 442210284Sjmallett/** 443210284Sjmallett * Enable packet IO on a management port 444210284Sjmallett * 445210284Sjmallett * @param port Management port 446210284Sjmallett * 447210284Sjmallett * @return CVMX_MGMT_PORT_SUCCESS or an error code 448210284Sjmallett */ 449210284Sjmallettcvmx_mgmt_port_result_t cvmx_mgmt_port_enable(int port) 450210284Sjmallett{ 451210284Sjmallett cvmx_mgmt_port_state_t *state; 452210284Sjmallett cvmx_agl_gmx_inf_mode_t agl_gmx_inf_mode; 453210284Sjmallett cvmx_agl_gmx_rxx_frm_ctl_t rxx_frm_ctl; 454210284Sjmallett 455210284Sjmallett if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports())) 456210284Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 457210284Sjmallett 458210284Sjmallett state = cvmx_mgmt_port_state_ptr + port; 459210284Sjmallett 460210284Sjmallett cvmx_spinlock_lock(&state->lock); 461210284Sjmallett 462210284Sjmallett rxx_frm_ctl.u64 = 0; 463210284Sjmallett rxx_frm_ctl.s.pre_align = 1; 464210284Sjmallett rxx_frm_ctl.s.pad_len = 1; /* When set, disables the length check for non-min sized pkts with padding in the client data */ 465210284Sjmallett rxx_frm_ctl.s.vlan_len = 1; /* When set, disables the length check for VLAN pkts */ 466210284Sjmallett rxx_frm_ctl.s.pre_free = 1; /* When set, PREAMBLE checking is less strict */ 467210284Sjmallett rxx_frm_ctl.s.ctl_smac = 0; /* Control Pause Frames can match station SMAC */ 468210284Sjmallett rxx_frm_ctl.s.ctl_mcst = 1; /* Control Pause Frames can match globally assign Multicast address */ 469210284Sjmallett rxx_frm_ctl.s.ctl_bck = 1; /* Forward pause information to TX block */ 470210284Sjmallett rxx_frm_ctl.s.ctl_drp = 1; /* Drop Control Pause Frames */ 471210284Sjmallett rxx_frm_ctl.s.pre_strp = 1; /* Strip off the preamble */ 472210284Sjmallett rxx_frm_ctl.s.pre_chk = 1; /* This port is configured to send PREAMBLE+SFD to begin every frame. GMX checks that the PREAMBLE is sent correctly */ 473210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_CTL(port), rxx_frm_ctl.u64); 474210284Sjmallett 475210284Sjmallett /* Enable the AGL block */ 476215990Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN5XXX)) 477215990Sjmallett { 478215990Sjmallett agl_gmx_inf_mode.u64 = 0; 479215990Sjmallett agl_gmx_inf_mode.s.en = 1; 480215990Sjmallett cvmx_write_csr(CVMX_AGL_GMX_INF_MODE, agl_gmx_inf_mode.u64); 481215990Sjmallett } 482210284Sjmallett 483210284Sjmallett /* Configure the port duplex and enables */ 484215990Sjmallett cvmx_mgmt_port_link_set(port, cvmx_mgmt_port_link_get(port)); 485210284Sjmallett 486210284Sjmallett cvmx_spinlock_unlock(&state->lock); 487210284Sjmallett return CVMX_MGMT_PORT_SUCCESS; 488210284Sjmallett} 489210284Sjmallett 490210284Sjmallett 491210284Sjmallett/** 492210284Sjmallett * Disable packet IO on a management port 493210284Sjmallett * 494210284Sjmallett * @param port Management port 495210284Sjmallett * 496210284Sjmallett * @return CVMX_MGMT_PORT_SUCCESS or an error code 497210284Sjmallett */ 498210284Sjmallettcvmx_mgmt_port_result_t cvmx_mgmt_port_disable(int port) 499210284Sjmallett{ 500210284Sjmallett cvmx_mgmt_port_state_t *state; 501210284Sjmallett cvmx_agl_gmx_prtx_cfg_t agl_gmx_prtx; 502210284Sjmallett 503210284Sjmallett if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports())) 504210284Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 505210284Sjmallett 506210284Sjmallett state = cvmx_mgmt_port_state_ptr + port; 507210284Sjmallett 508210284Sjmallett cvmx_spinlock_lock(&state->lock); 509210284Sjmallett 510210284Sjmallett agl_gmx_prtx.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); 511210284Sjmallett agl_gmx_prtx.s.en = 0; 512210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64); 513210284Sjmallett 514210284Sjmallett cvmx_spinlock_unlock(&state->lock); 515210284Sjmallett return CVMX_MGMT_PORT_SUCCESS; 516210284Sjmallett} 517210284Sjmallett 518210284Sjmallett 519210284Sjmallett/** 520210284Sjmallett * Send a packet out the management port. The packet is copied so 521210284Sjmallett * the input buffer isn't used after this call. 522210284Sjmallett * 523210284Sjmallett * @param port Management port 524210284Sjmallett * @param packet_len Length of the packet to send. It does not include the final CRC 525210284Sjmallett * @param buffer Packet data 526210284Sjmallett * 527210284Sjmallett * @return CVMX_MGMT_PORT_SUCCESS or an error code 528210284Sjmallett */ 529210284Sjmallettcvmx_mgmt_port_result_t cvmx_mgmt_port_send(int port, int packet_len, void *buffer) 530210284Sjmallett{ 531210284Sjmallett cvmx_mgmt_port_state_t *state; 532210284Sjmallett cvmx_mixx_oring2_t mix_oring2; 533210284Sjmallett 534210284Sjmallett if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports())) 535210284Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 536210284Sjmallett 537210284Sjmallett /* Max sure the packet size is valid */ 538210284Sjmallett if ((packet_len < 1) || (packet_len > CVMX_MGMT_PORT_TX_BUFFER_SIZE)) 539210284Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 540210284Sjmallett 541210284Sjmallett if (buffer == NULL) 542210284Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 543210284Sjmallett 544210284Sjmallett state = cvmx_mgmt_port_state_ptr + port; 545210284Sjmallett 546210284Sjmallett cvmx_spinlock_lock(&state->lock); 547210284Sjmallett 548210284Sjmallett mix_oring2.u64 = cvmx_read_csr(CVMX_MIXX_ORING2(port)); 549210284Sjmallett if (mix_oring2.s.odbell >= CVMX_MGMT_PORT_NUM_TX_BUFFERS - 1) 550210284Sjmallett { 551210284Sjmallett /* No room for another packet */ 552210284Sjmallett cvmx_spinlock_unlock(&state->lock); 553210284Sjmallett return CVMX_MGMT_PORT_NO_MEMORY; 554210284Sjmallett } 555210284Sjmallett else 556210284Sjmallett { 557210284Sjmallett /* Copy the packet into the output buffer */ 558210284Sjmallett memcpy(state->tx_buffers[state->tx_write_index], buffer, packet_len); 559210284Sjmallett /* Insert the source MAC */ 560210284Sjmallett memcpy(state->tx_buffers[state->tx_write_index] + 6, ((char*)&state->mac) + 2, 6); 561210284Sjmallett /* Update the TX ring buffer entry size */ 562210284Sjmallett state->tx_ring[state->tx_write_index].s.len = packet_len; 563215990Sjmallett /* This code doesn't support TX timestamps */ 564215990Sjmallett state->tx_ring[state->tx_write_index].s.tstamp = 0; 565210284Sjmallett /* Increment our TX index */ 566210284Sjmallett state->tx_write_index = (state->tx_write_index + 1) % CVMX_MGMT_PORT_NUM_TX_BUFFERS; 567215990Sjmallett /* Ring the doorbell, sending the packet */ 568210284Sjmallett CVMX_SYNCWS; 569210284Sjmallett cvmx_write_csr(CVMX_MIXX_ORING2(port), 1); 570210284Sjmallett if (cvmx_read_csr(CVMX_MIXX_ORCNT(port))) 571210284Sjmallett cvmx_write_csr(CVMX_MIXX_ORCNT(port), cvmx_read_csr(CVMX_MIXX_ORCNT(port))); 572210284Sjmallett 573210284Sjmallett cvmx_spinlock_unlock(&state->lock); 574210284Sjmallett return CVMX_MGMT_PORT_SUCCESS; 575210284Sjmallett } 576210284Sjmallett} 577210284Sjmallett 578210284Sjmallett 579217214Sjmallett#if defined(__FreeBSD__) 580210284Sjmallett/** 581217214Sjmallett * Send a packet out the management port. The packet is copied so 582217214Sjmallett * the input mbuf isn't used after this call. 583217214Sjmallett * 584217214Sjmallett * @param port Management port 585217214Sjmallett * @param m Packet mbuf (with pkthdr) 586217214Sjmallett * 587217214Sjmallett * @return CVMX_MGMT_PORT_SUCCESS or an error code 588217214Sjmallett */ 589217214Sjmallettcvmx_mgmt_port_result_t cvmx_mgmt_port_sendm(int port, const struct mbuf *m) 590217214Sjmallett{ 591217214Sjmallett cvmx_mgmt_port_state_t *state; 592217214Sjmallett cvmx_mixx_oring2_t mix_oring2; 593217214Sjmallett 594217214Sjmallett if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports())) 595217214Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 596217214Sjmallett 597217214Sjmallett /* Max sure the packet size is valid */ 598217214Sjmallett if ((m->m_pkthdr.len < 1) || (m->m_pkthdr.len > CVMX_MGMT_PORT_TX_BUFFER_SIZE)) 599217214Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 600217214Sjmallett 601217214Sjmallett state = cvmx_mgmt_port_state_ptr + port; 602217214Sjmallett 603217214Sjmallett cvmx_spinlock_lock(&state->lock); 604217214Sjmallett 605217214Sjmallett mix_oring2.u64 = cvmx_read_csr(CVMX_MIXX_ORING2(port)); 606217214Sjmallett if (mix_oring2.s.odbell >= CVMX_MGMT_PORT_NUM_TX_BUFFERS - 1) 607217214Sjmallett { 608217214Sjmallett /* No room for another packet */ 609217214Sjmallett cvmx_spinlock_unlock(&state->lock); 610217214Sjmallett return CVMX_MGMT_PORT_NO_MEMORY; 611217214Sjmallett } 612217214Sjmallett else 613217214Sjmallett { 614217214Sjmallett /* Copy the packet into the output buffer */ 615217214Sjmallett m_copydata(m, 0, m->m_pkthdr.len, state->tx_buffers[state->tx_write_index]); 616217214Sjmallett /* Update the TX ring buffer entry size */ 617217214Sjmallett state->tx_ring[state->tx_write_index].s.len = m->m_pkthdr.len; 618217214Sjmallett /* This code doesn't support TX timestamps */ 619217214Sjmallett state->tx_ring[state->tx_write_index].s.tstamp = 0; 620217214Sjmallett /* Increment our TX index */ 621217214Sjmallett state->tx_write_index = (state->tx_write_index + 1) % CVMX_MGMT_PORT_NUM_TX_BUFFERS; 622217214Sjmallett /* Ring the doorbell, sending the packet */ 623217214Sjmallett CVMX_SYNCWS; 624217214Sjmallett cvmx_write_csr(CVMX_MIXX_ORING2(port), 1); 625217214Sjmallett if (cvmx_read_csr(CVMX_MIXX_ORCNT(port))) 626217214Sjmallett cvmx_write_csr(CVMX_MIXX_ORCNT(port), cvmx_read_csr(CVMX_MIXX_ORCNT(port))); 627217214Sjmallett 628217214Sjmallett cvmx_spinlock_unlock(&state->lock); 629217214Sjmallett return CVMX_MGMT_PORT_SUCCESS; 630217214Sjmallett } 631217214Sjmallett} 632217214Sjmallett#endif 633217214Sjmallett 634217214Sjmallett 635217214Sjmallett/** 636210284Sjmallett * Receive a packet from the management port. 637210284Sjmallett * 638210284Sjmallett * @param port Management port 639210284Sjmallett * @param buffer_len Size of the buffer to receive the packet into 640210284Sjmallett * @param buffer Buffer to receive the packet into 641210284Sjmallett * 642210284Sjmallett * @return The size of the packet, or a negative erorr code on failure. Zero 643210284Sjmallett * means that no packets were available. 644210284Sjmallett */ 645217214Sjmallettint cvmx_mgmt_port_receive(int port, int buffer_len, uint8_t *buffer) 646210284Sjmallett{ 647210284Sjmallett cvmx_mixx_ircnt_t mix_ircnt; 648210284Sjmallett cvmx_mgmt_port_state_t *state; 649210284Sjmallett int result; 650210284Sjmallett 651210284Sjmallett if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports())) 652210284Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 653210284Sjmallett 654210284Sjmallett /* Max sure the buffer size is valid */ 655210284Sjmallett if (buffer_len < 1) 656210284Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 657210284Sjmallett 658210284Sjmallett if (buffer == NULL) 659210284Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 660210284Sjmallett 661210284Sjmallett state = cvmx_mgmt_port_state_ptr + port; 662210284Sjmallett 663210284Sjmallett cvmx_spinlock_lock(&state->lock); 664210284Sjmallett 665210284Sjmallett /* Find out how many RX packets are pending */ 666210284Sjmallett mix_ircnt.u64 = cvmx_read_csr(CVMX_MIXX_IRCNT(port)); 667210284Sjmallett if (mix_ircnt.s.ircnt) 668210284Sjmallett { 669217214Sjmallett uint64_t *source = (void *)state->rx_buffers[state->rx_read_index]; 670217214Sjmallett uint64_t *zero_check = source; 671210284Sjmallett /* CN56XX pass 1 has an errata where packets might start 8 bytes 672210284Sjmallett into the buffer instead of at their correct lcoation. If the 673210284Sjmallett first 8 bytes is zero we assume this has happened */ 674210284Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X) && (*zero_check == 0)) 675217214Sjmallett source++; 676210284Sjmallett /* Start off with zero bytes received */ 677210284Sjmallett result = 0; 678210284Sjmallett /* While the completion code signals more data, copy the buffers 679210284Sjmallett into the user's data */ 680210284Sjmallett while (state->rx_ring[state->rx_read_index].s.code == 16) 681210284Sjmallett { 682210284Sjmallett /* Only copy what will fit in the user's buffer */ 683210284Sjmallett int length = state->rx_ring[state->rx_read_index].s.len; 684210284Sjmallett if (length > buffer_len) 685210284Sjmallett length = buffer_len; 686210284Sjmallett memcpy(buffer, source, length); 687210284Sjmallett /* Reduce the size of the buffer to the remaining space. If we run 688210284Sjmallett out we will signal an error when the code 15 buffer doesn't fit */ 689210284Sjmallett buffer += length; 690210284Sjmallett buffer_len -= length; 691210284Sjmallett result += length; 692210284Sjmallett /* Update this buffer for reuse in future receives. This size is 693210284Sjmallett -8 due to an errata for CN56XX pass 1 */ 694210284Sjmallett state->rx_ring[state->rx_read_index].s.code = 0; 695210284Sjmallett state->rx_ring[state->rx_read_index].s.len = CVMX_MGMT_PORT_RX_BUFFER_SIZE - 8; 696210284Sjmallett state->rx_read_index = (state->rx_read_index + 1) % CVMX_MGMT_PORT_NUM_RX_BUFFERS; 697210284Sjmallett /* Zero the beginning of the buffer for use by the errata check */ 698210284Sjmallett *zero_check = 0; 699210284Sjmallett CVMX_SYNCWS; 700210284Sjmallett /* Increment the number of RX buffers */ 701210284Sjmallett cvmx_write_csr(CVMX_MIXX_IRING2(port), 1); 702217214Sjmallett source = (void *)state->rx_buffers[state->rx_read_index]; 703210284Sjmallett zero_check = source; 704210284Sjmallett } 705210284Sjmallett 706210284Sjmallett /* Check for the final good completion code */ 707210284Sjmallett if (state->rx_ring[state->rx_read_index].s.code == 15) 708210284Sjmallett { 709210284Sjmallett if (buffer_len >= state->rx_ring[state->rx_read_index].s.len) 710210284Sjmallett { 711210284Sjmallett int length = state->rx_ring[state->rx_read_index].s.len; 712210284Sjmallett memcpy(buffer, source, length); 713210284Sjmallett result += length; 714210284Sjmallett } 715210284Sjmallett else 716210284Sjmallett { 717210284Sjmallett /* Not enough room for the packet */ 718210284Sjmallett cvmx_dprintf("ERROR: cvmx_mgmt_port_receive: Packet (%d) larger than supplied buffer (%d)\n", state->rx_ring[state->rx_read_index].s.len, buffer_len); 719210284Sjmallett result = CVMX_MGMT_PORT_NO_MEMORY; 720210284Sjmallett } 721210284Sjmallett } 722210284Sjmallett else 723210284Sjmallett { 724210284Sjmallett cvmx_dprintf("ERROR: cvmx_mgmt_port_receive: Receive error code %d. Packet dropped(Len %d), \n", 725210284Sjmallett state->rx_ring[state->rx_read_index].s.code, state->rx_ring[state->rx_read_index].s.len + result); 726210284Sjmallett result = -state->rx_ring[state->rx_read_index].s.code; 727210284Sjmallett 728210284Sjmallett 729210284Sjmallett /* Check to see if we need to change the duplex. */ 730215990Sjmallett cvmx_mgmt_port_link_set(port, cvmx_mgmt_port_link_get(port)); 731210284Sjmallett } 732210284Sjmallett 733210284Sjmallett /* Clean out the ring buffer entry. This size is -8 due to an errata 734210284Sjmallett for CN56XX pass 1 */ 735210284Sjmallett state->rx_ring[state->rx_read_index].s.code = 0; 736210284Sjmallett state->rx_ring[state->rx_read_index].s.len = CVMX_MGMT_PORT_RX_BUFFER_SIZE - 8; 737210284Sjmallett state->rx_read_index = (state->rx_read_index + 1) % CVMX_MGMT_PORT_NUM_RX_BUFFERS; 738210284Sjmallett /* Zero the beginning of the buffer for use by the errata check */ 739210284Sjmallett *zero_check = 0; 740210284Sjmallett CVMX_SYNCWS; 741210284Sjmallett /* Increment the number of RX buffers */ 742210284Sjmallett cvmx_write_csr(CVMX_MIXX_IRING2(port), 1); 743210284Sjmallett /* Decrement the pending RX count */ 744210284Sjmallett cvmx_write_csr(CVMX_MIXX_IRCNT(port), 1); 745210284Sjmallett } 746210284Sjmallett else 747210284Sjmallett { 748210284Sjmallett /* No packets available */ 749210284Sjmallett result = 0; 750210284Sjmallett } 751210284Sjmallett cvmx_spinlock_unlock(&state->lock); 752210284Sjmallett return result; 753210284Sjmallett} 754210284Sjmallett 755210284Sjmallett/** 756210284Sjmallett * Set the MAC address for a management port 757210284Sjmallett * 758210284Sjmallett * @param port Management port 759210284Sjmallett * @param mac New MAC address. The lower 6 bytes are used. 760210284Sjmallett * 761210284Sjmallett * @return CVMX_MGMT_PORT_SUCCESS or an error code 762210284Sjmallett */ 763210284Sjmallettcvmx_mgmt_port_result_t cvmx_mgmt_port_set_mac(int port, uint64_t mac) 764210284Sjmallett{ 765210284Sjmallett cvmx_mgmt_port_state_t *state; 766210284Sjmallett cvmx_agl_gmx_rxx_adr_ctl_t agl_gmx_rxx_adr_ctl; 767210284Sjmallett 768210284Sjmallett if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports())) 769210284Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 770210284Sjmallett 771210284Sjmallett state = cvmx_mgmt_port_state_ptr + port; 772210284Sjmallett 773210284Sjmallett cvmx_spinlock_lock(&state->lock); 774210284Sjmallett 775210284Sjmallett agl_gmx_rxx_adr_ctl.u64 = 0; 776210284Sjmallett agl_gmx_rxx_adr_ctl.s.cam_mode = 1; /* Only accept matching MAC addresses */ 777210284Sjmallett agl_gmx_rxx_adr_ctl.s.mcst = 0; /* Drop multicast */ 778210284Sjmallett agl_gmx_rxx_adr_ctl.s.bcst = 1; /* Allow broadcast */ 779210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CTL(port), agl_gmx_rxx_adr_ctl.u64); 780210284Sjmallett 781210284Sjmallett /* Only using one of the CAMs */ 782210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM0(port), (mac >> 40) & 0xff); 783210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM1(port), (mac >> 32) & 0xff); 784210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM2(port), (mac >> 24) & 0xff); 785210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM3(port), (mac >> 16) & 0xff); 786210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM4(port), (mac >> 8) & 0xff); 787210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM5(port), (mac >> 0) & 0xff); 788210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM_EN(port), 1); 789210284Sjmallett state->mac = mac; 790210284Sjmallett 791210284Sjmallett cvmx_spinlock_unlock(&state->lock); 792210284Sjmallett return CVMX_MGMT_PORT_SUCCESS; 793210284Sjmallett} 794210284Sjmallett 795210284Sjmallett 796210284Sjmallett/** 797210284Sjmallett * Get the MAC address for a management port 798210284Sjmallett * 799210284Sjmallett * @param port Management port 800210284Sjmallett * 801210284Sjmallett * @return MAC address 802210284Sjmallett */ 803210284Sjmallettuint64_t cvmx_mgmt_port_get_mac(int port) 804210284Sjmallett{ 805210284Sjmallett if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports())) 806210284Sjmallett return CVMX_MGMT_PORT_INVALID_PARAM; 807210284Sjmallett 808210284Sjmallett return cvmx_mgmt_port_state_ptr[port].mac; 809210284Sjmallett} 810210284Sjmallett 811210284Sjmallett/** 812210284Sjmallett * Set the multicast list. 813210284Sjmallett * 814210284Sjmallett * @param port Management port 815210284Sjmallett * @param flags Interface flags 816210284Sjmallett * 817210284Sjmallett * @return 818210284Sjmallett */ 819210284Sjmallettvoid cvmx_mgmt_port_set_multicast_list(int port, int flags) 820210284Sjmallett{ 821210284Sjmallett cvmx_mgmt_port_state_t *state; 822210284Sjmallett cvmx_agl_gmx_rxx_adr_ctl_t agl_gmx_rxx_adr_ctl; 823210284Sjmallett 824210284Sjmallett if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports())) 825210284Sjmallett return; 826210284Sjmallett 827210284Sjmallett state = cvmx_mgmt_port_state_ptr + port; 828210284Sjmallett 829210284Sjmallett cvmx_spinlock_lock(&state->lock); 830210284Sjmallett 831210284Sjmallett agl_gmx_rxx_adr_ctl.u64 = cvmx_read_csr(CVMX_AGL_GMX_RXX_ADR_CTL(port)); 832215990Sjmallett 833210284Sjmallett /* Allow broadcast MAC addresses */ 834210284Sjmallett if (!agl_gmx_rxx_adr_ctl.s.bcst) 835210284Sjmallett agl_gmx_rxx_adr_ctl.s.bcst = 1; 836210284Sjmallett 837210284Sjmallett if ((flags & CVMX_IFF_ALLMULTI) || (flags & CVMX_IFF_PROMISC)) 838210284Sjmallett agl_gmx_rxx_adr_ctl.s.mcst = 2; /* Force accept multicast packets */ 839210284Sjmallett else 840210284Sjmallett agl_gmx_rxx_adr_ctl.s.mcst = 1; /* Force reject multicast packets */ 841210284Sjmallett 842210284Sjmallett if (flags & CVMX_IFF_PROMISC) 843210284Sjmallett agl_gmx_rxx_adr_ctl.s.cam_mode = 0; /* Reject matches if promisc. Since CAM is shut off, should accept everything */ 844210284Sjmallett else 845210284Sjmallett agl_gmx_rxx_adr_ctl.s.cam_mode = 1; /* Filter packets based on the CAM */ 846210284Sjmallett 847210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CTL(port), agl_gmx_rxx_adr_ctl.u64); 848210284Sjmallett 849210284Sjmallett if (flags & CVMX_IFF_PROMISC) 850210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM_EN(port), 0); 851210284Sjmallett else 852210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM_EN(port), 1); 853215990Sjmallett 854210284Sjmallett cvmx_spinlock_unlock(&state->lock); 855210284Sjmallett} 856210284Sjmallett 857210284Sjmallett 858210284Sjmallett/** 859210284Sjmallett * Set the maximum packet allowed in. Size is specified 860210284Sjmallett * including L2 but without FCS. A normal MTU would corespond 861210284Sjmallett * to 1514 assuming the standard 14 byte L2 header. 862210284Sjmallett * 863210284Sjmallett * @param port Management port 864210284Sjmallett * @param size_without_fcs 865210284Sjmallett * Size in bytes without FCS 866210284Sjmallett */ 867210284Sjmallettvoid cvmx_mgmt_port_set_max_packet_size(int port, int size_without_fcs) 868210284Sjmallett{ 869210284Sjmallett cvmx_mgmt_port_state_t *state; 870210284Sjmallett 871210284Sjmallett if ((port < 0) || (port >= __cvmx_mgmt_port_num_ports())) 872210284Sjmallett return; 873210284Sjmallett 874210284Sjmallett state = cvmx_mgmt_port_state_ptr + port; 875210284Sjmallett 876210284Sjmallett cvmx_spinlock_lock(&state->lock); 877210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_MAX(port), size_without_fcs); 878210284Sjmallett cvmx_write_csr(CVMX_AGL_GMX_RXX_JABBER(port), (size_without_fcs+7) & 0xfff8); 879210284Sjmallett cvmx_spinlock_unlock(&state->lock); 880210284Sjmallett} 881210284Sjmallett 882215990Sjmallett/** 883215990Sjmallett * Return the link state of an RGMII/MII port as returned by 884215990Sjmallett * auto negotiation. The result of this function may not match 885215990Sjmallett * Octeon's link config if auto negotiation has changed since 886215990Sjmallett * the last call to cvmx_mgmt_port_link_set(). 887215990Sjmallett * 888215990Sjmallett * @param port The RGMII/MII interface port to query 889215990Sjmallett * 890215990Sjmallett * @return Link state 891215990Sjmallett */ 892215990Sjmallettcvmx_helper_link_info_t cvmx_mgmt_port_link_get(int port) 893215990Sjmallett{ 894215990Sjmallett cvmx_mgmt_port_state_t *state; 895215990Sjmallett cvmx_helper_link_info_t result; 896215990Sjmallett 897215990Sjmallett state = cvmx_mgmt_port_state_ptr + port; 898215990Sjmallett result.u64 = 0; 899215990Sjmallett 900215990Sjmallett if (port > __cvmx_mgmt_port_num_ports()) 901215990Sjmallett { 902215990Sjmallett cvmx_dprintf("WARNING: Invalid port %d\n", port); 903215990Sjmallett return result; 904215990Sjmallett } 905215990Sjmallett 906215990Sjmallett if (state->port != -1) 907215990Sjmallett return __cvmx_helper_board_link_get(state->port); 908215990Sjmallett else // Simulator does not have PHY, use some defaults. 909215990Sjmallett { 910215990Sjmallett result.s.full_duplex = 1; 911215990Sjmallett result.s.link_up = 1; 912215990Sjmallett result.s.speed = 100; 913215990Sjmallett return result; 914215990Sjmallett } 915215990Sjmallett return result; 916215990Sjmallett} 917215990Sjmallett 918215990Sjmallett/** 919215990Sjmallett * Configure RGMII/MII port for the specified link state. This 920215990Sjmallett * function does not influence auto negotiation at the PHY level. 921215990Sjmallett * 922215990Sjmallett * @param port RGMII/MII interface port 923215990Sjmallett * @param link_info The new link state 924215990Sjmallett * 925215990Sjmallett * @return Zero on success, negative on failure 926215990Sjmallett */ 927215990Sjmallettint cvmx_mgmt_port_link_set(int port, cvmx_helper_link_info_t link_info) 928215990Sjmallett{ 929215990Sjmallett cvmx_agl_gmx_prtx_cfg_t agl_gmx_prtx; 930215990Sjmallett 931215990Sjmallett /* Disable GMX before we make any changes. */ 932215990Sjmallett agl_gmx_prtx.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); 933215990Sjmallett agl_gmx_prtx.s.en = 0; 934215990Sjmallett agl_gmx_prtx.s.tx_en = 0; 935215990Sjmallett agl_gmx_prtx.s.rx_en = 0; 936215990Sjmallett cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64); 937215990Sjmallett 938215990Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) 939215990Sjmallett { 940215990Sjmallett uint64_t one_second = cvmx_clock_get_rate(CVMX_CLOCK_CORE); 941215990Sjmallett /* Wait for GMX to be idle */ 942215990Sjmallett if (CVMX_WAIT_FOR_FIELD64(CVMX_AGL_GMX_PRTX_CFG(port), cvmx_agl_gmx_prtx_cfg_t, rx_idle, ==, 1, one_second) 943215990Sjmallett || CVMX_WAIT_FOR_FIELD64(CVMX_AGL_GMX_PRTX_CFG(port), cvmx_agl_gmx_prtx_cfg_t, tx_idle, ==, 1, one_second)) 944215990Sjmallett { 945215990Sjmallett cvmx_dprintf("MIX%d: Timeout waiting for GMX to be idle\n", port); 946215990Sjmallett return -1; 947215990Sjmallett } 948215990Sjmallett } 949215990Sjmallett 950215990Sjmallett agl_gmx_prtx.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); 951215990Sjmallett 952215990Sjmallett /* Set duplex mode */ 953215990Sjmallett if (!link_info.s.link_up) 954215990Sjmallett agl_gmx_prtx.s.duplex = 1; /* Force full duplex on down links */ 955215990Sjmallett else 956215990Sjmallett agl_gmx_prtx.s.duplex = link_info.s.full_duplex; 957215990Sjmallett 958215990Sjmallett switch(link_info.s.speed) 959215990Sjmallett { 960215990Sjmallett case 10: 961215990Sjmallett agl_gmx_prtx.s.speed = 0; 962215990Sjmallett agl_gmx_prtx.s.slottime = 0; 963215990Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) 964215990Sjmallett { 965215990Sjmallett agl_gmx_prtx.s.speed_msb = 1; 966215990Sjmallett agl_gmx_prtx.s.burst = 1; 967215990Sjmallett } 968215990Sjmallett break; 969215990Sjmallett 970215990Sjmallett case 100: 971215990Sjmallett agl_gmx_prtx.s.speed = 0; 972215990Sjmallett agl_gmx_prtx.s.slottime = 0; 973215990Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) 974215990Sjmallett { 975215990Sjmallett agl_gmx_prtx.s.speed_msb = 0; 976215990Sjmallett agl_gmx_prtx.s.burst = 1; 977215990Sjmallett } 978215990Sjmallett break; 979215990Sjmallett 980215990Sjmallett case 1000: 981215990Sjmallett /* 1000 MBits is only supported on 6XXX chips */ 982215990Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) 983215990Sjmallett { 984215990Sjmallett agl_gmx_prtx.s.speed_msb = 0; 985215990Sjmallett agl_gmx_prtx.s.speed = 1; 986215990Sjmallett agl_gmx_prtx.s.slottime = 1; /* Only matters for half-duplex */ 987215990Sjmallett agl_gmx_prtx.s.burst = agl_gmx_prtx.s.duplex; 988215990Sjmallett } 989215990Sjmallett break; 990215990Sjmallett 991215990Sjmallett /* No link */ 992215990Sjmallett case 0: 993215990Sjmallett default: 994215990Sjmallett break; 995215990Sjmallett } 996215990Sjmallett 997215990Sjmallett /* Write the new GMX setting with the port still disabled. */ 998215990Sjmallett cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64); 999215990Sjmallett 1000215990Sjmallett /* Read GMX CFG again to make sure the config is completed. */ 1001215990Sjmallett agl_gmx_prtx.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); 1002215990Sjmallett 1003215990Sjmallett 1004215990Sjmallett if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) 1005215990Sjmallett { 1006215990Sjmallett cvmx_mgmt_port_state_t *state = cvmx_mgmt_port_state_ptr + port; 1007215990Sjmallett cvmx_agl_gmx_txx_clk_t agl_clk; 1008215990Sjmallett agl_clk.u64 = cvmx_read_csr(CVMX_AGL_GMX_TXX_CLK(port)); 1009215990Sjmallett agl_clk.s.clk_cnt = 1; /* MII (both speeds) and RGMII 1000 setting */ 1010215990Sjmallett if (state->mode == CVMX_MGMT_PORT_RGMII_MODE) 1011215990Sjmallett { 1012215990Sjmallett if (link_info.s.speed == 10) 1013215990Sjmallett agl_clk.s.clk_cnt = 50; 1014215990Sjmallett else if (link_info.s.speed == 100) 1015215990Sjmallett agl_clk.s.clk_cnt = 5; 1016215990Sjmallett } 1017215990Sjmallett cvmx_write_csr(CVMX_AGL_GMX_TXX_CLK(port), agl_clk.u64); 1018215990Sjmallett } 1019215990Sjmallett 1020215990Sjmallett /* Enable transmit and receive ports */ 1021215990Sjmallett agl_gmx_prtx.s.tx_en = 1; 1022215990Sjmallett agl_gmx_prtx.s.rx_en = 1; 1023215990Sjmallett cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64); 1024215990Sjmallett 1025215990Sjmallett /* Enable the link. */ 1026215990Sjmallett agl_gmx_prtx.s.en = 1; 1027215990Sjmallett cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64); 1028215990Sjmallett return 0; 1029215990Sjmallett} 1030