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/mii.h>
  30#include <net/dst.h>
  31
  32#include <asm/octeon/octeon.h>
  33
  34#include "ethernet-defines.h"
  35#include "octeon-ethernet.h"
  36#include "ethernet-util.h"
  37
  38#include "cvmx-helper.h"
  39
  40#include <asm/octeon/cvmx-ipd-defs.h>
  41#include <asm/octeon/cvmx-npi-defs.h>
  42#include "cvmx-gmxx-defs.h"
  43
  44DEFINE_SPINLOCK(global_register_lock);
  45
  46static int number_rgmii_ports;
  47
  48static void cvm_oct_rgmii_poll(struct net_device *dev)
  49{
  50        struct octeon_ethernet *priv = netdev_priv(dev);
  51        unsigned long flags;
  52        cvmx_helper_link_info_t link_info;
  53
  54        /*
  55         * Take the global register lock since we are going to touch
  56         * registers that affect more than one port.
  57         */
  58        spin_lock_irqsave(&global_register_lock, flags);
  59
  60        link_info = cvmx_helper_link_get(priv->port);
  61        if (link_info.u64 == priv->link_info) {
  62
  63                /*
  64                 * If the 10Mbps preamble workaround is supported and we're
  65                 * at 10Mbps we may need to do some special checking.
  66                 */
  67                if (USE_10MBPS_PREAMBLE_WORKAROUND && (link_info.s.speed == 10)) {
  68
  69                        /*
  70                         * Read the GMXX_RXX_INT_REG[PCTERR] bit and
  71                         * see if we are getting preamble errors.
  72                         */
  73                        int interface = INTERFACE(priv->port);
  74                        int index = INDEX(priv->port);
  75                        union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
  76                        gmxx_rxx_int_reg.u64 =
  77                            cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
  78                                          (index, interface));
  79                        if (gmxx_rxx_int_reg.s.pcterr) {
  80
  81                                /*
  82                                 * We are getting preamble errors at
  83                                 * 10Mbps.  Most likely the PHY is
  84                                 * giving us packets with mis aligned
  85                                 * preambles. In order to get these
  86                                 * packets we need to disable preamble
  87                                 * checking and do it in software.
  88                                 */
  89                                union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
  90                                union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
  91
  92                                /* Disable preamble checking */
  93                                gmxx_rxx_frm_ctl.u64 =
  94                                    cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL
  95                                                  (index, interface));
  96                                gmxx_rxx_frm_ctl.s.pre_chk = 0;
  97                                cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL
  98                                               (index, interface),
  99                                               gmxx_rxx_frm_ctl.u64);
 100
 101                                /* Disable FCS stripping */
 102                                ipd_sub_port_fcs.u64 =
 103                                    cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
 104                                ipd_sub_port_fcs.s.port_bit &=
 105                                    0xffffffffull ^ (1ull << priv->port);
 106                                cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS,
 107                                               ipd_sub_port_fcs.u64);
 108
 109                                /* Clear any error bits */
 110                                cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
 111                                               (index, interface),
 112                                               gmxx_rxx_int_reg.u64);
 113                                DEBUGPRINT("%s: Using 10Mbps with software "
 114                                           "preamble removal\n",
 115                                     dev->name);
 116                        }
 117                }
 118                spin_unlock_irqrestore(&global_register_lock, flags);
 119                return;
 120        }
 121
 122        /* If the 10Mbps preamble workaround is allowed we need to on
 123           preamble checking, FCS stripping, and clear error bits on
 124           every speed change. If errors occur during 10Mbps operation
 125           the above code will change this stuff */
 126        if (USE_10MBPS_PREAMBLE_WORKAROUND) {
 127
 128                union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
 129                union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
 130                union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
 131                int interface = INTERFACE(priv->port);
 132                int index = INDEX(priv->port);
 133
 134                /* Enable preamble checking */
 135                gmxx_rxx_frm_ctl.u64 =
 136                    cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
 137                gmxx_rxx_frm_ctl.s.pre_chk = 1;
 138                cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface),
 139                               gmxx_rxx_frm_ctl.u64);
 140                /* Enable FCS stripping */
 141                ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
 142                ipd_sub_port_fcs.s.port_bit |= 1ull << priv->port;
 143                cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
 144                /* Clear any error bits */
 145                gmxx_rxx_int_reg.u64 =
 146                    cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
 147                cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface),
 148                               gmxx_rxx_int_reg.u64);
 149        }
 150
 151        link_info = cvmx_helper_link_autoconf(priv->port);
 152        priv->link_info = link_info.u64;
 153        spin_unlock_irqrestore(&global_register_lock, flags);
 154
 155        /* Tell Linux */
 156        if (link_info.s.link_up) {
 157
 158                if (!netif_carrier_ok(dev))
 159                        netif_carrier_on(dev);
 160                if (priv->queue != -1)
 161                        DEBUGPRINT
 162                            ("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
 163                             dev->name, link_info.s.speed,
 164                             (link_info.s.full_duplex) ? "Full" : "Half",
 165                             priv->port, priv->queue);
 166                else
 167                        DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, POW\n",
 168                                   dev->name, link_info.s.speed,
 169                                   (link_info.s.full_duplex) ? "Full" : "Half",
 170                                   priv->port);
 171        } else {
 172
 173                if (netif_carrier_ok(dev))
 174                        netif_carrier_off(dev);
 175                DEBUGPRINT("%s: Link down\n", dev->name);
 176        }
 177}
 178
 179static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id)
 180{
 181        union cvmx_npi_rsl_int_blocks rsl_int_blocks;
 182        int index;
 183        irqreturn_t return_status = IRQ_NONE;
 184
 185        rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
 186
 187        /* Check and see if this interrupt was caused by the GMX0 block */
 188        if (rsl_int_blocks.s.gmx0) {
 189
 190                int interface = 0;
 191                /* Loop through every port of this interface */
 192                for (index = 0;
 193                     index < cvmx_helper_ports_on_interface(interface);
 194                     index++) {
 195
 196                        /* Read the GMX interrupt status bits */
 197                        union cvmx_gmxx_rxx_int_reg gmx_rx_int_reg;
 198                        gmx_rx_int_reg.u64 =
 199                            cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
 200                                          (index, interface));
 201                        gmx_rx_int_reg.u64 &=
 202                            cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
 203                                          (index, interface));
 204                        /* Poll the port if inband status changed */
 205                        if (gmx_rx_int_reg.s.phy_dupx
 206                            || gmx_rx_int_reg.s.phy_link
 207                            || gmx_rx_int_reg.s.phy_spd) {
 208
 209                                struct net_device *dev =
 210                                    cvm_oct_device[cvmx_helper_get_ipd_port
 211                                                   (interface, index)];
 212                                if (dev)
 213                                        cvm_oct_rgmii_poll(dev);
 214                                gmx_rx_int_reg.u64 = 0;
 215                                gmx_rx_int_reg.s.phy_dupx = 1;
 216                                gmx_rx_int_reg.s.phy_link = 1;
 217                                gmx_rx_int_reg.s.phy_spd = 1;
 218                                cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
 219                                               (index, interface),
 220                                               gmx_rx_int_reg.u64);
 221                                return_status = IRQ_HANDLED;
 222                        }
 223                }
 224        }
 225
 226        /* Check and see if this interrupt was caused by the GMX1 block */
 227        if (rsl_int_blocks.s.gmx1) {
 228
 229                int interface = 1;
 230                /* Loop through every port of this interface */
 231                for (index = 0;
 232                     index < cvmx_helper_ports_on_interface(interface);
 233                     index++) {
 234
 235                        /* Read the GMX interrupt status bits */
 236                        union cvmx_gmxx_rxx_int_reg gmx_rx_int_reg;
 237                        gmx_rx_int_reg.u64 =
 238                            cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
 239                                          (index, interface));
 240                        gmx_rx_int_reg.u64 &=
 241                            cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
 242                                          (index, interface));
 243                        /* Poll the port if inband status changed */
 244                        if (gmx_rx_int_reg.s.phy_dupx
 245                            || gmx_rx_int_reg.s.phy_link
 246                            || gmx_rx_int_reg.s.phy_spd) {
 247
 248                                struct net_device *dev =
 249                                    cvm_oct_device[cvmx_helper_get_ipd_port
 250                                                   (interface, index)];
 251                                if (dev)
 252                                        cvm_oct_rgmii_poll(dev);
 253                                gmx_rx_int_reg.u64 = 0;
 254                                gmx_rx_int_reg.s.phy_dupx = 1;
 255                                gmx_rx_int_reg.s.phy_link = 1;
 256                                gmx_rx_int_reg.s.phy_spd = 1;
 257                                cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
 258                                               (index, interface),
 259                                               gmx_rx_int_reg.u64);
 260                                return_status = IRQ_HANDLED;
 261                        }
 262                }
 263        }
 264        return return_status;
 265}
 266
 267int cvm_oct_rgmii_open(struct net_device *dev)
 268{
 269        union cvmx_gmxx_prtx_cfg gmx_cfg;
 270        struct octeon_ethernet *priv = netdev_priv(dev);
 271        int interface = INTERFACE(priv->port);
 272        int index = INDEX(priv->port);
 273        cvmx_helper_link_info_t link_info;
 274
 275        gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
 276        gmx_cfg.s.en = 1;
 277        cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
 278
 279        if (!octeon_is_simulation()) {
 280                link_info = cvmx_helper_link_get(priv->port);
 281                if (!link_info.s.link_up)
 282                        netif_carrier_off(dev);
 283        }
 284
 285        return 0;
 286}
 287
 288int cvm_oct_rgmii_stop(struct net_device *dev)
 289{
 290        union cvmx_gmxx_prtx_cfg gmx_cfg;
 291        struct octeon_ethernet *priv = netdev_priv(dev);
 292        int interface = INTERFACE(priv->port);
 293        int index = INDEX(priv->port);
 294
 295        gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
 296        gmx_cfg.s.en = 0;
 297        cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
 298        return 0;
 299}
 300
 301int cvm_oct_rgmii_init(struct net_device *dev)
 302{
 303        struct octeon_ethernet *priv = netdev_priv(dev);
 304        int r;
 305
 306        cvm_oct_common_init(dev);
 307        dev->netdev_ops->ndo_stop(dev);
 308
 309        /*
 310         * Due to GMX errata in CN3XXX series chips, it is necessary
 311         * to take the link down immediately whne the PHY changes
 312         * state. In order to do this we call the poll function every
 313         * time the RGMII inband status changes.  This may cause
 314         * problems if the PHY doesn't implement inband status
 315         * properly.
 316         */
 317        if (number_rgmii_ports == 0) {
 318                r = request_irq(OCTEON_IRQ_RML, cvm_oct_rgmii_rml_interrupt,
 319                                IRQF_SHARED, "RGMII", &number_rgmii_ports);
 320        }
 321        number_rgmii_ports++;
 322
 323        /*
 324         * Only true RGMII ports need to be polled. In GMII mode, port
 325         * 0 is really a RGMII port.
 326         */
 327        if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
 328             && (priv->port == 0))
 329            || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
 330
 331                if (!octeon_is_simulation()) {
 332
 333                        union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
 334                        int interface = INTERFACE(priv->port);
 335                        int index = INDEX(priv->port);
 336
 337                        /*
 338                         * Enable interrupts on inband status changes
 339                         * for this port.
 340                         */
 341                        gmx_rx_int_en.u64 =
 342                            cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
 343                                          (index, interface));
 344                        gmx_rx_int_en.s.phy_dupx = 1;
 345                        gmx_rx_int_en.s.phy_link = 1;
 346                        gmx_rx_int_en.s.phy_spd = 1;
 347                        cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
 348                                       gmx_rx_int_en.u64);
 349                        priv->poll = cvm_oct_rgmii_poll;
 350                }
 351        }
 352
 353        return 0;
 354}
 355
 356void cvm_oct_rgmii_uninit(struct net_device *dev)
 357{
 358        struct octeon_ethernet *priv = netdev_priv(dev);
 359        cvm_oct_common_uninit(dev);
 360
 361        /*
 362         * Only true RGMII ports need to be polled. In GMII mode, port
 363         * 0 is really a RGMII port.
 364         */
 365        if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
 366             && (priv->port == 0))
 367            || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
 368
 369                if (!octeon_is_simulation()) {
 370
 371                        union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
 372                        int interface = INTERFACE(priv->port);
 373                        int index = INDEX(priv->port);
 374
 375                        /*
 376                         * Disable interrupts on inband status changes
 377                         * for this port.
 378                         */
 379                        gmx_rx_int_en.u64 =
 380                            cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
 381                                          (index, interface));
 382                        gmx_rx_int_en.s.phy_dupx = 0;
 383                        gmx_rx_int_en.s.phy_link = 0;
 384                        gmx_rx_int_en.s.phy_spd = 0;
 385                        cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
 386                                       gmx_rx_int_en.u64);
 387                }
 388        }
 389
 390        /* Remove the interrupt handler when the last port is removed. */
 391        number_rgmii_ports--;
 392        if (number_rgmii_ports == 0)
 393                free_irq(OCTEON_IRQ_RML, &number_rgmii_ports);
 394}
 395