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