diff options
Diffstat (limited to 'drivers/net')
35 files changed, 2216 insertions, 124 deletions
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 3a15015bc409..a508402c4ecb 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -393,6 +393,24 @@ mt7530_fdb_write(struct mt7530_priv *priv, u16 vid, mt7530_write(priv, MT7530_ATA1 + (i * 4), reg[i]); } +/* Set up switch core clock for MT7530 */ +static void mt7530_pll_setup(struct mt7530_priv *priv) +{ + /* Disable PLL */ + core_write(priv, CORE_GSWPLL_GRP1, 0); + + /* Set core clock into 500Mhz */ + core_write(priv, CORE_GSWPLL_GRP2, + RG_GSWPLL_POSDIV_500M(1) | + RG_GSWPLL_FBKDIV_500M(25)); + + /* Enable PLL */ + core_write(priv, CORE_GSWPLL_GRP1, + RG_GSWPLL_EN_PRE | + RG_GSWPLL_POSDIV_200M(2) | + RG_GSWPLL_FBKDIV_200M(32)); +} + /* Setup TX circuit including relevant PAD and driving */ static int mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface) @@ -453,21 +471,6 @@ mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface) core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN | REG_TRGMIICK_EN); - /* Setup core clock for MT7530 */ - /* Disable PLL */ - core_write(priv, CORE_GSWPLL_GRP1, 0); - - /* Set core clock into 500Mhz */ - core_write(priv, CORE_GSWPLL_GRP2, - RG_GSWPLL_POSDIV_500M(1) | - RG_GSWPLL_FBKDIV_500M(25)); - - /* Enable PLL */ - core_write(priv, CORE_GSWPLL_GRP1, - RG_GSWPLL_EN_PRE | - RG_GSWPLL_POSDIV_200M(2) | - RG_GSWPLL_FBKDIV_200M(32)); - /* Setup the MT7530 TRGMII Tx Clock */ core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1)); core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0)); @@ -2196,6 +2199,8 @@ mt7530_setup(struct dsa_switch *ds) SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST | SYS_CTRL_REG_RST); + mt7530_pll_setup(priv); + /* Enable Port 6 only; P5 as GMAC5 which currently is not supported */ val = mt7530_read(priv, MT7530_MHWTRAP); val &= ~MHWTRAP_P6_DIS & ~MHWTRAP_PHY_ACCESS; diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 323ec56e8a74..1917da784191 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -132,6 +132,16 @@ source "drivers/net/ethernet/mscc/Kconfig" source "drivers/net/ethernet/microsoft/Kconfig" source "drivers/net/ethernet/moxa/Kconfig" source "drivers/net/ethernet/myricom/Kconfig" + +config FEALNX + tristate "Myson MTD-8xx PCI Ethernet support" + depends on PCI + select CRC32 + select MII + help + Say Y here to support the Myson MTD-800 family of PCI-based Ethernet + cards. <http://www.myson.com.tw/> + source "drivers/net/ethernet/ni/Kconfig" source "drivers/net/ethernet/natsemi/Kconfig" source "drivers/net/ethernet/neterion/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 2fedbaa545eb..0d872d4efcd1 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/ obj-$(CONFIG_NET_VENDOR_MICROSEMI) += mscc/ obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/ obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/ +obj-$(CONFIG_FEALNX) += fealnx.o obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/ obj-$(CONFIG_NET_VENDOR_NETERION) += neterion/ obj-$(CONFIG_NET_VENDOR_NETRONOME) += netronome/ diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 3038386a5afd..1761df8fb7f9 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -890,13 +890,13 @@ static void bgmac_chip_reset_idm_config(struct bgmac *bgmac) if (iost & BGMAC_BCMA_IOST_ATTACHED) { flags = BGMAC_BCMA_IOCTL_SW_CLKEN; - if (!bgmac->has_robosw) + if (bgmac->in_init || !bgmac->has_robosw) flags |= BGMAC_BCMA_IOCTL_SW_RESET; } bgmac_clk_enable(bgmac, flags); } - if (iost & BGMAC_BCMA_IOST_ATTACHED && !bgmac->has_robosw) + if (iost & BGMAC_BCMA_IOST_ATTACHED && (bgmac->in_init || !bgmac->has_robosw)) bgmac_idm_write(bgmac, BCMA_IOCTL, bgmac_idm_read(bgmac, BCMA_IOCTL) & ~BGMAC_BCMA_IOCTL_SW_RESET); @@ -1490,6 +1490,8 @@ int bgmac_enet_probe(struct bgmac *bgmac) struct net_device *net_dev = bgmac->net_dev; int err; + bgmac->in_init = true; + bgmac_chip_intrs_off(bgmac); net_dev->irq = bgmac->irq; @@ -1542,6 +1544,8 @@ int bgmac_enet_probe(struct bgmac *bgmac) /* Omit FCS from max MTU size */ net_dev->max_mtu = BGMAC_RX_MAX_FRAME_SIZE - ETH_FCS_LEN; + bgmac->in_init = false; + err = register_netdev(bgmac->net_dev); if (err) { dev_err(bgmac->dev, "Cannot register net device\n"); diff --git a/drivers/net/ethernet/broadcom/bgmac.h b/drivers/net/ethernet/broadcom/bgmac.h index e05ac92c0650..d73ef262991d 100644 --- a/drivers/net/ethernet/broadcom/bgmac.h +++ b/drivers/net/ethernet/broadcom/bgmac.h @@ -472,6 +472,8 @@ struct bgmac { int irq; u32 int_mask; + bool in_init; + /* Current MAC state */ int mac_speed; int mac_duplex; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 5d4b1f2ebeac..808236dc898b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -3145,7 +3145,7 @@ static int bnxt_alloc_ring(struct bnxt *bp, struct bnxt_ring_mem_info *rmem) static void bnxt_free_tpa_info(struct bnxt *bp) { - int i; + int i, j; for (i = 0; i < bp->rx_nr_rings; i++) { struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i]; @@ -3153,8 +3153,10 @@ static void bnxt_free_tpa_info(struct bnxt *bp) kfree(rxr->rx_tpa_idx_map); rxr->rx_tpa_idx_map = NULL; if (rxr->rx_tpa) { - kfree(rxr->rx_tpa[0].agg_arr); - rxr->rx_tpa[0].agg_arr = NULL; + for (j = 0; j < bp->max_tpa; j++) { + kfree(rxr->rx_tpa[j].agg_arr); + rxr->rx_tpa[j].agg_arr = NULL; + } } kfree(rxr->rx_tpa); rxr->rx_tpa = NULL; @@ -3163,14 +3165,13 @@ static void bnxt_free_tpa_info(struct bnxt *bp) static int bnxt_alloc_tpa_info(struct bnxt *bp) { - int i, j, total_aggs = 0; + int i, j; bp->max_tpa = MAX_TPA; if (bp->flags & BNXT_FLAG_CHIP_P5) { if (!bp->max_tpa_v2) return 0; bp->max_tpa = max_t(u16, bp->max_tpa_v2, MAX_TPA_P5); - total_aggs = bp->max_tpa * MAX_SKB_FRAGS; } for (i = 0; i < bp->rx_nr_rings; i++) { @@ -3184,12 +3185,12 @@ static int bnxt_alloc_tpa_info(struct bnxt *bp) if (!(bp->flags & BNXT_FLAG_CHIP_P5)) continue; - agg = kcalloc(total_aggs, sizeof(*agg), GFP_KERNEL); - rxr->rx_tpa[0].agg_arr = agg; - if (!agg) - return -ENOMEM; - for (j = 1; j < bp->max_tpa; j++) - rxr->rx_tpa[j].agg_arr = agg + j * MAX_SKB_FRAGS; + for (j = 0; j < bp->max_tpa; j++) { + agg = kcalloc(MAX_SKB_FRAGS, sizeof(*agg), GFP_KERNEL); + if (!agg) + return -ENOMEM; + rxr->rx_tpa[j].agg_arr = agg; + } rxr->rx_tpa_idx_map = kzalloc(sizeof(*rxr->rx_tpa_idx_map), GFP_KERNEL); if (!rxr->rx_tpa_idx_map) @@ -13204,8 +13205,6 @@ static void bnxt_remove_one(struct pci_dev *pdev) bnxt_free_hwrm_resources(bp); bnxt_ethtool_free(bp); bnxt_dcb_free(bp); - kfree(bp->edev); - bp->edev = NULL; kfree(bp->ptp_cfg); bp->ptp_cfg = NULL; kfree(bp->fw_health); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c index d4cc9c371e7b..e7b5e28ee29f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c @@ -317,9 +317,11 @@ static void bnxt_aux_dev_release(struct device *dev) { struct bnxt_aux_priv *aux_priv = container_of(dev, struct bnxt_aux_priv, aux_dev.dev); + struct bnxt *bp = netdev_priv(aux_priv->edev->net); ida_free(&bnxt_aux_dev_ids, aux_priv->id); kfree(aux_priv->edev->ulp_tbl); + bp->edev = NULL; kfree(aux_priv->edev); kfree(aux_priv); } diff --git a/drivers/net/ethernet/fealnx.c b/drivers/net/ethernet/fealnx.c new file mode 100644 index 000000000000..ed18450fd2cc --- /dev/null +++ b/drivers/net/ethernet/fealnx.c @@ -0,0 +1,1953 @@ +/* + Written 1998-2000 by Donald Becker. + + This software may be used and distributed according to the terms of + the GNU General Public License (GPL), incorporated herein by reference. + Drivers based on or derived from this code fall under the GPL and must + retain the authorship, copyright and license notice. This file is not + a complete program and may only be used when the entire operating + system is licensed under the GPL. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + Support information and updates available at + http://www.scyld.com/network/pci-skeleton.html + + Linux kernel updates: + + Version 2.51, Nov 17, 2001 (jgarzik): + - Add ethtool support + - Replace some MII-related magic numbers with constants + +*/ + +#define DRV_NAME "fealnx" + +static int debug; /* 1-> print debug message */ +static int max_interrupt_work = 20; + +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). */ +static int multicast_filter_limit = 32; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. */ +/* Setting to > 1518 effectively disables this feature. */ +static int rx_copybreak; + +/* Used to pass the media type, etc. */ +/* Both 'options[]' and 'full_duplex[]' should exist for driver */ +/* interoperability. */ +/* The media type is usually passed in 'options[]'. */ +#define MAX_UNITS 8 /* More are supported, limit only on options */ +static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 }; +static int full_duplex[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 }; + +/* Operational parameters that are set at compile time. */ +/* Keep the ring sizes a power of two for compile efficiency. */ +/* The compiler will convert <unsigned>'%'<2^N> into a bit mask. */ +/* Making the Tx ring too large decreases the effectiveness of channel */ +/* bonding and packet priority. */ +/* There are no ill effects from too-large receive rings. */ +// 88-12-9 modify, +// #define TX_RING_SIZE 16 +// #define RX_RING_SIZE 32 +#define TX_RING_SIZE 6 +#define RX_RING_SIZE 12 +#define TX_TOTAL_SIZE TX_RING_SIZE*sizeof(struct fealnx_desc) +#define RX_TOTAL_SIZE RX_RING_SIZE*sizeof(struct fealnx_desc) + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (2*HZ) + +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */ + + +/* Include files, designed to support most kernel versions 2.0.0 and later. */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/init.h> +#include <linux/mii.h> +#include <linux/ethtool.h> +#include <linux/crc32.h> +#include <linux/delay.h> +#include <linux/bitops.h> + +#include <asm/processor.h> /* Processor type for cache alignment. */ +#include <asm/io.h> +#include <linux/uaccess.h> +#include <asm/byteorder.h> + +/* This driver was written to use PCI memory space, however some x86 systems + work only with I/O space accesses. */ +#ifndef __alpha__ +#define USE_IO_OPS +#endif + +/* Kernel compatibility defines, some common to David Hinds' PCMCIA package. */ +/* This is only in the support-all-kernels source code. */ + +#define RUN_AT(x) (jiffies + (x)) + +MODULE_AUTHOR("Myson or whoever"); +MODULE_DESCRIPTION("Myson MTD-8xx 100/10M Ethernet PCI Adapter Driver"); +MODULE_LICENSE("GPL"); +module_param(max_interrupt_work, int, 0); +module_param(debug, int, 0); +module_param(rx_copybreak, int, 0); +module_param(multicast_filter_limit, int, 0); +module_param_array(options, int, NULL, 0); +module_param_array(full_duplex, int, NULL, 0); +MODULE_PARM_DESC(max_interrupt_work, "fealnx maximum events handled per interrupt"); +MODULE_PARM_DESC(debug, "fealnx enable debugging (0-1)"); +MODULE_PARM_DESC(rx_copybreak, "fealnx copy breakpoint for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, "fealnx maximum number of filtered multicast addresses"); +MODULE_PARM_DESC(options, "fealnx: Bits 0-3: media type, bit 17: full duplex"); +MODULE_PARM_DESC(full_duplex, "fealnx full duplex setting(s) (1)"); + +enum { + MIN_REGION_SIZE = 136, +}; + +/* A chip capabilities table, matching the entries in pci_tbl[] above. */ +enum chip_capability_flags { + HAS_MII_XCVR, + HAS_CHIP_XCVR, +}; + +/* 89/6/13 add, */ +/* for different PHY */ +enum phy_type_flags { + MysonPHY = 1, + AhdocPHY = 2, + SeeqPHY = 3, + MarvellPHY = 4, + Myson981 = 5, + LevelOnePHY = 6, + OtherPHY = 10, +}; + +struct chip_info { + char *chip_name; + int flags; +}; + +static const struct chip_info skel_netdrv_tbl[] = { + { "100/10M Ethernet PCI Adapter", HAS_MII_XCVR }, + { "100/10M Ethernet PCI Adapter", HAS_CHIP_XCVR }, + { "1000/100/10M Ethernet PCI Adapter", HAS_MII_XCVR }, +}; + +/* Offsets to the Command and Status Registers. */ +enum fealnx_offsets { + PAR0 = 0x0, /* physical address 0-3 */ + PAR1 = 0x04, /* physical address 4-5 */ + MAR0 = 0x08, /* multicast address 0-3 */ + MAR1 = 0x0C, /* multicast address 4-7 */ + FAR0 = 0x10, /* flow-control address 0-3 */ + FAR1 = 0x14, /* flow-control address 4-5 */ + TCRRCR = 0x18, /* receive & transmit configuration */ + BCR = 0x1C, /* bus command */ + TXPDR = 0x20, /* transmit polling demand */ + RXPDR = 0x24, /* receive polling demand */ + RXCWP = 0x28, /* receive current word pointer */ + TXLBA = 0x2C, /* transmit list base address */ + RXLBA = 0x30, /* receive list base address */ + ISR = 0x34, /* interrupt status */ + IMR = 0x38, /* interrupt mask */ + FTH = 0x3C, /* flow control high/low threshold */ + MANAGEMENT = 0x40, /* bootrom/eeprom and mii management */ + TALLY = 0x44, /* tally counters for crc and mpa */ + TSR = 0x48, /* tally counter for transmit status */ + BMCRSR = 0x4c, /* basic mode control and status */ + PHYIDENTIFIER = 0x50, /* phy identifier */ + ANARANLPAR = 0x54, /* auto-negotiation advertisement and link + partner ability */ + ANEROCR = 0x58, /* auto-negotiation expansion and pci conf. */ + BPREMRPSR = 0x5c, /* bypass & receive error mask and phy status */ +}; + +/* Bits in the interrupt status/enable registers. */ +/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */ +enum intr_status_bits { + RFCON = 0x00020000, /* receive flow control xon packet */ + RFCOFF = 0x00010000, /* receive flow control xoff packet */ + LSCStatus = 0x00008000, /* link status change */ + ANCStatus = 0x00004000, /* autonegotiation completed */ + FBE = 0x00002000, /* fatal bus error */ + FBEMask = 0x00001800, /* mask bit12-11 */ + ParityErr = 0x00000000, /* parity error */ + TargetErr = 0x00001000, /* target abort */ + MasterErr = 0x00000800, /* master error */ + TUNF = 0x00000400, /* transmit underflow */ + ROVF = 0x00000200, /* receive overflow */ + ETI = 0x00000100, /* transmit early int */ + ERI = 0x00000080, /* receive early int */ + CNTOVF = 0x00000040, /* counter overflow */ + RBU = 0x00000020, /* receive buffer unavailable */ + TBU = 0x00000010, /* transmit buffer unavilable */ + TI = 0x00000008, /* transmit interrupt */ + RI = 0x00000004, /* receive interrupt */ + RxErr = 0x00000002, /* receive error */ +}; + +/* Bits in the NetworkConfig register, W for writing, R for reading */ +/* FIXME: some names are invented by me. Marked with (name?) */ +/* If you have docs and know bit names, please fix 'em */ +enum rx_mode_bits { + CR_W_ENH = 0x02000000, /* enhanced mode (name?) */ + CR_W_FD = 0x00100000, /* full duplex */ + CR_W_PS10 = 0x00080000, /* 10 mbit */ + CR_W_TXEN = 0x00040000, /* tx enable (name?) */ + CR_W_PS1000 = 0x00010000, /* 1000 mbit */ + /* CR_W_RXBURSTMASK= 0x00000e00, Im unsure about this */ + CR_W_RXMODEMASK = 0x000000e0, + CR_W_PROM = 0x00000080, /* promiscuous mode */ + CR_W_AB = 0x00000040, /* accept broadcast */ + CR_W_AM = 0x00000020, /* accept mutlicast */ + CR_W_ARP = 0x00000008, /* receive runt pkt */ + CR_W_ALP = 0x00000004, /* receive long pkt */ + CR_W_SEP = 0x00000002, /* receive error pkt */ + CR_W_RXEN = 0x00000001, /* rx enable (unicast?) (name?) */ + + CR_R_TXSTOP = 0x04000000, /* tx stopped (name?) */ + CR_R_FD = 0x00100000, /* full duplex detected */ + CR_R_PS10 = 0x00080000, /* 10 mbit detected */ + CR_R_RXSTOP = 0x00008000, /* rx stopped (name?) */ +}; + +/* The Tulip Rx and Tx buffer descriptors. */ +struct fealnx_desc { + s32 status; + s32 control; + u32 buffer; + u32 next_desc; + struct fealnx_desc *next_desc_logical; + struct sk_buff *skbuff; + u32 reserved1; + u32 reserved2; +}; + +/* Bits in network_desc.status */ +enum rx_desc_status_bits { + RXOWN = 0x80000000, /* own bit */ + FLNGMASK = 0x0fff0000, /* frame length */ + FLNGShift = 16, + MARSTATUS = 0x00004000, /* multicast address received */ + BARSTATUS = 0x00002000, /* broadcast address received */ + PHYSTATUS = 0x00001000, /* physical address received */ + RXFSD = 0x00000800, /* first descriptor */ + RXLSD = 0x00000400, /* last descriptor */ + ErrorSummary = 0x80, /* error summary */ + RUNTPKT = 0x40, /* runt packet received */ + LONGPKT = 0x20, /* long packet received */ + FAE = 0x10, /* frame align error */ + CRC = 0x08, /* crc error */ + RXER = 0x04, /* receive error */ +}; + +enum rx_desc_control_bits { + RXIC = 0x00800000, /* interrupt control */ + RBSShift = 0, +}; + +enum tx_desc_status_bits { + TXOWN = 0x80000000, /* own bit */ + JABTO = 0x00004000, /* jabber timeout */ + CSL = 0x00002000, /* carrier sense lost */ + LC = 0x00001000, /* late collision */ + EC = 0x00000800, /* excessive collision */ + UDF = 0x00000400, /* fifo underflow */ + DFR = 0x00000200, /* deferred */ + HF = 0x00000100, /* heartbeat fail */ + NCRMask = 0x000000ff, /* collision retry count */ + NCRShift = 0, +}; + +enum tx_desc_control_bits { + TXIC = 0x80000000, /* interrupt control */ + ETIControl = 0x40000000, /* early transmit interrupt */ + TXLD = 0x20000000, /* last descriptor */ + TXFD = 0x10000000, /* first descriptor */ + CRCEnable = 0x08000000, /* crc control */ + PADEnable = 0x04000000, /* padding control */ + RetryTxLC = 0x02000000, /* retry late collision */ + PKTSMask = 0x3ff800, /* packet size bit21-11 */ + PKTSShift = 11, + TBSMask = 0x000007ff, /* transmit buffer bit 10-0 */ + TBSShift = 0, +}; + +/* BootROM/EEPROM/MII Management Register */ +#define MASK_MIIR_MII_READ 0x00000000 +#define MASK_MIIR_MII_WRITE 0x00000008 +#define MASK_MIIR_MII_MDO 0x00000004 +#define MASK_MIIR_MII_MDI 0x00000002 +#define MASK_MIIR_MII_MDC 0x00000001 + +/* ST+OP+PHYAD+REGAD+TA */ +#define OP_READ 0x6000 /* ST:01+OP:10+PHYAD+REGAD+TA:Z0 */ +#define OP_WRITE 0x5002 /* ST:01+OP:01+PHYAD+REGAD+TA:10 */ + +/* ------------------------------------------------------------------------- */ +/* Constants for Myson PHY */ +/* ------------------------------------------------------------------------- */ +#define MysonPHYID 0xd0000302 +/* 89-7-27 add, (begin) */ +#define MysonPHYID0 0x0302 +#define StatusRegister 18 +#define SPEED100 0x0400 // bit10 +#define FULLMODE 0x0800 // bit11 +/* 89-7-27 add, (end) */ + +/* ------------------------------------------------------------------------- */ +/* Constants for Seeq 80225 PHY */ +/* ------------------------------------------------------------------------- */ +#define SeeqPHYID0 0x0016 + +#define MIIRegister18 18 +#define SPD_DET_100 0x80 +#define DPLX_DET_FULL 0x40 + +/* ------------------------------------------------------------------------- */ +/* Constants for Ahdoc 101 PHY */ +/* ------------------------------------------------------------------------- */ +#define AhdocPHYID0 0x0022 + +#define DiagnosticReg 18 +#define DPLX_FULL 0x0800 +#define Speed_100 0x0400 + +/* 89/6/13 add, */ +/* -------------------------------------------------------------------------- */ +/* Constants */ +/* -------------------------------------------------------------------------- */ +#define MarvellPHYID0 0x0141 +#define LevelOnePHYID0 0x0013 + +#define MII1000BaseTControlReg 9 +#define MII1000BaseTStatusReg 10 +#define SpecificReg 17 + +/* for 1000BaseT Control Register */ +#define PHYAbletoPerform1000FullDuplex 0x0200 +#define PHYAbletoPerform1000HalfDuplex 0x0100 +#define PHY1000AbilityMask 0x300 + +// for phy specific status register, marvell phy. +#define SpeedMask 0x0c000 +#define Speed_1000M 0x08000 +#define Speed_100M 0x4000 +#define Speed_10M 0 +#define Full_Duplex 0x2000 + +// 89/12/29 add, for phy specific status register, levelone phy, (begin) +#define LXT1000_100M 0x08000 +#define LXT1000_1000M 0x0c000 +#define LXT1000_Full 0x200 +// 89/12/29 add, for phy specific status register, levelone phy, (end) + +/* for 3-in-1 case, BMCRSR register */ +#define LinkIsUp2 0x00040000 + +/* for PHY */ +#define LinkIsUp 0x0004 + + +struct netdev_private { + /* Descriptor rings first for alignment. */ + struct fealnx_desc *rx_ring; + struct fealnx_desc *tx_ring; + + dma_addr_t rx_ring_dma; + dma_addr_t tx_ring_dma; + + spinlock_t lock; + + /* Media monitoring timer. */ + struct timer_list timer; + + /* Reset timer */ + struct timer_list reset_timer; + int reset_timer_armed; + unsigned long crvalue_sv; + unsigned long imrvalue_sv; + + /* Frequently used values: keep some adjacent for cache effect. */ + int flags; + struct pci_dev *pci_dev; + unsigned long crvalue; + unsigned long bcrvalue; + unsigned long imrvalue; + struct fealnx_desc *cur_rx; + struct fealnx_desc *lack_rxbuf; + int really_rx_count; + struct fealnx_desc *cur_tx; + struct fealnx_desc *cur_tx_copy; + int really_tx_count; + int free_tx_count; + unsigned int rx_buf_sz; /* Based on MTU+slack. */ + + /* These values are keep track of the transceiver/media in use. */ + unsigned int linkok; + unsigned int line_speed; + unsigned int duplexmode; + unsigned int default_port:4; /* Last dev->if_port value. */ + unsigned int PHYType; + + /* MII transceiver section. */ + int mii_cnt; /* MII device addresses. */ + unsigned char phys[2]; /* MII device addresses. */ + struct mii_if_info mii; + void __iomem *mem; +}; + + +static int mdio_read(struct net_device *dev, int phy_id, int location); +static void mdio_write(struct net_device *dev, int phy_id, int location, int value); +static int netdev_open(struct net_device *dev); +static void getlinktype(struct net_device *dev); +static void getlinkstatus(struct net_device *dev); +static void netdev_timer(struct timer_list *t); +static void reset_timer(struct timer_list *t); +static void fealnx_tx_timeout(struct net_device *dev, unsigned int txqueue); +static void init_ring(struct net_device *dev); +static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t intr_handler(int irq, void *dev_instance); +static int netdev_rx(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); +static void __set_rx_mode(struct net_device *dev); +static struct net_device_stats *get_stats(struct net_device *dev); +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static const struct ethtool_ops netdev_ethtool_ops; +static int netdev_close(struct net_device *dev); +static void reset_rx_descriptors(struct net_device *dev); +static void reset_tx_descriptors(struct net_device *dev); + +static void stop_nic_rx(void __iomem *ioaddr, long crvalue) +{ + int delay = 0x1000; + iowrite32(crvalue & ~(CR_W_RXEN), ioaddr + TCRRCR); + while (--delay) { + if ( (ioread32(ioaddr + TCRRCR) & CR_R_RXSTOP) == CR_R_RXSTOP) + break; + } +} + + +static void stop_nic_rxtx(void __iomem *ioaddr, long crvalue) +{ + int delay = 0x1000; + iowrite32(crvalue & ~(CR_W_RXEN+CR_W_TXEN), ioaddr + TCRRCR); + while (--delay) { + if ( (ioread32(ioaddr + TCRRCR) & (CR_R_RXSTOP+CR_R_TXSTOP)) + == (CR_R_RXSTOP+CR_R_TXSTOP) ) + break; + } +} + +static const struct net_device_ops netdev_ops = { + .ndo_open = netdev_open, + .ndo_stop = netdev_close, + .ndo_start_xmit = start_tx, + .ndo_get_stats = get_stats, + .ndo_set_rx_mode = set_rx_mode, + .ndo_eth_ioctl = mii_ioctl, + .ndo_tx_timeout = fealnx_tx_timeout, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int fealnx_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct netdev_private *np; + int i, option, err, irq; + static int card_idx = -1; + char boardname[12]; + void __iomem *ioaddr; + unsigned long len; + unsigned int chip_id = ent->driver_data; + struct net_device *dev; + void *ring_space; + dma_addr_t ring_dma; + u8 addr[ETH_ALEN]; +#ifdef USE_IO_OPS + int bar = 0; +#else + int bar = 1; +#endif + + card_idx++; + sprintf(boardname, "fealnx%d", card_idx); + + option = card_idx < MAX_UNITS ? options[card_idx] : 0; + + i = pci_enable_device(pdev); + if (i) return i; + pci_set_master(pdev); + + len = pci_resource_len(pdev, bar); + if (len < MIN_REGION_SIZE) { + dev_err(&pdev->dev, + "region size %ld too small, aborting\n", len); + return -ENODEV; + } + + i = pci_request_regions(pdev, boardname); + if (i) + return i; + + irq = pdev->irq; + + ioaddr = pci_iomap(pdev, bar, len); + if (!ioaddr) { + err = -ENOMEM; + goto err_out_res; + } + + dev = alloc_etherdev(sizeof(struct netdev_private)); + if (!dev) { + err = -ENOMEM; + goto err_out_unmap; + } + SET_NETDEV_DEV(dev, &pdev->dev); + + /* read ethernet id */ + for (i = 0; i < 6; ++i) + addr[i] = ioread8(ioaddr + PAR0 + i); + eth_hw_addr_set(dev, addr); + + /* Reset the chip to erase previous misconfiguration. */ + iowrite32(0x00000001, ioaddr + BCR); + + /* Make certain the descriptor lists are aligned. */ + np = netdev_priv(dev); + np->mem = ioaddr; + spin_lock_init(&np->lock); + np->pci_dev = pdev; + np->flags = skel_netdrv_tbl[chip_id].flags; + pci_set_drvdata(pdev, dev); + np->mii.dev = dev; + np->mii.mdio_read = mdio_read; + np->mii.mdio_write = mdio_write; + np->mii.phy_id_mask = 0x1f; + np->mii.reg_num_mask = 0x1f; + + ring_space = dma_alloc_coherent(&pdev->dev, RX_TOTAL_SIZE, &ring_dma, + GFP_KERNEL); + if (!ring_space) { + err = -ENOMEM; + goto err_out_free_dev; + } + np->rx_ring = ring_space; + np->rx_ring_dma = ring_dma; + + ring_space = dma_alloc_coherent(&pdev->dev, TX_TOTAL_SIZE, &ring_dma, + GFP_KERNEL); + if (!ring_space) { + err = -ENOMEM; + goto err_out_free_rx; + } + np->tx_ring = ring_space; + np->tx_ring_dma = ring_dma; + + /* find the connected MII xcvrs */ + if (np->flags == HAS_MII_XCVR) { + int phy, phy_idx = 0; + + for (phy = 1; phy < 32 && phy_idx < ARRAY_SIZE(np->phys); + phy++) { + int mii_status = mdio_read(dev, phy, 1); + + if (mii_status != 0xffff && mii_status != 0x0000) { + np->phys[phy_idx++] = phy; + dev_info(&pdev->dev, + "MII PHY found at address %d, status " + "0x%4.4x.\n", phy, mii_status); + /* get phy type */ + { + unsigned int data; + + data = mdio_read(dev, np->phys[0], 2); + if (data == SeeqPHYID0) + np->PHYType = SeeqPHY; + else if (data == AhdocPHYID0) + np->PHYType = AhdocPHY; + else if (data == MarvellPHYID0) + np->PHYType = MarvellPHY; + else if (data == MysonPHYID0) + np->PHYType = Myson981; + else if (data == LevelOnePHYID0) + np->PHYType = LevelOnePHY; + else + np->PHYType = OtherPHY; + } + } + } + + np->mii_cnt = phy_idx; + if (phy_idx == 0) + dev_warn(&pdev->dev, + "MII PHY not found -- this device may " + "not operate correctly.\n"); + } else { + np->phys[0] = 32; +/* 89/6/23 add, (begin) */ + /* get phy type */ + if (ioread32(ioaddr + PHYIDENTIFIER) == MysonPHYID) + np->PHYType = MysonPHY; + else + np->PHYType = OtherPHY; + } + np->mii.phy_id = np->phys[0]; + + if (dev->mem_start) + option = dev->mem_start; + + /* The lower four bits are the media type. */ + if (option > 0) { + if (option & 0x200) + np->mii.full_duplex = 1; + np->default_port = option & 15; + } + + if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0) + np->mii.full_duplex = full_duplex[card_idx]; + + if (np->mii.full_duplex) { + dev_info(&pdev->dev, "Media type forced to Full Duplex.\n"); +/* 89/6/13 add, (begin) */ +// if (np->PHYType==MarvellPHY) + if ((np->PHYType == MarvellPHY) || (np->PHYType == LevelOnePHY)) { + unsigned int data; + + data = mdio_read(dev, np->phys[0], 9); + data = (data & 0xfcff) | 0x0200; + mdio_write(dev, np->phys[0], 9, data); + } +/* 89/6/13 add, (end) */ + if (np->flags == HAS_MII_XCVR) + mdio_write(dev, np->phys[0], MII_ADVERTISE, ADVERTISE_FULL); + else + iowrite32(ADVERTISE_FULL, ioaddr + ANARANLPAR); + np->mii.force_media = 1; + } + + dev->netdev_ops = &netdev_ops; + dev->ethtool_ops = &netdev_ethtool_ops; + dev->watchdog_timeo = TX_TIMEOUT; + + err = register_netdev(dev); + if (err) + goto err_out_free_tx; + + printk(KERN_INFO "%s: %s at %p, %pM, IRQ %d.\n", + dev->name, skel_netdrv_tbl[chip_id].chip_name, ioaddr, + dev->dev_addr, irq); + + return 0; + +err_out_free_tx: + dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, np->tx_ring, + np->tx_ring_dma); +err_out_free_rx: + dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, np->rx_ring, + np->rx_ring_dma); +err_out_free_dev: + free_netdev(dev); +err_out_unmap: + pci_iounmap(pdev, ioaddr); +err_out_res: + pci_release_regions(pdev); + return err; +} + + +static void fealnx_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + if (dev) { + struct netdev_private *np = netdev_priv(dev); + + dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, np->tx_ring, + np->tx_ring_dma); + dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, np->rx_ring, + np->rx_ring_dma); + unregister_netdev(dev); + pci_iounmap(pdev, np->mem); + free_netdev(dev); + pci_release_regions(pdev); + } else + printk(KERN_ERR "fealnx: remove for unknown device\n"); +} + + +static ulong m80x_send_cmd_to_phy(void __iomem *miiport, int opcode, int phyad, int regad) +{ + ulong miir; + int i; + unsigned int mask, data; + + /* enable MII output */ + miir = (ulong) ioread32(miiport); + miir &= 0xfffffff0; + + miir |= MASK_MIIR_MII_WRITE + MASK_MIIR_MII_MDO; + + /* send 32 1's preamble */ + for (i = 0; i < 32; i++) { + /* low MDC; MDO is already high (miir) */ + miir &= ~MASK_MIIR_MII_MDC; + iowrite32(miir, miiport); + + /* high MDC */ + miir |= MASK_MIIR_MII_MDC; + iowrite32(miir, miiport); + } + + /* calculate ST+OP+PHYAD+REGAD+TA */ + data = opcode | (phyad << 7) | (regad << 2); + + /* sent out */ + mask = 0x8000; + while (mask) { + /* low MDC, prepare MDO */ + miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO); + if (mask & data) + miir |= MASK_MIIR_MII_MDO; + + iowrite32(miir, miiport); + /* high MDC */ + miir |= MASK_MIIR_MII_MDC; + iowrite32(miir, miiport); + udelay(30); + + /* next */ + mask >>= 1; + if (mask == 0x2 && opcode == OP_READ) + miir &= ~MASK_MIIR_MII_WRITE; + } + return miir; +} + + +static int mdio_read(struct net_device *dev, int phyad, int regad) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *miiport = np->mem + MANAGEMENT; + ulong miir; + unsigned int mask, data; + + miir = m80x_send_cmd_to_phy(miiport, OP_READ, phyad, regad); + + /* read data */ + mask = 0x8000; + data = 0; + while (mask) { + /* low MDC */ + miir &= ~MASK_MIIR_MII_MDC; + iowrite32(miir, miiport); + + /* read MDI */ + miir = ioread32(miiport); + if (miir & MASK_MIIR_MII_MDI) + data |= mask; + + /* high MDC, and wait */ + miir |= MASK_MIIR_MII_MDC; + iowrite32(miir, miiport); + udelay(30); + + /* next */ + mask >>= 1; + } + + /* low MDC */ + miir &= ~MASK_MIIR_MII_MDC; + iowrite32(miir, miiport); + + return data & 0xffff; +} + + +static void mdio_write(struct net_device *dev, int phyad, int regad, int data) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *miiport = np->mem + MANAGEMENT; + ulong miir; + unsigned int mask; + + miir = m80x_send_cmd_to_phy(miiport, OP_WRITE, phyad, regad); + + /* write data */ + mask = 0x8000; + while (mask) { + /* low MDC, prepare MDO */ + miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO); + if (mask & data) + miir |= MASK_MIIR_MII_MDO; + iowrite32(miir, miiport); + + /* high MDC */ + miir |= MASK_MIIR_MII_MDC; + iowrite32(miir, miiport); + + /* next */ + mask >>= 1; + } + + /* low MDC */ + miir &= ~MASK_MIIR_MII_MDC; + iowrite32(miir, miiport); +} + + +static int netdev_open(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->mem; + const int irq = np->pci_dev->irq; + int rc, i; + + iowrite32(0x00000001, ioaddr + BCR); /* Reset */ + + rc = request_irq(irq, intr_handler, IRQF_SHARED, dev->name, dev); + if (rc) + return -EAGAIN; + + for (i = 0; i < 3; i++) + iowrite16(((const unsigned short *)dev->dev_addr)[i], + ioaddr + PAR0 + i*2); + + init_ring(dev); + + iowrite32(np->rx_ring_dma, ioaddr + RXLBA); + iowrite32(np->tx_ring_dma, ioaddr + TXLBA); + + /* Initialize other registers. */ + /* Configure the PCI bus bursts and FIFO thresholds. + 486: Set 8 longword burst. + 586: no burst limit. + Burst length 5:3 + 0 0 0 1 + 0 0 1 4 + 0 1 0 8 + 0 1 1 16 + 1 0 0 32 + 1 0 1 64 + 1 1 0 128 + 1 1 1 256 + Wait the specified 50 PCI cycles after a reset by initializing + Tx and Rx queues and the address filter list. + FIXME (Ueimor): optimistic for alpha + posted writes ? */ + + np->bcrvalue = 0x10; /* little-endian, 8 burst length */ +#ifdef __BIG_ENDIAN + np->bcrvalue |= 0x04; /* big-endian */ +#endif + +#if defined(__i386__) && !defined(MODULE) && !defined(CONFIG_UML) + if (boot_cpu_data.x86 <= 4) + np->crvalue = 0xa00; + else +#endif + np->crvalue = 0xe00; /* rx 128 burst length */ + + +// 89/12/29 add, +// 90/1/16 modify, +// np->imrvalue=FBE|TUNF|CNTOVF|RBU|TI|RI; + np->imrvalue = TUNF | CNTOVF | RBU | TI | RI; + if (np->pci_dev->device == 0x891) { + np->bcrvalue |= 0x200; /* set PROG bit */ + np->crvalue |= CR_W_ENH; /* set enhanced bit */ + np->imrvalue |= ETI; + } + iowrite32(np->bcrvalue, ioaddr + BCR); + + if (dev->if_port == 0) + dev->if_port = np->default_port; + + iowrite32(0, ioaddr + RXPDR); +// 89/9/1 modify, +// np->crvalue = 0x00e40001; /* tx store and forward, tx/rx enable */ + np->crvalue |= 0x00e40001; /* tx store and forward, tx/rx enable */ + np->mii.full_duplex = np->mii.force_media; + getlinkstatus(dev); + if (np->linkok) + getlinktype(dev); + __set_rx_mode(dev); + + netif_start_queue(dev); + + /* Clear and Enable interrupts by setting the interrupt mask. */ + iowrite32(FBE | TUNF | CNTOVF | RBU | TI | RI, ioaddr + ISR); + iowrite32(np->imrvalue, ioaddr + IMR); + + if (debug) + printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name); + + /* Set the timer to check for link beat. */ + timer_setup(&np->timer, netdev_timer, 0); + np->timer.expires = RUN_AT(3 * HZ); + + /* timer handler */ + add_timer(&np->timer); + + timer_setup(&np->reset_timer, reset_timer, 0); + np->reset_timer_armed = 0; + return rc; +} + + +static void getlinkstatus(struct net_device *dev) +/* function: Routine will read MII Status Register to get link status. */ +/* input : dev... pointer to the adapter block. */ +/* output : none. */ +{ + struct netdev_private *np = netdev_priv(dev); + unsigned int i, DelayTime = 0x1000; + + np->linkok = 0; + + if (np->PHYType == MysonPHY) { + for (i = 0; i < DelayTime; ++i) { + if (ioread32(np->mem + BMCRSR) & LinkIsUp2) { + np->linkok = 1; + return; + } + udelay(100); + } + } else { + for (i = 0; i < DelayTime; ++i) { + if (mdio_read(dev, np->phys[0], MII_BMSR) & BMSR_LSTATUS) { + np->linkok = 1; + return; + } + udelay(100); + } + } +} + + +static void getlinktype(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + + if (np->PHYType == MysonPHY) { /* 3-in-1 case */ + if (ioread32(np->mem + TCRRCR) & CR_R_FD) + np->duplexmode = 2; /* full duplex */ + else + np->duplexmode = 1; /* half duplex */ + if (ioread32(np->mem + TCRRCR) & CR_R_PS10) + np->line_speed = 1; /* 10M */ + else + np->line_speed = 2; /* 100M */ + } else { + if (np->PHYType == SeeqPHY) { /* this PHY is SEEQ 80225 */ + unsigned int data; + + data = mdio_read(dev, np->phys[0], MIIRegister18); + if (data & SPD_DET_100) + np->line_speed = 2; /* 100M */ + else + np->line_speed = 1; /* 10M */ + if (data & DPLX_DET_FULL) + np->duplexmode = 2; /* full duplex mode */ + else + np->duplexmode = 1; /* half duplex mode */ + } else if (np->PHYType == AhdocPHY) { + unsigned int data; + + data = mdio_read(dev, np->phys[0], DiagnosticReg); + if (data & Speed_100) + np->line_speed = 2; /* 100M */ + else + np->line_speed = 1; /* 10M */ + if (data & DPLX_FULL) + np->duplexmode = 2; /* full duplex mode */ + else + np->duplexmode = 1; /* half duplex mode */ + } +/* 89/6/13 add, (begin) */ + else if (np->PHYType == MarvellPHY) { + unsigned int data; + + data = mdio_read(dev, np->phys[0], SpecificReg); + if (data & Full_Duplex) + np->duplexmode = 2; /* full duplex mode */ + else + np->duplexmode = 1; /* half duplex mode */ + data &= SpeedMask; + if (data == Speed_1000M) + np->line_speed = 3; /* 1000M */ + else if (data == Speed_100M) + np->line_speed = 2; /* 100M */ + else + np->line_speed = 1; /* 10M */ + } +/* 89/6/13 add, (end) */ +/* 89/7/27 add, (begin) */ + else if (np->PHYType == Myson981) { + unsigned int data; + + data = mdio_read(dev, np->phys[0], StatusRegister); + + if (data & SPEED100) + np->line_speed = 2; + else + np->line_speed = 1; + + if (data & FULLMODE) + np->duplexmode = 2; + else + np->duplexmode = 1; + } +/* 89/7/27 add, (end) */ +/* 89/12/29 add */ + else if (np->PHYType == LevelOnePHY) { + unsigned int data; + + data = mdio_read(dev, np->phys[0], SpecificReg); + if (data & LXT1000_Full) + np->duplexmode = 2; /* full duplex mode */ + else + np->duplexmode = 1; /* half duplex mode */ + data &= SpeedMask; + if (data == LXT1000_1000M) + np->line_speed = 3; /* 1000M */ + else if (data == LXT1000_100M) + np->line_speed = 2; /* 100M */ + else + np->line_speed = 1; /* 10M */ + } + np->crvalue &= (~CR_W_PS10) & (~CR_W_FD) & (~CR_W_PS1000); + if (np->line_speed == 1) + np->crvalue |= CR_W_PS10; + else if (np->line_speed == 3) + np->crvalue |= CR_W_PS1000; + if (np->duplexmode == 2) + np->crvalue |= CR_W_FD; + } +} + + +/* Take lock before calling this */ +static void allocate_rx_buffers(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + + /* allocate skb for rx buffers */ + while (np->really_rx_count != RX_RING_SIZE) { + struct sk_buff *skb; + + skb = netdev_alloc_skb(dev, np->rx_buf_sz); + if (skb == NULL) + break; /* Better luck next round. */ + + while (np->lack_rxbuf->skbuff) + np->lack_rxbuf = np->lack_rxbuf->next_desc_logical; + + np->lack_rxbuf->skbuff = skb; + np->lack_rxbuf->buffer = dma_map_single(&np->pci_dev->dev, + skb->data, + np->rx_buf_sz, + DMA_FROM_DEVICE); + np->lack_rxbuf->status = RXOWN; + ++np->really_rx_count; + } +} + + +static void netdev_timer(struct timer_list *t) +{ + struct netdev_private *np = from_timer(np, t, timer); + struct net_device *dev = np->mii.dev; + void __iomem *ioaddr = np->mem; + int old_crvalue = np->crvalue; + unsigned int old_linkok = np->linkok; + unsigned long flags; + + if (debug) + printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x " + "config %8.8x.\n", dev->name, ioread32(ioaddr + ISR), + ioread32(ioaddr + TCRRCR)); + + spin_lock_irqsave(&np->lock, flags); + + if (np->flags == HAS_MII_XCVR) { + getlinkstatus(dev); + if ((old_linkok == 0) && (np->linkok == 1)) { /* we need to detect the media type again */ + getlinktype(dev); + if (np->crvalue != old_crvalue) { + stop_nic_rxtx(ioaddr, np->crvalue); + iowrite32(np->crvalue, ioaddr + TCRRCR); + } + } + } + + allocate_rx_buffers(dev); + + spin_unlock_irqrestore(&np->lock, flags); + + np->timer.expires = RUN_AT(10 * HZ); + add_timer(&np->timer); +} + + +/* Take lock before calling */ +/* Reset chip and disable rx, tx and interrupts */ +static void reset_and_disable_rxtx(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->mem; + int delay=51; + + /* Reset the chip's Tx and Rx processes. */ + stop_nic_rxtx(ioaddr, 0); + + /* Disable interrupts by clearing the interrupt mask. */ + iowrite32(0, ioaddr + IMR); + + /* Reset the chip to erase previous misconfiguration. */ + iowrite32(0x00000001, ioaddr + BCR); + + /* Ueimor: wait for 50 PCI cycles (and flush posted writes btw). + We surely wait too long (address+data phase). Who cares? */ + while (--delay) { + ioread32(ioaddr + BCR); + rmb(); + } +} + + +/* Take lock before calling */ +/* Restore chip after reset */ +static void enable_rxtx(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->mem; + + reset_rx_descriptors(dev); + + iowrite32(np->tx_ring_dma + ((char*)np->cur_tx - (char*)np->tx_ring), + ioaddr + TXLBA); + iowrite32(np->rx_ring_dma + ((char*)np->cur_rx - (char*)np->rx_ring), + ioaddr + RXLBA); + + iowrite32(np->bcrvalue, ioaddr + BCR); + + iowrite32(0, ioaddr + RXPDR); + __set_rx_mode(dev); /* changes np->crvalue, writes it into TCRRCR */ + + /* Clear and Enable interrupts by setting the interrupt mask. */ + iowrite32(FBE | TUNF | CNTOVF | RBU | TI | RI, ioaddr + ISR); + iowrite32(np->imrvalue, ioaddr + IMR); + + iowrite32(0, ioaddr + TXPDR); +} + + +static void reset_timer(struct timer_list *t) +{ + struct netdev_private *np = from_timer(np, t, reset_timer); + struct net_device *dev = np->mii.dev; + unsigned long flags; + + printk(KERN_WARNING "%s: resetting tx and rx machinery\n", dev->name); + + spin_lock_irqsave(&np->lock, flags); + np->crvalue = np->crvalue_sv; + np->imrvalue = np->imrvalue_sv; + + reset_and_disable_rxtx(dev); + /* works for me without this: + reset_tx_descriptors(dev); */ + enable_rxtx(dev); + netif_start_queue(dev); /* FIXME: or netif_wake_queue(dev); ? */ + + np->reset_timer_armed = 0; + + spin_unlock_irqrestore(&np->lock, flags); +} + + +static void fealnx_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->mem; + unsigned long flags; + int i; + + printk(KERN_WARNING + "%s: Transmit timed out, status %8.8x, resetting...\n", + dev->name, ioread32(ioaddr + ISR)); + + { + printk(KERN_DEBUG " Rx ring %p: ", np->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + printk(KERN_CONT " %8.8x", + (unsigned int) np->rx_ring[i].status); + printk(KERN_CONT "\n"); + printk(KERN_DEBUG " Tx ring %p: ", np->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(KERN_CONT " %4.4x", np->tx_ring[i].status); + printk(KERN_CONT "\n"); + } + + spin_lock_irqsave(&np->lock, flags); + + reset_and_disable_rxtx(dev); + reset_tx_descriptors(dev); + enable_rxtx(dev); + + spin_unlock_irqrestore(&np->lock, flags); + + netif_trans_update(dev); /* prevent tx timeout */ + dev->stats.tx_errors++; + netif_wake_queue(dev); /* or .._start_.. ?? */ +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void init_ring(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + int i; + + /* initialize rx variables */ + np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); + np->cur_rx = &np->rx_ring[0]; + np->lack_rxbuf = np->rx_ring; + np->really_rx_count = 0; + + /* initial rx descriptors. */ + for (i = 0; i < RX_RING_SIZE; i++) { + np->rx_ring[i].status = 0; + np->rx_ring[i].control = np->rx_buf_sz << RBSShift; + np->rx_ring[i].next_desc = np->rx_ring_dma + + (i + 1)*sizeof(struct fealnx_desc); + np->rx_ring[i].next_desc_logical = &np->rx_ring[i + 1]; + np->rx_ring[i].skbuff = NULL; + } + + /* for the last rx descriptor */ + np->rx_ring[i - 1].next_desc = np->rx_ring_dma; + np->rx_ring[i - 1].next_desc_logical = np->rx_ring; + + /* allocate skb for rx buffers */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = netdev_alloc_skb(dev, np->rx_buf_sz); + + if (skb == NULL) { + np->lack_rxbuf = &np->rx_ring[i]; + break; + } + + ++np->really_rx_count; + np->rx_ring[i].skbuff = skb; + np->rx_ring[i].buffer = dma_map_single(&np->pci_dev->dev, + skb->data, + np->rx_buf_sz, + DMA_FROM_DEVICE); + np->rx_ring[i].status = RXOWN; + np->rx_ring[i].control |= RXIC; + } + + /* initialize tx variables */ + np->cur_tx = &np->tx_ring[0]; + np->cur_tx_copy = &np->tx_ring[0]; + np->really_tx_count = 0; + np->free_tx_count = TX_RING_SIZE; + + for (i = 0; i < TX_RING_SIZE; i++) { + np->tx_ring[i].status = 0; + /* do we need np->tx_ring[i].control = XXX; ?? */ + np->tx_ring[i].next_desc = np->tx_ring_dma + + (i + 1)*sizeof(struct fealnx_desc); + np->tx_ring[i].next_desc_logical = &np->tx_ring[i + 1]; + np->tx_ring[i].skbuff = NULL; + } + + /* for the last tx descriptor */ + np->tx_ring[i - 1].next_desc = np->tx_ring_dma; + np->tx_ring[i - 1].next_desc_logical = &np->tx_ring[0]; +} + + +static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&np->lock, flags); + + np->cur_tx_copy->skbuff = skb; + +#define one_buffer +#define BPT 1022 +#if defined(one_buffer) + np->cur_tx_copy->buffer = dma_map_single(&np->pci_dev->dev, skb->data, + skb->len, DMA_TO_DEVICE); + np->cur_tx_copy->control = TXIC | TXLD | TXFD | CRCEnable | PADEnable; + np->cur_tx_copy->control |= (skb->len << PKTSShift); /* pkt size */ + np->cur_tx_copy->control |= (skb->len << TBSShift); /* buffer size */ +// 89/12/29 add, + if (np->pci_dev->device == 0x891) + np->cur_tx_copy->control |= ETIControl | RetryTxLC; + np->cur_tx_copy->status = TXOWN; + np->cur_tx_copy = np->cur_tx_copy->next_desc_logical; + --np->free_tx_count; +#elif defined(two_buffer) + if (skb->len > BPT) { + struct fealnx_desc *next; + + /* for the first descriptor */ + np->cur_tx_copy->buffer = dma_map_single(&np->pci_dev->dev, + skb->data, BPT, + DMA_TO_DEVICE); + np->cur_tx_copy->control = TXIC | TXFD | CRCEnable | PADEnable; + np->cur_tx_copy->control |= (skb->len << PKTSShift); /* pkt size */ + np->cur_tx_copy->control |= (BPT << TBSShift); /* buffer size */ + + /* for the last descriptor */ + next = np->cur_tx_copy->next_desc_logical; + next->skbuff = skb; + next->control = TXIC | TXLD | CRCEnable | PADEnable; + next->control |= (skb->len << PKTSShift); /* pkt size */ + next->control |= ((skb->len - BPT) << TBSShift); /* buf size */ +// 89/12/29 add, + if (np->pci_dev->device == 0x891) + np->cur_tx_copy->control |= ETIControl | RetryTxLC; + next->buffer = dma_map_single(&ep->pci_dev->dev, + skb->data + BPT, skb->len - BPT, + DMA_TO_DEVICE); + + next->status = TXOWN; + np->cur_tx_copy->status = TXOWN; + + np->cur_tx_copy = next->next_desc_logical; + np->free_tx_count -= 2; + } else { + np->cur_tx_copy->buffer = dma_map_single(&np->pci_dev->dev, + skb->data, skb->len, + DMA_TO_DEVICE); + np->cur_tx_copy->control = TXIC | TXLD | TXFD | CRCEnable | PADEnable; + np->cur_tx_copy->control |= (skb->len << PKTSShift); /* pkt size */ + np->cur_tx_copy->control |= (skb->len << TBSShift); /* buffer size */ +// 89/12/29 add, + if (np->pci_dev->device == 0x891) + np->cur_tx_copy->control |= ETIControl | RetryTxLC; + np->cur_tx_copy->status = TXOWN; + np->cur_tx_copy = np->cur_tx_copy->next_desc_logical; + --np->free_tx_count; + } +#endif + + if (np->free_tx_count < 2) + netif_stop_queue(dev); + ++np->really_tx_count; + iowrite32(0, np->mem + TXPDR); + + spin_unlock_irqrestore(&np->lock, flags); + return NETDEV_TX_OK; +} + + +/* Take lock before calling */ +/* Chip probably hosed tx ring. Clean up. */ +static void reset_tx_descriptors(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + struct fealnx_desc *cur; + int i; + + /* initialize tx variables */ + np->cur_tx = &np->tx_ring[0]; + np->cur_tx_copy = &np->tx_ring[0]; + np->really_tx_count = 0; + np->free_tx_count = TX_RING_SIZE; + + for (i = 0; i < TX_RING_SIZE; i++) { + cur = &np->tx_ring[i]; + if (cur->skbuff) { + dma_unmap_single(&np->pci_dev->dev, cur->buffer, + cur->skbuff->len, DMA_TO_DEVICE); + dev_kfree_skb_any(cur->skbuff); + cur->skbuff = NULL; + } + cur->status = 0; + cur->control = 0; /* needed? */ + /* probably not needed. We do it for purely paranoid reasons */ + cur->next_desc = np->tx_ring_dma + + (i + 1)*sizeof(struct fealnx_desc); + cur->next_desc_logical = &np->tx_ring[i + 1]; + } + /* for the last tx descriptor */ + np->tx_ring[TX_RING_SIZE - 1].next_desc = np->tx_ring_dma; + np->tx_ring[TX_RING_SIZE - 1].next_desc_logical = &np->tx_ring[0]; +} + + +/* Take lock and stop rx before calling this */ +static void reset_rx_descriptors(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + struct fealnx_desc *cur = np->cur_rx; + int i; + + allocate_rx_buffers(dev); + + for (i = 0; i < RX_RING_SIZE; i++) { + if (cur->skbuff) + cur->status = RXOWN; + cur = cur->next_desc_logical; + } + + iowrite32(np->rx_ring_dma + ((char*)np->cur_rx - (char*)np->rx_ring), + np->mem + RXLBA); +} + + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static irqreturn_t intr_handler(int irq, void *dev_instance) +{ + struct net_device *dev = (struct net_device *) dev_instance; + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->mem; + long boguscnt = max_interrupt_work; + unsigned int num_tx = 0; + int handled = 0; + + spin_lock(&np->lock); + + iowrite32(0, ioaddr + IMR); + + do { + u32 intr_status = ioread32(ioaddr + ISR); + + /* Acknowledge all of the current interrupt sources ASAP. */ + iowrite32(intr_status, ioaddr + ISR); + + if (debug) + printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", dev->name, + intr_status); + + if (!(intr_status & np->imrvalue)) + break; + + handled = 1; + +// 90/1/16 delete, +// +// if (intr_status & FBE) +// { /* fatal error */ +// stop_nic_tx(ioaddr, 0); +// stop_nic_rx(ioaddr, 0); +// break; +// }; + + if (intr_status & TUNF) + iowrite32(0, ioaddr + TXPDR); + + if (intr_status & CNTOVF) { + /* missed pkts */ + dev->stats.rx_missed_errors += + ioread32(ioaddr + TALLY) & 0x7fff; + + /* crc error */ + dev->stats.rx_crc_errors += + (ioread32(ioaddr + TALLY) & 0x7fff0000) >> 16; + } + + if (intr_status & (RI | RBU)) { + if (intr_status & RI) + netdev_rx(dev); + else { + stop_nic_rx(ioaddr, np->crvalue); + reset_rx_descriptors(dev); + iowrite32(np->crvalue, ioaddr + TCRRCR); + } + } + + while (np->really_tx_count) { + long tx_status = np->cur_tx->status; + long tx_control = np->cur_tx->control; + + if (!(tx_control & TXLD)) { /* this pkt is combined by two tx descriptors */ + struct fealnx_desc *next; + + next = np->cur_tx->next_desc_logical; + tx_status = next->status; + tx_control = next->control; + } + + if (tx_status & TXOWN) + break; + + if (!(np->crvalue & CR_W_ENH)) { + if (tx_status & (CSL | LC | EC | UDF | HF)) { + dev->stats.tx_errors++; + if (tx_status & EC) + dev->stats.tx_aborted_errors++; + if (tx_status & CSL) + dev->stats.tx_carrier_errors++; + if (tx_status & LC) + dev->stats.tx_window_errors++; + if (tx_status & UDF) + dev->stats.tx_fifo_errors++; + if ((tx_status & HF) && np->mii.full_duplex == 0) + dev->stats.tx_heartbeat_errors++; + + } else { + dev->stats.tx_bytes += + ((tx_control & PKTSMask) >> PKTSShift); + + dev->stats.collisions += + ((tx_status & NCRMask) >> NCRShift); + dev->stats.tx_packets++; + } + } else { + dev->stats.tx_bytes += + ((tx_control & PKTSMask) >> PKTSShift); + dev->stats.tx_packets++; + } + + /* Free the original skb. */ + dma_unmap_single(&np->pci_dev->dev, + np->cur_tx->buffer, + np->cur_tx->skbuff->len, + DMA_TO_DEVICE); + dev_consume_skb_irq(np->cur_tx->skbuff); + np->cur_tx->skbuff = NULL; + --np->really_tx_count; + if (np->cur_tx->control & TXLD) { + np->cur_tx = np->cur_tx->next_desc_logical; + ++np->free_tx_count; + } else { + np->cur_tx = np->cur_tx->next_desc_logical; + np->cur_tx = np->cur_tx->next_desc_logical; + np->free_tx_count += 2; + } + num_tx++; + } /* end of for loop */ + + if (num_tx && np->free_tx_count >= 2) + netif_wake_queue(dev); + + /* read transmit status for enhanced mode only */ + if (np->crvalue & CR_W_ENH) { + long data; + + data = ioread32(ioaddr + TSR); + dev->stats.tx_errors += (data & 0xff000000) >> 24; + dev->stats.tx_aborted_errors += + (data & 0xff000000) >> 24; + dev->stats.tx_window_errors += + (data & 0x00ff0000) >> 16; + dev->stats.collisions += (data & 0x0000ffff); + } + + if (--boguscnt < 0) { + printk(KERN_WARNING "%s: Too much work at interrupt, " + "status=0x%4.4x.\n", dev->name, intr_status); + if (!np->reset_timer_armed) { + np->reset_timer_armed = 1; + np->reset_timer.expires = RUN_AT(HZ/2); + add_timer(&np->reset_timer); + stop_nic_rxtx(ioaddr, 0); + netif_stop_queue(dev); + /* or netif_tx_disable(dev); ?? */ + /* Prevent other paths from enabling tx,rx,intrs */ + np->crvalue_sv = np->crvalue; + np->imrvalue_sv = np->imrvalue; + np->crvalue &= ~(CR_W_TXEN | CR_W_RXEN); /* or simply = 0? */ + np->imrvalue = 0; + } + + break; + } + } while (1); + + /* read the tally counters */ + /* missed pkts */ + dev->stats.rx_missed_errors += ioread32(ioaddr + TALLY) & 0x7fff; + + /* crc error */ + dev->stats.rx_crc_errors += + (ioread32(ioaddr + TALLY) & 0x7fff0000) >> 16; + + if (debug) + printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", + dev->name, ioread32(ioaddr + ISR)); + + iowrite32(np->imrvalue, ioaddr + IMR); + + spin_unlock(&np->lock); + + return IRQ_RETVAL(handled); +} + + +/* This routine is logically part of the interrupt handler, but separated + for clarity and better register allocation. */ +static int netdev_rx(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->mem; + + /* If EOP is set on the next entry, it's a new packet. Send it up. */ + while (!(np->cur_rx->status & RXOWN) && np->cur_rx->skbuff) { + s32 rx_status = np->cur_rx->status; + + if (np->really_rx_count == 0) + break; + + if (debug) + printk(KERN_DEBUG " netdev_rx() status was %8.8x.\n", rx_status); + + if ((!((rx_status & RXFSD) && (rx_status & RXLSD))) || + (rx_status & ErrorSummary)) { + if (rx_status & ErrorSummary) { /* there was a fatal error */ + if (debug) + printk(KERN_DEBUG + "%s: Receive error, Rx status %8.8x.\n", + dev->name, rx_status); + + dev->stats.rx_errors++; /* end of a packet. */ + if (rx_status & (LONGPKT | RUNTPKT)) + dev->stats.rx_length_errors++; + if (rx_status & RXER) + dev->stats.rx_frame_errors++; + if (rx_status & CRC) + dev->stats.rx_crc_errors++; + } else { + int need_to_reset = 0; + int desno = 0; + + if (rx_status & RXFSD) { /* this pkt is too long, over one rx buffer */ + struct fealnx_desc *cur; + + /* check this packet is received completely? */ + cur = np->cur_rx; + while (desno <= np->really_rx_count) { + ++desno; + if ((!(cur->status & RXOWN)) && + (cur->status & RXLSD)) + break; + /* goto next rx descriptor */ + cur = cur->next_desc_logical; + } + if (desno > np->really_rx_count) + need_to_reset = 1; + } else /* RXLSD did not find, something error */ + need_to_reset = 1; + + if (need_to_reset == 0) { + int i; + + dev->stats.rx_length_errors++; + + /* free all rx descriptors related this long pkt */ + for (i = 0; i < desno; ++i) { + if (!np->cur_rx->skbuff) { + printk(KERN_DEBUG + "%s: I'm scared\n", dev->name); + break; + } + np->cur_rx->status = RXOWN; + np->cur_rx = np->cur_rx->next_desc_logical; + } + continue; + } else { /* rx error, need to reset this chip */ + stop_nic_rx(ioaddr, np->crvalue); + reset_rx_descriptors(dev); + iowrite32(np->crvalue, ioaddr + TCRRCR); + } + break; /* exit the while loop */ + } + } else { /* this received pkt is ok */ + + struct sk_buff *skb; + /* Omit the four octet CRC from the length. */ + short pkt_len = ((rx_status & FLNGMASK) >> FLNGShift) - 4; + +#ifndef final_version + if (debug) + printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d" + " status %x.\n", pkt_len, rx_status); +#endif + + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ + if (pkt_len < rx_copybreak && + (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) { + skb_reserve(skb, 2); /* 16 byte align the IP header */ + dma_sync_single_for_cpu(&np->pci_dev->dev, + np->cur_rx->buffer, + np->rx_buf_sz, + DMA_FROM_DEVICE); + /* Call copy + cksum if available. */ + +#if ! defined(__alpha__) + skb_copy_to_linear_data(skb, + np->cur_rx->skbuff->data, pkt_len); + skb_put(skb, pkt_len); +#else + skb_put_data(skb, np->cur_rx->skbuff->data, + pkt_len); +#endif + dma_sync_single_for_device(&np->pci_dev->dev, + np->cur_rx->buffer, + np->rx_buf_sz, + DMA_FROM_DEVICE); + } else { + dma_unmap_single(&np->pci_dev->dev, + np->cur_rx->buffer, + np->rx_buf_sz, + DMA_FROM_DEVICE); + skb_put(skb = np->cur_rx->skbuff, pkt_len); + np->cur_rx->skbuff = NULL; + --np->really_rx_count; + } + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + } + + np->cur_rx = np->cur_rx->next_desc_logical; + } /* end of while loop */ + + /* allocate skb for rx buffers */ + allocate_rx_buffers(dev); + + return 0; +} + + +static struct net_device_stats *get_stats(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->mem; + + /* The chip only need report frame silently dropped. */ + if (netif_running(dev)) { + dev->stats.rx_missed_errors += + ioread32(ioaddr + TALLY) & 0x7fff; + dev->stats.rx_crc_errors += + (ioread32(ioaddr + TALLY) & 0x7fff0000) >> 16; + } + + return &dev->stats; +} + + +/* for dev->set_multicast_list */ +static void set_rx_mode(struct net_device *dev) +{ + spinlock_t *lp = &((struct netdev_private *)netdev_priv(dev))->lock; + unsigned long flags; + spin_lock_irqsave(lp, flags); + __set_rx_mode(dev); + spin_unlock_irqrestore(lp, flags); +} + + +/* Take lock before calling */ +static void __set_rx_mode(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->mem; + u32 mc_filter[2]; /* Multicast hash filter */ + u32 rx_mode; + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + memset(mc_filter, 0xff, sizeof(mc_filter)); + rx_mode = CR_W_PROM | CR_W_AB | CR_W_AM; + } else if ((netdev_mc_count(dev) > multicast_filter_limit) || + (dev->flags & IFF_ALLMULTI)) { + /* Too many to match, or accept all multicasts. */ + memset(mc_filter, 0xff, sizeof(mc_filter)); + rx_mode = CR_W_AB | CR_W_AM; + } else { + struct netdev_hw_addr *ha; + + memset(mc_filter, 0, sizeof(mc_filter)); + netdev_for_each_mc_addr(ha, dev) { + unsigned int bit; + bit = (ether_crc(ETH_ALEN, ha->addr) >> 26) ^ 0x3F; + mc_filter[bit >> 5] |= (1 << bit); + } + rx_mode = CR_W_AB | CR_W_AM; + } + + stop_nic_rxtx(ioaddr, np->crvalue); + + iowrite32(mc_filter[0], ioaddr + MAR0); + iowrite32(mc_filter[1], ioaddr + MAR1); + np->crvalue &= ~CR_W_RXMODEMASK; + np->crvalue |= rx_mode; + iowrite32(np->crvalue, ioaddr + TCRRCR); +} + +static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct netdev_private *np = netdev_priv(dev); + + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); +} + +static int netdev_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) +{ + struct netdev_private *np = netdev_priv(dev); + + spin_lock_irq(&np->lock); + mii_ethtool_get_link_ksettings(&np->mii, cmd); + spin_unlock_irq(&np->lock); + + return 0; +} + +static int netdev_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) +{ + struct netdev_private *np = netdev_priv(dev); + int rc; + + spin_lock_irq(&np->lock); + rc = mii_ethtool_set_link_ksettings(&np->mii, cmd); + spin_unlock_irq(&np->lock); + + return rc; +} + +static int netdev_nway_reset(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + return mii_nway_restart(&np->mii); +} + +static u32 netdev_get_link(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + return mii_link_ok(&np->mii); +} + +static u32 netdev_get_msglevel(struct net_device *dev) +{ + return debug; +} + +static void netdev_set_msglevel(struct net_device *dev, u32 value) +{ + debug = value; +} + +static const struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, + .nway_reset = netdev_nway_reset, + .get_link = netdev_get_link, + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, + .get_link_ksettings = netdev_get_link_ksettings, + .set_link_ksettings = netdev_set_link_ksettings, +}; + +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct netdev_private *np = netdev_priv(dev); + int rc; + + if (!netif_running(dev)) + return -EINVAL; + + spin_lock_irq(&np->lock); + rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL); + spin_unlock_irq(&np->lock); + + return rc; +} + + +static int netdev_close(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->mem; + int i; + + netif_stop_queue(dev); + + /* Disable interrupts by clearing the interrupt mask. */ + iowrite32(0x0000, ioaddr + IMR); + + /* Stop the chip's Tx and Rx processes. */ + stop_nic_rxtx(ioaddr, 0); + + del_timer_sync(&np->timer); + del_timer_sync(&np->reset_timer); + + free_irq(np->pci_dev->irq, dev); + + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = np->rx_ring[i].skbuff; + + np->rx_ring[i].status = 0; + if (skb) { + dma_unmap_single(&np->pci_dev->dev, + np->rx_ring[i].buffer, np->rx_buf_sz, + DMA_FROM_DEVICE); + dev_kfree_skb(skb); + np->rx_ring[i].skbuff = NULL; + } + } + + for (i = 0; i < TX_RING_SIZE; i++) { + struct sk_buff *skb = np->tx_ring[i].skbuff; + + if (skb) { + dma_unmap_single(&np->pci_dev->dev, + np->tx_ring[i].buffer, skb->len, + DMA_TO_DEVICE); + dev_kfree_skb(skb); + np->tx_ring[i].skbuff = NULL; + } + } + + return 0; +} + +static const struct pci_device_id fealnx_pci_tbl[] = { + {0x1516, 0x0800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x1516, 0x0803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + {0x1516, 0x0891, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, + {} /* terminate list */ +}; +MODULE_DEVICE_TABLE(pci, fealnx_pci_tbl); + + +static struct pci_driver fealnx_driver = { + .name = "fealnx", + .id_table = fealnx_pci_tbl, + .probe = fealnx_init_one, + .remove = fealnx_remove_one, +}; + +module_pci_driver(fealnx_driver); diff --git a/drivers/net/ethernet/intel/ice/ice_dcb.c b/drivers/net/ethernet/intel/ice/ice_dcb.c index c557dfc50aad..396e555023aa 100644 --- a/drivers/net/ethernet/intel/ice/ice_dcb.c +++ b/drivers/net/ethernet/intel/ice/ice_dcb.c @@ -1411,7 +1411,7 @@ ice_add_dscp_pfc_tlv(struct ice_lldp_org_tlv *tlv, struct ice_dcbx_cfg *dcbcfg) tlv->ouisubtype = htonl(ouisubtype); buf[0] = dcbcfg->pfc.pfccap & 0xF; - buf[1] = dcbcfg->pfc.pfcena & 0xF; + buf[1] = dcbcfg->pfc.pfcena; } /** diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index b360bd8f1599..f86e814354a3 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -4331,6 +4331,8 @@ ice_get_module_eeprom(struct net_device *netdev, * SFP modules only ever use page 0. */ if (page == 0 || !(data[0x2] & 0x4)) { + u32 copy_len; + /* If i2c bus is busy due to slow page change or * link management access, call can fail. This is normal. * So we retry this a few times. @@ -4354,8 +4356,8 @@ ice_get_module_eeprom(struct net_device *netdev, } /* Make sure we have enough room for the new block */ - if ((i + SFF_READ_BLOCK_SIZE) < ee->len) - memcpy(data + i, value, SFF_READ_BLOCK_SIZE); + copy_len = min_t(u32, SFF_READ_BLOCK_SIZE, ee->len - i); + memcpy(data + i, value, copy_len); } } return 0; diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index 781475480ff2..0f52ea38b6f3 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -2126,7 +2126,7 @@ int ice_vsi_cfg_xdp_txqs(struct ice_vsi *vsi) ice_for_each_rxq(vsi, i) ice_tx_xsk_pool(vsi, i); - return ret; + return 0; } /** @@ -2693,12 +2693,14 @@ ice_vsi_cfg_def(struct ice_vsi *vsi, struct ice_vsi_cfg_params *params) return ret; /* allocate memory for Tx/Rx ring stat pointers */ - if (ice_vsi_alloc_stat_arrays(vsi)) + ret = ice_vsi_alloc_stat_arrays(vsi); + if (ret) goto unroll_vsi_alloc; ice_alloc_fd_res(vsi); - if (ice_vsi_get_qs(vsi)) { + ret = ice_vsi_get_qs(vsi); + if (ret) { dev_err(dev, "Failed to allocate queues. vsi->idx = %d\n", vsi->idx); goto unroll_vsi_alloc_stat; @@ -2811,6 +2813,7 @@ ice_vsi_cfg_def(struct ice_vsi *vsi, struct ice_vsi_cfg_params *params) break; default: /* clean up the resources and exit */ + ret = -EINVAL; goto unroll_vsi_init; } @@ -3508,10 +3511,10 @@ int ice_vsi_rebuild(struct ice_vsi *vsi, u32 vsi_flags) if (vsi_flags & ICE_VSI_FLAG_INIT) { ret = -EIO; goto err_vsi_cfg_tc_lan; - } else { - kfree(coalesce); - return ice_schedule_reset(pf, ICE_RESET_PFR); } + + kfree(coalesce); + return ice_schedule_reset(pf, ICE_RESET_PFR); } ice_vsi_realloc_stat_arrays(vsi, prev_txq, prev_rxq); @@ -3759,7 +3762,7 @@ int ice_vsi_cfg_tc(struct ice_vsi *vsi, u8 ena_tc) dev = ice_pf_to_dev(pf); if (vsi->tc_cfg.ena_tc == ena_tc && vsi->mqprio_qopt.mode != TC_MQPRIO_MODE_CHANNEL) - return ret; + return 0; ice_for_each_traffic_class(i) { /* build bitmap of enabled TCs */ diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.c b/drivers/net/ethernet/intel/ice/ice_tc_lib.c index 6b48cbc049c6..76f29a5bf8d7 100644 --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.c @@ -1455,8 +1455,8 @@ ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi, if (match.mask->vlan_priority) { fltr->flags |= ICE_TC_FLWR_FIELD_VLAN_PRIO; headers->vlan_hdr.vlan_prio = - cpu_to_be16((match.key->vlan_priority << - VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK); + be16_encode_bits(match.key->vlan_priority, + VLAN_PRIO_MASK); } if (match.mask->vlan_tpid) @@ -1489,8 +1489,8 @@ ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi, if (match.mask->vlan_priority) { fltr->flags |= ICE_TC_FLWR_FIELD_CVLAN_PRIO; headers->cvlan_hdr.vlan_prio = - cpu_to_be16((match.key->vlan_priority << - VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK); + be16_encode_bits(match.key->vlan_priority, + VLAN_PRIO_MASK); } } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index 389663a13d1d..ef721caeac49 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -884,6 +884,9 @@ int rvu_cpt_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int lf, int rvu_cpt_ctx_flush(struct rvu *rvu, u16 pcifunc); int rvu_cpt_init(struct rvu *rvu); +#define NDC_AF_BANK_MASK GENMASK_ULL(7, 0) +#define NDC_AF_BANK_LINE_MASK GENMASK_ULL(31, 16) + /* CN10K RVU */ int rvu_set_channels_base(struct rvu *rvu); void rvu_program_channels(struct rvu *rvu); @@ -902,6 +905,8 @@ static inline void rvu_dbg_init(struct rvu *rvu) {} static inline void rvu_dbg_exit(struct rvu *rvu) {} #endif +int rvu_ndc_fix_locked_cacheline(struct rvu *rvu, int blkaddr); + /* RVU Switch */ void rvu_switch_enable(struct rvu *rvu); void rvu_switch_disable(struct rvu *rvu); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c index fa280ebd3052..26cfa501f1a1 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c @@ -198,9 +198,6 @@ enum cpt_eng_type { CPT_IE_TYPE = 3, }; -#define NDC_MAX_BANK(rvu, blk_addr) (rvu_read64(rvu, \ - blk_addr, NDC_AF_CONST) & 0xFF) - #define rvu_dbg_NULL NULL #define rvu_dbg_open_NULL NULL @@ -1448,6 +1445,7 @@ static int ndc_blk_hits_miss_stats(struct seq_file *s, int idx, int blk_addr) struct nix_hw *nix_hw; struct rvu *rvu; int bank, max_bank; + u64 ndc_af_const; if (blk_addr == BLKADDR_NDC_NPA0) { rvu = s->private; @@ -1456,7 +1454,8 @@ static int ndc_blk_hits_miss_stats(struct seq_file *s, int idx, int blk_addr) rvu = nix_hw->rvu; } - max_bank = NDC_MAX_BANK(rvu, blk_addr); + ndc_af_const = rvu_read64(rvu, blk_addr, NDC_AF_CONST); + max_bank = FIELD_GET(NDC_AF_BANK_MASK, ndc_af_const); for (bank = 0; bank < max_bank; bank++) { seq_printf(s, "BANK:%d\n", bank); seq_printf(s, "\tHits:\t%lld\n", diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 26e639e57dae..4ad707e758b9 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -790,6 +790,7 @@ static int nix_aq_enqueue_wait(struct rvu *rvu, struct rvu_block *block, struct nix_aq_res_s *result; int timeout = 1000; u64 reg, head; + int ret; result = (struct nix_aq_res_s *)aq->res->base; @@ -813,9 +814,22 @@ static int nix_aq_enqueue_wait(struct rvu *rvu, struct rvu_block *block, return -EBUSY; } - if (result->compcode != NIX_AQ_COMP_GOOD) + if (result->compcode != NIX_AQ_COMP_GOOD) { /* TODO: Replace this with some error code */ + if (result->compcode == NIX_AQ_COMP_CTX_FAULT || + result->compcode == NIX_AQ_COMP_LOCKERR || + result->compcode == NIX_AQ_COMP_CTX_POISON) { + ret = rvu_ndc_fix_locked_cacheline(rvu, BLKADDR_NDC_NIX0_RX); + ret |= rvu_ndc_fix_locked_cacheline(rvu, BLKADDR_NDC_NIX0_TX); + ret |= rvu_ndc_fix_locked_cacheline(rvu, BLKADDR_NDC_NIX1_RX); + ret |= rvu_ndc_fix_locked_cacheline(rvu, BLKADDR_NDC_NIX1_TX); + if (ret) + dev_err(rvu->dev, + "%s: Not able to unlock cachelines\n", __func__); + } + return -EBUSY; + } return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c index 70bd036ed76e..4f5ca5ab13a4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c @@ -4,7 +4,7 @@ * Copyright (C) 2018 Marvell. * */ - +#include <linux/bitfield.h> #include <linux/module.h> #include <linux/pci.h> @@ -42,9 +42,18 @@ static int npa_aq_enqueue_wait(struct rvu *rvu, struct rvu_block *block, return -EBUSY; } - if (result->compcode != NPA_AQ_COMP_GOOD) + if (result->compcode != NPA_AQ_COMP_GOOD) { /* TODO: Replace this with some error code */ + if (result->compcode == NPA_AQ_COMP_CTX_FAULT || + result->compcode == NPA_AQ_COMP_LOCKERR || + result->compcode == NPA_AQ_COMP_CTX_POISON) { + if (rvu_ndc_fix_locked_cacheline(rvu, BLKADDR_NDC_NPA0)) + dev_err(rvu->dev, + "%s: Not able to unlock cachelines\n", __func__); + } + return -EBUSY; + } return 0; } @@ -545,3 +554,48 @@ void rvu_npa_lf_teardown(struct rvu *rvu, u16 pcifunc, int npalf) npa_ctx_free(rvu, pfvf); } + +/* Due to an Hardware errata, in some corner cases, AQ context lock + * operations can result in a NDC way getting into an illegal state + * of not valid but locked. + * + * This API solves the problem by clearing the lock bit of the NDC block. + * The operation needs to be done for each line of all the NDC banks. + */ +int rvu_ndc_fix_locked_cacheline(struct rvu *rvu, int blkaddr) +{ + int bank, max_bank, line, max_line, err; + u64 reg, ndc_af_const; + + /* Set the ENABLE bit(63) to '0' */ + reg = rvu_read64(rvu, blkaddr, NDC_AF_CAMS_RD_INTERVAL); + rvu_write64(rvu, blkaddr, NDC_AF_CAMS_RD_INTERVAL, reg & GENMASK_ULL(62, 0)); + + /* Poll until the BUSY bits(47:32) are set to '0' */ + err = rvu_poll_reg(rvu, blkaddr, NDC_AF_CAMS_RD_INTERVAL, GENMASK_ULL(47, 32), true); + if (err) { + dev_err(rvu->dev, "Timed out while polling for NDC CAM busy bits.\n"); + return err; + } + + ndc_af_const = rvu_read64(rvu, blkaddr, NDC_AF_CONST); + max_bank = FIELD_GET(NDC_AF_BANK_MASK, ndc_af_const); + max_line = FIELD_GET(NDC_AF_BANK_LINE_MASK, ndc_af_const); + for (bank = 0; bank < max_bank; bank++) { + for (line = 0; line < max_line; line++) { + /* Check if 'cache line valid bit(63)' is not set + * but 'cache line lock bit(60)' is set and on + * success, reset the lock bit(60). + */ + reg = rvu_read64(rvu, blkaddr, + NDC_AF_BANKX_LINEX_METADATA(bank, line)); + if (!(reg & BIT_ULL(63)) && (reg & BIT_ULL(60))) { + rvu_write64(rvu, blkaddr, + NDC_AF_BANKX_LINEX_METADATA(bank, line), + reg & ~BIT_ULL(60)); + } + } + } + + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h index 1729b22580ce..7007f0b8e659 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h @@ -694,6 +694,7 @@ #define NDC_AF_INTR_ENA_W1S (0x00068) #define NDC_AF_INTR_ENA_W1C (0x00070) #define NDC_AF_ACTIVE_PC (0x00078) +#define NDC_AF_CAMS_RD_INTERVAL (0x00080) #define NDC_AF_BP_TEST_ENABLE (0x001F8) #define NDC_AF_BP_TEST(a) (0x00200 | (a) << 3) #define NDC_AF_BLK_RST (0x002F0) @@ -709,6 +710,8 @@ (0x00F00 | (a) << 5 | (b) << 4) #define NDC_AF_BANKX_HIT_PC(a) (0x01000 | (a) << 3) #define NDC_AF_BANKX_MISS_PC(a) (0x01100 | (a) << 3) +#define NDC_AF_BANKX_LINEX_METADATA(a, b) \ + (0x10000 | (a) << 12 | (b) << 3) /* LBK */ #define LBK_CONST (0x10ull) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 14be6ea51b88..3cb43623d3db 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -616,7 +616,8 @@ static int mtk_mac_finish(struct phylink_config *config, unsigned int mode, mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); mcr_new = mcr_cur; mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE | - MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK; + MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK | + MAC_MCR_RX_FIFO_CLR_DIS; /* Only update control register when needed! */ if (mcr_new != mcr_cur) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index afc9d52e79bf..b65de174c3d9 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -397,6 +397,7 @@ #define MAC_MCR_FORCE_MODE BIT(15) #define MAC_MCR_TX_EN BIT(14) #define MAC_MCR_RX_EN BIT(13) +#define MAC_MCR_RX_FIFO_CLR_DIS BIT(12) #define MAC_MCR_BACKOFF_EN BIT(9) #define MAC_MCR_BACKPR_EN BIT(8) #define MAC_MCR_FORCE_RX_FC BIT(5) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_police.c b/drivers/net/ethernet/microchip/lan966x/lan966x_police.c index a9aec900d608..7d66fe75cd3b 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_police.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_police.c @@ -194,7 +194,7 @@ int lan966x_police_port_del(struct lan966x_port *port, return -EINVAL; } - err = lan966x_police_del(port, port->tc.police_id); + err = lan966x_police_del(port, POL_IDX_PORT + port->chip_port); if (err) { NL_SET_ERR_MSG_MOD(extack, "Failed to add policer to port"); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c index 871a3e62f852..2d763664dcda 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c @@ -249,6 +249,21 @@ static int sparx5_dcb_ieee_dscp_setdel(struct net_device *dev, return 0; } +static int sparx5_dcb_ieee_delapp(struct net_device *dev, struct dcb_app *app) +{ + int err; + + if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP) + err = sparx5_dcb_ieee_dscp_setdel(dev, app, dcb_ieee_delapp); + else + err = dcb_ieee_delapp(dev, app); + + if (err < 0) + return err; + + return sparx5_dcb_app_update(dev); +} + static int sparx5_dcb_ieee_setapp(struct net_device *dev, struct dcb_app *app) { struct dcb_app app_itr; @@ -264,7 +279,7 @@ static int sparx5_dcb_ieee_setapp(struct net_device *dev, struct dcb_app *app) if (prio) { app_itr = *app; app_itr.priority = prio; - dcb_ieee_delapp(dev, &app_itr); + sparx5_dcb_ieee_delapp(dev, &app_itr); } if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP) @@ -281,21 +296,6 @@ out: return err; } -static int sparx5_dcb_ieee_delapp(struct net_device *dev, struct dcb_app *app) -{ - int err; - - if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP) - err = sparx5_dcb_ieee_dscp_setdel(dev, app, dcb_ieee_delapp); - else - err = dcb_ieee_delapp(dev, app); - - if (err < 0) - return err; - - return sparx5_dcb_app_update(dev); -} - static int sparx5_dcb_setapptrust(struct net_device *dev, u8 *selectors, int nselectors) { diff --git a/drivers/net/ethernet/netronome/nfp/nfd3/dp.c b/drivers/net/ethernet/netronome/nfp/nfd3/dp.c index 59fb0583cc08..0cc026b0aefd 100644 --- a/drivers/net/ethernet/netronome/nfp/nfd3/dp.c +++ b/drivers/net/ethernet/netronome/nfp/nfd3/dp.c @@ -324,14 +324,15 @@ netdev_tx_t nfp_nfd3_tx(struct sk_buff *skb, struct net_device *netdev) /* Do not reorder - tso may adjust pkt cnt, vlan may override fields */ nfp_nfd3_tx_tso(r_vec, txbuf, txd, skb, md_bytes); - nfp_nfd3_tx_csum(dp, r_vec, txbuf, txd, skb); + if (ipsec) + nfp_nfd3_ipsec_tx(txd, skb); + else + nfp_nfd3_tx_csum(dp, r_vec, txbuf, txd, skb); if (skb_vlan_tag_present(skb) && dp->ctrl & NFP_NET_CFG_CTRL_TXVLAN) { txd->flags |= NFD3_DESC_TX_VLAN; txd->vlan = cpu_to_le16(skb_vlan_tag_get(skb)); } - if (ipsec) - nfp_nfd3_ipsec_tx(txd, skb); /* Gather DMA */ if (nr_frags > 0) { __le64 second_half; diff --git a/drivers/net/ethernet/netronome/nfp/nfd3/ipsec.c b/drivers/net/ethernet/netronome/nfp/nfd3/ipsec.c index e90f8c975903..51087693072c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfd3/ipsec.c +++ b/drivers/net/ethernet/netronome/nfp/nfd3/ipsec.c @@ -10,9 +10,30 @@ void nfp_nfd3_ipsec_tx(struct nfp_nfd3_tx_desc *txd, struct sk_buff *skb) { struct xfrm_state *x = xfrm_input_state(skb); + struct xfrm_offload *xo = xfrm_offload(skb); + struct iphdr *iph = ip_hdr(skb); + int l4_proto; if (x->xso.dev && (x->xso.dev->features & NETIF_F_HW_ESP_TX_CSUM)) { - txd->flags |= NFD3_DESC_TX_CSUM | NFD3_DESC_TX_IP4_CSUM | - NFD3_DESC_TX_TCP_CSUM | NFD3_DESC_TX_UDP_CSUM; + txd->flags |= NFD3_DESC_TX_CSUM; + + if (iph->version == 4) + txd->flags |= NFD3_DESC_TX_IP4_CSUM; + + if (x->props.mode == XFRM_MODE_TRANSPORT) + l4_proto = xo->proto; + else if (x->props.mode == XFRM_MODE_TUNNEL) + l4_proto = xo->inner_ipproto; + else + return; + + switch (l4_proto) { + case IPPROTO_UDP: + txd->flags |= NFD3_DESC_TX_UDP_CSUM; + return; + case IPPROTO_TCP: + txd->flags |= NFD3_DESC_TX_TCP_CSUM; + return; + } } } diff --git a/drivers/net/ethernet/netronome/nfp/nfdk/dp.c b/drivers/net/ethernet/netronome/nfp/nfdk/dp.c index d60c0e991a91..33b6d74adb4b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfdk/dp.c +++ b/drivers/net/ethernet/netronome/nfp/nfdk/dp.c @@ -387,7 +387,8 @@ netdev_tx_t nfp_nfdk_tx(struct sk_buff *skb, struct net_device *netdev) if (!skb_is_gso(skb)) { real_len = skb->len; /* Metadata desc */ - metadata = nfp_nfdk_tx_csum(dp, r_vec, 1, skb, metadata); + if (!ipsec) + metadata = nfp_nfdk_tx_csum(dp, r_vec, 1, skb, metadata); txd->raw = cpu_to_le64(metadata); txd++; } else { @@ -395,7 +396,8 @@ netdev_tx_t nfp_nfdk_tx(struct sk_buff *skb, struct net_device *netdev) (txd + 1)->raw = nfp_nfdk_tx_tso(r_vec, txbuf, skb); real_len = txbuf->real_len; /* Metadata desc */ - metadata = nfp_nfdk_tx_csum(dp, r_vec, txbuf->pkt_cnt, skb, metadata); + if (!ipsec) + metadata = nfp_nfdk_tx_csum(dp, r_vec, txbuf->pkt_cnt, skb, metadata); txd->raw = cpu_to_le64(metadata); txd += 2; txbuf++; diff --git a/drivers/net/ethernet/netronome/nfp/nfdk/ipsec.c b/drivers/net/ethernet/netronome/nfp/nfdk/ipsec.c index 58d8f59eb885..cec199f4c852 100644 --- a/drivers/net/ethernet/netronome/nfp/nfdk/ipsec.c +++ b/drivers/net/ethernet/netronome/nfp/nfdk/ipsec.c @@ -9,9 +9,13 @@ u64 nfp_nfdk_ipsec_tx(u64 flags, struct sk_buff *skb) { struct xfrm_state *x = xfrm_input_state(skb); + struct iphdr *iph = ip_hdr(skb); - if (x->xso.dev && (x->xso.dev->features & NETIF_F_HW_ESP_TX_CSUM)) - flags |= NFDK_DESC_TX_L3_CSUM | NFDK_DESC_TX_L4_CSUM; + if (x->xso.dev && (x->xso.dev->features & NETIF_F_HW_ESP_TX_CSUM)) { + if (iph->version == 4) + flags |= NFDK_DESC_TX_L3_CSUM; + flags |= NFDK_DESC_TX_L4_CSUM; + } return flags; } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 81b7ca0ad222..62f0bf91d1e1 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -38,6 +38,7 @@ #include <net/tls.h> #include <net/vxlan.h> #include <net/xdp_sock_drv.h> +#include <net/xfrm.h> #include "nfpcore/nfp_dev.h" #include "nfpcore/nfp_nsp.h" @@ -1897,6 +1898,9 @@ nfp_net_features_check(struct sk_buff *skb, struct net_device *dev, features &= ~NETIF_F_GSO_MASK; } + if (xfrm_offload(skb)) + return features; + /* VXLAN/GRE check */ switch (vlan_get_protocol(skb)) { case htons(ETH_P_IP): diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index e4902a7bb61e..8f543c3ab5c5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1170,6 +1170,7 @@ static int stmmac_init_phy(struct net_device *dev) phylink_ethtool_get_wol(priv->phylink, &wol); device_set_wakeup_capable(priv->device, !!wol.supported); + device_set_wakeup_enable(priv->device, !!wol.wolopts); } return ret; diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c index e1a569b99e4a..0b0c6c0764fe 100644 --- a/drivers/net/ieee802154/ca8210.c +++ b/drivers/net/ieee802154/ca8210.c @@ -1913,6 +1913,8 @@ static int ca8210_skb_tx( * packet */ mac_len = ieee802154_hdr_peek_addrs(skb, &header); + if (mac_len < 0) + return mac_len; secspec.security_level = header.sec.level; secspec.key_id_mode = header.sec.key_id_mode; diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c index ccecee2524ce..0b88635f4fbc 100644 --- a/drivers/net/phy/microchip.c +++ b/drivers/net/phy/microchip.c @@ -342,6 +342,37 @@ static int lan88xx_config_aneg(struct phy_device *phydev) return genphy_config_aneg(phydev); } +static void lan88xx_link_change_notify(struct phy_device *phydev) +{ + int temp; + + /* At forced 100 F/H mode, chip may fail to set mode correctly + * when cable is switched between long(~50+m) and short one. + * As workaround, set to 10 before setting to 100 + * at forced 100 F/H mode. + */ + if (!phydev->autoneg && phydev->speed == 100) { + /* disable phy interrupt */ + temp = phy_read(phydev, LAN88XX_INT_MASK); + temp &= ~LAN88XX_INT_MASK_MDINTPIN_EN_; + phy_write(phydev, LAN88XX_INT_MASK, temp); + + temp = phy_read(phydev, MII_BMCR); + temp &= ~(BMCR_SPEED100 | BMCR_SPEED1000); + phy_write(phydev, MII_BMCR, temp); /* set to 10 first */ + temp |= BMCR_SPEED100; + phy_write(phydev, MII_BMCR, temp); /* set to 100 later */ + + /* clear pending interrupt generated while workaround */ + temp = phy_read(phydev, LAN88XX_INT_STS); + + /* enable phy interrupt back */ + temp = phy_read(phydev, LAN88XX_INT_MASK); + temp |= LAN88XX_INT_MASK_MDINTPIN_EN_; + phy_write(phydev, LAN88XX_INT_MASK, temp); + } +} + static struct phy_driver microchip_phy_driver[] = { { .phy_id = 0x0007c132, @@ -359,6 +390,7 @@ static struct phy_driver microchip_phy_driver[] = { .config_init = lan88xx_config_init, .config_aneg = lan88xx_config_aneg, + .link_change_notify = lan88xx_link_change_notify, .config_intr = lan88xx_phy_config_intr, .handle_interrupt = lan88xx_handle_interrupt, diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 3f8a64fb9d71..1785f1cead97 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -3098,8 +3098,6 @@ static int phy_probe(struct device *dev) if (phydrv->flags & PHY_IS_INTERNAL) phydev->is_internal = true; - mutex_lock(&phydev->lock); - /* Deassert the reset signal */ phy_device_reset(phydev, 0); @@ -3146,7 +3144,7 @@ static int phy_probe(struct device *dev) */ err = genphy_c45_read_eee_adv(phydev, phydev->advertising_eee); if (err) - return err; + goto out; /* There is no "enabled" flag. If PHY is advertising, assume it is * kind of enabled. @@ -3188,12 +3186,10 @@ static int phy_probe(struct device *dev) phydev->state = PHY_READY; out: - /* Assert the reset signal */ + /* Re-assert the reset signal on error */ if (err) phy_device_reset(phydev, 1); - mutex_unlock(&phydev->lock); - return err; } @@ -3203,9 +3199,7 @@ static int phy_remove(struct device *dev) cancel_delayed_work_sync(&phydev->state_queue); - mutex_lock(&phydev->lock); phydev->state = PHY_DOWN; - mutex_unlock(&phydev->lock); sfp_bus_del_upstream(phydev->sfp_bus); phydev->sfp_bus = NULL; diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index ac7481ce2fc1..00d9eff91dcf 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -44,7 +44,6 @@ static struct smsc_hw_stat smsc_hw_stats[] = { }; struct smsc_phy_priv { - u16 intmask; bool energy_enable; }; @@ -57,7 +56,6 @@ static int smsc_phy_ack_interrupt(struct phy_device *phydev) static int smsc_phy_config_intr(struct phy_device *phydev) { - struct smsc_phy_priv *priv = phydev->priv; int rc; if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { @@ -65,14 +63,9 @@ static int smsc_phy_config_intr(struct phy_device *phydev) if (rc) return rc; - priv->intmask = MII_LAN83C185_ISF_INT4 | MII_LAN83C185_ISF_INT6; - if (priv->energy_enable) - priv->intmask |= MII_LAN83C185_ISF_INT7; - - rc = phy_write(phydev, MII_LAN83C185_IM, priv->intmask); + rc = phy_write(phydev, MII_LAN83C185_IM, + MII_LAN83C185_ISF_INT_PHYLIB_EVENTS); } else { - priv->intmask = 0; - rc = phy_write(phydev, MII_LAN83C185_IM, 0); if (rc) return rc; @@ -85,7 +78,6 @@ static int smsc_phy_config_intr(struct phy_device *phydev) static irqreturn_t smsc_phy_handle_interrupt(struct phy_device *phydev) { - struct smsc_phy_priv *priv = phydev->priv; int irq_status; irq_status = phy_read(phydev, MII_LAN83C185_ISF); @@ -96,7 +88,7 @@ static irqreturn_t smsc_phy_handle_interrupt(struct phy_device *phydev) return IRQ_NONE; } - if (!(irq_status & priv->intmask)) + if (!(irq_status & MII_LAN83C185_ISF_INT_PHYLIB_EVENTS)) return IRQ_NONE; phy_trigger_machine(phydev); diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index c89639381eca..cd4083e0b3b9 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -665,6 +665,11 @@ static const struct usb_device_id mbim_devs[] = { .driver_info = (unsigned long)&cdc_mbim_info_avoid_altsetting_toggle, }, + /* Telit FE990 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x1bc7, 0x1081, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long)&cdc_mbim_info_avoid_altsetting_toggle, + }, + /* default entry */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&cdc_mbim_info_zlp, diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index f18ab8e220db..068488890d57 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -2115,33 +2115,8 @@ static void lan78xx_remove_mdio(struct lan78xx_net *dev) static void lan78xx_link_status_change(struct net_device *net) { struct phy_device *phydev = net->phydev; - int temp; - - /* At forced 100 F/H mode, chip may fail to set mode correctly - * when cable is switched between long(~50+m) and short one. - * As workaround, set to 10 before setting to 100 - * at forced 100 F/H mode. - */ - if (!phydev->autoneg && (phydev->speed == 100)) { - /* disable phy interrupt */ - temp = phy_read(phydev, LAN88XX_INT_MASK); - temp &= ~LAN88XX_INT_MASK_MDINTPIN_EN_; - phy_write(phydev, LAN88XX_INT_MASK, temp); - temp = phy_read(phydev, MII_BMCR); - temp &= ~(BMCR_SPEED100 | BMCR_SPEED1000); - phy_write(phydev, MII_BMCR, temp); /* set to 10 first */ - temp |= BMCR_SPEED100; - phy_write(phydev, MII_BMCR, temp); /* set to 100 later */ - - /* clear pending interrupt generated while workaround */ - temp = phy_read(phydev, LAN88XX_INT_STS); - - /* enable phy interrupt back */ - temp = phy_read(phydev, LAN88XX_INT_MASK); - temp |= LAN88XX_INT_MASK_MDINTPIN_EN_; - phy_write(phydev, LAN88XX_INT_MASK, temp); - } + phy_print_status(phydev); } static int irq_map(struct irq_domain *d, unsigned int irq, diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index a808d718c012..571e37e67f9c 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1364,6 +1364,7 @@ static const struct usb_device_id products[] = { {QMI_QUIRK_SET_DTR(0x1bc7, 0x1057, 2)}, /* Telit FN980 */ {QMI_QUIRK_SET_DTR(0x1bc7, 0x1060, 2)}, /* Telit LN920 */ {QMI_QUIRK_SET_DTR(0x1bc7, 0x1070, 2)}, /* Telit FN990 */ + {QMI_QUIRK_SET_DTR(0x1bc7, 0x1080, 2)}, /* Telit FE990 */ {QMI_FIXED_INTF(0x1bc7, 0x1100, 3)}, /* Telit ME910 */ {QMI_FIXED_INTF(0x1bc7, 0x1101, 3)}, /* Telit ME910 dual modem */ {QMI_FIXED_INTF(0x1bc7, 0x1200, 5)}, /* Telit LE920 */ diff --git a/drivers/net/wireguard/queueing.h b/drivers/net/wireguard/queueing.h index 583adb37ee1e..125284b346a7 100644 --- a/drivers/net/wireguard/queueing.h +++ b/drivers/net/wireguard/queueing.h @@ -106,7 +106,7 @@ static inline int wg_cpumask_choose_online(int *stored_cpu, unsigned int id) { unsigned int cpu = *stored_cpu, cpu_index, i; - if (unlikely(cpu == nr_cpumask_bits || + if (unlikely(cpu >= nr_cpu_ids || !cpumask_test_cpu(cpu, cpu_online_mask))) { cpu_index = id % cpumask_weight(cpu_online_mask); cpu = cpumask_first(cpu_online_mask); |