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 int number_rgmii_ports;
  34
  35static void cvm_oct_set_hw_preamble(struct octeon_ethernet *priv, bool enable)
  36{
  37        union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
  38        union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
  39        union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
  40        int interface = INTERFACE(priv->port);
  41        int index = INDEX(priv->port);
  42
  43        /* Set preamble checking. */
  44        gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index,
  45                                                                   interface));
  46        gmxx_rxx_frm_ctl.s.pre_chk = enable;
  47        cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface),
  48                       gmxx_rxx_frm_ctl.u64);
  49
  50        /* Set FCS stripping. */
  51        ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
  52        if (enable)
  53                ipd_sub_port_fcs.s.port_bit |= 1ull << priv->port;
  54        else
  55                ipd_sub_port_fcs.s.port_bit &=
  56                                        0xffffffffull ^ (1ull << priv->port);
  57        cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
  58
  59        /* Clear any error bits. */
  60        gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index,
  61                                                                   interface));
  62        cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface),
  63                       gmxx_rxx_int_reg.u64);
  64}
  65
  66static void cvm_oct_rgmii_poll(struct net_device *dev)
  67{
  68        struct octeon_ethernet *priv = netdev_priv(dev);
  69        unsigned long flags = 0;
  70        cvmx_helper_link_info_t link_info;
  71        int use_global_register_lock = (priv->phydev == NULL);
  72
  73        BUG_ON(in_interrupt());
  74        if (use_global_register_lock) {
  75                /*
  76                 * Take the global register lock since we are going to
  77                 * touch registers that affect more than one port.
  78                 */
  79                spin_lock_irqsave(&global_register_lock, flags);
  80        } else {
  81                mutex_lock(&priv->phydev->bus->mdio_lock);
  82        }
  83
  84        link_info = cvmx_helper_link_get(priv->port);
  85        if (link_info.u64 == priv->link_info) {
  86                if (link_info.s.speed == 10) {
  87                        /*
  88                         * Read the GMXX_RXX_INT_REG[PCTERR] bit and
  89                         * see if we are getting preamble errors.
  90                         */
  91                        int interface = INTERFACE(priv->port);
  92                        int index = INDEX(priv->port);
  93                        union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
  94
  95                        gmxx_rxx_int_reg.u64 =
  96                            cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
  97                                          (index, interface));
  98                        if (gmxx_rxx_int_reg.s.pcterr) {
  99                                /*
 100                                 * We are getting preamble errors at
 101                                 * 10Mbps.  Most likely the PHY is
 102                                 * giving us packets with mis aligned
 103                                 * preambles. In order to get these
 104                                 * packets we need to disable preamble
 105                                 * checking and do it in software.
 106                                 */
 107                                cvm_oct_set_hw_preamble(priv, false);
 108                                printk_ratelimited("%s: Using 10Mbps with software preamble removal\n",
 109                                                   dev->name);
 110                        }
 111                }
 112
 113                if (use_global_register_lock)
 114                        spin_unlock_irqrestore(&global_register_lock, flags);
 115                else
 116                        mutex_unlock(&priv->phydev->bus->mdio_lock);
 117                return;
 118        }
 119
 120        /* Since the 10Mbps preamble workaround is allowed we need to enable
 121         * preamble checking, FCS stripping, and clear error bits on
 122         * every speed change. If errors occur during 10Mbps operation
 123         * the above code will change this stuff
 124         */
 125        cvm_oct_set_hw_preamble(priv, true);
 126
 127        if (priv->phydev == NULL) {
 128                link_info = cvmx_helper_link_autoconf(priv->port);
 129                priv->link_info = link_info.u64;
 130        }
 131
 132        if (use_global_register_lock)
 133                spin_unlock_irqrestore(&global_register_lock, flags);
 134        else
 135                mutex_unlock(&priv->phydev->bus->mdio_lock);
 136
 137        if (priv->phydev == NULL) {
 138                /* Tell core. */
 139                if (link_info.s.link_up) {
 140                        if (!netif_carrier_ok(dev))
 141                                netif_carrier_on(dev);
 142                } else if (netif_carrier_ok(dev)) {
 143                        netif_carrier_off(dev);
 144                }
 145                cvm_oct_note_carrier(priv, link_info);
 146        }
 147}
 148
 149static int cmv_oct_rgmii_gmx_interrupt(int interface)
 150{
 151        int index;
 152        int count = 0;
 153
 154        /* Loop through every port of this interface */
 155        for (index = 0;
 156             index < cvmx_helper_ports_on_interface(interface);
 157             index++) {
 158                union cvmx_gmxx_rxx_int_reg gmx_rx_int_reg;
 159
 160                /* Read the GMX interrupt status bits */
 161                gmx_rx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
 162                                          (index, interface));
 163                gmx_rx_int_reg.u64 &= cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
 164                                          (index, interface));
 165
 166                /* Poll the port if inband status changed */
 167                if (gmx_rx_int_reg.s.phy_dupx || gmx_rx_int_reg.s.phy_link ||
 168                    gmx_rx_int_reg.s.phy_spd) {
 169                        struct net_device *dev =
 170                                    cvm_oct_device[cvmx_helper_get_ipd_port
 171                                                   (interface, index)];
 172                        struct octeon_ethernet *priv = netdev_priv(dev);
 173
 174                        if (dev && !atomic_read(&cvm_oct_poll_queue_stopping))
 175                                queue_work(cvm_oct_poll_queue,
 176                                           &priv->port_work);
 177
 178                        gmx_rx_int_reg.u64 = 0;
 179                        gmx_rx_int_reg.s.phy_dupx = 1;
 180                        gmx_rx_int_reg.s.phy_link = 1;
 181                        gmx_rx_int_reg.s.phy_spd = 1;
 182                        cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface),
 183                                       gmx_rx_int_reg.u64);
 184                        count++;
 185                }
 186        }
 187        return count;
 188}
 189
 190static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id)
 191{
 192        union cvmx_npi_rsl_int_blocks rsl_int_blocks;
 193        int count = 0;
 194
 195        rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
 196
 197        /* Check and see if this interrupt was caused by the GMX0 block */
 198        if (rsl_int_blocks.s.gmx0)
 199                count += cmv_oct_rgmii_gmx_interrupt(0);
 200
 201        /* Check and see if this interrupt was caused by the GMX1 block */
 202        if (rsl_int_blocks.s.gmx1)
 203                count += cmv_oct_rgmii_gmx_interrupt(1);
 204
 205        return count ? IRQ_HANDLED : IRQ_NONE;
 206}
 207
 208int cvm_oct_rgmii_open(struct net_device *dev)
 209{
 210        return cvm_oct_common_open(dev, cvm_oct_rgmii_poll);
 211}
 212
 213static void cvm_oct_rgmii_immediate_poll(struct work_struct *work)
 214{
 215        struct octeon_ethernet *priv =
 216                container_of(work, struct octeon_ethernet, port_work);
 217        cvm_oct_rgmii_poll(cvm_oct_device[priv->port]);
 218}
 219
 220int cvm_oct_rgmii_init(struct net_device *dev)
 221{
 222        struct octeon_ethernet *priv = netdev_priv(dev);
 223        int r;
 224
 225        cvm_oct_common_init(dev);
 226        INIT_WORK(&priv->port_work, cvm_oct_rgmii_immediate_poll);
 227        /*
 228         * Due to GMX errata in CN3XXX series chips, it is necessary
 229         * to take the link down immediately when the PHY changes
 230         * state. In order to do this we call the poll function every
 231         * time the RGMII inband status changes.  This may cause
 232         * problems if the PHY doesn't implement inband status
 233         * properly.
 234         */
 235        if (number_rgmii_ports == 0) {
 236                r = request_irq(OCTEON_IRQ_RML, cvm_oct_rgmii_rml_interrupt,
 237                                IRQF_SHARED, "RGMII", &number_rgmii_ports);
 238                if (r != 0)
 239                        return r;
 240        }
 241        number_rgmii_ports++;
 242
 243        /*
 244         * Only true RGMII ports need to be polled. In GMII mode, port
 245         * 0 is really a RGMII port.
 246         */
 247        if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
 248             && (priv->port == 0))
 249            || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
 250
 251                if (!octeon_is_simulation()) {
 252
 253                        union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
 254                        int interface = INTERFACE(priv->port);
 255                        int index = INDEX(priv->port);
 256
 257                        /*
 258                         * Enable interrupts on inband status changes
 259                         * for this port.
 260                         */
 261                        gmx_rx_int_en.u64 = 0;
 262                        gmx_rx_int_en.s.phy_dupx = 1;
 263                        gmx_rx_int_en.s.phy_link = 1;
 264                        gmx_rx_int_en.s.phy_spd = 1;
 265                        cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
 266                                       gmx_rx_int_en.u64);
 267                }
 268        }
 269
 270        return 0;
 271}
 272
 273void cvm_oct_rgmii_uninit(struct net_device *dev)
 274{
 275        struct octeon_ethernet *priv = netdev_priv(dev);
 276
 277        cvm_oct_common_uninit(dev);
 278
 279        /*
 280         * Only true RGMII ports need to be polled. In GMII mode, port
 281         * 0 is really a RGMII port.
 282         */
 283        if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
 284             && (priv->port == 0))
 285            || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
 286
 287                if (!octeon_is_simulation()) {
 288
 289                        union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
 290                        int interface = INTERFACE(priv->port);
 291                        int index = INDEX(priv->port);
 292
 293                        /*
 294                         * Disable interrupts on inband status changes
 295                         * for this port.
 296                         */
 297                        gmx_rx_int_en.u64 =
 298                            cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
 299                                          (index, interface));
 300                        gmx_rx_int_en.s.phy_dupx = 0;
 301                        gmx_rx_int_en.s.phy_link = 0;
 302                        gmx_rx_int_en.s.phy_spd = 0;
 303                        cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
 304                                       gmx_rx_int_en.u64);
 305                }
 306        }
 307
 308        /* Remove the interrupt handler when the last port is removed. */
 309        number_rgmii_ports--;
 310        if (number_rgmii_ports == 0)
 311                free_irq(OCTEON_IRQ_RML, &number_rgmii_ports);
 312        cancel_work_sync(&priv->port_work);
 313}
 314