linux/drivers/staging/octeon/ethernet-spi.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 <net/dst.h>
  15
  16#include <asm/octeon/octeon.h>
  17
  18#include "ethernet-defines.h"
  19#include "octeon-ethernet.h"
  20#include "ethernet-util.h"
  21
  22#include <asm/octeon/cvmx-spi.h>
  23
  24#include <asm/octeon/cvmx-npi-defs.h>
  25#include <asm/octeon/cvmx-spxx-defs.h>
  26#include <asm/octeon/cvmx-stxx-defs.h>
  27
  28static int number_spi_ports;
  29static int need_retrain[2] = { 0, 0 };
  30
  31static void cvm_oct_spxx_int_pr(union cvmx_spxx_int_reg spx_int_reg, int index)
  32{
  33        if (spx_int_reg.s.spf)
  34                pr_err("SPI%d: SRX Spi4 interface down\n", index);
  35        if (spx_int_reg.s.calerr)
  36                pr_err("SPI%d: SRX Spi4 Calendar table parity error\n", index);
  37        if (spx_int_reg.s.syncerr)
  38                pr_err("SPI%d: SRX Consecutive Spi4 DIP4 errors have exceeded SPX_ERR_CTL[ERRCNT]\n",
  39                       index);
  40        if (spx_int_reg.s.diperr)
  41                pr_err("SPI%d: SRX Spi4 DIP4 error\n", index);
  42        if (spx_int_reg.s.tpaovr)
  43                pr_err("SPI%d: SRX Selected port has hit TPA overflow\n",
  44                       index);
  45        if (spx_int_reg.s.rsverr)
  46                pr_err("SPI%d: SRX Spi4 reserved control word detected\n",
  47                       index);
  48        if (spx_int_reg.s.drwnng)
  49                pr_err("SPI%d: SRX Spi4 receive FIFO drowning/overflow\n",
  50                       index);
  51        if (spx_int_reg.s.clserr)
  52                pr_err("SPI%d: SRX Spi4 packet closed on non-16B alignment without EOP\n",
  53                       index);
  54        if (spx_int_reg.s.spiovr)
  55                pr_err("SPI%d: SRX Spi4 async FIFO overflow\n", index);
  56        if (spx_int_reg.s.abnorm)
  57                pr_err("SPI%d: SRX Abnormal packet termination (ERR bit)\n",
  58                       index);
  59        if (spx_int_reg.s.prtnxa)
  60                pr_err("SPI%d: SRX Port out of range\n", index);
  61}
  62
  63static void cvm_oct_stxx_int_pr(union cvmx_stxx_int_reg stx_int_reg, int index)
  64{
  65        if (stx_int_reg.s.syncerr)
  66                pr_err("SPI%d: STX Interface encountered a fatal error\n",
  67                       index);
  68        if (stx_int_reg.s.frmerr)
  69                pr_err("SPI%d: STX FRMCNT has exceeded STX_DIP_CNT[MAXFRM]\n",
  70                       index);
  71        if (stx_int_reg.s.unxfrm)
  72                pr_err("SPI%d: STX Unexpected framing sequence\n", index);
  73        if (stx_int_reg.s.nosync)
  74                pr_err("SPI%d: STX ERRCNT has exceeded STX_DIP_CNT[MAXDIP]\n",
  75                       index);
  76        if (stx_int_reg.s.diperr)
  77                pr_err("SPI%d: STX DIP2 error on the Spi4 Status channel\n",
  78                       index);
  79        if (stx_int_reg.s.datovr)
  80                pr_err("SPI%d: STX Spi4 FIFO overflow error\n", index);
  81        if (stx_int_reg.s.ovrbst)
  82                pr_err("SPI%d: STX Transmit packet burst too big\n", index);
  83        if (stx_int_reg.s.calpar1)
  84                pr_err("SPI%d: STX Calendar Table Parity Error Bank%d\n",
  85                       index, 1);
  86        if (stx_int_reg.s.calpar0)
  87                pr_err("SPI%d: STX Calendar Table Parity Error Bank%d\n",
  88                       index, 0);
  89}
  90
  91static irqreturn_t cvm_oct_spi_spx_int(int index)
  92{
  93        union cvmx_spxx_int_reg spx_int_reg;
  94        union cvmx_stxx_int_reg stx_int_reg;
  95
  96        spx_int_reg.u64 = cvmx_read_csr(CVMX_SPXX_INT_REG(index));
  97        cvmx_write_csr(CVMX_SPXX_INT_REG(index), spx_int_reg.u64);
  98        if (!need_retrain[index]) {
  99                spx_int_reg.u64 &= cvmx_read_csr(CVMX_SPXX_INT_MSK(index));
 100                cvm_oct_spxx_int_pr(spx_int_reg, index);
 101        }
 102
 103        stx_int_reg.u64 = cvmx_read_csr(CVMX_STXX_INT_REG(index));
 104        cvmx_write_csr(CVMX_STXX_INT_REG(index), stx_int_reg.u64);
 105        if (!need_retrain[index]) {
 106                stx_int_reg.u64 &= cvmx_read_csr(CVMX_STXX_INT_MSK(index));
 107                cvm_oct_stxx_int_pr(stx_int_reg, index);
 108        }
 109
 110        cvmx_write_csr(CVMX_SPXX_INT_MSK(index), 0);
 111        cvmx_write_csr(CVMX_STXX_INT_MSK(index), 0);
 112        need_retrain[index] = 1;
 113
 114        return IRQ_HANDLED;
 115}
 116
 117static irqreturn_t cvm_oct_spi_rml_interrupt(int cpl, void *dev_id)
 118{
 119        irqreturn_t return_status = IRQ_NONE;
 120        union cvmx_npi_rsl_int_blocks rsl_int_blocks;
 121
 122        /* Check and see if this interrupt was caused by the GMX block */
 123        rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
 124        if (rsl_int_blocks.s.spx1) /* 19 - SPX1_INT_REG & STX1_INT_REG */
 125                return_status = cvm_oct_spi_spx_int(1);
 126
 127        if (rsl_int_blocks.s.spx0) /* 18 - SPX0_INT_REG & STX0_INT_REG */
 128                return_status = cvm_oct_spi_spx_int(0);
 129
 130        return return_status;
 131}
 132
 133static void cvm_oct_spi_enable_error_reporting(int interface)
 134{
 135        union cvmx_spxx_int_msk spxx_int_msk;
 136        union cvmx_stxx_int_msk stxx_int_msk;
 137
 138        spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface));
 139        spxx_int_msk.s.calerr = 1;
 140        spxx_int_msk.s.syncerr = 1;
 141        spxx_int_msk.s.diperr = 1;
 142        spxx_int_msk.s.tpaovr = 1;
 143        spxx_int_msk.s.rsverr = 1;
 144        spxx_int_msk.s.drwnng = 1;
 145        spxx_int_msk.s.clserr = 1;
 146        spxx_int_msk.s.spiovr = 1;
 147        spxx_int_msk.s.abnorm = 1;
 148        spxx_int_msk.s.prtnxa = 1;
 149        cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64);
 150
 151        stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface));
 152        stxx_int_msk.s.frmerr = 1;
 153        stxx_int_msk.s.unxfrm = 1;
 154        stxx_int_msk.s.nosync = 1;
 155        stxx_int_msk.s.diperr = 1;
 156        stxx_int_msk.s.datovr = 1;
 157        stxx_int_msk.s.ovrbst = 1;
 158        stxx_int_msk.s.calpar1 = 1;
 159        stxx_int_msk.s.calpar0 = 1;
 160        cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64);
 161}
 162
 163static void cvm_oct_spi_poll(struct net_device *dev)
 164{
 165        static int spi4000_port;
 166        struct octeon_ethernet *priv = netdev_priv(dev);
 167        int interface;
 168
 169        for (interface = 0; interface < 2; interface++) {
 170                if ((priv->port == interface * 16) && need_retrain[interface]) {
 171                        if (cvmx_spi_restart_interface
 172                            (interface, CVMX_SPI_MODE_DUPLEX, 10) == 0) {
 173                                need_retrain[interface] = 0;
 174                                cvm_oct_spi_enable_error_reporting(interface);
 175                        }
 176                }
 177
 178                /*
 179                 * The SPI4000 TWSI interface is very slow. In order
 180                 * not to bring the system to a crawl, we only poll a
 181                 * single port every second. This means negotiation
 182                 * speed changes take up to 10 seconds, but at least
 183                 * we don't waste absurd amounts of time waiting for
 184                 * TWSI.
 185                 */
 186                if (priv->port == spi4000_port) {
 187                        /*
 188                         * This function does nothing if it is called on an
 189                         * interface without a SPI4000.
 190                         */
 191                        cvmx_spi4000_check_speed(interface, priv->port);
 192                        /*
 193                         * Normal ordering increments. By decrementing
 194                         * we only match once per iteration.
 195                         */
 196                        spi4000_port--;
 197                        if (spi4000_port < 0)
 198                                spi4000_port = 10;
 199                }
 200        }
 201}
 202
 203int cvm_oct_spi_init(struct net_device *dev)
 204{
 205        int r;
 206        struct octeon_ethernet *priv = netdev_priv(dev);
 207
 208        if (number_spi_ports == 0) {
 209                r = request_irq(OCTEON_IRQ_RML, cvm_oct_spi_rml_interrupt,
 210                                IRQF_SHARED, "SPI", &number_spi_ports);
 211                if (r)
 212                        return r;
 213        }
 214        number_spi_ports++;
 215
 216        if ((priv->port == 0) || (priv->port == 16)) {
 217                cvm_oct_spi_enable_error_reporting(INTERFACE(priv->port));
 218                priv->poll = cvm_oct_spi_poll;
 219        }
 220        cvm_oct_common_init(dev);
 221        return 0;
 222}
 223
 224void cvm_oct_spi_uninit(struct net_device *dev)
 225{
 226        int interface;
 227
 228        cvm_oct_common_uninit(dev);
 229        number_spi_ports--;
 230        if (number_spi_ports == 0) {
 231                for (interface = 0; interface < 2; interface++) {
 232                        cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0);
 233                        cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0);
 234                }
 235                free_irq(OCTEON_IRQ_RML, &number_spi_ports);
 236        }
 237}
 238