linux/drivers/staging/octeon/ethernet-rgmii.c
<<
>>
Prefs
   1/*
   2 * This file is based on code from OCTEON SDK by Cavium Networks.
   3 *
   4 * Copyright (c) 2003-2007 Cavium Networks
   5 *
   6 * This file is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License, Version 2, as
   8 * published by the Free Software Foundation.
   9 */
  10
  11#include <linux/kernel.h>
  12#include <linux/netdevice.h>
  13#include <linux/interrupt.h>
  14#include <linux/phy.h>
  15#include <linux/ratelimit.h>
  16#include <net/dst.h>
  17
  18#include <asm/octeon/octeon.h>
  19
  20#include "ethernet-defines.h"
  21#include "octeon-ethernet.h"
  22#include "ethernet-util.h"
  23#include "ethernet-mdio.h"
  24
  25#include <asm/octeon/cvmx-helper.h>
  26
  27#include <asm/octeon/cvmx-ipd-defs.h>
  28#include <asm/octeon/cvmx-npi-defs.h>
  29#include <asm/octeon/cvmx-gmxx-defs.h>
  30
  31static DEFINE_SPINLOCK(global_register_lock);
  32
  33static void cvm_oct_set_hw_preamble(struct octeon_ethernet *priv, bool enable)
  34{
  35        union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
  36        union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
  37        union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
  38        int interface = INTERFACE(priv->port);
  39        int index = INDEX(priv->port);
  40
  41        /* Set preamble checking. */
  42        gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index,
  43                                                                   interface));
  44        gmxx_rxx_frm_ctl.s.pre_chk = enable;
  45        cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface),
  46                       gmxx_rxx_frm_ctl.u64);
  47
  48        /* Set FCS stripping. */
  49        ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
  50        if (enable)
  51                ipd_sub_port_fcs.s.port_bit |= 1ull << priv->port;
  52        else
  53                ipd_sub_port_fcs.s.port_bit &=
  54                                        0xffffffffull ^ (1ull << priv->port);
  55        cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
  56
  57        /* Clear any error bits. */
  58        gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index,
  59                                                                   interface));
  60        cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface),
  61                       gmxx_rxx_int_reg.u64);
  62}
  63
  64static void cvm_oct_check_preamble_errors(struct net_device *dev)
  65{
  66        struct octeon_ethernet *priv = netdev_priv(dev);
  67        cvmx_helper_link_info_t link_info;
  68        unsigned long flags;
  69
  70        link_info.u64 = priv->link_info;
  71
  72        /*
  73         * Take the global register lock since we are going to
  74         * touch registers that affect more than one port.
  75         */
  76        spin_lock_irqsave(&global_register_lock, flags);
  77
  78        if (link_info.s.speed == 10 && priv->last_speed == 10) {
  79                /*
  80                 * Read the GMXX_RXX_INT_REG[PCTERR] bit and see if we are
  81                 * getting preamble errors.
  82                 */
  83                int interface = INTERFACE(priv->port);
  84                int index = INDEX(priv->port);
  85                union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
  86
  87                gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
  88                                                        (index, interface));
  89                if (gmxx_rxx_int_reg.s.pcterr) {
  90                        /*
  91                         * We are getting preamble errors at 10Mbps. Most
  92                         * likely the PHY is giving us packets with misaligned
  93                         * preambles. In order to get these packets we need to
  94                         * disable preamble checking and do it in software.
  95                         */
  96                        cvm_oct_set_hw_preamble(priv, false);
  97                        printk_ratelimited("%s: Using 10Mbps with software preamble removal\n",
  98                                           dev->name);
  99                }
 100        } else {
 101                /*
 102                 * Since the 10Mbps preamble workaround is allowed we need to
 103                 * enable preamble checking, FCS stripping, and clear error
 104                 * bits on every speed change. If errors occur during 10Mbps
 105                 * operation the above code will change this stuff
 106                 */
 107                if (priv->last_speed != link_info.s.speed)
 108                        cvm_oct_set_hw_preamble(priv, true);
 109                priv->last_speed = link_info.s.speed;
 110        }
 111        spin_unlock_irqrestore(&global_register_lock, flags);
 112}
 113
 114static void cvm_oct_rgmii_poll(struct net_device *dev)
 115{
 116        struct octeon_ethernet *priv = netdev_priv(dev);
 117        cvmx_helper_link_info_t link_info;
 118        bool status_change;
 119
 120        link_info = cvmx_helper_link_get(priv->port);
 121        if (priv->link_info != link_info.u64 &&
 122            cvmx_helper_link_set(priv->port, link_info))
 123                link_info.u64 = priv->link_info;
 124        status_change = priv->link_info != link_info.u64;
 125        priv->link_info = link_info.u64;
 126
 127        cvm_oct_check_preamble_errors(dev);
 128
 129        if (likely(!status_change))
 130                return;
 131
 132        /* Tell core. */
 133        if (link_info.s.link_up) {
 134                if (!netif_carrier_ok(dev))
 135                        netif_carrier_on(dev);
 136        } else if (netif_carrier_ok(dev)) {
 137                netif_carrier_off(dev);
 138        }
 139        cvm_oct_note_carrier(priv, link_info);
 140}
 141
 142int cvm_oct_rgmii_open(struct net_device *dev)
 143{
 144        struct octeon_ethernet *priv = netdev_priv(dev);
 145        int ret;
 146
 147        ret = cvm_oct_common_open(dev, cvm_oct_rgmii_poll);
 148        if (ret)
 149                return ret;
 150
 151        if (dev->phydev) {
 152                /*
 153                 * In phydev mode, we need still periodic polling for the
 154                 * preamble error checking, and we also need to call this
 155                 * function on every link state change.
 156                 *
 157                 * Only true RGMII ports need to be polled. In GMII mode, port
 158                 * 0 is really a RGMII port.
 159                 */
 160                if ((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII &&
 161                     priv->port  == 0) ||
 162                    (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
 163                        priv->poll = cvm_oct_check_preamble_errors;
 164                        cvm_oct_check_preamble_errors(dev);
 165                }
 166        }
 167
 168        return 0;
 169}
 170