aboutsummaryrefslogtreecommitdiff
path: root/sys/mips/cavium/octe
diff options
context:
space:
mode:
Diffstat (limited to 'sys/mips/cavium/octe')
-rw-r--r--sys/mips/cavium/octe/cavium-ethernet.h99
-rw-r--r--sys/mips/cavium/octe/ethernet-common.c292
-rw-r--r--sys/mips/cavium/octe/ethernet-common.h52
-rw-r--r--sys/mips/cavium/octe/ethernet-defines.h101
-rw-r--r--sys/mips/cavium/octe/ethernet-headers.h50
-rw-r--r--sys/mips/cavium/octe/ethernet-mdio.c141
-rw-r--r--sys/mips/cavium/octe/ethernet-mdio.h40
-rw-r--r--sys/mips/cavium/octe/ethernet-mem.c207
-rw-r--r--sys/mips/cavium/octe/ethernet-mem.h33
-rw-r--r--sys/mips/cavium/octe/ethernet-mv88e61xx.c127
-rw-r--r--sys/mips/cavium/octe/ethernet-mv88e61xx.h34
-rw-r--r--sys/mips/cavium/octe/ethernet-rgmii.c340
-rw-r--r--sys/mips/cavium/octe/ethernet-rx.c416
-rw-r--r--sys/mips/cavium/octe/ethernet-rx.h37
-rw-r--r--sys/mips/cavium/octe/ethernet-sgmii.c117
-rw-r--r--sys/mips/cavium/octe/ethernet-spi.c309
-rw-r--r--sys/mips/cavium/octe/ethernet-tx.c402
-rw-r--r--sys/mips/cavium/octe/ethernet-tx.h34
-rw-r--r--sys/mips/cavium/octe/ethernet-util.h84
-rw-r--r--sys/mips/cavium/octe/ethernet-xaui.c116
-rw-r--r--sys/mips/cavium/octe/ethernet.c594
-rw-r--r--sys/mips/cavium/octe/mv88e61xxphy.c630
-rw-r--r--sys/mips/cavium/octe/mv88e61xxphyreg.h149
-rw-r--r--sys/mips/cavium/octe/octe.c522
-rw-r--r--sys/mips/cavium/octe/octebus.c123
-rw-r--r--sys/mips/cavium/octe/octebusvar.h41
-rw-r--r--sys/mips/cavium/octe/wrapper-cvmx-includes.h50
27 files changed, 5140 insertions, 0 deletions
diff --git a/sys/mips/cavium/octe/cavium-ethernet.h b/sys/mips/cavium/octe/cavium-ethernet.h
new file mode 100644
index 000000000000..dce2d2bcfa79
--- /dev/null
+++ b/sys/mips/cavium/octe/cavium-ethernet.h
@@ -0,0 +1,99 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+/**
+ * @file
+ * External interface for the Cavium Octeon ethernet driver.
+ *
+ * $Id: cavium-ethernet.h 41589 2009-03-19 19:58:58Z cchavva $
+ *
+ */
+#ifndef CAVIUM_ETHERNET_H
+#define CAVIUM_ETHERNET_H
+
+#include <net/if_media.h>
+
+/**
+ * This is the definition of the Ethernet driver's private
+ * driver state stored in ifp->if_softc.
+ */
+typedef struct {
+ /* XXX FreeBSD device softcs must start with an ifnet pointer. */
+ struct ifnet *ifp;
+
+ int port; /* PKO hardware output port */
+ int queue; /* PKO hardware queue for the port */
+ int fau; /* Hardware fetch and add to count outstanding tx buffers */
+ int imode; /* Type of port. This is one of the enums in cvmx_helper_interface_mode_t */
+#if 0
+ struct ifnet_stats stats; /* Device statistics */
+#endif
+ uint64_t link_info; /* Last negotiated link state */
+ void (*poll)(struct ifnet *ifp); /* Called periodically to check link status */
+
+ /*
+ * FreeBSD additions.
+ */
+ device_t dev;
+ device_t miibus;
+
+ int (*open)(struct ifnet *ifp);
+ int (*stop)(struct ifnet *ifp);
+
+ int (*init)(struct ifnet *ifp);
+ void (*uninit)(struct ifnet *ifp);
+
+ uint8_t mac[6];
+ int phy_id;
+ const char *phy_device;
+ int (*mdio_read)(struct ifnet *, int, int);
+ void (*mdio_write)(struct ifnet *, int, int, int);
+
+ struct ifqueue tx_free_queue[16];
+
+ int need_link_update;
+ struct task link_task;
+ struct ifmedia media;
+ int if_flags;
+
+ struct mtx tx_mtx;
+} cvm_oct_private_t;
+
+
+/**
+ * Free a work queue entry received in a intercept callback.
+ *
+ * @param work_queue_entry
+ * Work queue entry to free
+ * @return Zero on success, Negative on failure.
+ */
+int cvm_oct_free_work(void *work_queue_entry);
+
+#endif
diff --git a/sys/mips/cavium/octe/ethernet-common.c b/sys/mips/cavium/octe/ethernet-common.c
new file mode 100644
index 000000000000..9db9830915e8
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-common.c
@@ -0,0 +1,292 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+extern int octeon_is_simulation(void);
+extern cvmx_bootinfo_t *octeon_bootinfo;
+extern int pow_send_group;
+extern int always_use_pow;
+extern char pow_send_list[];
+
+
+/**
+ * Get the low level ethernet statistics
+ *
+ * @param dev Device to get the statistics from
+ * @return Pointer to the statistics
+ */
+#if 0
+static struct ifnet_stats *cvm_oct_common_get_stats(struct ifnet *ifp)
+{
+ cvmx_pip_port_status_t rx_status;
+ cvmx_pko_port_status_t tx_status;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+
+ if (priv->port < CVMX_PIP_NUM_INPUT_PORTS) {
+ if (octeon_is_simulation()) {
+ /* The simulator doesn't support statistics */
+ memset(&rx_status, 0, sizeof(rx_status));
+ memset(&tx_status, 0, sizeof(tx_status));
+ } else {
+ cvmx_pip_get_port_status(priv->port, 1, &rx_status);
+ cvmx_pko_get_port_status(priv->port, 1, &tx_status);
+ }
+
+ priv->stats.rx_packets += rx_status.inb_packets;
+ priv->stats.tx_packets += tx_status.packets;
+ priv->stats.rx_bytes += rx_status.inb_octets;
+ priv->stats.tx_bytes += tx_status.octets;
+ priv->stats.multicast += rx_status.multicast_packets;
+ priv->stats.rx_crc_errors += rx_status.inb_errors;
+ priv->stats.rx_frame_errors += rx_status.fcs_align_err_packets;
+
+ /* The drop counter must be incremented atomically since the RX
+ tasklet also increments it */
+#ifdef CONFIG_64BIT
+ cvmx_atomic_add64_nosync(&priv->stats.rx_dropped, rx_status.dropped_packets);
+#else
+ cvmx_atomic_add32_nosync((int32_t *)&priv->stats.rx_dropped, rx_status.dropped_packets);
+#endif
+ }
+
+ return &priv->stats;
+}
+#endif
+
+
+/**
+ * Set the multicast list. Currently unimplemented.
+ *
+ * @param dev Device to work on
+ */
+void cvm_oct_common_set_multicast_list(struct ifnet *ifp)
+{
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ if ((interface < 2) && (cvmx_helper_interface_get_mode(interface) != CVMX_HELPER_INTERFACE_MODE_SPI)) {
+ cvmx_gmxx_rxx_adr_ctl_t control;
+ control.u64 = 0;
+ control.s.bcst = 1; /* Allow broadcast MAC addresses */
+
+ if (/*ifp->mc_list || */(ifp->if_flags&IFF_ALLMULTI) ||
+ (ifp->if_flags & IFF_PROMISC))
+ control.s.mcst = 2; /* Force accept multicast packets */
+ else
+ control.s.mcst = 1; /* Force reject multicat packets */
+
+ if (ifp->if_flags & IFF_PROMISC)
+ control.s.cam_mode = 0; /* Reject matches if promisc. Since CAM is shut off, should accept everything */
+ else
+ control.s.cam_mode = 1; /* Filter packets based on the CAM */
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64 & ~1ull);
+
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CTL(index, interface), control.u64);
+ if (ifp->if_flags&IFF_PROMISC)
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN(index, interface), 0);
+ else
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN(index, interface), 1);
+
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+ }
+}
+
+
+/**
+ * Set the hardware MAC address for a device
+ *
+ * @param dev Device to change the MAC address for
+ * @param addr Address structure to change it too.
+ */
+void cvm_oct_common_set_mac_address(struct ifnet *ifp, const void *addr)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ memcpy(priv->mac, addr, 6);
+
+ if ((interface < 2) && (cvmx_helper_interface_get_mode(interface) != CVMX_HELPER_INTERFACE_MODE_SPI)) {
+ int i;
+ const uint8_t *ptr = addr;
+ uint64_t mac = 0;
+ for (i = 0; i < 6; i++)
+ mac = (mac<<8) | (uint64_t)(ptr[i]);
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64 & ~1ull);
+
+ cvmx_write_csr(CVMX_GMXX_SMACX(index, interface), mac);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM0(index, interface), ptr[0]);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM1(index, interface), ptr[1]);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM2(index, interface), ptr[2]);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM3(index, interface), ptr[3]);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM4(index, interface), ptr[4]);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM5(index, interface), ptr[5]);
+ cvm_oct_common_set_multicast_list(ifp);
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+ }
+}
+
+
+/**
+ * Change the link MTU. Unimplemented
+ *
+ * @param dev Device to change
+ * @param new_mtu The new MTU
+ * @return Zero on success
+ */
+int cvm_oct_common_change_mtu(struct ifnet *ifp, int new_mtu)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+ int vlan_bytes = 4;
+
+ /* Limit the MTU to make sure the ethernet packets are between 64 bytes
+ and 65535 bytes */
+ if ((new_mtu + 14 + 4 + vlan_bytes < 64) || (new_mtu + 14 + 4 + vlan_bytes > 65392)) {
+ printf("MTU must be between %d and %d.\n", 64-14-4-vlan_bytes, 65392-14-4-vlan_bytes);
+ return -EINVAL;
+ }
+ ifp->if_mtu = new_mtu;
+
+ if ((interface < 2) && (cvmx_helper_interface_get_mode(interface) != CVMX_HELPER_INTERFACE_MODE_SPI)) {
+ int max_packet = new_mtu + 14 + 4 + vlan_bytes; /* Add ethernet header and FCS, and VLAN if configured. */
+
+ if (OCTEON_IS_MODEL(OCTEON_CN3XXX) || OCTEON_IS_MODEL(OCTEON_CN58XX)) {
+ /* Signal errors on packets larger than the MTU */
+ cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(index, interface), max_packet);
+ } else {
+ /* Set the hardware to truncate packets larger than the MTU and
+ smaller the 64 bytes */
+ cvmx_pip_frm_len_chkx_t frm_len_chk;
+ frm_len_chk.u64 = 0;
+ frm_len_chk.s.minlen = 64;
+ frm_len_chk.s.maxlen = max_packet;
+ cvmx_write_csr(CVMX_PIP_FRM_LEN_CHKX(interface), frm_len_chk.u64);
+ }
+ /* Set the hardware to truncate packets larger than the MTU. The
+ jabber register must be set to a multiple of 8 bytes, so round up */
+ cvmx_write_csr(CVMX_GMXX_RXX_JABBER(index, interface), (max_packet + 7) & ~7u);
+ }
+ return 0;
+}
+
+
+/**
+ * Per network device initialization
+ *
+ * @param dev Device to initialize
+ * @return Zero on success
+ */
+int cvm_oct_common_init(struct ifnet *ifp)
+{
+ static int count;
+ char mac[6] = {
+ octeon_bootinfo->mac_addr_base[0],
+ octeon_bootinfo->mac_addr_base[1],
+ octeon_bootinfo->mac_addr_base[2],
+ octeon_bootinfo->mac_addr_base[3],
+ octeon_bootinfo->mac_addr_base[4],
+ octeon_bootinfo->mac_addr_base[5] + count};
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+
+ /* Force the interface to use the POW send if always_use_pow was
+ specified or it is in the pow send list */
+ if ((pow_send_group != -1) && (always_use_pow || strstr(pow_send_list, if_name(ifp))))
+ priv->queue = -1;
+
+ ifp->if_mtu = ETHERMTU;
+
+ count++;
+
+#if 0
+ ifp->get_stats = cvm_oct_common_get_stats;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ ifp->poll_controller = cvm_oct_poll_controller;
+#endif
+#endif
+
+ cvm_oct_mdio_setup_device(ifp);
+
+ cvm_oct_common_set_mac_address(ifp, mac);
+ cvm_oct_common_change_mtu(ifp, ifp->if_mtu);
+
+#if 0
+ /* Zero out stats for port so we won't mistakenly show counters from the
+ bootloader */
+ memset(ifp->get_stats(ifp), 0, sizeof(struct ifnet_stats));
+#endif
+
+ /*
+ * Do any last-minute board-specific initialization.
+ */
+ switch (cvmx_sysinfo_get()->board_type) {
+#if defined(OCTEON_VENDOR_LANNER)
+ case CVMX_BOARD_TYPE_CUST_LANNER_MR320:
+ if (priv->phy_id == 16)
+ cvm_oct_mv88e61xx_setup_device(ifp);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ device_attach(priv->dev);
+
+ return 0;
+}
+
+void cvm_oct_common_uninit(struct ifnet *ifp)
+{
+ /* Currently nothing to do */
+}
+
diff --git a/sys/mips/cavium/octe/ethernet-common.h b/sys/mips/cavium/octe/ethernet-common.h
new file mode 100644
index 000000000000..ef6e4aa06da2
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-common.h
@@ -0,0 +1,52 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+int cvm_oct_common_init(struct ifnet *ifp);
+void cvm_oct_common_uninit(struct ifnet *ifp);
+
+int cvm_oct_common_change_mtu(struct ifnet *ifp, int new_mtu);
+void cvm_oct_common_set_multicast_list(struct ifnet *ifp);
+void cvm_oct_common_set_mac_address(struct ifnet *ifp, const void *);
+
+int cvm_oct_init_module(device_t);
+void cvm_oct_cleanup_module(void);
+
+/*
+ * XXX/juli
+ * These belong elsewhere but we can't stomach the nested extern.
+ */
+int cvm_oct_rgmii_init(struct ifnet *ifp);
+void cvm_oct_rgmii_uninit(struct ifnet *ifp);
+int cvm_oct_sgmii_init(struct ifnet *ifp);
+void cvm_oct_sgmii_uninit(struct ifnet *ifp);
+int cvm_oct_spi_init(struct ifnet *ifp);
+void cvm_oct_spi_uninit(struct ifnet *ifp);
+int cvm_oct_xaui_init(struct ifnet *ifp);
+void cvm_oct_xaui_uninit(struct ifnet *ifp);
diff --git a/sys/mips/cavium/octe/ethernet-defines.h b/sys/mips/cavium/octe/ethernet-defines.h
new file mode 100644
index 000000000000..64babf5a9614
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-defines.h
@@ -0,0 +1,101 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+/*
+ * A few defines are used to control the operation of this driver:
+ * CONFIG_CAVIUM_RESERVE32
+ * This kernel config options controls the amount of memory configured
+ * in a wired TLB entry for all processes to share. If this is set, the
+ * driver will use this memory instead of kernel memory for pools. This
+ * allows 32bit userspace application to access the buffers, but also
+ * requires all received packets to be copied.
+ * CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS
+ * This kernel config option allows the user to control the number of
+ * packet and work queue buffers allocated by the driver. If this is zero,
+ * the driver uses the default from below.
+ * USE_MBUFS_IN_HW
+ * Tells the driver to populate the packet buffers with kernel mbufs.
+ * This allows the driver to receive packets without copying them. It also
+ * means that 32bit userspace can't access the packet buffers.
+ * USE_32BIT_SHARED
+ * This define tells the driver to allocate memory for buffers from the
+ * 32bit sahred region instead of the kernel memory space.
+ * USE_HW_TCPUDP_CHECKSUM
+ * Controls if the Octeon TCP/UDP checksum engine is used for packet
+ * output. If this is zero, the kernel will perform the checksum in
+ * software.
+ * USE_MULTICORE_RECEIVE
+ * Process receive interrupts on multiple cores. This spreads the network
+ * load across the first 8 processors. If ths is zero, only one core
+ * processes incomming packets.
+ * USE_ASYNC_IOBDMA
+ * Use asynchronous IO access to hardware. This uses Octeon's asynchronous
+ * IOBDMAs to issue IO accesses without stalling. Set this to zero
+ * to disable this. Note that IOBDMAs require CVMSEG.
+ */
+#ifndef CONFIG_CAVIUM_RESERVE32
+#define CONFIG_CAVIUM_RESERVE32 0
+#endif
+
+#if CONFIG_CAVIUM_RESERVE32
+ #define USE_32BIT_SHARED 1
+ #define USE_MBUFS_IN_HW 0
+#else
+ #define USE_32BIT_SHARED 0
+ #define USE_MBUFS_IN_HW 1
+#endif
+
+#define INTERRUPT_LIMIT 10000 /* Max interrupts per second per core */
+/*#define INTERRUPT_LIMIT 0 *//* Don't limit the number of interrupts */
+#define USE_HW_TCPUDP_CHECKSUM 1
+#define USE_MULTICORE_RECEIVE 1
+#define USE_RED 1 /* Enable Random Early Dropping under load */
+#if 0
+#define USE_ASYNC_IOBDMA (CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE > 0)
+#else
+#define USE_ASYNC_IOBDMA 0
+#endif
+#define USE_10MBPS_PREAMBLE_WORKAROUND 1 /* Allow SW based preamble removal at 10Mbps to workaround PHYs giving us bad preambles */
+#define DONT_WRITEBACK(x) (x) /* Use this to have all FPA frees also tell the L2 not to write data to memory */
+/*#define DONT_WRITEBACK(x) 0 *//* Use this to not have FPA frees control L2 */
+
+#define MAX_RX_PACKETS 120 /* Maximum number of packets to process per interrupt. */
+#define MAX_OUT_QUEUE_DEPTH 1000
+
+#ifndef SMP
+#undef USE_MULTICORE_RECEIVE
+#define USE_MULTICORE_RECEIVE 0
+#endif
+
+#define IP_PROTOCOL_TCP 6
+#define IP_PROTOCOL_UDP 0x11
+#define FAU_NUM_PACKET_BUFFERS_TO_FREE (CVMX_FAU_REG_END - sizeof(uint32_t))
+#define TOTAL_NUMBER_OF_PORTS (CVMX_PIP_NUM_INPUT_PORTS+1)
+
diff --git a/sys/mips/cavium/octe/ethernet-headers.h b/sys/mips/cavium/octe/ethernet-headers.h
new file mode 100644
index 000000000000..3945e13ac678
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-headers.h
@@ -0,0 +1,50 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+#ifndef __ETHERNET_HEADERS_H__
+#define __ETHERNET_HEADERS_H__
+
+#include "cavium-ethernet.h"
+#include "ethernet-common.h"
+#include "ethernet-defines.h"
+#include "ethernet-mdio.h"
+#include "ethernet-mem.h"
+#include "ethernet-rx.h"
+#include "ethernet-tx.h"
+#include "ethernet-util.h"
+
+/*
+ * Any board- or vendor-specific includes.
+ */
+#ifdef OCTEON_VENDOR_LANNER
+#include "ethernet-mv88e61xx.h"
+#endif
+
+#endif
diff --git a/sys/mips/cavium/octe/ethernet-mdio.c b/sys/mips/cavium/octe/ethernet-mdio.c
new file mode 100644
index 000000000000..19b4d43c70dd
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-mdio.c
@@ -0,0 +1,141 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+struct mtx cvm_oct_mdio_mtx;
+MTX_SYSINIT(cvm_oct_mdio, &cvm_oct_mdio_mtx, "MDIO", MTX_DEF);
+
+/**
+ * Perform an MII read. Called by the generic MII routines
+ *
+ * @param dev Device to perform read for
+ * @param phy_id The MII phy id
+ * @param location Register location to read
+ * @return Result from the read or zero on failure
+ */
+int cvm_oct_mdio_read(struct ifnet *ifp, int phy_id, int location)
+{
+ cvmx_smi_cmd_t smi_cmd;
+ cvmx_smi_rd_dat_t smi_rd;
+
+ MDIO_LOCK();
+ smi_cmd.u64 = 0;
+ smi_cmd.s.phy_op = 1;
+ smi_cmd.s.phy_adr = phy_id;
+ smi_cmd.s.reg_adr = location;
+ cvmx_write_csr(CVMX_SMI_CMD, smi_cmd.u64);
+
+ do {
+#if 0
+ if (!in_interrupt())
+ yield();
+#endif
+ smi_rd.u64 = cvmx_read_csr(CVMX_SMI_RD_DAT);
+ } while (smi_rd.s.pending);
+
+ MDIO_UNLOCK();
+
+ if (smi_rd.s.val)
+ return smi_rd.s.dat;
+ else
+ return 0;
+}
+
+
+/**
+ * Perform an MII write. Called by the generic MII routines
+ *
+ * @param dev Device to perform write for
+ * @param phy_id The MII phy id
+ * @param location Register location to write
+ * @param val Value to write
+ */
+void cvm_oct_mdio_write(struct ifnet *ifp, int phy_id, int location, int val)
+{
+ cvmx_smi_cmd_t smi_cmd;
+ cvmx_smi_wr_dat_t smi_wr;
+
+ MDIO_LOCK();
+ smi_wr.u64 = 0;
+ smi_wr.s.dat = val;
+ cvmx_write_csr(CVMX_SMI_WR_DAT, smi_wr.u64);
+
+ smi_cmd.u64 = 0;
+ smi_cmd.s.phy_op = 0;
+ smi_cmd.s.phy_adr = phy_id;
+ smi_cmd.s.reg_adr = location;
+ cvmx_write_csr(CVMX_SMI_CMD, smi_cmd.u64);
+
+ do {
+#if 0
+ if (!in_interrupt())
+ yield();
+#endif
+ smi_wr.u64 = cvmx_read_csr(CVMX_SMI_WR_DAT);
+ } while (smi_wr.s.pending);
+ MDIO_UNLOCK();
+}
+
+/**
+ * Setup the MDIO device structures
+ *
+ * @param dev Device to setup
+ *
+ * @return Zero on success, negative on failure
+ */
+int cvm_oct_mdio_setup_device(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+
+ priv->phy_id = cvmx_helper_board_get_mii_address(priv->port);
+ priv->phy_device = NULL;
+ priv->mdio_read = NULL;
+ priv->mdio_write = NULL;
+
+ return 0;
+}
+
diff --git a/sys/mips/cavium/octe/ethernet-mdio.h b/sys/mips/cavium/octe/ethernet-mdio.h
new file mode 100644
index 000000000000..c2c4ec69230e
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-mdio.h
@@ -0,0 +1,40 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+extern struct mtx cvm_oct_mdio_mtx;
+
+#define MDIO_LOCK() mtx_lock(&cvm_oct_mdio_mtx)
+#define MDIO_UNLOCK() mtx_unlock(&cvm_oct_mdio_mtx)
+#define MDIO_TRYLOCK() mtx_trylock(&cvm_oct_mdio_mtx)
+
+int cvm_oct_mdio_read(struct ifnet *ifp, int phy_id, int location);
+void cvm_oct_mdio_write(struct ifnet *ifp, int phy_id, int location, int val);
+int cvm_oct_mdio_setup_device(struct ifnet *ifp);
+
diff --git a/sys/mips/cavium/octe/ethernet-mem.c b/sys/mips/cavium/octe/ethernet-mem.c
new file mode 100644
index 000000000000..5d2fa5462162
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-mem.c
@@ -0,0 +1,207 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+/**
+ * Fill the supplied hardware pool with mbufs
+ *
+ * @param pool Pool to allocate an mbuf for
+ * @param size Size of the buffer needed for the pool
+ * @param elements Number of buffers to allocate
+ */
+static int cvm_oct_fill_hw_mbuf(int pool, int size, int elements)
+{
+ int freed = elements;
+ while (freed) {
+ KASSERT(size <= MCLBYTES - 128, ("mbuf clusters are too small"));
+
+ struct mbuf *m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (__predict_false(m == NULL)) {
+ printf("Failed to allocate mbuf for hardware pool %d\n", pool);
+ break;
+ }
+
+ m->m_data += 128 - (((uintptr_t)m->m_data) & 0x7f);
+ *(struct mbuf **)(m->m_data - sizeof(void *)) = m;
+ cvmx_fpa_free(m->m_data, pool, DONT_WRITEBACK(size/128));
+ freed--;
+ }
+ return (elements - freed);
+}
+
+
+/**
+ * Free the supplied hardware pool of mbufs
+ *
+ * @param pool Pool to allocate an mbuf for
+ * @param size Size of the buffer needed for the pool
+ * @param elements Number of buffers to allocate
+ */
+static void cvm_oct_free_hw_mbuf(int pool, int size, int elements)
+{
+ char *memory;
+
+ do {
+ memory = cvmx_fpa_alloc(pool);
+ if (memory) {
+ struct mbuf *m = *(struct mbuf **)(memory - sizeof(void *));
+ elements--;
+ m_freem(m);
+ }
+ } while (memory);
+
+ if (elements < 0)
+ printf("Warning: Freeing of pool %u had too many mbufs (%d)\n", pool, elements);
+ else if (elements > 0)
+ printf("Warning: Freeing of pool %u is missing %d mbufs\n", pool, elements);
+}
+
+
+/**
+ * This function fills a hardware pool with memory. Depending
+ * on the config defines, this memory might come from the
+ * kernel or global 32bit memory allocated with
+ * cvmx_bootmem_alloc.
+ *
+ * @param pool Pool to populate
+ * @param size Size of each buffer in the pool
+ * @param elements Number of buffers to allocate
+ */
+static int cvm_oct_fill_hw_memory(int pool, int size, int elements)
+{
+ char *memory;
+ int freed = elements;
+
+ if (USE_32BIT_SHARED) {
+#if 0
+ extern uint64_t octeon_reserve32_memory;
+
+ memory = cvmx_bootmem_alloc_range(elements*size, 128, octeon_reserve32_memory,
+ octeon_reserve32_memory + (CONFIG_CAVIUM_RESERVE32<<20) - 1);
+ if (memory == NULL)
+ panic("Unable to allocate %u bytes for FPA pool %d\n", elements*size, pool);
+
+ printf("Memory range %p - %p reserved for hardware\n", memory, memory + elements*size - 1);
+
+ while (freed) {
+ cvmx_fpa_free(memory, pool, 0);
+ memory += size;
+ freed--;
+ }
+#else
+ panic("%s: may need to implement using shared memory.", __func__);
+#endif
+ } else {
+ while (freed) {
+ /* We need to force alignment to 128 bytes here */
+#if 0
+ memory = kmalloc(size + 127, GFP_ATOMIC);
+#else
+ panic("%s: not yet implemented.", __func__);
+#endif
+ if (__predict_false(memory == NULL)) {
+ printf("Unable to allocate %u bytes for FPA pool %d\n", elements*size, pool);
+ break;
+ }
+ memory = (char *)(((unsigned long)memory+127) & -128);
+ cvmx_fpa_free(memory, pool, 0);
+ freed--;
+ }
+ }
+ return (elements - freed);
+}
+
+
+/**
+ * Free memory previously allocated with cvm_oct_fill_hw_memory
+ *
+ * @param pool FPA pool to free
+ * @param size Size of each buffer in the pool
+ * @param elements Number of buffers that should be in the pool
+ */
+static void cvm_oct_free_hw_memory(int pool, int size, int elements)
+{
+ if (USE_32BIT_SHARED) {
+ printf("Warning: 32 shared memory is not freeable\n");
+ } else {
+ char *memory;
+ do {
+ memory = cvmx_fpa_alloc(pool);
+ if (memory) {
+ elements--;
+#if 0
+ kfree(phys_to_virt(cvmx_ptr_to_phys(memory)));
+#else
+ panic("%s: not yet implemented.", __func__);
+#endif
+ }
+ } while (memory);
+
+ if (elements < 0)
+ printf("Freeing of pool %u had too many buffers (%d)\n", pool, elements);
+ else if (elements > 0)
+ printf("Warning: Freeing of pool %u is missing %d buffers\n", pool, elements);
+ }
+}
+
+
+int cvm_oct_mem_fill_fpa(int pool, int size, int elements)
+{
+ int freed;
+ if (USE_MBUFS_IN_HW)
+ freed = cvm_oct_fill_hw_mbuf(pool, size, elements);
+ else
+ freed = cvm_oct_fill_hw_memory(pool, size, elements);
+ return (freed);
+}
+
+void cvm_oct_mem_empty_fpa(int pool, int size, int elements)
+{
+ if (USE_MBUFS_IN_HW)
+ cvm_oct_free_hw_mbuf(pool, size, elements);
+ else
+ cvm_oct_free_hw_memory(pool, size, elements);
+}
+
diff --git a/sys/mips/cavium/octe/ethernet-mem.h b/sys/mips/cavium/octe/ethernet-mem.h
new file mode 100644
index 000000000000..d9058d9b18c6
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-mem.h
@@ -0,0 +1,33 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+int cvm_oct_mem_fill_fpa(int pool, int size, int elements);
+void cvm_oct_mem_empty_fpa(int pool, int size, int elements);
+
diff --git a/sys/mips/cavium/octe/ethernet-mv88e61xx.c b/sys/mips/cavium/octe/ethernet-mv88e61xx.c
new file mode 100644
index 000000000000..31dbd74ecd25
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-mv88e61xx.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Interface to the Marvell 88E61XX SMI/MDIO.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <dev/mii/mii.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+#define MV88E61XX_SMI_REG_CMD 0x00 /* Indirect command register. */
+#define MV88E61XX_SMI_CMD_BUSY 0x8000 /* Busy bit. */
+#define MV88E61XX_SMI_CMD_22 0x1000 /* Clause 22 (default 45.) */
+#define MV88E61XX_SMI_CMD_READ 0x0800 /* Read command. */
+#define MV88E61XX_SMI_CMD_WRITE 0x0400 /* Write command. */
+#define MV88E61XX_SMI_CMD_PHY(phy) (((phy) & 0x1f) << 5)
+#define MV88E61XX_SMI_CMD_REG(reg) ((reg) & 0x1f)
+
+#define MV88E61XX_SMI_REG_DAT 0x01 /* Indirect data register. */
+
+static int cvm_oct_mv88e61xx_smi_read(struct ifnet *, int, int);
+static void cvm_oct_mv88e61xx_smi_write(struct ifnet *, int, int, int);
+static int cvm_oct_mv88e61xx_smi_wait(struct ifnet *);
+
+int
+cvm_oct_mv88e61xx_setup_device(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+
+ priv->mdio_read = cvm_oct_mv88e61xx_smi_read;
+ priv->mdio_write = cvm_oct_mv88e61xx_smi_write;
+ priv->phy_device = "mv88e61xxphy";
+
+ return (0);
+}
+
+static int
+cvm_oct_mv88e61xx_smi_read(struct ifnet *ifp, int phy_id, int location)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int error;
+
+ error = cvm_oct_mv88e61xx_smi_wait(ifp);
+ if (error != 0)
+ return (0);
+
+ cvm_oct_mdio_write(ifp, priv->phy_id, MV88E61XX_SMI_REG_CMD,
+ MV88E61XX_SMI_CMD_BUSY | MV88E61XX_SMI_CMD_22 |
+ MV88E61XX_SMI_CMD_READ | MV88E61XX_SMI_CMD_PHY(phy_id) |
+ MV88E61XX_SMI_CMD_REG(location));
+
+ error = cvm_oct_mv88e61xx_smi_wait(ifp);
+ if (error != 0)
+ return (0);
+
+ return (cvm_oct_mdio_read(ifp, priv->phy_id, MV88E61XX_SMI_REG_DAT));
+}
+
+static void
+cvm_oct_mv88e61xx_smi_write(struct ifnet *ifp, int phy_id, int location, int val)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+
+ cvm_oct_mv88e61xx_smi_wait(ifp);
+ cvm_oct_mdio_write(ifp, priv->phy_id, MV88E61XX_SMI_REG_DAT, val);
+ cvm_oct_mdio_write(ifp, priv->phy_id, MV88E61XX_SMI_REG_CMD,
+ MV88E61XX_SMI_CMD_BUSY | MV88E61XX_SMI_CMD_22 |
+ MV88E61XX_SMI_CMD_WRITE | MV88E61XX_SMI_CMD_PHY(phy_id) |
+ MV88E61XX_SMI_CMD_REG(location));
+ cvm_oct_mv88e61xx_smi_wait(ifp);
+}
+
+static int
+cvm_oct_mv88e61xx_smi_wait(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ uint16_t cmd;
+ unsigned i;
+
+ for (i = 0; i < 10000; i++) {
+ cmd = cvm_oct_mdio_read(ifp, priv->phy_id, MV88E61XX_SMI_REG_CMD);
+ if ((cmd & MV88E61XX_SMI_CMD_BUSY) == 0)
+ return (0);
+ }
+ return (ETIMEDOUT);
+}
diff --git a/sys/mips/cavium/octe/ethernet-mv88e61xx.h b/sys/mips/cavium/octe/ethernet-mv88e61xx.h
new file mode 100644
index 000000000000..b61c05494d98
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-mv88e61xx.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _CAVIUM_OCTE_ETHERNET_MV88E61XX_H_
+#define _CAVIUM_OCTE_ETHERNET_MV88E61XX_H_
+
+int cvm_oct_mv88e61xx_setup_device(struct ifnet *ifp);
+
+#endif /* !_CAVIUM_OCTE_ETHERNET_MV88E61XX_H_ */
diff --git a/sys/mips/cavium/octe/ethernet-rgmii.c b/sys/mips/cavium/octe/ethernet-rgmii.c
new file mode 100644
index 000000000000..ac55de8a705b
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-rgmii.c
@@ -0,0 +1,340 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+#include "octebusvar.h"
+
+extern int octeon_is_simulation(void);
+extern struct ifnet *cvm_oct_device[];
+
+static struct mtx global_register_lock;
+MTX_SYSINIT(global_register_lock, &global_register_lock,
+ "RGMII Global", MTX_SPIN);
+
+static int number_rgmii_ports;
+
+static void cvm_oct_rgmii_poll(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ cvmx_helper_link_info_t link_info;
+
+ /* Take the global register lock since we are going to touch
+ registers that affect more than one port */
+ mtx_lock_spin(&global_register_lock);
+
+ link_info = cvmx_helper_link_get(priv->port);
+ if (link_info.u64 == priv->link_info) {
+
+ /* If the 10Mbps preamble workaround is supported and we're
+ at 10Mbps we may need to do some special checking */
+ if (USE_10MBPS_PREAMBLE_WORKAROUND && (link_info.s.speed == 10)) {
+
+ /* Read the GMXX_RXX_INT_REG[PCTERR] bit and
+ see if we are getting preamble errors */
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+ cvmx_gmxx_rxx_int_reg_t gmxx_rxx_int_reg;
+ gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
+ if (gmxx_rxx_int_reg.s.pcterr) {
+
+ /* We are getting preamble errors at 10Mbps.
+ Most likely the PHY is giving us packets
+ with mis aligned preambles. In order to get
+ these packets we need to disable preamble
+ checking and do it in software */
+ cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl;
+ cvmx_ipd_sub_port_fcs_t ipd_sub_port_fcs;
+
+ /* Disable preamble checking */
+ gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
+ gmxx_rxx_frm_ctl.s.pre_chk = 0;
+ cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), gmxx_rxx_frm_ctl.u64);
+
+ /* Disable FCS stripping */
+ ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
+ ipd_sub_port_fcs.s.port_bit &= 0xffffffffull ^ (1ull<<priv->port);
+ cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
+
+ /* Clear any error bits */
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmxx_rxx_int_reg.u64);
+ DEBUGPRINT("%s: Using 10Mbps with software preamble removal\n", if_name(ifp));
+ }
+ }
+ mtx_unlock_spin(&global_register_lock);
+ return;
+ }
+
+ /* If the 10Mbps preamble workaround is allowed we need to on
+ preamble checking, FCS stripping, and clear error bits on
+ every speed change. If errors occur during 10Mbps operation
+ the above code will change this stuff */
+ if (USE_10MBPS_PREAMBLE_WORKAROUND) {
+
+ cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl;
+ cvmx_ipd_sub_port_fcs_t ipd_sub_port_fcs;
+ cvmx_gmxx_rxx_int_reg_t gmxx_rxx_int_reg;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ /* Enable preamble checking */
+ gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
+ gmxx_rxx_frm_ctl.s.pre_chk = 1;
+ cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), gmxx_rxx_frm_ctl.u64);
+ /* Enable FCS stripping */
+ ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
+ ipd_sub_port_fcs.s.port_bit |= 1ull<<priv->port;
+ cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
+ /* Clear any error bits */
+ gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmxx_rxx_int_reg.u64);
+ }
+
+ link_info = cvmx_helper_link_autoconf(priv->port);
+ priv->link_info = link_info.u64;
+ priv->need_link_update = 1;
+ mtx_unlock_spin(&global_register_lock);
+}
+
+
+static int cvm_oct_rgmii_rml_interrupt(void *dev_id)
+{
+ cvmx_npi_rsl_int_blocks_t rsl_int_blocks;
+ int index;
+ int return_status = FILTER_STRAY;
+
+ rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
+
+ /* Check and see if this interrupt was caused by the GMX0 block */
+ if (rsl_int_blocks.s.gmx0) {
+
+ int interface = 0;
+ /* Loop through every port of this interface */
+ for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
+
+ /* Read the GMX interrupt status bits */
+ cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg;
+ gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
+ gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
+ /* Poll the port if inband status changed */
+ if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) {
+
+ struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
+ if (ifp)
+ cvm_oct_rgmii_poll(ifp);
+ gmx_rx_int_reg.u64 = 0;
+ gmx_rx_int_reg.s.phy_dupx = 1;
+ gmx_rx_int_reg.s.phy_link = 1;
+ gmx_rx_int_reg.s.phy_spd = 1;
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64);
+ return_status = FILTER_HANDLED;
+ }
+ }
+ }
+
+ /* Check and see if this interrupt was caused by the GMX1 block */
+ if (rsl_int_blocks.s.gmx1) {
+
+ int interface = 1;
+ /* Loop through every port of this interface */
+ for (index = 0; index < cvmx_helper_ports_on_interface(interface); index++) {
+
+ /* Read the GMX interrupt status bits */
+ cvmx_gmxx_rxx_int_reg_t gmx_rx_int_reg;
+ gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
+ gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
+ /* Poll the port if inband status changed */
+ if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link || gmx_rx_int_reg.s.phy_spd) {
+
+ struct ifnet *ifp = cvm_oct_device[cvmx_helper_get_ipd_port(interface, index)];
+ if (ifp)
+ cvm_oct_rgmii_poll(ifp);
+ gmx_rx_int_reg.u64 = 0;
+ gmx_rx_int_reg.s.phy_dupx = 1;
+ gmx_rx_int_reg.s.phy_link = 1;
+ gmx_rx_int_reg.s.phy_spd = 1;
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), gmx_rx_int_reg.u64);
+ return_status = FILTER_HANDLED;
+ }
+ }
+ }
+ return return_status;
+}
+
+
+static int cvm_oct_rgmii_open(struct ifnet *ifp)
+{
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+ cvmx_helper_link_info_t link_info;
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 1;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+
+ if (!octeon_is_simulation()) {
+ link_info = cvmx_helper_link_get(priv->port);
+ if (!link_info.s.link_up)
+ if_link_state_change(ifp, LINK_STATE_DOWN);
+ else
+ if_link_state_change(ifp, LINK_STATE_UP);
+ }
+
+ return 0;
+}
+
+static int cvm_oct_rgmii_stop(struct ifnet *ifp)
+{
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 0;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+ return 0;
+}
+
+int cvm_oct_rgmii_init(struct ifnet *ifp)
+{
+ struct octebus_softc *sc;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int error;
+ int rid;
+
+ cvm_oct_common_init(ifp);
+ priv->open = cvm_oct_rgmii_open;
+ priv->stop = cvm_oct_rgmii_stop;
+ priv->stop(ifp);
+
+ /* Due to GMX errata in CN3XXX series chips, it is necessary to take the
+ link down immediately whne the PHY changes state. In order to do this
+ we call the poll function every time the RGMII inband status changes.
+ This may cause problems if the PHY doesn't implement inband status
+ properly */
+ if (number_rgmii_ports == 0) {
+ sc = device_get_softc(device_get_parent(priv->dev));
+
+ rid = 0;
+ sc->sc_rgmii_irq = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ,
+ &rid, CVMX_IRQ_RML,
+ CVMX_IRQ_RML, 1,
+ RF_ACTIVE);
+ if (sc->sc_rgmii_irq == NULL) {
+ device_printf(sc->sc_dev, "could not allocate RGMII irq");
+ return ENXIO;
+ }
+
+ error = bus_setup_intr(sc->sc_dev, sc->sc_rgmii_irq,
+ INTR_TYPE_NET | INTR_MPSAFE,
+ cvm_oct_rgmii_rml_interrupt, NULL,
+ &number_rgmii_ports, NULL);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not setup RGMII irq");
+ return error;
+ }
+ }
+ number_rgmii_ports++;
+
+ /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
+ a RGMII port */
+ if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
+ (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
+
+ if (!octeon_is_simulation()) {
+
+ cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ /* Enable interrupts on inband status changes for this port */
+ gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
+ gmx_rx_int_en.s.phy_dupx = 1;
+ gmx_rx_int_en.s.phy_link = 1;
+ gmx_rx_int_en.s.phy_spd = 1;
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64);
+ priv->poll = cvm_oct_rgmii_poll;
+ }
+ }
+
+ return 0;
+}
+
+void cvm_oct_rgmii_uninit(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ cvm_oct_common_uninit(ifp);
+
+ /* Only true RGMII ports need to be polled. In GMII mode, port 0 is really
+ a RGMII port */
+ if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII) && (priv->port == 0)) ||
+ (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
+
+ if (!octeon_is_simulation()) {
+
+ cvmx_gmxx_rxx_int_en_t gmx_rx_int_en;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ /* Disable interrupts on inband status changes for this port */
+ gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(index, interface));
+ gmx_rx_int_en.s.phy_dupx = 0;
+ gmx_rx_int_en.s.phy_link = 0;
+ gmx_rx_int_en.s.phy_spd = 0;
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface), gmx_rx_int_en.u64);
+ }
+ }
+
+ /* Remove the interrupt handler when the last port is removed */
+ number_rgmii_ports--;
+ if (number_rgmii_ports == 0)
+ panic("%s: need to implement IRQ release.", __func__);
+}
+
diff --git a/sys/mips/cavium/octe/ethernet-rx.c b/sys/mips/cavium/octe/ethernet-rx.c
new file mode 100644
index 000000000000..2f8f0a8fbae0
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-rx.c
@@ -0,0 +1,416 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
+#include <sys/smp.h>
+#include <sys/taskqueue.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+extern int pow_receive_group;
+extern struct ifnet *cvm_oct_device[];
+
+static struct task cvm_oct_task;
+static struct taskqueue *cvm_oct_taskq;
+
+/**
+ * Interrupt handler. The interrupt occurs whenever the POW
+ * transitions from 0->1 packets in our group.
+ *
+ * @param cpl
+ * @param dev_id
+ * @param regs
+ * @return
+ */
+int cvm_oct_do_interrupt(void *dev_id)
+{
+ /* Acknowledge the interrupt */
+ if (INTERRUPT_LIMIT)
+ cvmx_write_csr(CVMX_POW_WQ_INT, 1<<pow_receive_group);
+ else
+ cvmx_write_csr(CVMX_POW_WQ_INT, 0x10001<<pow_receive_group);
+ taskqueue_enqueue(cvm_oct_taskq, &cvm_oct_task);
+ return FILTER_HANDLED;
+}
+
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/**
+ * This is called when the kernel needs to manually poll the
+ * device. For Octeon, this is simply calling the interrupt
+ * handler. We actually poll all the devices, not just the
+ * one supplied.
+ *
+ * @param dev Device to poll. Unused
+ */
+void cvm_oct_poll_controller(struct ifnet *ifp)
+{
+ taskqueue_enqueue(cvm_oct_taskq, &cvm_oct_task);
+}
+#endif
+
+/**
+ * This is called on receive errors, and determines if the packet
+ * can be dropped early-on in cvm_oct_tasklet_rx().
+ *
+ * @param work Work queue entry pointing to the packet.
+ * @return Non-zero if the packet can be dropped, zero otherwise.
+ */
+static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work)
+{
+ if ((work->word2.snoip.err_code == 10) && (work->len <= 64)) {
+ /* Ignore length errors on min size packets. Some equipment
+ incorrectly pads packets to 64+4FCS instead of 60+4FCS.
+ Note these packets still get counted as frame errors. */
+ } else
+ if (USE_10MBPS_PREAMBLE_WORKAROUND && ((work->word2.snoip.err_code == 5) || (work->word2.snoip.err_code == 7))) {
+
+ /* We received a packet with either an alignment error or a
+ FCS error. This may be signalling that we are running
+ 10Mbps with GMXX_RXX_FRM_CTL[PRE_CHK} off. If this is the
+ case we need to parse the packet to determine if we can
+ remove a non spec preamble and generate a correct packet */
+ int interface = cvmx_helper_get_interface_num(work->ipprt);
+ int index = cvmx_helper_get_interface_index_num(work->ipprt);
+ cvmx_gmxx_rxx_frm_ctl_t gmxx_rxx_frm_ctl;
+ gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
+ if (gmxx_rxx_frm_ctl.s.pre_chk == 0) {
+
+ uint8_t *ptr = cvmx_phys_to_ptr(work->packet_ptr.s.addr);
+ int i = 0;
+
+ while (i < work->len-1) {
+ if (*ptr != 0x55)
+ break;
+ ptr++;
+ i++;
+ }
+
+ if (*ptr == 0xd5) {
+ /*
+ DEBUGPRINT("Port %d received 0xd5 preamble\n", work->ipprt);
+ */
+ work->packet_ptr.s.addr += i+1;
+ work->len -= i+5;
+ } else
+ if ((*ptr & 0xf) == 0xd) {
+ /*
+ DEBUGPRINT("Port %d received 0x?d preamble\n", work->ipprt);
+ */
+ work->packet_ptr.s.addr += i;
+ work->len -= i+4;
+ for (i = 0; i < work->len; i++) {
+ *ptr = ((*ptr&0xf0)>>4) | ((*(ptr+1)&0xf)<<4);
+ ptr++;
+ }
+ } else {
+ DEBUGPRINT("Port %d unknown preamble, packet dropped\n", work->ipprt);
+ /*
+ cvmx_helper_dump_packet(work);
+ */
+ cvm_oct_free_work(work);
+ return 1;
+ }
+ }
+ } else {
+ DEBUGPRINT("Port %d receive error code %d, packet dropped\n", work->ipprt, work->word2.snoip.err_code);
+ cvm_oct_free_work(work);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Tasklet function that is scheduled on a core when an interrupt occurs.
+ *
+ * @param unused
+ */
+void cvm_oct_tasklet_rx(void *context, int pending)
+{
+ int coreid;
+ uint64_t old_group_mask;
+ uint64_t old_scratch;
+ int rx_count = 0;
+ int number_to_free;
+ int num_freed;
+ int packet_not_copied;
+
+ sched_pin();
+ coreid = cvmx_get_core_num();
+
+ /* Prefetch cvm_oct_device since we know we need it soon */
+ CVMX_PREFETCH(cvm_oct_device, 0);
+
+ if (USE_ASYNC_IOBDMA) {
+ /* Save scratch in case userspace is using it */
+ CVMX_SYNCIOBDMA;
+ old_scratch = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
+ }
+
+ /* Only allow work for our group (and preserve priorities) */
+ old_group_mask = cvmx_read_csr(CVMX_POW_PP_GRP_MSKX(coreid));
+ cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid),
+ (old_group_mask & ~0xFFFFull) | 1<<pow_receive_group);
+
+ if (USE_ASYNC_IOBDMA)
+ cvmx_pow_work_request_async(CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT);
+
+ while (1) {
+ struct mbuf *m = NULL;
+ int mbuf_in_hw;
+ cvmx_wqe_t *work;
+
+ if (USE_ASYNC_IOBDMA) {
+ work = cvmx_pow_work_response_async(CVMX_SCR_SCRATCH);
+ } else {
+ if ((INTERRUPT_LIMIT == 0) || (rx_count < MAX_RX_PACKETS))
+ work = cvmx_pow_work_request_sync(CVMX_POW_NO_WAIT);
+ else
+ work = NULL;
+ }
+ CVMX_PREFETCH(work, 0);
+ if (work == NULL)
+ break;
+
+ /* Limit each core to processing MAX_RX_PACKETS packets without a break.
+ This way the RX can't starve the TX task. */
+ if (USE_ASYNC_IOBDMA) {
+
+ if ((INTERRUPT_LIMIT == 0) || (rx_count < MAX_RX_PACKETS))
+ cvmx_pow_work_request_async_nocheck(CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT);
+ else {
+ cvmx_scratch_write64(CVMX_SCR_SCRATCH, 0x8000000000000000ull);
+ cvmx_pow_tag_sw_null_nocheck();
+ }
+ }
+
+ mbuf_in_hw = USE_MBUFS_IN_HW && work->word2.s.bufs == 1;
+ if ((mbuf_in_hw)) {
+ m = *(struct mbuf **)(cvm_oct_get_buffer_ptr(work->packet_ptr) - sizeof(void *));
+ CVMX_PREFETCH(m, offsetof(struct mbuf, m_data));
+ CVMX_PREFETCH(m, offsetof(struct mbuf, m_pkthdr));
+ }
+ CVMX_PREFETCH(cvm_oct_device[work->ipprt], 0);
+ //CVMX_PREFETCH(m, 0);
+
+
+ rx_count++;
+ /* Immediately throw away all packets with receive errors */
+ if ((work->word2.snoip.rcv_error)) {
+ if (cvm_oct_check_rcv_error(work))
+ continue;
+ }
+
+ /* We can only use the zero copy path if mbufs are in the FPA pool
+ and the packet fits in a single buffer */
+ if ((mbuf_in_hw)) {
+ CVMX_PREFETCH(m->m_data, 0);
+
+ m->m_pkthdr.len = m->m_len = work->len;
+
+ packet_not_copied = 1;
+
+ /*
+ * Adjust the data pointer based on the offset
+ * of the packet within the buffer.
+ */
+ m->m_data += (work->packet_ptr.s.back << 7) + (work->packet_ptr.s.addr & 0x7f);
+ } else {
+
+ /* We have to copy the packet. First allocate an
+ mbuf for it */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ DEBUGPRINT("Port %d failed to allocate mbuf, packet dropped\n", work->ipprt);
+ cvm_oct_free_work(work);
+ continue;
+ }
+
+ /* Check if we've received a packet that was entirely
+ stored in the work entry. This is untested */
+ if ((work->word2.s.bufs == 0)) {
+ uint8_t *ptr = work->packet_data;
+
+ if (cvmx_likely(!work->word2.s.not_IP)) {
+ /* The beginning of the packet moves
+ for IP packets */
+ if (work->word2.s.is_v6)
+ ptr += 2;
+ else
+ ptr += 6;
+ }
+ panic("%s: not yet implemented; copy in small packet.", __func__);
+ /* No packet buffers to free */
+ } else {
+ int segments = work->word2.s.bufs;
+ cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
+ int len = work->len;
+
+ while (segments--) {
+ cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
+ /* Octeon Errata PKI-100: The segment
+ size is wrong. Until it is fixed,
+ calculate the segment size based on
+ the packet pool buffer size. When
+ it is fixed, the following line
+ should be replaced with this one:
+ int segment_size = segment_ptr.s.size; */
+ int segment_size = CVMX_FPA_PACKET_POOL_SIZE - (segment_ptr.s.addr - (((segment_ptr.s.addr >> 7) - segment_ptr.s.back) << 7));
+ /* Don't copy more than what is left
+ in the packet */
+ if (segment_size > len)
+ segment_size = len;
+ /* Copy the data into the packet */
+ panic("%s: not yet implemented; copy in packet segments.", __func__);
+#if 0
+ memcpy(m_put(m, segment_size), cvmx_phys_to_ptr(segment_ptr.s.addr), segment_size);
+#endif
+ /* Reduce the amount of bytes left
+ to copy */
+ len -= segment_size;
+ segment_ptr = next_ptr;
+ }
+ }
+ packet_not_copied = 0;
+ }
+
+ if (((work->ipprt < TOTAL_NUMBER_OF_PORTS) &&
+ cvm_oct_device[work->ipprt])) {
+ struct ifnet *ifp = cvm_oct_device[work->ipprt];
+
+ /* Only accept packets for devices
+ that are currently up */
+ if ((ifp->if_flags & IFF_UP)) {
+ m->m_pkthdr.rcvif = ifp;
+
+ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) {
+ if ((work->word2.s.not_IP || work->word2.s.IP_exc || work->word2.s.L4_error))
+ m->m_pkthdr.csum_flags = 0; /* XXX */
+ else {
+ m->m_pkthdr.csum_flags = CSUM_IP_CHECKED | CSUM_IP_VALID | CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+ } else {
+ m->m_pkthdr.csum_flags = 0; /* XXX */
+ }
+
+ ifp->if_ipackets++;
+
+ (*ifp->if_input)(ifp, m);
+ } else {
+ /* Drop any packet received for a device that isn't up */
+ /*
+ DEBUGPRINT("%s: Device not up, packet dropped\n",
+ if_name(ifp));
+ */
+ m_freem(m);
+ }
+ } else {
+ /* Drop any packet received for a device that
+ doesn't exist */
+ DEBUGPRINT("Port %d not controlled by Linux, packet dropped\n", work->ipprt);
+ m_freem(m);
+ }
+
+ /* Check to see if the mbuf and work share
+ the same packet buffer */
+ if (USE_MBUFS_IN_HW && (packet_not_copied)) {
+ /* This buffer needs to be replaced, increment
+ the number of buffers we need to free by one */
+ cvmx_fau_atomic_add32(
+ FAU_NUM_PACKET_BUFFERS_TO_FREE, 1);
+
+ cvmx_fpa_free(work, CVMX_FPA_WQE_POOL,
+ DONT_WRITEBACK(1));
+ } else
+ cvm_oct_free_work(work);
+ }
+
+ /* Restore the original POW group mask */
+ cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid), old_group_mask);
+ if (USE_ASYNC_IOBDMA) {
+ /* Restore the scratch area */
+ cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
+ }
+
+ if (USE_MBUFS_IN_HW) {
+ /* Refill the packet buffer pool */
+ number_to_free =
+ cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
+
+ if (number_to_free > 0) {
+ cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE,
+ -number_to_free);
+ num_freed =
+ cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL,
+ CVMX_FPA_PACKET_POOL_SIZE,
+ number_to_free);
+ if (num_freed != number_to_free) {
+ cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE,
+ number_to_free - num_freed);
+ }
+ }
+ }
+ sched_unpin();
+}
+
+
+
+void cvm_oct_rx_initialize(void)
+{
+ TASK_INIT(&cvm_oct_task, 0, cvm_oct_tasklet_rx, NULL);
+
+ cvm_oct_taskq = taskqueue_create_fast("oct_rx", M_NOWAIT,
+ taskqueue_thread_enqueue,
+ &cvm_oct_taskq);
+ taskqueue_start_threads(&cvm_oct_taskq, min(mp_ncpus, MAXCPU),
+ PI_NET, "octe taskq");
+}
+
+void cvm_oct_rx_shutdown(void)
+{
+ panic("%s: not yet implemented.", __func__);
+}
+
diff --git a/sys/mips/cavium/octe/ethernet-rx.h b/sys/mips/cavium/octe/ethernet-rx.h
new file mode 100644
index 000000000000..735d08724328
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-rx.h
@@ -0,0 +1,37 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+int cvm_oct_do_interrupt(void *dev_id);
+void cvm_oct_poll_controller(struct ifnet *ifp);
+void cvm_oct_tasklet_rx(void *context, int pending);
+
+void cvm_oct_rx_initialize(void);
+void cvm_oct_rx_shutdown(void);
+
diff --git a/sys/mips/cavium/octe/ethernet-sgmii.c b/sys/mips/cavium/octe/ethernet-sgmii.c
new file mode 100644
index 000000000000..17e6be712bc4
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-sgmii.c
@@ -0,0 +1,117 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+extern int octeon_is_simulation(void);
+
+static int cvm_oct_sgmii_open(struct ifnet *ifp)
+{
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+ cvmx_helper_link_info_t link_info;
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 1;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+
+ if (!octeon_is_simulation()) {
+ link_info = cvmx_helper_link_get(priv->port);
+ if (!link_info.s.link_up)
+ if_link_state_change(ifp, LINK_STATE_DOWN);
+ else
+ if_link_state_change(ifp, LINK_STATE_UP);
+ }
+
+ return 0;
+}
+
+static int cvm_oct_sgmii_stop(struct ifnet *ifp)
+{
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 0;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+ return 0;
+}
+
+static void cvm_oct_sgmii_poll(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ cvmx_helper_link_info_t link_info;
+
+ link_info = cvmx_helper_link_get(priv->port);
+ if (link_info.u64 == priv->link_info)
+ return;
+
+ link_info = cvmx_helper_link_autoconf(priv->port);
+ priv->link_info = link_info.u64;
+ priv->need_link_update = 1;
+}
+
+int cvm_oct_sgmii_init(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ cvm_oct_common_init(ifp);
+ priv->open = cvm_oct_sgmii_open;
+ priv->stop = cvm_oct_sgmii_stop;
+ priv->stop(ifp);
+ if (!octeon_is_simulation())
+ priv->poll = cvm_oct_sgmii_poll;
+
+ /* FIXME: Need autoneg logic */
+ return 0;
+}
+
+void cvm_oct_sgmii_uninit(struct ifnet *ifp)
+{
+ cvm_oct_common_uninit(ifp);
+}
+
diff --git a/sys/mips/cavium/octe/ethernet-spi.c b/sys/mips/cavium/octe/ethernet-spi.c
new file mode 100644
index 000000000000..100f642ef195
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-spi.c
@@ -0,0 +1,309 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+#include "octebusvar.h"
+
+static int number_spi_ports;
+static int need_retrain[2] = {0, 0};
+
+static int cvm_oct_spi_rml_interrupt(void *dev_id)
+{
+ int return_status = FILTER_STRAY;
+ cvmx_npi_rsl_int_blocks_t rsl_int_blocks;
+
+ /* Check and see if this interrupt was caused by the GMX block */
+ rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
+ if (rsl_int_blocks.s.spx1) { /* 19 - SPX1_INT_REG & STX1_INT_REG */
+
+ cvmx_spxx_int_reg_t spx_int_reg;
+ cvmx_stxx_int_reg_t stx_int_reg;
+
+ spx_int_reg.u64 = cvmx_read_csr(CVMX_SPXX_INT_REG(1));
+ cvmx_write_csr(CVMX_SPXX_INT_REG(1), spx_int_reg.u64);
+ if (!need_retrain[1]) {
+
+ spx_int_reg.u64 &= cvmx_read_csr(CVMX_SPXX_INT_MSK(1));
+ if (spx_int_reg.s.spf)
+ printf("SPI1: SRX Spi4 interface down\n");
+ if (spx_int_reg.s.calerr)
+ printf("SPI1: SRX Spi4 Calendar table parity error\n");
+ if (spx_int_reg.s.syncerr)
+ printf("SPI1: SRX Consecutive Spi4 DIP4 errors have exceeded SPX_ERR_CTL[ERRCNT]\n");
+ if (spx_int_reg.s.diperr)
+ printf("SPI1: SRX Spi4 DIP4 error\n");
+ if (spx_int_reg.s.tpaovr)
+ printf("SPI1: SRX Selected port has hit TPA overflow\n");
+ if (spx_int_reg.s.rsverr)
+ printf("SPI1: SRX Spi4 reserved control word detected\n");
+ if (spx_int_reg.s.drwnng)
+ printf("SPI1: SRX Spi4 receive FIFO drowning/overflow\n");
+ if (spx_int_reg.s.clserr)
+ printf("SPI1: SRX Spi4 packet closed on non-16B alignment without EOP\n");
+ if (spx_int_reg.s.spiovr)
+ printf("SPI1: SRX Spi4 async FIFO overflow\n");
+ if (spx_int_reg.s.abnorm)
+ printf("SPI1: SRX Abnormal packet termination (ERR bit)\n");
+ if (spx_int_reg.s.prtnxa)
+ printf("SPI1: SRX Port out of range\n");
+ }
+
+ stx_int_reg.u64 = cvmx_read_csr(CVMX_STXX_INT_REG(1));
+ cvmx_write_csr(CVMX_STXX_INT_REG(1), stx_int_reg.u64);
+ if (!need_retrain[1]) {
+
+ stx_int_reg.u64 &= cvmx_read_csr(CVMX_STXX_INT_MSK(1));
+ if (stx_int_reg.s.syncerr)
+ printf("SPI1: STX Interface encountered a fatal error\n");
+ if (stx_int_reg.s.frmerr)
+ printf("SPI1: STX FRMCNT has exceeded STX_DIP_CNT[MAXFRM]\n");
+ if (stx_int_reg.s.unxfrm)
+ printf("SPI1: STX Unexpected framing sequence\n");
+ if (stx_int_reg.s.nosync)
+ printf("SPI1: STX ERRCNT has exceeded STX_DIP_CNT[MAXDIP]\n");
+ if (stx_int_reg.s.diperr)
+ printf("SPI1: STX DIP2 error on the Spi4 Status channel\n");
+ if (stx_int_reg.s.datovr)
+ printf("SPI1: STX Spi4 FIFO overflow error\n");
+ if (stx_int_reg.s.ovrbst)
+ printf("SPI1: STX Transmit packet burst too big\n");
+ if (stx_int_reg.s.calpar1)
+ printf("SPI1: STX Calendar Table Parity Error Bank1\n");
+ if (stx_int_reg.s.calpar0)
+ printf("SPI1: STX Calendar Table Parity Error Bank0\n");
+ }
+
+ cvmx_write_csr(CVMX_SPXX_INT_MSK(1), 0);
+ cvmx_write_csr(CVMX_STXX_INT_MSK(1), 0);
+ need_retrain[1] = 1;
+ return_status = FILTER_HANDLED;
+ }
+
+ if (rsl_int_blocks.s.spx0) { /* 18 - SPX0_INT_REG & STX0_INT_REG */
+ cvmx_spxx_int_reg_t spx_int_reg;
+ cvmx_stxx_int_reg_t stx_int_reg;
+
+ spx_int_reg.u64 = cvmx_read_csr(CVMX_SPXX_INT_REG(0));
+ cvmx_write_csr(CVMX_SPXX_INT_REG(0), spx_int_reg.u64);
+ if (!need_retrain[0]) {
+
+ spx_int_reg.u64 &= cvmx_read_csr(CVMX_SPXX_INT_MSK(0));
+ if (spx_int_reg.s.spf)
+ printf("SPI0: SRX Spi4 interface down\n");
+ if (spx_int_reg.s.calerr)
+ printf("SPI0: SRX Spi4 Calendar table parity error\n");
+ if (spx_int_reg.s.syncerr)
+ printf("SPI0: SRX Consecutive Spi4 DIP4 errors have exceeded SPX_ERR_CTL[ERRCNT]\n");
+ if (spx_int_reg.s.diperr)
+ printf("SPI0: SRX Spi4 DIP4 error\n");
+ if (spx_int_reg.s.tpaovr)
+ printf("SPI0: SRX Selected port has hit TPA overflow\n");
+ if (spx_int_reg.s.rsverr)
+ printf("SPI0: SRX Spi4 reserved control word detected\n");
+ if (spx_int_reg.s.drwnng)
+ printf("SPI0: SRX Spi4 receive FIFO drowning/overflow\n");
+ if (spx_int_reg.s.clserr)
+ printf("SPI0: SRX Spi4 packet closed on non-16B alignment without EOP\n");
+ if (spx_int_reg.s.spiovr)
+ printf("SPI0: SRX Spi4 async FIFO overflow\n");
+ if (spx_int_reg.s.abnorm)
+ printf("SPI0: SRX Abnormal packet termination (ERR bit)\n");
+ if (spx_int_reg.s.prtnxa)
+ printf("SPI0: SRX Port out of range\n");
+ }
+
+ stx_int_reg.u64 = cvmx_read_csr(CVMX_STXX_INT_REG(0));
+ cvmx_write_csr(CVMX_STXX_INT_REG(0), stx_int_reg.u64);
+ if (!need_retrain[0]) {
+
+ stx_int_reg.u64 &= cvmx_read_csr(CVMX_STXX_INT_MSK(0));
+ if (stx_int_reg.s.syncerr)
+ printf("SPI0: STX Interface encountered a fatal error\n");
+ if (stx_int_reg.s.frmerr)
+ printf("SPI0: STX FRMCNT has exceeded STX_DIP_CNT[MAXFRM]\n");
+ if (stx_int_reg.s.unxfrm)
+ printf("SPI0: STX Unexpected framing sequence\n");
+ if (stx_int_reg.s.nosync)
+ printf("SPI0: STX ERRCNT has exceeded STX_DIP_CNT[MAXDIP]\n");
+ if (stx_int_reg.s.diperr)
+ printf("SPI0: STX DIP2 error on the Spi4 Status channel\n");
+ if (stx_int_reg.s.datovr)
+ printf("SPI0: STX Spi4 FIFO overflow error\n");
+ if (stx_int_reg.s.ovrbst)
+ printf("SPI0: STX Transmit packet burst too big\n");
+ if (stx_int_reg.s.calpar1)
+ printf("SPI0: STX Calendar Table Parity Error Bank1\n");
+ if (stx_int_reg.s.calpar0)
+ printf("SPI0: STX Calendar Table Parity Error Bank0\n");
+ }
+
+ cvmx_write_csr(CVMX_SPXX_INT_MSK(0), 0);
+ cvmx_write_csr(CVMX_STXX_INT_MSK(0), 0);
+ need_retrain[0] = 1;
+ return_status = FILTER_HANDLED;
+ }
+
+ return return_status;
+}
+
+static void cvm_oct_spi_enable_error_reporting(int interface)
+{
+ cvmx_spxx_int_msk_t spxx_int_msk;
+ cvmx_stxx_int_msk_t stxx_int_msk;
+
+ spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface));
+ spxx_int_msk.s.calerr = 1;
+ spxx_int_msk.s.syncerr = 1;
+ spxx_int_msk.s.diperr = 1;
+ spxx_int_msk.s.tpaovr = 1;
+ spxx_int_msk.s.rsverr = 1;
+ spxx_int_msk.s.drwnng = 1;
+ spxx_int_msk.s.clserr = 1;
+ spxx_int_msk.s.spiovr = 1;
+ spxx_int_msk.s.abnorm = 1;
+ spxx_int_msk.s.prtnxa = 1;
+ cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64);
+
+ stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface));
+ stxx_int_msk.s.frmerr = 1;
+ stxx_int_msk.s.unxfrm = 1;
+ stxx_int_msk.s.nosync = 1;
+ stxx_int_msk.s.diperr = 1;
+ stxx_int_msk.s.datovr = 1;
+ stxx_int_msk.s.ovrbst = 1;
+ stxx_int_msk.s.calpar1 = 1;
+ stxx_int_msk.s.calpar0 = 1;
+ cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64);
+}
+
+static void cvm_oct_spi_poll(struct ifnet *ifp)
+{
+ static int spi4000_port;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface;
+
+ for (interface = 0; interface < 2; interface++) {
+
+ if ((priv->port == interface*16) && need_retrain[interface]) {
+
+ if (cvmx_spi_restart_interface(interface, CVMX_SPI_MODE_DUPLEX, 10) == 0) {
+ need_retrain[interface] = 0;
+ cvm_oct_spi_enable_error_reporting(interface);
+ }
+ }
+
+ /* The SPI4000 TWSI interface is very slow. In order not to
+ bring the system to a crawl, we only poll a single port
+ every second. This means negotiation speed changes
+ take up to 10 seconds, but at least we don't waste
+ absurd amounts of time waiting for TWSI */
+ if (priv->port == spi4000_port) {
+ /* This function does nothing if it is called on an
+ interface without a SPI4000 */
+ cvmx_spi4000_check_speed(interface, priv->port);
+ /* Normal ordering increments. By decrementing
+ we only match once per iteration */
+ spi4000_port--;
+ if (spi4000_port < 0)
+ spi4000_port = 10;
+ }
+ }
+}
+
+
+int cvm_oct_spi_init(struct ifnet *ifp)
+{
+ struct octebus_softc *sc;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int error;
+ int rid;
+
+ if (number_spi_ports == 0) {
+ sc = device_get_softc(device_get_parent(priv->dev));
+
+ rid = 0;
+ sc->sc_spi_irq = bus_alloc_resource(sc->sc_dev, SYS_RES_IRQ,
+ &rid, CVMX_IRQ_RML,
+ CVMX_IRQ_RML, 1,
+ RF_ACTIVE);
+ if (sc->sc_spi_irq == NULL) {
+ device_printf(sc->sc_dev, "could not allocate SPI irq");
+ return ENXIO;
+ }
+
+ error = bus_setup_intr(sc->sc_dev, sc->sc_spi_irq,
+ INTR_TYPE_NET | INTR_MPSAFE,
+ cvm_oct_spi_rml_interrupt, NULL,
+ &number_spi_ports, NULL);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not setup SPI irq");
+ return error;
+ }
+ }
+ number_spi_ports++;
+
+ if ((priv->port == 0) || (priv->port == 16)) {
+ cvm_oct_spi_enable_error_reporting(INTERFACE(priv->port));
+ priv->poll = cvm_oct_spi_poll;
+ }
+ cvm_oct_common_init(ifp);
+ return 0;
+}
+
+void cvm_oct_spi_uninit(struct ifnet *ifp)
+{
+ int interface;
+
+ cvm_oct_common_uninit(ifp);
+ number_spi_ports--;
+ if (number_spi_ports == 0) {
+ for (interface = 0; interface < 2; interface++) {
+ cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0);
+ cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0);
+ }
+ panic("%s: IRQ release not yet implemented.", __func__);
+ }
+}
diff --git a/sys/mips/cavium/octe/ethernet-tx.c b/sys/mips/cavium/octe/ethernet-tx.c
new file mode 100644
index 000000000000..8866f1203446
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-tx.c
@@ -0,0 +1,402 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <net/bpf.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+/* You can define GET_MBUF_QOS() to override how the mbuf output function
+ determines which output queue is used. The default implementation
+ always uses the base queue for the port. If, for example, you wanted
+ to use the m->priority fieid, define GET_MBUF_QOS as:
+ #define GET_MBUF_QOS(m) ((m)->priority) */
+#ifndef GET_MBUF_QOS
+ #define GET_MBUF_QOS(m) 0
+#endif
+
+extern int pow_send_group;
+
+
+/**
+ * Packet transmit
+ *
+ * @param m Packet to send
+ * @param dev Device info structure
+ * @return Always returns zero
+ */
+int cvm_oct_xmit(struct mbuf *m, struct ifnet *ifp)
+{
+ cvmx_pko_command_word0_t pko_command;
+ cvmx_buf_ptr_t hw_buffer;
+ uint64_t old_scratch;
+ uint64_t old_scratch2;
+ int dropped;
+ int qos;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int32_t in_use;
+ int32_t buffers_to_free;
+ cvmx_wqe_t *work;
+
+ /* Prefetch the private data structure.
+ It is larger that one cache line */
+ CVMX_PREFETCH(priv, 0);
+
+ /* Start off assuming no drop */
+ dropped = 0;
+
+ /* The check on CVMX_PKO_QUEUES_PER_PORT_* is designed to completely
+ remove "qos" in the event neither interface supports multiple queues
+ per port */
+ if ((CVMX_PKO_QUEUES_PER_PORT_INTERFACE0 > 1) ||
+ (CVMX_PKO_QUEUES_PER_PORT_INTERFACE1 > 1)) {
+ qos = GET_MBUF_QOS(m);
+ if (qos <= 0)
+ qos = 0;
+ else if (qos >= cvmx_pko_get_num_queues(priv->port))
+ qos = 0;
+ } else
+ qos = 0;
+
+ if (USE_ASYNC_IOBDMA) {
+ /* Save scratch in case userspace is using it */
+ CVMX_SYNCIOBDMA;
+ old_scratch = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
+ old_scratch2 = cvmx_scratch_read64(CVMX_SCR_SCRATCH+8);
+
+ /* Assume we're going to be able t osend this packet. Fetch and increment
+ the number of pending packets for output */
+ cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH+8, FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
+ cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH, priv->fau+qos*4, 1);
+ }
+
+ /* The CN3XXX series of parts has an errata (GMX-401) which causes the
+ GMX block to hang if a collision occurs towards the end of a
+ <68 byte packet. As a workaround for this, we pad packets to be
+ 68 bytes whenever we are in half duplex mode. We don't handle
+ the case of having a small packet but no room to add the padding.
+ The kernel should always give us at least a cache line */
+ if (__predict_false(m->m_pkthdr.len < 64) && OCTEON_IS_MODEL(OCTEON_CN3XXX)) {
+ cvmx_gmxx_prtx_cfg_t gmx_prt_cfg;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ if (interface < 2) {
+ /* We only need to pad packet in half duplex mode */
+ gmx_prt_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ if (gmx_prt_cfg.s.duplex == 0) {
+ static uint8_t pad[64];
+
+ if (!m_append(m, sizeof pad - m->m_pkthdr.len, pad))
+ printf("%s: unable to padd small packet.", __func__);
+ }
+ }
+ }
+
+ /*
+ * If the packet is not fragmented.
+ */
+ if (m->m_pkthdr.len == m->m_len) {
+ /* Build the PKO buffer pointer */
+ hw_buffer.u64 = 0;
+ hw_buffer.s.addr = cvmx_ptr_to_phys(m->m_data);
+ hw_buffer.s.pool = 0;
+ hw_buffer.s.size = m->m_len;
+
+ /* Build the PKO command */
+ pko_command.u64 = 0;
+ pko_command.s.segs = 1;
+
+ work = NULL;
+ } else {
+ struct mbuf *n;
+ unsigned segs;
+ uint64_t *gp;
+
+ /*
+ * The packet is fragmented, we need to send a list of segments
+ * in memory we borrow from the WQE pool.
+ */
+ work = cvmx_fpa_alloc(CVMX_FPA_WQE_POOL);
+ gp = (uint64_t *)work;
+
+ segs = 0;
+ for (n = m; n != NULL; n = n->m_next) {
+ if (segs == CVMX_FPA_WQE_POOL_SIZE / sizeof (uint64_t))
+ panic("%s: too many segments in packet; call m_collapse().", __func__);
+
+ /* Build the PKO buffer pointer */
+ hw_buffer.u64 = 0;
+ hw_buffer.s.addr = cvmx_ptr_to_phys(n->m_data);
+ hw_buffer.s.pool = 0;
+ hw_buffer.s.size = n->m_len;
+
+ *gp++ = hw_buffer.u64;
+ segs++;
+ }
+
+ /* Build the PKO buffer gather list pointer */
+ hw_buffer.u64 = 0;
+ hw_buffer.s.addr = cvmx_ptr_to_phys(work);
+ hw_buffer.s.pool = CVMX_FPA_WQE_POOL;
+ hw_buffer.s.size = segs;
+
+ /* Build the PKO command */
+ pko_command.u64 = 0;
+ pko_command.s.segs = segs;
+ pko_command.s.gather = 1;
+ }
+
+ /* Finish building the PKO command */
+ pko_command.s.n2 = 1; /* Don't pollute L2 with the outgoing packet */
+ pko_command.s.dontfree = 1;
+ pko_command.s.reg0 = priv->fau+qos*4;
+ pko_command.s.reg0 = priv->fau+qos*4;
+ pko_command.s.total_bytes = m->m_pkthdr.len;
+ pko_command.s.size0 = CVMX_FAU_OP_SIZE_32;
+ pko_command.s.subone0 = 1;
+
+ /* Check if we can use the hardware checksumming */
+ if (USE_HW_TCPUDP_CHECKSUM &&
+ (m->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP)) != 0) {
+ /* Use hardware checksum calc */
+ pko_command.s.ipoffp1 = ETHER_HDR_LEN + 1;
+ }
+
+ IF_LOCK(&priv->tx_free_queue[qos]);
+ if (USE_ASYNC_IOBDMA) {
+ /* Get the number of mbufs in use by the hardware */
+ CVMX_SYNCIOBDMA;
+ in_use = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
+ buffers_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH+8);
+ } else {
+ /* Get the number of mbufs in use by the hardware */
+ in_use = cvmx_fau_fetch_and_add32(priv->fau+qos*4, 1);
+ buffers_to_free = cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
+ }
+
+ cvmx_pko_send_packet_prepare(priv->port, priv->queue + qos, CVMX_PKO_LOCK_CMD_QUEUE);
+
+ /* Drop this packet if we have too many already queued to the HW */
+ if (_IF_QFULL(&priv->tx_free_queue[qos])) {
+ dropped = 1;
+ }
+ /* Send the packet to the output queue */
+ else
+ if (__predict_false(cvmx_pko_send_packet_finish(priv->port, priv->queue + qos, pko_command, hw_buffer, CVMX_PKO_LOCK_CMD_QUEUE))) {
+ DEBUGPRINT("%s: Failed to send the packet\n", if_name(ifp));
+ dropped = 1;
+ }
+
+ if (USE_ASYNC_IOBDMA) {
+ /* Restore the scratch area */
+ cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
+ cvmx_scratch_write64(CVMX_SCR_SCRATCH+8, old_scratch2);
+ }
+
+ if (__predict_false(dropped)) {
+ m_freem(m);
+ cvmx_fau_atomic_add32(priv->fau+qos*4, -1);
+ ifp->if_oerrors++;
+ } else {
+ /* Put this packet on the queue to be freed later */
+ _IF_ENQUEUE(&priv->tx_free_queue[qos], m);
+
+ /* Pass it to any BPF listeners. */
+ ETHER_BPF_MTAP(ifp, m);
+ }
+ if (work != NULL)
+ cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
+
+ /* Free mbufs not in use by the hardware */
+ if (_IF_QLEN(&priv->tx_free_queue[qos]) > in_use) {
+ while (_IF_QLEN(&priv->tx_free_queue[qos]) > in_use) {
+ _IF_DEQUEUE(&priv->tx_free_queue[qos], m);
+ m_freem(m);
+ }
+ }
+ IF_UNLOCK(&priv->tx_free_queue[qos]);
+
+ return dropped;
+}
+
+
+/**
+ * Packet transmit to the POW
+ *
+ * @param m Packet to send
+ * @param dev Device info structure
+ * @return Always returns zero
+ */
+int cvm_oct_xmit_pow(struct mbuf *m, struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ char *packet_buffer;
+ char *copy_location;
+
+ /* Get a work queue entry */
+ cvmx_wqe_t *work = cvmx_fpa_alloc(CVMX_FPA_WQE_POOL);
+ if (__predict_false(work == NULL)) {
+ DEBUGPRINT("%s: Failed to allocate a work queue entry\n", if_name(ifp));
+ ifp->if_oerrors++;
+ m_freem(m);
+ return 0;
+ }
+
+ /* Get a packet buffer */
+ packet_buffer = cvmx_fpa_alloc(CVMX_FPA_PACKET_POOL);
+ if (__predict_false(packet_buffer == NULL)) {
+ DEBUGPRINT("%s: Failed to allocate a packet buffer\n",
+ if_name(ifp));
+ cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
+ ifp->if_oerrors++;
+ m_freem(m);
+ return 0;
+ }
+
+ /* Calculate where we need to copy the data to. We need to leave 8 bytes
+ for a next pointer (unused). We also need to include any configure
+ skip. Then we need to align the IP packet src and dest into the same
+ 64bit word. The below calculation may add a little extra, but that
+ doesn't hurt */
+ copy_location = packet_buffer + sizeof(uint64_t);
+ copy_location += ((CVMX_HELPER_FIRST_MBUFF_SKIP+7)&0xfff8) + 6;
+
+ /* We have to copy the packet since whoever processes this packet
+ will free it to a hardware pool. We can't use the trick of
+ counting outstanding packets like in cvm_oct_xmit */
+ m_copydata(m, 0, m->m_pkthdr.len, copy_location);
+
+ /* Fill in some of the work queue fields. We may need to add more
+ if the software at the other end needs them */
+#if 0
+ work->hw_chksum = m->csum;
+#endif
+ work->len = m->m_pkthdr.len;
+ work->ipprt = priv->port;
+ work->qos = priv->port & 0x7;
+ work->grp = pow_send_group;
+ work->tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
+ work->tag = pow_send_group; /* FIXME */
+ work->word2.u64 = 0; /* Default to zero. Sets of zero later are commented out */
+ work->word2.s.bufs = 1;
+ work->packet_ptr.u64 = 0;
+ work->packet_ptr.s.addr = cvmx_ptr_to_phys(copy_location);
+ work->packet_ptr.s.pool = CVMX_FPA_PACKET_POOL;
+ work->packet_ptr.s.size = CVMX_FPA_PACKET_POOL_SIZE;
+ work->packet_ptr.s.back = (copy_location - packet_buffer)>>7;
+
+ panic("%s: POW transmit not quite implemented yet.", __func__);
+#if 0
+ if (m->protocol == htons(ETH_P_IP)) {
+ work->word2.s.ip_offset = 14;
+ #if 0
+ work->word2.s.vlan_valid = 0; /* FIXME */
+ work->word2.s.vlan_cfi = 0; /* FIXME */
+ work->word2.s.vlan_id = 0; /* FIXME */
+ work->word2.s.dec_ipcomp = 0; /* FIXME */
+ #endif
+ work->word2.s.tcp_or_udp = (ip_hdr(m)->protocol == IP_PROTOCOL_TCP) || (ip_hdr(m)->protocol == IP_PROTOCOL_UDP);
+ #if 0
+ work->word2.s.dec_ipsec = 0; /* FIXME */
+ work->word2.s.is_v6 = 0; /* We only support IPv4 right now */
+ work->word2.s.software = 0; /* Hardware would set to zero */
+ work->word2.s.L4_error = 0; /* No error, packet is internal */
+ #endif
+ work->word2.s.is_frag = !((ip_hdr(m)->frag_off == 0) || (ip_hdr(m)->frag_off == 1<<14));
+ #if 0
+ work->word2.s.IP_exc = 0; /* Assume Linux is sending a good packet */
+ #endif
+ work->word2.s.is_bcast = (m->pkt_type == PACKET_BROADCAST);
+ work->word2.s.is_mcast = (m->pkt_type == PACKET_MULTICAST);
+ #if 0
+ work->word2.s.not_IP = 0; /* This is an IP packet */
+ work->word2.s.rcv_error = 0; /* No error, packet is internal */
+ work->word2.s.err_code = 0; /* No error, packet is internal */
+ #endif
+
+ /* When copying the data, include 4 bytes of the ethernet header to
+ align the same way hardware does */
+ memcpy(work->packet_data, m->data + 10, sizeof(work->packet_data));
+ } else {
+ #if 0
+ work->word2.snoip.vlan_valid = 0; /* FIXME */
+ work->word2.snoip.vlan_cfi = 0; /* FIXME */
+ work->word2.snoip.vlan_id = 0; /* FIXME */
+ work->word2.snoip.software = 0; /* Hardware would set to zero */
+ #endif
+ work->word2.snoip.is_rarp = m->protocol == htons(ETH_P_RARP);
+ work->word2.snoip.is_arp = m->protocol == htons(ETH_P_ARP);
+ work->word2.snoip.is_bcast = (m->pkt_type == PACKET_BROADCAST);
+ work->word2.snoip.is_mcast = (m->pkt_type == PACKET_MULTICAST);
+ work->word2.snoip.not_IP = 1; /* IP was done up above */
+ #if 0
+ work->word2.snoip.rcv_error = 0; /* No error, packet is internal */
+ work->word2.snoip.err_code = 0; /* No error, packet is internal */
+ #endif
+ memcpy(work->packet_data, m->data, sizeof(work->packet_data));
+ }
+#endif
+
+ /* Submit the packet to the POW */
+ cvmx_pow_work_submit(work, work->tag, work->tag_type, work->qos, work->grp);
+ ifp->if_opackets++;
+ ifp->if_obytes += m->m_pkthdr.len;
+ m_freem(m);
+ return 0;
+}
+
+
+/**
+ * This function frees all mbufs that are currenty queued for TX.
+ *
+ * @param dev Device being shutdown
+ */
+void cvm_oct_tx_shutdown(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int qos;
+
+ for (qos = 0; qos < 16; qos++) {
+ IF_DRAIN(&priv->tx_free_queue[qos]);
+ }
+}
diff --git a/sys/mips/cavium/octe/ethernet-tx.h b/sys/mips/cavium/octe/ethernet-tx.h
new file mode 100644
index 000000000000..33c551a716bf
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-tx.h
@@ -0,0 +1,34 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+int cvm_oct_xmit(struct mbuf *m, struct ifnet *ifp);
+int cvm_oct_xmit_pow(struct mbuf *m, struct ifnet *ifp);
+void cvm_oct_tx_shutdown(struct ifnet *ifp);
+
diff --git a/sys/mips/cavium/octe/ethernet-util.h b/sys/mips/cavium/octe/ethernet-util.h
new file mode 100644
index 000000000000..37b75bb2af59
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-util.h
@@ -0,0 +1,84 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+#define DEBUGPRINT(format, ...) printf(format, ##__VA_ARGS__)
+
+/**
+ * Given a packet data address, return a pointer to the
+ * beginning of the packet buffer.
+ *
+ * @param packet_ptr Packet data hardware address
+ * @return Packet buffer pointer
+ */
+static inline char *cvm_oct_get_buffer_ptr(cvmx_buf_ptr_t packet_ptr)
+{
+ return cvmx_phys_to_ptr(((packet_ptr.s.addr >> 7) - packet_ptr.s.back) << 7);
+}
+
+
+/**
+ * Given an IPD/PKO port number, return the logical interface it is
+ * on.
+ *
+ * @param ipd_port Port to check
+ *
+ * @return Logical interface
+ */
+static inline int INTERFACE(int ipd_port)
+{
+ if (ipd_port < 32) /* Interface 0 or 1 for RGMII,GMII,SPI, etc */
+ return ipd_port>>4;
+ else if (ipd_port < 36) /* Interface 2 for NPI */
+ return 2;
+ else if (ipd_port < 40) /* Interface 3 for loopback */
+ return 3;
+ else if (ipd_port == 40) /* Non existant interface for POW0 */
+ return 4;
+ else
+ panic("Illegal ipd_port %d passed to INTERFACE\n", ipd_port);
+}
+
+
+/**
+ * Given an IPD/PKO port number, return the port's index on a
+ * logical interface.
+ *
+ * @param ipd_port Port to check
+ *
+ * @return Index into interface port list
+ */
+static inline int INDEX(int ipd_port)
+{
+ if (ipd_port < 32)
+ return ipd_port & 15;
+ else
+ return ipd_port & 3;
+}
+
diff --git a/sys/mips/cavium/octe/ethernet-xaui.c b/sys/mips/cavium/octe/ethernet-xaui.c
new file mode 100644
index 000000000000..455aec67e036
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet-xaui.c
@@ -0,0 +1,116 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+extern int octeon_is_simulation(void);
+
+static int cvm_oct_xaui_open(struct ifnet *ifp)
+{
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+ cvmx_helper_link_info_t link_info;
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 1;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+
+ if (!octeon_is_simulation()) {
+ link_info = cvmx_helper_link_get(priv->port);
+ if (!link_info.s.link_up)
+ if_link_state_change(ifp, LINK_STATE_DOWN);
+ else
+ if_link_state_change(ifp, LINK_STATE_UP);
+ }
+ return 0;
+}
+
+static int cvm_oct_xaui_stop(struct ifnet *ifp)
+{
+ cvmx_gmxx_prtx_cfg_t gmx_cfg;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 0;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+ return 0;
+}
+
+static void cvm_oct_xaui_poll(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ cvmx_helper_link_info_t link_info;
+
+ link_info = cvmx_helper_link_get(priv->port);
+ if (link_info.u64 == priv->link_info)
+ return;
+
+ link_info = cvmx_helper_link_autoconf(priv->port);
+ priv->link_info = link_info.u64;
+ priv->need_link_update = 1;
+}
+
+
+int cvm_oct_xaui_init(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)ifp->if_softc;
+ cvm_oct_common_init(ifp);
+ priv->open = cvm_oct_xaui_open;
+ priv->stop = cvm_oct_xaui_stop;
+ priv->stop(ifp);
+ if (!octeon_is_simulation())
+ priv->poll = cvm_oct_xaui_poll;
+
+ return 0;
+}
+
+void cvm_oct_xaui_uninit(struct ifnet *ifp)
+{
+ cvm_oct_common_uninit(ifp);
+}
+
diff --git a/sys/mips/cavium/octe/ethernet.c b/sys/mips/cavium/octe/ethernet.c
new file mode 100644
index 000000000000..1463c8ad4c93
--- /dev/null
+++ b/sys/mips/cavium/octe/ethernet.c
@@ -0,0 +1,594 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+*************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/rman.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/module.h>
+#include <sys/smp.h>
+#include <sys/taskqueue.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_types.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "ethernet-headers.h"
+
+#include "octebusvar.h"
+
+/*
+ * XXX/juli
+ * Convert 0444 to tunables, 0644 to sysctls.
+ */
+#if defined(CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS) && CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS
+int num_packet_buffers = CONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS;
+#else
+int num_packet_buffers = 1024;
+#endif
+TUNABLE_INT("hw.octe.num_packet_buffers", &num_packet_buffers);
+/*
+ "\t\tNumber of packet buffers to allocate and store in the\n"
+ "\t\tFPA. By default, 1024 packet buffers are used unless\n"
+ "\t\tCONFIG_CAVIUM_OCTEON_NUM_PACKET_BUFFERS is defined." */
+
+int pow_receive_group = 15;
+TUNABLE_INT("hw.octe.pow_receive_group", &pow_receive_group);
+/*
+ "\t\tPOW group to receive packets from. All ethernet hardware\n"
+ "\t\twill be configured to send incomming packets to this POW\n"
+ "\t\tgroup. Also any other software can submit packets to this\n"
+ "\t\tgroup for the kernel to process." */
+
+int pow_send_group = -1; /* XXX Should be a sysctl. */
+TUNABLE_INT("hw.octe.pow_send_group", &pow_send_group);
+/*
+ "\t\tPOW group to send packets to other software on. This\n"
+ "\t\tcontrols the creation of the virtual device pow0.\n"
+ "\t\talways_use_pow also depends on this value." */
+
+int always_use_pow;
+TUNABLE_INT("hw.octe.always_use_pow", &always_use_pow);
+/*
+ "\t\tWhen set, always send to the pow group. This will cause\n"
+ "\t\tpackets sent to real ethernet devices to be sent to the\n"
+ "\t\tPOW group instead of the hardware. Unless some other\n"
+ "\t\tapplication changes the config, packets will still be\n"
+ "\t\treceived from the low level hardware. Use this option\n"
+ "\t\tto allow a CVMX app to intercept all packets from the\n"
+ "\t\tlinux kernel. You must specify pow_send_group along with\n"
+ "\t\tthis option." */
+
+char pow_send_list[128] = "";
+TUNABLE_STR("hw.octe.pow_send_list", pow_send_list, sizeof pow_send_list);
+/*
+ "\t\tComma separated list of ethernet devices that should use the\n"
+ "\t\tPOW for transmit instead of the actual ethernet hardware. This\n"
+ "\t\tis a per port version of always_use_pow. always_use_pow takes\n"
+ "\t\tprecedence over this list. For example, setting this to\n"
+ "\t\t\"eth2,spi3,spi7\" would cause these three devices to transmit\n"
+ "\t\tusing the pow_send_group." */
+
+
+static int disable_core_queueing = 1;
+TUNABLE_INT("hw.octe.disable_core_queueing", &disable_core_queueing);
+/*
+ "\t\tWhen set the networking core's tx_queue_len is set to zero. This\n"
+ "\t\tallows packets to be sent without lock contention in the packet scheduler\n"
+ "\t\tresulting in some cases in improved throughput.\n" */
+
+extern int octeon_is_simulation(void);
+
+/**
+ * Exported from the kernel so we can determine board information. It is
+ * passed by the bootloader to the kernel.
+ */
+extern cvmx_bootinfo_t *octeon_bootinfo;
+
+/**
+ * Periodic timer to check auto negotiation
+ */
+static struct callout cvm_oct_poll_timer;
+
+/**
+ * Array of every ethernet device owned by this driver indexed by
+ * the ipd input port number.
+ */
+struct ifnet *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
+
+/**
+ * Task to handle link status changes.
+ */
+static struct taskqueue *cvm_oct_link_taskq;
+
+/**
+ * Function to update link status.
+ */
+static void cvm_oct_update_link(void *context, int pending)
+{
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)context;
+ struct ifnet *ifp = priv->ifp;
+ cvmx_helper_link_info_t link_info;
+
+ link_info.u64 = priv->link_info;
+
+ if (link_info.s.link_up) {
+ if_link_state_change(ifp, LINK_STATE_UP);
+ if (priv->queue != -1)
+ DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
+ if_name(ifp), link_info.s.speed,
+ (link_info.s.full_duplex) ? "Full" : "Half",
+ priv->port, priv->queue);
+ else
+ DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, POW\n",
+ if_name(ifp), link_info.s.speed,
+ (link_info.s.full_duplex) ? "Full" : "Half",
+ priv->port);
+ } else {
+ if_link_state_change(ifp, LINK_STATE_DOWN);
+ DEBUGPRINT("%s: Link down\n", if_name(ifp));
+ }
+ priv->need_link_update = 0;
+}
+
+/**
+ * Periodic timer tick for slow management operations
+ *
+ * @param arg Device to check
+ */
+static void cvm_do_timer(void *arg)
+{
+ static int port;
+ static int updated;
+ if (port < CVMX_PIP_NUM_INPUT_PORTS) {
+ if (cvm_oct_device[port]) {
+ int queues_per_port;
+ int qos;
+ cvm_oct_private_t *priv = (cvm_oct_private_t *)cvm_oct_device[port]->if_softc;
+ if (priv->poll)
+ {
+ /* skip polling if we don't get the lock */
+ if (MDIO_TRYLOCK()) {
+ priv->poll(cvm_oct_device[port]);
+ MDIO_UNLOCK();
+
+ if (priv->need_link_update) {
+ updated++;
+ taskqueue_enqueue(cvm_oct_link_taskq, &priv->link_task);
+ }
+ }
+ }
+
+ queues_per_port = cvmx_pko_get_num_queues(port);
+ /* Drain any pending packets in the free list */
+ for (qos = 0; qos < queues_per_port; qos++) {
+ if (_IF_QLEN(&priv->tx_free_queue[qos]) > 0) {
+ IF_LOCK(&priv->tx_free_queue[qos]);
+ while (_IF_QLEN(&priv->tx_free_queue[qos]) > cvmx_fau_fetch_and_add32(priv->fau+qos*4, 0)) {
+ struct mbuf *m;
+
+ _IF_DEQUEUE(&priv->tx_free_queue[qos], m);
+ m_freem(m);
+ }
+ IF_UNLOCK(&priv->tx_free_queue[qos]);
+
+ /*
+ * XXX locking!
+ */
+ priv->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ }
+ }
+#if 0
+ cvm_oct_device[port]->get_stats(cvm_oct_device[port]);
+#endif
+ }
+ port++;
+ /* Poll the next port in a 50th of a second.
+ This spreads the polling of ports out a little bit */
+ callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
+ } else {
+ port = 0;
+ /* If any updates were made in this run, continue iterating at
+ * 1/50th of a second, so that if a link has merely gone down
+ * temporarily (e.g. because of interface reinitialization) it
+ * will not be forced to stay down for an entire second.
+ */
+ if (updated > 0) {
+ updated = 0;
+ callout_reset(&cvm_oct_poll_timer, hz / 50, cvm_do_timer, NULL);
+ } else {
+ /* All ports have been polled. Start the next iteration through
+ the ports in one second */
+ callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
+ }
+ }
+}
+
+
+/**
+ * Configure common hardware for all interfaces
+ */
+static void cvm_oct_configure_common_hw(device_t bus)
+{
+ struct octebus_softc *sc;
+ int error;
+ int rid;
+
+ sc = device_get_softc(bus);
+
+ /* Setup the FPA */
+ cvmx_fpa_enable();
+ cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
+ cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
+ if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
+ cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
+
+ if (USE_RED)
+ cvmx_helper_setup_red(num_packet_buffers/4, num_packet_buffers/8);
+
+ /* Enable the MII interface */
+ if (!octeon_is_simulation())
+ cvmx_write_csr(CVMX_SMI_EN, 1);
+
+ /* Register an IRQ hander for to receive POW interrupts */
+ rid = 0;
+ sc->sc_rx_irq = bus_alloc_resource(bus, SYS_RES_IRQ, &rid,
+ CVMX_IRQ_WORKQ0 + pow_receive_group,
+ CVMX_IRQ_WORKQ0 + pow_receive_group,
+ 1, RF_ACTIVE);
+ if (sc->sc_rx_irq == NULL) {
+ device_printf(bus, "could not allocate workq irq");
+ return;
+ }
+
+ error = bus_setup_intr(bus, sc->sc_rx_irq, INTR_TYPE_NET | INTR_MPSAFE,
+ cvm_oct_do_interrupt, NULL, cvm_oct_device,
+ NULL);
+ if (error != 0) {
+ device_printf(bus, "could not setup workq irq");
+ return;
+ }
+
+
+#ifdef SMP
+ if (USE_MULTICORE_RECEIVE) {
+ critical_enter();
+ {
+ int cpu;
+ for (cpu = 0; cpu < mp_maxid; cpu++) {
+ if (!CPU_ABSENT(cpu) &&
+ (cpu != PCPU_GET(cpuid))) {
+ cvmx_ciu_intx0_t en;
+ en.u64 = cvmx_read_csr(CVMX_CIU_INTX_EN0(cpu*2));
+ en.s.workq |= (1<<pow_receive_group);
+ cvmx_write_csr(CVMX_CIU_INTX_EN0(cpu*2), en.u64);
+ }
+ }
+ }
+ critical_exit();
+ }
+#endif
+}
+
+
+/**
+ * Free a work queue entry received in a intercept callback.
+ *
+ * @param work_queue_entry
+ * Work queue entry to free
+ * @return Zero on success, Negative on failure.
+ */
+int cvm_oct_free_work(void *work_queue_entry)
+{
+ cvmx_wqe_t *work = work_queue_entry;
+
+ int segments = work->word2.s.bufs;
+ cvmx_buf_ptr_t segment_ptr = work->packet_ptr;
+
+ while (segments--) {
+ cvmx_buf_ptr_t next_ptr = *(cvmx_buf_ptr_t *)cvmx_phys_to_ptr(segment_ptr.s.addr-8);
+ if (__predict_false(!segment_ptr.s.i))
+ cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr), segment_ptr.s.pool, DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE/128));
+ segment_ptr = next_ptr;
+ }
+ cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
+
+ return 0;
+}
+
+
+/**
+ * Module/ driver initialization. Creates the linux network
+ * devices.
+ *
+ * @return Zero on success
+ */
+int cvm_oct_init_module(device_t bus)
+{
+ device_t dev;
+ int ifnum;
+ int num_interfaces;
+ int interface;
+ int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
+ int qos;
+
+ printf("cavium-ethernet: %s\n", OCTEON_SDK_VERSION_STRING);
+
+#if 0
+ cvm_oct_proc_initialize();
+#endif
+ cvm_oct_rx_initialize();
+ cvm_oct_configure_common_hw(bus);
+
+ cvmx_helper_initialize_packet_io_global();
+
+ /* Change the input group for all ports before input is enabled */
+ num_interfaces = cvmx_helper_get_number_of_interfaces();
+ for (interface = 0; interface < num_interfaces; interface++) {
+ int num_ports = cvmx_helper_ports_on_interface(interface);
+ int port;
+
+ for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
+ cvmx_pip_prt_tagx_t pip_prt_tagx;
+ pip_prt_tagx.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(port));
+ pip_prt_tagx.s.grp = pow_receive_group;
+ cvmx_write_csr(CVMX_PIP_PRT_TAGX(port), pip_prt_tagx.u64);
+ }
+ }
+
+ cvmx_helper_ipd_and_packet_input_enable();
+
+ memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
+
+ cvm_oct_link_taskq = taskqueue_create("octe link", M_NOWAIT,
+ taskqueue_thread_enqueue, &cvm_oct_link_taskq);
+ taskqueue_start_threads(&cvm_oct_link_taskq, 1, PI_NET,
+ "octe link taskq");
+
+ /* Initialize the FAU used for counting packet buffers that need to be freed */
+ cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
+
+ if ((pow_send_group != -1)) {
+ struct ifnet *ifp;
+
+ printf("\tConfiguring device for POW only access\n");
+ dev = BUS_ADD_CHILD(bus, 0, "pow", 0);
+ if (dev != NULL)
+ ifp = if_alloc(IFT_ETHER);
+ if (dev != NULL && ifp != NULL) {
+ /* Initialize the device private structure. */
+ cvm_oct_private_t *priv;
+
+ device_probe(dev);
+ priv = device_get_softc(dev);
+ priv->dev = dev;
+ priv->ifp = ifp;
+ priv->init = cvm_oct_common_init;
+ priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ priv->port = CVMX_PIP_NUM_INPUT_PORTS;
+ priv->queue = -1;
+ TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
+
+ device_set_desc(dev, "Cavium Octeon POW Ethernet\n");
+
+ ifp->if_softc = priv;
+
+ if (priv->init(ifp) < 0) {
+ printf("\t\tFailed to register ethernet device for POW\n");
+ panic("%s: need to free ifp.", __func__);
+ } else {
+ cvm_oct_device[CVMX_PIP_NUM_INPUT_PORTS] = ifp;
+ printf("\t\t%s: POW send group %d, receive group %d\n",
+ if_name(ifp), pow_send_group, pow_receive_group);
+ }
+ } else {
+ printf("\t\tFailed to allocate ethernet device for POW\n");
+ }
+ }
+
+ ifnum = 0;
+ num_interfaces = cvmx_helper_get_number_of_interfaces();
+ for (interface = 0; interface < num_interfaces; interface++) {
+ cvmx_helper_interface_mode_t imode = cvmx_helper_interface_get_mode(interface);
+ int num_ports = cvmx_helper_ports_on_interface(interface);
+ int port;
+
+ for (port = cvmx_helper_get_ipd_port(interface, 0); port < cvmx_helper_get_ipd_port(interface, num_ports); port++) {
+ cvm_oct_private_t *priv;
+ struct ifnet *ifp;
+
+ dev = BUS_ADD_CHILD(bus, 0, "octe", ifnum++);
+ if (dev != NULL)
+ ifp = if_alloc(IFT_ETHER);
+ if (dev == NULL || ifp == NULL) {
+ printf("\t\tFailed to allocate ethernet device for port %d\n", port);
+ continue;
+ }
+ /* XXX/juli set max send q len. */
+#if 0
+ if (disable_core_queueing)
+ ifp->tx_queue_len = 0;
+#endif
+
+ /* Initialize the device private structure. */
+ device_probe(dev);
+ priv = device_get_softc(dev);
+ priv->dev = dev;
+ priv->ifp = ifp;
+ priv->imode = imode;
+ priv->port = port;
+ priv->queue = cvmx_pko_get_base_queue(priv->port);
+ priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
+ for (qos = 0; qos < cvmx_pko_get_num_queues(port); qos++)
+ cvmx_fau_atomic_write32(priv->fau+qos*4, 0);
+ TASK_INIT(&priv->link_task, 0, cvm_oct_update_link, priv);
+
+ switch (priv->imode) {
+
+ /* These types don't support ports to IPD/PKO */
+ case CVMX_HELPER_INTERFACE_MODE_DISABLED:
+ case CVMX_HELPER_INTERFACE_MODE_PCIE:
+ case CVMX_HELPER_INTERFACE_MODE_PICMG:
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_NPI:
+ priv->init = cvm_oct_common_init;
+ priv->uninit = cvm_oct_common_uninit;
+ device_set_desc(dev, "Cavium Octeon NPI Ethernet");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_XAUI:
+ priv->init = cvm_oct_xaui_init;
+ priv->uninit = cvm_oct_xaui_uninit;
+ device_set_desc(dev, "Cavium Octeon XAUI Ethernet");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_LOOP:
+ priv->init = cvm_oct_common_init;
+ priv->uninit = cvm_oct_common_uninit;
+ device_set_desc(dev, "Cavium Octeon LOOP Ethernet");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_SGMII:
+ priv->init = cvm_oct_sgmii_init;
+ priv->uninit = cvm_oct_sgmii_uninit;
+ device_set_desc(dev, "Cavium Octeon SGMII Ethernet");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_SPI:
+ priv->init = cvm_oct_spi_init;
+ priv->uninit = cvm_oct_spi_uninit;
+ device_set_desc(dev, "Cavium Octeon SPI Ethernet");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_RGMII:
+ priv->init = cvm_oct_rgmii_init;
+ priv->uninit = cvm_oct_rgmii_uninit;
+ device_set_desc(dev, "Cavium Octeon RGMII Ethernet");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_GMII:
+ priv->init = cvm_oct_rgmii_init;
+ priv->uninit = cvm_oct_rgmii_uninit;
+ device_set_desc(dev, "Cavium Octeon GMII Ethernet");
+ break;
+ }
+
+ ifp->if_softc = priv;
+
+ if (!priv->init) {
+ panic("%s: unsupported device type, need to free ifp.", __func__);
+ } else
+ if (priv->init(ifp) < 0) {
+ printf("\t\tFailed to register ethernet device for interface %d, port %d\n",
+ interface, priv->port);
+ panic("%s: init failed, need to free ifp.", __func__);
+ } else {
+ cvm_oct_device[priv->port] = ifp;
+ fau -= cvmx_pko_get_num_queues(priv->port) * sizeof(uint32_t);
+ }
+ }
+ }
+
+ if (INTERRUPT_LIMIT) {
+ /* Set the POW timer rate to give an interrupt at most INTERRUPT_LIMIT times per second */
+ cvmx_write_csr(CVMX_POW_WQ_INT_PC, octeon_bootinfo->eclock_hz/(INTERRUPT_LIMIT*16*256)<<8);
+
+ /* Enable POW timer interrupt. It will count when there are packets available */
+ cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1ful<<24);
+ } else {
+ /* Enable POW interrupt when our port has at least one packet */
+ cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0x1001);
+ }
+
+ callout_init(&cvm_oct_poll_timer, CALLOUT_MPSAFE);
+ callout_reset(&cvm_oct_poll_timer, hz, cvm_do_timer, NULL);
+
+ return 0;
+}
+
+
+/**
+ * Module / driver shutdown
+ *
+ * @return Zero on success
+ */
+void cvm_oct_cleanup_module(void)
+{
+ int port;
+
+ /* Disable POW interrupt */
+ cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
+
+ cvmx_ipd_disable();
+
+#if 0
+ /* Free the interrupt handler */
+ free_irq(8 + pow_receive_group, cvm_oct_device);
+#endif
+
+ callout_stop(&cvm_oct_poll_timer);
+ cvm_oct_rx_shutdown();
+ cvmx_pko_disable();
+
+ /* Free the ethernet devices */
+ for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
+ if (cvm_oct_device[port]) {
+ cvm_oct_tx_shutdown(cvm_oct_device[port]);
+#if 0
+ unregister_netdev(cvm_oct_device[port]);
+ kfree(cvm_oct_device[port]);
+#else
+ panic("%s: need to detach and free interface.", __func__);
+#endif
+ cvm_oct_device[port] = NULL;
+ }
+ }
+
+ cvmx_pko_shutdown();
+#if 0
+ cvm_oct_proc_shutdown();
+#endif
+
+ cvmx_ipd_free_ptr();
+
+ /* Free the HW pools */
+ cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE, num_packet_buffers);
+ cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE, num_packet_buffers);
+ if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
+ cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL, CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
+}
diff --git a/sys/mips/cavium/octe/mv88e61xxphy.c b/sys/mips/cavium/octe/mv88e61xxphy.c
new file mode 100644
index 000000000000..6d018c7cb74c
--- /dev/null
+++ b/sys/mips/cavium/octe/mv88e61xxphy.c
@@ -0,0 +1,630 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for the Marvell 88E61xx family of switch PHYs
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/sysctl.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include "miibus_if.h"
+
+#include "mv88e61xxphyreg.h"
+
+struct mv88e61xxphy_softc;
+
+struct mv88e61xxphy_port_softc {
+ struct mv88e61xxphy_softc *sc_switch;
+ unsigned sc_port;
+ unsigned sc_domain;
+ unsigned sc_vlan;
+ unsigned sc_priority;
+ unsigned sc_flags;
+};
+
+#define MV88E61XXPHY_PORT_FLAG_VTU_UPDATE (0x0001)
+
+struct mv88e61xxphy_softc {
+ device_t sc_dev;
+ struct mv88e61xxphy_port_softc sc_ports[MV88E61XX_PORTS];
+};
+
+enum mv88e61xxphy_vtu_membership_type {
+ MV88E61XXPHY_VTU_UNMODIFIED,
+ MV88E61XXPHY_VTU_UNTAGGED,
+ MV88E61XXPHY_VTU_TAGGED,
+ MV88E61XXPHY_VTU_DISCARDED,
+};
+
+enum mv88e61xxphy_sysctl_link_type {
+ MV88E61XXPHY_LINK_SYSCTL_DUPLEX,
+ MV88E61XXPHY_LINK_SYSCTL_LINK,
+ MV88E61XXPHY_LINK_SYSCTL_MEDIA,
+};
+
+enum mv88e61xxphy_sysctl_port_type {
+ MV88E61XXPHY_PORT_SYSCTL_DOMAIN,
+ MV88E61XXPHY_PORT_SYSCTL_VLAN,
+ MV88E61XXPHY_PORT_SYSCTL_PRIORITY,
+};
+
+/*
+ * Register access macros.
+ */
+#define MV88E61XX_READ(sc, phy, reg) \
+ MIIBUS_READREG(device_get_parent((sc)->sc_dev), (phy), (reg))
+
+#define MV88E61XX_WRITE(sc, phy, reg, val) \
+ MIIBUS_WRITEREG(device_get_parent((sc)->sc_dev), (phy), (reg), (val))
+
+#define MV88E61XX_READ_PORT(psc, reg) \
+ MV88E61XX_READ((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg))
+
+#define MV88E61XX_WRITE_PORT(psc, reg, val) \
+ MV88E61XX_WRITE((psc)->sc_switch, MV88E61XX_PORT((psc)->sc_port), (reg), (val))
+
+static int mv88e61xxphy_probe(device_t);
+static int mv88e61xxphy_attach(device_t);
+
+static void mv88e61xxphy_init(struct mv88e61xxphy_softc *);
+static void mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *);
+static void mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *);
+static int mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS);
+static int mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS);
+static void mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *, uint16_t);
+static void mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *, unsigned, enum mv88e61xxphy_vtu_membership_type);
+static void mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *);
+
+static int
+mv88e61xxphy_probe(device_t dev)
+{
+ uint16_t val;
+
+ val = MIIBUS_READREG(device_get_parent(dev), MV88E61XX_PORT(0),
+ MV88E61XX_PORT_REVISION);
+ switch (val >> 4) {
+ case 0x121:
+ device_set_desc(dev, "Marvell Link Street 88E6123 3-Port Gigabit Switch");
+ return (0);
+ case 0x161:
+ device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Gigabit Switch");
+ return (0);
+ case 0x165:
+ device_set_desc(dev, "Marvell Link Street 88E6161 6-Port Advanced Gigabit Switch");
+ return (0);
+ default:
+ return (ENXIO);
+ }
+}
+
+static int
+mv88e61xxphy_attach(device_t dev)
+{
+ char portbuf[] = "N";
+ struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
+ struct sysctl_oid *tree = device_get_sysctl_tree(dev);
+ struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree);
+ struct sysctl_oid *port_node, *portN_node;
+ struct sysctl_oid_list *port_tree, *portN_tree;
+ struct mv88e61xxphy_softc *sc;
+ unsigned port;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ /*
+ * Initialize port softcs.
+ */
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+ psc->sc_switch = sc;
+ psc->sc_port = port;
+ psc->sc_domain = 0; /* One broadcast domain by default. */
+ psc->sc_vlan = port + 1; /* Tag VLANs by default. */
+ psc->sc_priority = 0; /* No default special priority. */
+ psc->sc_flags = 0;
+ }
+
+ /*
+ * Add per-port sysctl tree/handlers.
+ */
+ port_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "port",
+ CTLFLAG_RD, NULL, "Switch Ports");
+ port_tree = SYSCTL_CHILDREN(port_node);
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+
+ portbuf[0] = '0' + port;
+ portN_node = SYSCTL_ADD_NODE(ctx, port_tree, OID_AUTO, portbuf,
+ CTLFLAG_RD, NULL, "Switch Port");
+ portN_tree = SYSCTL_CHILDREN(portN_node);
+
+ SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "duplex",
+ CTLFLAG_RD | CTLTYPE_INT, psc,
+ MV88E61XXPHY_LINK_SYSCTL_DUPLEX,
+ mv88e61xxphy_sysctl_link_proc, "IU",
+ "Media duplex status (0 = half duplex; 1 = full duplex)");
+
+ SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "link",
+ CTLFLAG_RD | CTLTYPE_INT, psc,
+ MV88E61XXPHY_LINK_SYSCTL_LINK,
+ mv88e61xxphy_sysctl_link_proc, "IU",
+ "Link status (0 = down; 1 = up)");
+
+ SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "media",
+ CTLFLAG_RD | CTLTYPE_INT, psc,
+ MV88E61XXPHY_LINK_SYSCTL_MEDIA,
+ mv88e61xxphy_sysctl_link_proc, "IU",
+ "Media speed (0 = unknown; 10 = 10Mbps; 100 = 100Mbps; 1000 = 1Gbps)");
+
+ SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "domain",
+ CTLFLAG_RW | CTLTYPE_INT, psc,
+ MV88E61XXPHY_PORT_SYSCTL_DOMAIN,
+ mv88e61xxphy_sysctl_port_proc, "IU",
+ "Broadcast domain (ports can only talk to other ports in the same domain)");
+
+ SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "vlan",
+ CTLFLAG_RW | CTLTYPE_INT, psc,
+ MV88E61XXPHY_PORT_SYSCTL_VLAN,
+ mv88e61xxphy_sysctl_port_proc, "IU",
+ "Tag packets from/for this port with a given VLAN.");
+
+ SYSCTL_ADD_PROC(ctx, portN_tree, OID_AUTO, "priority",
+ CTLFLAG_RW | CTLTYPE_INT, psc,
+ MV88E61XXPHY_PORT_SYSCTL_PRIORITY,
+ mv88e61xxphy_sysctl_port_proc, "IU",
+ "Default packet priority for this port.");
+ }
+
+ mv88e61xxphy_init(sc);
+
+ return (0);
+}
+
+static void
+mv88e61xxphy_init(struct mv88e61xxphy_softc *sc)
+{
+ unsigned port;
+ uint16_t val;
+ unsigned i;
+
+ /* Disable all ports. */
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+
+ val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL);
+ val &= ~0x3;
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val);
+ }
+
+ DELAY(2000);
+
+ /* Reset the switch. */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_CONTROL, 0xc400);
+ for (i = 0; i < 100; i++) {
+ val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_STATUS);
+ if ((val & 0xc800) == 0xc800)
+ break;
+ DELAY(10);
+ }
+ if (i == 100) {
+ device_printf(sc->sc_dev, "%s: switch reset timed out.\n", __func__);
+ return;
+ }
+
+ /* Disable PPU. */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_CONTROL, 0x0000);
+
+ /* Configure host port and send monitor frames to it. */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_MONITOR,
+ (MV88E61XX_HOST_PORT << 12) | (MV88E61XX_HOST_PORT << 8) |
+ (MV88E61XX_HOST_PORT << 4));
+
+ /* Disable remote management. */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_REMOTE_MGMT, 0x0000);
+
+ /* Send all specifically-addressed frames to the host port. */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_MANAGE_2X, 0xffff);
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_MANAGE_0X, 0xffff);
+
+ /* Remove provider-supplied tag and use it for switching. */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL2, MV88E61XX_GLOBAL2_CONTROL2,
+ MV88E61XX_GLOBAL2_CONTROL2_REMOVE_PTAG);
+
+ /* Configure all ports. */
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+ mv88e61xxphy_init_port(psc);
+ }
+
+ /* Reprogram VLAN table (VTU.) */
+ mv88e61xxphy_init_vtu(sc);
+
+ /* Enable all ports. */
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+
+ val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_CONTROL);
+ val |= 0x3;
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, val);
+ }
+}
+
+static void
+mv88e61xxphy_init_port(struct mv88e61xxphy_port_softc *psc)
+{
+ struct mv88e61xxphy_softc *sc;
+ unsigned allow_mask;
+
+ sc = psc->sc_switch;
+
+ /* Set media type and flow control. */
+ if (psc->sc_port != MV88E61XX_HOST_PORT) {
+ /* Don't force any media type or flow control. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FORCE_MAC, 0x0003);
+ } else {
+ /* Make CPU port 1G FDX. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FORCE_MAC, 0x003e);
+ }
+
+ /* Don't limit flow control pauses. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PAUSE_CONTROL, 0x0000);
+
+ /* Set various port functions per Linux. */
+ if (psc->sc_port != MV88E61XX_HOST_PORT) {
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x04bc);
+ } else {
+ /*
+ * Send frames for unknown unicast and multicast groups to
+ * host, too.
+ */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL, 0x063f);
+ }
+
+ if (psc->sc_port != MV88E61XX_HOST_PORT) {
+ /* Disable trunking. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x0000);
+ } else {
+ /* Disable trunking and send learn messages to host. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_CONTROL2, 0x8000);
+ }
+
+ /*
+ * Port-based VLAN map; isolates MAC tables and forces ports to talk
+ * only to the host.
+ *
+ * Always allow the host to send to all ports and allow all ports to
+ * send to the host.
+ */
+ if (psc->sc_port != MV88E61XX_HOST_PORT) {
+ allow_mask = 1 << MV88E61XX_HOST_PORT;
+ } else {
+ allow_mask = (1 << MV88E61XX_PORTS) - 1;
+ allow_mask &= ~(1 << MV88E61XX_HOST_PORT);
+ }
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_VLAN_MAP,
+ (psc->sc_domain << 12) | allow_mask);
+
+ /* VLAN tagging. Set default priority and VLAN tag (or none.) */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_VLAN,
+ (psc->sc_priority << 14) | psc->sc_vlan);
+
+ if (psc->sc_port == MV88E61XX_HOST_PORT) {
+ /* Set provider ingress tag. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_PROVIDER_PROTO,
+ ETHERTYPE_VLAN);
+
+ /* Set provider egress tag. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_ETHER_PROTO,
+ ETHERTYPE_VLAN);
+
+ /* Use secure 802.1q mode and accept only tagged frames. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FILTER,
+ MV88E61XX_PORT_FILTER_MAP_DEST |
+ MV88E61XX_PORT_FILTER_8021Q_SECURE |
+ MV88E61XX_PORT_FILTER_DISCARD_UNTAGGED);
+ } else {
+ /* Don't allow tagged frames. */
+ MV88E61XX_WRITE_PORT(psc, MV88E61XX_PORT_FILTER,
+ MV88E61XX_PORT_FILTER_MAP_DEST |
+ MV88E61XX_PORT_FILTER_DISCARD_TAGGED);
+ }
+}
+
+static void
+mv88e61xxphy_init_vtu(struct mv88e61xxphy_softc *sc)
+{
+ unsigned port;
+
+ /*
+ * Start flush of the VTU.
+ */
+ mv88e61xxphy_vtu_wait(sc);
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP,
+ MV88E61XX_GLOBAL_VTU_OP_BUSY | MV88E61XX_GLOBAL_VTU_OP_OP_FLUSH);
+
+ /*
+ * Queue each port's VLAN to be programmed.
+ */
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+ psc->sc_flags &= ~MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
+ if (psc->sc_vlan == 0)
+ continue;
+ psc->sc_flags |= MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
+ }
+
+ /*
+ * Program each VLAN that is in use.
+ */
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+ if ((psc->sc_flags & MV88E61XXPHY_PORT_FLAG_VTU_UPDATE) == 0)
+ continue;
+ mv88e61xxphy_vtu_load(sc, psc->sc_vlan);
+ }
+
+ /*
+ * Wait for last pending VTU operation to complete.
+ */
+ mv88e61xxphy_vtu_wait(sc);
+}
+
+static int
+mv88e61xxphy_sysctl_link_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct mv88e61xxphy_port_softc *psc = arg1;
+ enum mv88e61xxphy_sysctl_link_type type = arg2;
+ uint16_t val;
+ unsigned out;
+
+ val = MV88E61XX_READ_PORT(psc, MV88E61XX_PORT_STATUS);
+ switch (type) {
+ case MV88E61XXPHY_LINK_SYSCTL_DUPLEX:
+ if ((val & MV88E61XX_PORT_STATUS_DUPLEX) != 0)
+ out = 1;
+ else
+ out = 0;
+ break;
+ case MV88E61XXPHY_LINK_SYSCTL_LINK:
+ if ((val & MV88E61XX_PORT_STATUS_LINK) != 0)
+ out = 1;
+ else
+ out = 0;
+ break;
+ case MV88E61XXPHY_LINK_SYSCTL_MEDIA:
+ switch (val & MV88E61XX_PORT_STATUS_MEDIA) {
+ case MV88E61XX_PORT_STATUS_MEDIA_10M:
+ out = 10;
+ break;
+ case MV88E61XX_PORT_STATUS_MEDIA_100M:
+ out = 100;
+ break;
+ case MV88E61XX_PORT_STATUS_MEDIA_1G:
+ out = 1000;
+ break;
+ default:
+ out = 0;
+ break;
+ }
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (sysctl_handle_int(oidp, NULL, out, req));
+}
+
+static int
+mv88e61xxphy_sysctl_port_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct mv88e61xxphy_port_softc *psc = arg1;
+ enum mv88e61xxphy_sysctl_port_type type = arg2;
+ struct mv88e61xxphy_softc *sc = psc->sc_switch;
+ unsigned max, val, *valp;
+ int error;
+
+ switch (type) {
+ case MV88E61XXPHY_PORT_SYSCTL_DOMAIN:
+ valp = &psc->sc_domain;
+ max = 0xf;
+ break;
+ case MV88E61XXPHY_PORT_SYSCTL_VLAN:
+ valp = &psc->sc_vlan;
+ max = 0x1000;
+ break;
+ case MV88E61XXPHY_PORT_SYSCTL_PRIORITY:
+ valp = &psc->sc_priority;
+ max = 3;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ val = *valp;
+ error = sysctl_handle_int(oidp, &val, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Bounds check value. */
+ if (val >= max)
+ return (EINVAL);
+
+ /* Reinitialize switch with new value. */
+ *valp = val;
+ mv88e61xxphy_init(sc);
+
+ return (0);
+}
+
+static void
+mv88e61xxphy_vtu_load(struct mv88e61xxphy_softc *sc, uint16_t vid)
+{
+ unsigned port;
+
+ /*
+ * Wait for previous operation to complete.
+ */
+ mv88e61xxphy_vtu_wait(sc);
+
+ /*
+ * Set VID.
+ */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_VID,
+ MV88E61XX_GLOBAL_VTU_VID_VALID | vid);
+
+ /*
+ * Add ports to this VTU.
+ */
+ for (port = 0; port < MV88E61XX_PORTS; port++) {
+ struct mv88e61xxphy_port_softc *psc;
+
+ psc = &sc->sc_ports[port];
+ if (psc->sc_vlan == vid) {
+ /*
+ * Send this port its VLAN traffic untagged.
+ */
+ psc->sc_flags &= ~MV88E61XXPHY_PORT_FLAG_VTU_UPDATE;
+ mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_UNTAGGED);
+ } else if (psc->sc_port == MV88E61XX_HOST_PORT) {
+ /*
+ * The host sees all VLANs tagged.
+ */
+ mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_TAGGED);
+ } else {
+ /*
+ * This port isn't on this VLAN.
+ */
+ mv88e61xxphy_vtu_set_membership(sc, port, MV88E61XXPHY_VTU_DISCARDED);
+ }
+ }
+
+ /*
+ * Start adding this entry.
+ */
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP,
+ MV88E61XX_GLOBAL_VTU_OP_BUSY |
+ MV88E61XX_GLOBAL_VTU_OP_OP_VTU_LOAD);
+}
+
+static void
+mv88e61xxphy_vtu_set_membership(struct mv88e61xxphy_softc *sc, unsigned port,
+ enum mv88e61xxphy_vtu_membership_type type)
+{
+ unsigned shift, reg;
+ uint16_t bits;
+ uint16_t val;
+
+ switch (type) {
+ case MV88E61XXPHY_VTU_UNMODIFIED:
+ bits = 0;
+ break;
+ case MV88E61XXPHY_VTU_UNTAGGED:
+ bits = 1;
+ break;
+ case MV88E61XXPHY_VTU_TAGGED:
+ bits = 2;
+ break;
+ case MV88E61XXPHY_VTU_DISCARDED:
+ bits = 3;
+ break;
+ default:
+ return;
+ }
+
+ if (port < 4) {
+ reg = MV88E61XX_GLOBAL_VTU_DATA_P0P3;
+ shift = port * 4;
+ } else {
+ reg = MV88E61XX_GLOBAL_VTU_DATA_P4P5;
+ shift = (port - 4) * 4;
+ }
+
+ val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, reg);
+ val |= bits << shift;
+ MV88E61XX_WRITE(sc, MV88E61XX_GLOBAL, reg, val);
+}
+
+static void
+mv88e61xxphy_vtu_wait(struct mv88e61xxphy_softc *sc)
+{
+ uint16_t val;
+
+ for (;;) {
+ val = MV88E61XX_READ(sc, MV88E61XX_GLOBAL, MV88E61XX_GLOBAL_VTU_OP);
+ if ((val & MV88E61XX_GLOBAL_VTU_OP_BUSY) == 0)
+ return;
+ }
+}
+
+static device_method_t mv88e61xxphy_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, mv88e61xxphy_probe),
+ DEVMETHOD(device_attach, mv88e61xxphy_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ { 0, 0 }
+};
+
+static devclass_t mv88e61xxphy_devclass;
+
+static driver_t mv88e61xxphy_driver = {
+ "mv88e61xxphy",
+ mv88e61xxphy_methods,
+ sizeof(struct mv88e61xxphy_softc)
+};
+
+DRIVER_MODULE(mv88e61xxphy, octe, mv88e61xxphy_driver, mv88e61xxphy_devclass, 0, 0);
diff --git a/sys/mips/cavium/octe/mv88e61xxphyreg.h b/sys/mips/cavium/octe/mv88e61xxphyreg.h
new file mode 100644
index 000000000000..420741f2fe21
--- /dev/null
+++ b/sys/mips/cavium/octe/mv88e61xxphyreg.h
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Register definitions for Marvell MV88E61XX
+ *
+ * Note that names and definitions were gleaned from Linux and U-Boot patches
+ * released by Marvell, often by looking at contextual use of the registers
+ * involved, and may not be representative of the full functionality of those
+ * registers and are certainly not an exhaustive enumeration of registers.
+ *
+ * For an exhaustive enumeration of registers, check out the QD-DSDT package
+ * included in the Marvell ARM Feroceon Board Support Package for Linux.
+ */
+
+#ifndef _MIPS_CAVIUM_OCTE_MV88E61XXPHYREG_H_
+#define _MIPS_CAVIUM_OCTE_MV88E61XXPHYREG_H_
+
+/*
+ * Port addresses & per-port registers.
+ */
+#define MV88E61XX_PORT(x) (0x10 + (x))
+#define MV88E61XX_HOST_PORT (5)
+#define MV88E61XX_PORTS (6)
+
+#define MV88E61XX_PORT_STATUS (0x00)
+#define MV88E61XX_PORT_FORCE_MAC (0x01)
+#define MV88E61XX_PORT_PAUSE_CONTROL (0x02)
+#define MV88E61XX_PORT_REVISION (0x03)
+#define MV88E61XX_PORT_CONTROL (0x04)
+#define MV88E61XX_PORT_CONTROL2 (0x05)
+#define MV88E61XX_PORT_VLAN_MAP (0x06)
+#define MV88E61XX_PORT_VLAN (0x07)
+#define MV88E61XX_PORT_FILTER (0x08)
+#define MV88E61XX_PORT_EGRESS_CONTROL (0x09)
+#define MV88E61XX_PORT_EGRESS_CONTROL2 (0x0a)
+#define MV88E61XX_PORT_PORT_LEARN (0x0b)
+#define MV88E61XX_PORT_ATU_CONTROL (0x0c)
+#define MV88E61XX_PORT_PRIORITY_CONTROL (0x0d)
+#define MV88E61XX_PORT_ETHER_PROTO (0x0f)
+#define MV88E61XX_PORT_PROVIDER_PROTO (0x1a)
+#define MV88E61XX_PORT_PRIORITY_MAP (0x18)
+#define MV88E61XX_PORT_PRIORITY_MAP2 (0x19)
+
+/*
+ * Fields and values in each register.
+ */
+#define MV88E61XX_PORT_STATUS_MEDIA (0x0300)
+#define MV88E61XX_PORT_STATUS_MEDIA_10M (0x0000)
+#define MV88E61XX_PORT_STATUS_MEDIA_100M (0x0100)
+#define MV88E61XX_PORT_STATUS_MEDIA_1G (0x0200)
+#define MV88E61XX_PORT_STATUS_DUPLEX (0x0400)
+#define MV88E61XX_PORT_STATUS_LINK (0x0800)
+#define MV88E61XX_PORT_STATUS_FC (0x8000)
+
+#define MV88E61XX_PORT_CONTROL_DOUBLE_TAG (0x0200)
+
+#define MV88E61XX_PORT_FILTER_MAP_DEST (0x0080)
+#define MV88E61XX_PORT_FILTER_DISCARD_UNTAGGED (0x0100)
+#define MV88E61XX_PORT_FILTER_DISCARD_TAGGED (0x0200)
+#define MV88E61XX_PORT_FILTER_8021Q_MODE (0x0c00)
+#define MV88E61XX_PORT_FILTER_8021Q_DISABLED (0x0000)
+#define MV88E61XX_PORT_FILTER_8021Q_FALLBACK (0x0400)
+#define MV88E61XX_PORT_FILTER_8021Q_CHECK (0x0800)
+#define MV88E61XX_PORT_FILTER_8021Q_SECURE (0x0c00)
+
+/*
+ * Global address & global registers.
+ */
+#define MV88E61XX_GLOBAL (0x1b)
+
+#define MV88E61XX_GLOBAL_STATUS (0x00)
+#define MV88E61XX_GLOBAL_CONTROL (0x04)
+#define MV88E61XX_GLOBAL_VTU_OP (0x05)
+#define MV88E61XX_GLOBAL_VTU_VID (0x06)
+#define MV88E61XX_GLOBAL_VTU_DATA_P0P3 (0x07)
+#define MV88E61XX_GLOBAL_VTU_DATA_P4P5 (0x08)
+#define MV88E61XX_GLOBAL_ATU_CONTROL (0x0a)
+#define MV88E61XX_GLOBAL_PRIORITY_MAP (0x18)
+#define MV88E61XX_GLOBAL_MONITOR (0x1a)
+#define MV88E61XX_GLOBAL_REMOTE_MGMT (0x1c)
+#define MV88E61XX_GLOBAL_STATS (0x1d)
+
+/*
+ * Fields and values in each register.
+ */
+#define MV88E61XX_GLOBAL_VTU_OP_BUSY (0x8000)
+#define MV88E61XX_GLOBAL_VTU_OP_OP (0x7000)
+#define MV88E61XX_GLOBAL_VTU_OP_OP_FLUSH (0x1000)
+#define MV88E61XX_GLOBAL_VTU_OP_OP_VTU_LOAD (0x3000)
+
+#define MV88E61XX_GLOBAL_VTU_VID_VALID (0x1000)
+
+/*
+ * Second global address & second global registers.
+ */
+#define MV88E61XX_GLOBAL2 (0x1c)
+
+#define MV88E61XX_GLOBAL2_MANAGE_2X (0x02)
+#define MV88E61XX_GLOBAL2_MANAGE_0X (0x03)
+#define MV88E61XX_GLOBAL2_CONTROL2 (0x05)
+#define MV88E61XX_GLOBAL2_TRUNK_MASK (0x07)
+#define MV88E61XX_GLOBAL2_TRUNK_MAP (0x08)
+#define MV88E61XX_GLOBAL2_RATELIMIT (0x09)
+#define MV88E61XX_GLOBAL2_VLAN_CONTROL (0x0b)
+#define MV88E61XX_GLOBAL2_MAC_ADDRESS (0x0d)
+
+/*
+ * Fields and values in each register.
+ */
+#define MV88E61XX_GLOBAL2_CONTROL2_DOUBLE_USE (0x8000)
+#define MV88E61XX_GLOBAL2_CONTROL2_LOOP_PREVENT (0x4000)
+#define MV88E61XX_GLOBAL2_CONTROL2_FLOW_MESSAGE (0x2000)
+#define MV88E61XX_GLOBAL2_CONTROL2_FLOOD_BC (0x1000)
+#define MV88E61XX_GLOBAL2_CONTROL2_REMOVE_PTAG (0x0800)
+#define MV88E61XX_GLOBAL2_CONTROL2_AGE_INT (0x0400)
+#define MV88E61XX_GLOBAL2_CONTROL2_FLOW_TAG (0x0200)
+#define MV88E61XX_GLOBAL2_CONTROL2_ALWAYS_VTU (0x0100)
+#define MV88E61XX_GLOBAL2_CONTROL2_FORCE_FC_PRI (0x0080)
+#define MV88E61XX_GLOBAL2_CONTROL2_FC_PRI (0x0070)
+#define MV88E61XX_GLOBAL2_CONTROL2_MGMT_TO_HOST (0x0008)
+#define MV88E61XX_GLOBAL2_CONTROL2_MGMT_PRI (0x0007)
+
+#endif /* !_MIPS_CAVIUM_OCTE_MV88E61XXPHYREG_H_ */
diff --git a/sys/mips/cavium/octe/octe.c b/sys/mips/cavium/octe/octe.c
new file mode 100644
index 000000000000..2e0573459dbc
--- /dev/null
+++ b/sys/mips/cavium/octe/octe.c
@@ -0,0 +1,522 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Cavium Octeon Ethernet devices.
+ *
+ * XXX This file should be moved to if_octe.c
+ * XXX The driver may have sufficient locking but we need locking to protect
+ * the interfaces presented here, right?
+ */
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+
+#include <net/bpf.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_var.h>
+#include <net/if_vlan_var.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include "wrapper-cvmx-includes.h"
+#include "cavium-ethernet.h"
+
+#include "ethernet-common.h"
+#include "ethernet-defines.h"
+#include "ethernet-mdio.h"
+#include "ethernet-tx.h"
+
+#include "miibus_if.h"
+
+#define OCTE_TX_LOCK(priv) mtx_lock(&(priv)->tx_mtx)
+#define OCTE_TX_UNLOCK(priv) mtx_unlock(&(priv)->tx_mtx)
+
+static int octe_probe(device_t);
+static int octe_attach(device_t);
+static int octe_detach(device_t);
+static int octe_shutdown(device_t);
+
+static int octe_miibus_readreg(device_t, int, int);
+static int octe_miibus_writereg(device_t, int, int, int);
+
+static void octe_init(void *);
+static void octe_stop(void *);
+static void octe_start(struct ifnet *);
+
+static int octe_mii_medchange(struct ifnet *);
+static void octe_mii_medstat(struct ifnet *, struct ifmediareq *);
+
+static int octe_medchange(struct ifnet *);
+static void octe_medstat(struct ifnet *, struct ifmediareq *);
+
+static int octe_ioctl(struct ifnet *, u_long, caddr_t);
+
+static device_method_t octe_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, octe_probe),
+ DEVMETHOD(device_attach, octe_attach),
+ DEVMETHOD(device_detach, octe_detach),
+ DEVMETHOD(device_shutdown, octe_shutdown),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, octe_miibus_readreg),
+ DEVMETHOD(miibus_writereg, octe_miibus_writereg),
+
+ { 0, 0 }
+};
+
+static driver_t octe_driver = {
+ "octe",
+ octe_methods,
+ sizeof (cvm_oct_private_t),
+};
+
+static devclass_t octe_devclass;
+
+DRIVER_MODULE(octe, octebus, octe_driver, octe_devclass, 0, 0);
+DRIVER_MODULE(miibus, octe, miibus_driver, miibus_devclass, 0, 0);
+
+static driver_t pow_driver = {
+ "pow",
+ octe_methods,
+ sizeof (cvm_oct_private_t),
+};
+
+static devclass_t pow_devclass;
+
+DRIVER_MODULE(pow, octebus, pow_driver, pow_devclass, 0, 0);
+
+static int
+octe_probe(device_t dev)
+{
+ return (0);
+}
+
+static int
+octe_attach(device_t dev)
+{
+ struct ifnet *ifp;
+ cvm_oct_private_t *priv;
+ device_t child;
+ unsigned qos;
+ int error;
+
+ priv = device_get_softc(dev);
+ ifp = priv->ifp;
+
+ if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+
+ if (priv->phy_id != -1) {
+ if (priv->phy_device == NULL) {
+ error = mii_phy_probe(dev, &priv->miibus, octe_mii_medchange,
+ octe_mii_medstat);
+ if (error != 0)
+ device_printf(dev, "missing phy %u\n", priv->phy_id);
+ } else {
+ child = device_add_child(dev, priv->phy_device, -1);
+ if (child == NULL)
+ device_printf(dev, "missing phy %u device %s\n", priv->phy_id, priv->phy_device);
+ }
+ }
+
+ if (priv->miibus == NULL) {
+ ifmedia_init(&priv->media, 0, octe_medchange, octe_medstat);
+
+ ifmedia_add(&priv->media, IFM_ETHER | IFM_AUTO, 0, NULL);
+ ifmedia_set(&priv->media, IFM_ETHER | IFM_AUTO);
+ }
+
+ /*
+ * XXX
+ * We don't support programming the multicast filter right now, although it
+ * ought to be easy enough. (Presumably it's just a matter of putting
+ * multicast addresses in the CAM?)
+ */
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_ALLMULTI;
+ ifp->if_init = octe_init;
+ ifp->if_ioctl = octe_ioctl;
+ ifp->if_start = octe_start;
+
+ priv->if_flags = ifp->if_flags;
+
+ mtx_init(&priv->tx_mtx, ifp->if_xname, "octe tx send queue", MTX_DEF);
+
+ for (qos = 0; qos < 16; qos++) {
+ mtx_init(&priv->tx_free_queue[qos].ifq_mtx, ifp->if_xname, "octe tx free queue", MTX_DEF);
+ IFQ_SET_MAXLEN(&priv->tx_free_queue[qos], MAX_OUT_QUEUE_DEPTH);
+ }
+
+ ether_ifattach(ifp, priv->mac);
+
+ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
+ ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_HWCSUM;
+ ifp->if_capenable = ifp->if_capabilities;
+ ifp->if_hwassist = CSUM_TCP | CSUM_UDP;
+
+ OCTE_TX_LOCK(priv);
+ IFQ_SET_MAXLEN(&ifp->if_snd, MAX_OUT_QUEUE_DEPTH);
+ ifp->if_snd.ifq_drv_maxlen = MAX_OUT_QUEUE_DEPTH;
+ IFQ_SET_READY(&ifp->if_snd);
+ OCTE_TX_UNLOCK(priv);
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+octe_detach(device_t dev)
+{
+ return (0);
+}
+
+static int
+octe_shutdown(device_t dev)
+{
+ return (octe_detach(dev));
+}
+
+static int
+octe_miibus_readreg(device_t dev, int phy, int reg)
+{
+ cvm_oct_private_t *priv;
+
+ priv = device_get_softc(dev);
+
+ /*
+ * Try interface-specific MII routine.
+ */
+ if (priv->mdio_read != NULL)
+ return (priv->mdio_read(priv->ifp, phy, reg));
+
+ /*
+ * Try generic MII routine.
+ */
+ if (phy != priv->phy_id)
+ return (0);
+
+ return (cvm_oct_mdio_read(priv->ifp, phy, reg));
+}
+
+static int
+octe_miibus_writereg(device_t dev, int phy, int reg, int val)
+{
+ cvm_oct_private_t *priv;
+
+ priv = device_get_softc(dev);
+
+ /*
+ * Try interface-specific MII routine.
+ */
+ if (priv->mdio_write != NULL) {
+ priv->mdio_write(priv->ifp, phy, reg, val);
+ return (0);
+ }
+
+ /*
+ * Try generic MII routine.
+ */
+ KASSERT(phy == priv->phy_id,
+ ("write to phy %u but our phy is %u", phy, priv->phy_id));
+ cvm_oct_mdio_write(priv->ifp, phy, reg, val);
+
+ return (0);
+}
+
+static void
+octe_init(void *arg)
+{
+ struct ifnet *ifp;
+ cvm_oct_private_t *priv;
+
+ priv = arg;
+ ifp = priv->ifp;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+ octe_stop(priv);
+
+ if (priv->open != NULL)
+ priv->open(ifp);
+
+ if (((ifp->if_flags ^ priv->if_flags) & (IFF_ALLMULTI | IFF_MULTICAST | IFF_PROMISC)) != 0)
+ cvm_oct_common_set_multicast_list(ifp);
+
+ cvm_oct_common_set_mac_address(ifp, IF_LLADDR(ifp));
+
+ if (priv->poll != NULL)
+ priv->poll(ifp);
+ if (priv->miibus != NULL)
+ mii_mediachg(device_get_softc(priv->miibus));
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+}
+
+static void
+octe_stop(void *arg)
+{
+ struct ifnet *ifp;
+ cvm_oct_private_t *priv;
+
+ priv = arg;
+ ifp = priv->ifp;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ if (priv->stop != NULL)
+ priv->stop(ifp);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+}
+
+static void
+octe_start(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv;
+ struct mbuf *m;
+ int error;
+
+ priv = ifp->if_softc;
+
+ if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING)
+ return;
+
+ OCTE_TX_LOCK(priv);
+ while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ OCTE_TX_UNLOCK(priv);
+
+ if (priv->queue != -1) {
+ error = cvm_oct_xmit(m, ifp);
+ } else {
+ error = cvm_oct_xmit_pow(m, ifp);
+ }
+
+ if (error != 0) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ return;
+ }
+
+ OCTE_TX_LOCK(priv);
+ }
+ OCTE_TX_UNLOCK(priv);
+}
+
+static int
+octe_mii_medchange(struct ifnet *ifp)
+{
+ cvm_oct_private_t *priv;
+ struct mii_data *mii;
+
+ priv = ifp->if_softc;
+ mii = device_get_softc(priv->miibus);
+
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ mii_mediachg(mii);
+
+ return (0);
+}
+
+static void
+octe_mii_medstat(struct ifnet *ifp, struct ifmediareq *ifm)
+{
+ cvm_oct_private_t *priv;
+ struct mii_data *mii;
+
+ priv = ifp->if_softc;
+ mii = device_get_softc(priv->miibus);
+
+ mii_pollstat(mii);
+ ifm->ifm_active = mii->mii_media_active;
+ ifm->ifm_status = mii->mii_media_status;
+}
+
+static int
+octe_medchange(struct ifnet *ifp)
+{
+ return (ENOTSUP);
+}
+
+static void
+octe_medstat(struct ifnet *ifp, struct ifmediareq *ifm)
+{
+ cvm_oct_private_t *priv;
+ cvmx_helper_link_info_t link_info;
+
+ priv = ifp->if_softc;
+
+ ifm->ifm_status = IFM_AVALID;
+ ifm->ifm_active = IFT_ETHER;
+
+ if (priv->poll == NULL)
+ return;
+ priv->poll(ifp);
+
+ link_info.u64 = priv->link_info;
+
+ if (!link_info.s.link_up)
+ return;
+
+ ifm->ifm_status |= IFM_ACTIVE;
+
+ switch (link_info.s.speed) {
+ case 10:
+ ifm->ifm_active |= IFM_10_T;
+ break;
+ case 100:
+ ifm->ifm_active |= IFM_100_TX;
+ break;
+ case 1000:
+ ifm->ifm_active |= IFM_1000_T;
+ break;
+ case 10000:
+ ifm->ifm_active |= IFM_10G_T;
+ break;
+ }
+
+ if (link_info.s.full_duplex)
+ ifm->ifm_active |= IFM_FDX;
+ else
+ ifm->ifm_active |= IFM_HDX;
+}
+
+static int
+octe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ cvm_oct_private_t *priv;
+ struct mii_data *mii;
+ struct ifreq *ifr;
+#ifdef INET
+ struct ifaddr *ifa;
+#endif
+ int error;
+
+ priv = ifp->if_softc;
+ ifr = (struct ifreq *)data;
+#ifdef INET
+ ifa = (struct ifaddr *)data;
+#endif
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+#ifdef INET
+ /*
+ * Avoid reinitialization unless it's necessary.
+ */
+ if (ifa->ifa_addr->sa_family == AF_INET) {
+ ifp->if_flags |= IFF_UP;
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ octe_init(priv);
+ arp_ifinit(ifp, ifa);
+
+ return (0);
+ }
+#endif
+ error = ether_ioctl(ifp, cmd, data);
+ if (error != 0)
+ return (error);
+ return (0);
+
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags == priv->if_flags)
+ return (0);
+ if ((ifp->if_flags & IFF_UP) != 0) {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ octe_init(priv);
+ } else {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+ octe_stop(priv);
+ }
+ priv->if_flags = ifp->if_flags;
+ return (0);
+
+ case SIOCSIFCAP:
+ /*
+ * Just change the capabilities in software, currently none
+ * require reprogramming hardware, they just toggle whether we
+ * make use of already-present facilities in software.
+ */
+ ifp->if_capenable = ifr->ifr_reqcap;
+ return (0);
+
+ case SIOCSIFMTU:
+ error = cvm_oct_common_change_mtu(ifp, ifr->ifr_mtu);
+ if (error != 0)
+ return (EINVAL);
+ return (0);
+
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ if (priv->miibus != NULL) {
+ mii = device_get_softc(priv->miibus);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
+ if (error != 0)
+ return (error);
+ return (0);
+ }
+ error = ifmedia_ioctl(ifp, ifr, &priv->media, cmd);
+ if (error != 0)
+ return (error);
+ return (0);
+
+ default:
+ error = ether_ioctl(ifp, cmd, data);
+ if (error != 0)
+ return (error);
+ return (0);
+ }
+}
diff --git a/sys/mips/cavium/octe/octebus.c b/sys/mips/cavium/octe/octebus.c
new file mode 100644
index 000000000000..ec67cf6761f8
--- /dev/null
+++ b/sys/mips/cavium/octe/octebus.c
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Cavium Octeon Ethernet pseudo-bus attachment.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+
+#include "ethernet-common.h"
+
+#include "octebusvar.h"
+
+static void octebus_identify(driver_t *drv, device_t parent);
+static int octebus_probe(device_t dev);
+static int octebus_attach(device_t dev);
+static int octebus_detach(device_t dev);
+static int octebus_shutdown(device_t dev);
+
+static device_method_t octebus_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_identify, octebus_identify),
+ DEVMETHOD(device_probe, octebus_probe),
+ DEVMETHOD(device_attach, octebus_attach),
+ DEVMETHOD(device_detach, octebus_detach),
+ DEVMETHOD(device_shutdown, octebus_shutdown),
+
+ /* Bus interface. */
+ DEVMETHOD(bus_add_child, bus_generic_add_child),
+
+ { 0, 0 }
+};
+
+static driver_t octebus_driver = {
+ "octebus",
+ octebus_methods,
+ sizeof (struct octebus_softc),
+};
+
+static devclass_t octebus_devclass;
+
+DRIVER_MODULE(octebus, ciu, octebus_driver, octebus_devclass, 0, 0);
+
+static void
+octebus_identify(driver_t *drv, device_t parent)
+{
+ BUS_ADD_CHILD(parent, 0, "octebus", 0);
+}
+
+static int
+octebus_probe(device_t dev)
+{
+ if (device_get_unit(dev) != 0)
+ return (ENXIO);
+ device_set_desc(dev, "Cavium Octeon Ethernet pseudo-bus");
+ return (0);
+}
+
+static int
+octebus_attach(device_t dev)
+{
+ struct octebus_softc *sc;
+ int rv;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ rv = cvm_oct_init_module(dev);
+ if (rv != 0)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+octebus_detach(device_t dev)
+{
+ cvm_oct_cleanup_module();
+ return (0);
+}
+
+static int
+octebus_shutdown(device_t dev)
+{
+ return (octebus_detach(dev));
+}
diff --git a/sys/mips/cavium/octe/octebusvar.h b/sys/mips/cavium/octe/octebusvar.h
new file mode 100644
index 000000000000..061c02018407
--- /dev/null
+++ b/sys/mips/cavium/octe/octebusvar.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _CAVIUM_OCTE_OCTEBUSVAR_H_
+#define _CAVIUM_OCTE_OCTEBUSVAR_H_
+
+struct octebus_softc {
+ device_t sc_dev;
+
+ struct resource *sc_rx_irq;
+
+ struct resource *sc_rgmii_irq;
+ struct resource *sc_spi_irq;
+};
+
+#endif /* !_CAVIUM_OCTE_OCTEBUSVAR_H_ */
diff --git a/sys/mips/cavium/octe/wrapper-cvmx-includes.h b/sys/mips/cavium/octe/wrapper-cvmx-includes.h
new file mode 100644
index 000000000000..bc695052e715
--- /dev/null
+++ b/sys/mips/cavium/octe/wrapper-cvmx-includes.h
@@ -0,0 +1,50 @@
+/*************************************************************************
+Copyright (c) 2003-2007 Cavium Networks (support@cavium.com). All rights
+reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Cavium Networks nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+This Software, including technical data, may be subject to U.S. export control laws, including the U.S. Export Administration Act and its associated regulations, and may be subject to export or import regulations in other countries.
+
+TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+
+*************************************************************************/
+/* $FreeBSD$ */
+
+#ifndef __WRAPPER_CVMX_INCLUDES_H__
+#define __WRAPPER_CVMX_INCLUDES_H__
+
+#include <contrib/octeon-sdk/cvmx.h>
+#include <contrib/octeon-sdk/cvmx-version.h>
+#include <contrib/octeon-sdk/cvmx-atomic.h>
+#include <contrib/octeon-sdk/cvmx-ciu.h>
+#include <contrib/octeon-sdk/cvmx-pip.h>
+#include <contrib/octeon-sdk/cvmx-ipd.h>
+#include <contrib/octeon-sdk/cvmx-pko.h>
+#include <contrib/octeon-sdk/cvmx-pow.h>
+#include <contrib/octeon-sdk/cvmx-gmx.h>
+#include <contrib/octeon-sdk/cvmx-spi.h>
+#include <contrib/octeon-sdk/cvmx-bootmem.h>
+#include <contrib/octeon-sdk/cvmx-app-init.h>
+#include <contrib/octeon-sdk/cvmx-helper.h>
+#include <contrib/octeon-sdk/cvmx-helper-board.h>
+#include <contrib/octeon-sdk/cvmx-interrupt.h>
+
+#endif