linux/drivers/staging/octeon/ethernet-rgmii.c
<<
>>
Prefs
   1/*********************************************************************
   2 * Author: Cavium Networks
   3 *
   4 * Contact: support@caviumnetworks.com
   5 * This file is part of the OCTEON SDK
   6 *
   7 * Copyright (c) 2003-2007 Cavium Networks
   8 *
   9 * This file is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License, Version 2, as
  11 * published by the Free Software Foundation.
  12 *
  13 * This file is distributed in the hope that it will be useful, but
  14 * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
  15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
  16 * NONINFRINGEMENT.  See the GNU General Public License for more
  17 * details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this file; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  22 * or visit http://www.gnu.org/licenses/.
  23 *
  24 * This file may also be available under a different license from Cavium.
  25 * Contact Cavium Networks for more information
  26**********************************************************************/
  27#include <linux/kernel.h>
  28#include <linux/netdevice.h>
  29#include <linux/interrupt.h>
  30#include <linux/phy.h>
  31#include <linux/ratelimit.h>
  32#include <net/dst.h>
  33
  34#include <asm/octeon/octeon.h>
  35
  36#include "ethernet-defines.h"
  37#include "octeon-ethernet.h"
  38#include "ethernet-util.h"
  39
  40#include <asm/octeon/cvmx-helper.h>
  41
  42#include <asm/octeon/cvmx-ipd-defs.h>
  43#include <asm/octeon/cvmx-npi-defs.h>
  44#include <asm/octeon/cvmx-gmxx-defs.h>
  45
  46DEFINE_SPINLOCK(global_register_lock);
  47
  48static int number_rgmii_ports;
  49
  50static void cvm_oct_rgmii_poll(struct net_device *dev)
  51{
  52        struct octeon_ethernet *priv = netdev_priv(dev);
  53        unsigned long flags = 0;
  54        cvmx_helper_link_info_t link_info;
  55        int use_global_register_lock = (priv->phydev == NULL);
  56
  57        BUG_ON(in_interrupt());
  58        if (use_global_register_lock) {
  59                /*
  60                 * Take the global register lock since we are going to
  61                 * touch registers that affect more than one port.
  62                 */
  63                spin_lock_irqsave(&global_register_lock, flags);
  64        } else {
  65                mutex_lock(&priv->phydev->bus->mdio_lock);
  66        }
  67
  68        link_info = cvmx_helper_link_get(priv->port);
  69        if (link_info.u64 == priv->link_info) {
  70
  71                /*
  72                 * If the 10Mbps preamble workaround is supported and we're
  73                 * at 10Mbps we may need to do some special checking.
  74                 */
  75                if (USE_10MBPS_PREAMBLE_WORKAROUND && (link_info.s.speed == 10)) {
  76
  77                        /*
  78                         * Read the GMXX_RXX_INT_REG[PCTERR] bit and
  79                         * see if we are getting preamble errors.
  80                         */
  81                        int interface = INTERFACE(priv->port);
  82                        int index = INDEX(priv->port);
  83                        union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
  84                        gmxx_rxx_int_reg.u64 =
  85                            cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
  86                                          (index, interface));
  87                        if (gmxx_rxx_int_reg.s.pcterr) {
  88
  89                                /*
  90                                 * We are getting preamble errors at
  91                                 * 10Mbps.  Most likely the PHY is
  92                                 * giving us packets with mis aligned
  93                                 * preambles. In order to get these
  94                                 * packets we need to disable preamble
  95                                 * checking and do it in software.
  96                                 */
  97                                union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
  98                                union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
  99
 100                                /* Disable preamble checking */
 101                                gmxx_rxx_frm_ctl.u64 =
 102                                    cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL
 103                                                  (index, interface));
 104                                gmxx_rxx_frm_ctl.s.pre_chk = 0;
 105                                cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL
 106                                               (index, interface),
 107                                               gmxx_rxx_frm_ctl.u64);
 108
 109                                /* Disable FCS stripping */
 110                                ipd_sub_port_fcs.u64 =
 111                                    cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
 112                                ipd_sub_port_fcs.s.port_bit &=
 113                                    0xffffffffull ^ (1ull << priv->port);
 114                                cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS,
 115                                               ipd_sub_port_fcs.u64);
 116
 117                                /* Clear any error bits */
 118                                cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
 119                                               (index, interface),
 120                                               gmxx_rxx_int_reg.u64);
 121                                printk_ratelimited("%s: Using 10Mbps with software "
 122                                                   "preamble removal\n",
 123                                                   dev->name);
 124                        }
 125                }
 126
 127                if (use_global_register_lock)
 128                        spin_unlock_irqrestore(&global_register_lock, flags);
 129                else
 130                        mutex_unlock(&priv->phydev->bus->mdio_lock);
 131                return;
 132        }
 133
 134        /* If the 10Mbps preamble workaround is allowed we need to on
 135           preamble checking, FCS stripping, and clear error bits on
 136           every speed change. If errors occur during 10Mbps operation
 137           the above code will change this stuff */
 138        if (USE_10MBPS_PREAMBLE_WORKAROUND) {
 139
 140                union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
 141                union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
 142                union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
 143                int interface = INTERFACE(priv->port);
 144                int index = INDEX(priv->port);
 145
 146                /* Enable preamble checking */
 147                gmxx_rxx_frm_ctl.u64 =
 148                    cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
 149                gmxx_rxx_frm_ctl.s.pre_chk = 1;
 150                cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface),
 151                               gmxx_rxx_frm_ctl.u64);
 152                /* Enable FCS stripping */
 153                ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
 154                ipd_sub_port_fcs.s.port_bit |= 1ull << priv->port;
 155                cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
 156                /* Clear any error bits */
 157                gmxx_rxx_int_reg.u64 =
 158                    cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
 159                cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface),
 160                               gmxx_rxx_int_reg.u64);
 161        }
 162        if (priv->phydev == NULL) {
 163                link_info = cvmx_helper_link_autoconf(priv->port);
 164                priv->link_info = link_info.u64;
 165        }
 166
 167        if (use_global_register_lock)
 168                spin_unlock_irqrestore(&global_register_lock, flags);
 169        else {
 170                mutex_unlock(&priv->phydev->bus->mdio_lock);
 171        }
 172
 173        if (priv->phydev == NULL) {
 174                /* Tell core. */
 175                if (link_info.s.link_up) {
 176                        if (!netif_carrier_ok(dev))
 177                                netif_carrier_on(dev);
 178                        if (priv->queue != -1)
 179                                printk_ratelimited("%s: %u Mbps %s duplex, "
 180                                                   "port %2d, queue %2d\n",
 181                                                   dev->name, link_info.s.speed,
 182                                                   (link_info.s.full_duplex) ?
 183                                                   "Full" : "Half",
 184                                                   priv->port, priv->queue);
 185                        else
 186                                printk_ratelimited("%s: %u Mbps %s duplex, "
 187                                                   "port %2d, POW\n",
 188                                                   dev->name, link_info.s.speed,
 189                                                   (link_info.s.full_duplex) ?
 190                                                   "Full" : "Half",
 191                                                   priv->port);
 192                } else {
 193                        if (netif_carrier_ok(dev))
 194                                netif_carrier_off(dev);
 195                        printk_ratelimited("%s: Link down\n", dev->name);
 196                }
 197        }
 198}
 199
 200static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id)
 201{
 202        union cvmx_npi_rsl_int_blocks rsl_int_blocks;
 203        int index;
 204        irqreturn_t return_status = IRQ_NONE;
 205
 206        rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
 207
 208        /* Check and see if this interrupt was caused by the GMX0 block */
 209        if (rsl_int_blocks.s.gmx0) {
 210
 211                int interface = 0;
 212                /* Loop through every port of this interface */
 213                for (index = 0;
 214                     index < cvmx_helper_ports_on_interface(interface);
 215                     index++) {
 216
 217                        /* Read the GMX interrupt status bits */
 218                        union cvmx_gmxx_rxx_int_reg gmx_rx_int_reg;
 219                        gmx_rx_int_reg.u64 =
 220                            cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
 221                                          (index, interface));
 222                        gmx_rx_int_reg.u64 &=
 223                            cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
 224                                          (index, interface));
 225                        /* Poll the port if inband status changed */
 226                        if (gmx_rx_int_reg.s.phy_dupx
 227                            || gmx_rx_int_reg.s.phy_link
 228                            || gmx_rx_int_reg.s.phy_spd) {
 229
 230                                struct net_device *dev =
 231                                    cvm_oct_device[cvmx_helper_get_ipd_port
 232                                                   (interface, index)];
 233                                struct octeon_ethernet *priv = netdev_priv(dev);
 234
 235                                if (dev && !atomic_read(&cvm_oct_poll_queue_stopping))
 236                                        queue_work(cvm_oct_poll_queue, &priv->port_work);
 237
 238                                gmx_rx_int_reg.u64 = 0;
 239                                gmx_rx_int_reg.s.phy_dupx = 1;
 240                                gmx_rx_int_reg.s.phy_link = 1;
 241                                gmx_rx_int_reg.s.phy_spd = 1;
 242                                cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
 243                                               (index, interface),
 244                                               gmx_rx_int_reg.u64);
 245                                return_status = IRQ_HANDLED;
 246                        }
 247                }
 248        }
 249
 250        /* Check and see if this interrupt was caused by the GMX1 block */
 251        if (rsl_int_blocks.s.gmx1) {
 252
 253                int interface = 1;
 254                /* Loop through every port of this interface */
 255                for (index = 0;
 256                     index < cvmx_helper_ports_on_interface(interface);
 257                     index++) {
 258
 259                        /* Read the GMX interrupt status bits */
 260                        union cvmx_gmxx_rxx_int_reg gmx_rx_int_reg;
 261                        gmx_rx_int_reg.u64 =
 262                            cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
 263                                          (index, interface));
 264                        gmx_rx_int_reg.u64 &=
 265                            cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
 266                                          (index, interface));
 267                        /* Poll the port if inband status changed */
 268                        if (gmx_rx_int_reg.s.phy_dupx
 269                            || gmx_rx_int_reg.s.phy_link
 270                            || gmx_rx_int_reg.s.phy_spd) {
 271
 272                                struct net_device *dev =
 273                                    cvm_oct_device[cvmx_helper_get_ipd_port
 274                                                   (interface, index)];
 275                                struct octeon_ethernet *priv = netdev_priv(dev);
 276
 277                                if (dev && !atomic_read(&cvm_oct_poll_queue_stopping))
 278                                        queue_work(cvm_oct_poll_queue, &priv->port_work);
 279
 280                                gmx_rx_int_reg.u64 = 0;
 281                                gmx_rx_int_reg.s.phy_dupx = 1;
 282                                gmx_rx_int_reg.s.phy_link = 1;
 283                                gmx_rx_int_reg.s.phy_spd = 1;
 284                                cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
 285                                               (index, interface),
 286                                               gmx_rx_int_reg.u64);
 287                                return_status = IRQ_HANDLED;
 288                        }
 289                }
 290        }
 291        return return_status;
 292}
 293
 294int cvm_oct_rgmii_open(struct net_device *dev)
 295{
 296        union cvmx_gmxx_prtx_cfg gmx_cfg;
 297        struct octeon_ethernet *priv = netdev_priv(dev);
 298        int interface = INTERFACE(priv->port);
 299        int index = INDEX(priv->port);
 300        cvmx_helper_link_info_t link_info;
 301
 302        gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
 303        gmx_cfg.s.en = 1;
 304        cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
 305
 306        if (!octeon_is_simulation()) {
 307                link_info = cvmx_helper_link_get(priv->port);
 308                if (!link_info.s.link_up)
 309                        netif_carrier_off(dev);
 310        }
 311
 312        return 0;
 313}
 314
 315int cvm_oct_rgmii_stop(struct net_device *dev)
 316{
 317        union cvmx_gmxx_prtx_cfg gmx_cfg;
 318        struct octeon_ethernet *priv = netdev_priv(dev);
 319        int interface = INTERFACE(priv->port);
 320        int index = INDEX(priv->port);
 321
 322        gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
 323        gmx_cfg.s.en = 0;
 324        cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
 325        return 0;
 326}
 327
 328static void cvm_oct_rgmii_immediate_poll(struct work_struct *work)
 329{
 330        struct octeon_ethernet *priv = container_of(work, struct octeon_ethernet, port_work);
 331        cvm_oct_rgmii_poll(cvm_oct_device[priv->port]);
 332}
 333
 334int cvm_oct_rgmii_init(struct net_device *dev)
 335{
 336        struct octeon_ethernet *priv = netdev_priv(dev);
 337        int r;
 338
 339        cvm_oct_common_init(dev);
 340        dev->netdev_ops->ndo_stop(dev);
 341        INIT_WORK(&priv->port_work, cvm_oct_rgmii_immediate_poll);
 342        /*
 343         * Due to GMX errata in CN3XXX series chips, it is necessary
 344         * to take the link down immediately when the PHY changes
 345         * state. In order to do this we call the poll function every
 346         * time the RGMII inband status changes.  This may cause
 347         * problems if the PHY doesn't implement inband status
 348         * properly.
 349         */
 350        if (number_rgmii_ports == 0) {
 351                r = request_irq(OCTEON_IRQ_RML, cvm_oct_rgmii_rml_interrupt,
 352                                IRQF_SHARED, "RGMII", &number_rgmii_ports);
 353                if (r != 0)
 354                        return r;
 355        }
 356        number_rgmii_ports++;
 357
 358        /*
 359         * Only true RGMII ports need to be polled. In GMII mode, port
 360         * 0 is really a RGMII port.
 361         */
 362        if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
 363             && (priv->port == 0))
 364            || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
 365
 366                if (!octeon_is_simulation()) {
 367
 368                        union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
 369                        int interface = INTERFACE(priv->port);
 370                        int index = INDEX(priv->port);
 371
 372                        /*
 373                         * Enable interrupts on inband status changes
 374                         * for this port.
 375                         */
 376                        gmx_rx_int_en.u64 = 0;
 377                        gmx_rx_int_en.s.phy_dupx = 1;
 378                        gmx_rx_int_en.s.phy_link = 1;
 379                        gmx_rx_int_en.s.phy_spd = 1;
 380                        cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
 381                                       gmx_rx_int_en.u64);
 382                        priv->poll = cvm_oct_rgmii_poll;
 383                }
 384        }
 385
 386        return 0;
 387}
 388
 389void cvm_oct_rgmii_uninit(struct net_device *dev)
 390{
 391        struct octeon_ethernet *priv = netdev_priv(dev);
 392        cvm_oct_common_uninit(dev);
 393
 394        /*
 395         * Only true RGMII ports need to be polled. In GMII mode, port
 396         * 0 is really a RGMII port.
 397         */
 398        if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
 399             && (priv->port == 0))
 400            || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
 401
 402                if (!octeon_is_simulation()) {
 403
 404                        union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
 405                        int interface = INTERFACE(priv->port);
 406                        int index = INDEX(priv->port);
 407
 408                        /*
 409                         * Disable interrupts on inband status changes
 410                         * for this port.
 411                         */
 412                        gmx_rx_int_en.u64 =
 413                            cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
 414                                          (index, interface));
 415                        gmx_rx_int_en.s.phy_dupx = 0;
 416                        gmx_rx_int_en.s.phy_link = 0;
 417                        gmx_rx_int_en.s.phy_spd = 0;
 418                        cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
 419                                       gmx_rx_int_en.u64);
 420                }
 421        }
 422
 423        /* Remove the interrupt handler when the last port is removed. */
 424        number_rgmii_ports--;
 425        if (number_rgmii_ports == 0)
 426                free_irq(OCTEON_IRQ_RML, &number_rgmii_ports);
 427        cancel_work_sync(&priv->port_work);
 428}
 429