162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This code is derived from the VIA reference driver (copyright message 462306a36Sopenharmony_ci * below) provided to Red Hat by VIA Networking Technologies, Inc. for 562306a36Sopenharmony_ci * addition to the Linux kernel. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * The code has been merged into one source file, cleaned up to follow 862306a36Sopenharmony_ci * Linux coding style, ported to the Linux 2.6 kernel tree and cleaned 962306a36Sopenharmony_ci * for 64bit hardware platforms. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * TODO 1262306a36Sopenharmony_ci * rx_copybreak/alignment 1362306a36Sopenharmony_ci * More testing 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * The changes are (c) Copyright 2004, Red Hat Inc. <alan@lxorguk.ukuu.org.uk> 1662306a36Sopenharmony_ci * Additional fixes and clean up: Francois Romieu 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * This source has not been verified for use in safety critical systems. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Please direct queries about the revamped driver to the linux-kernel 2162306a36Sopenharmony_ci * list not VIA. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Original code: 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. 2662306a36Sopenharmony_ci * All rights reserved. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * Author: Chuang Liang-Shing, AJ Jiang 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * Date: Jan 24, 2003 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * MODULE_LICENSE("GPL"); 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <linux/module.h> 3862306a36Sopenharmony_ci#include <linux/types.h> 3962306a36Sopenharmony_ci#include <linux/bitops.h> 4062306a36Sopenharmony_ci#include <linux/init.h> 4162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 4262306a36Sopenharmony_ci#include <linux/mm.h> 4362306a36Sopenharmony_ci#include <linux/errno.h> 4462306a36Sopenharmony_ci#include <linux/ioport.h> 4562306a36Sopenharmony_ci#include <linux/pci.h> 4662306a36Sopenharmony_ci#include <linux/kernel.h> 4762306a36Sopenharmony_ci#include <linux/netdevice.h> 4862306a36Sopenharmony_ci#include <linux/etherdevice.h> 4962306a36Sopenharmony_ci#include <linux/skbuff.h> 5062306a36Sopenharmony_ci#include <linux/delay.h> 5162306a36Sopenharmony_ci#include <linux/timer.h> 5262306a36Sopenharmony_ci#include <linux/slab.h> 5362306a36Sopenharmony_ci#include <linux/interrupt.h> 5462306a36Sopenharmony_ci#include <linux/string.h> 5562306a36Sopenharmony_ci#include <linux/wait.h> 5662306a36Sopenharmony_ci#include <linux/io.h> 5762306a36Sopenharmony_ci#include <linux/if.h> 5862306a36Sopenharmony_ci#include <linux/uaccess.h> 5962306a36Sopenharmony_ci#include <linux/proc_fs.h> 6062306a36Sopenharmony_ci#include <linux/of.h> 6162306a36Sopenharmony_ci#include <linux/of_address.h> 6262306a36Sopenharmony_ci#include <linux/of_irq.h> 6362306a36Sopenharmony_ci#include <linux/inetdevice.h> 6462306a36Sopenharmony_ci#include <linux/platform_device.h> 6562306a36Sopenharmony_ci#include <linux/reboot.h> 6662306a36Sopenharmony_ci#include <linux/ethtool.h> 6762306a36Sopenharmony_ci#include <linux/mii.h> 6862306a36Sopenharmony_ci#include <linux/in.h> 6962306a36Sopenharmony_ci#include <linux/if_arp.h> 7062306a36Sopenharmony_ci#include <linux/if_vlan.h> 7162306a36Sopenharmony_ci#include <linux/ip.h> 7262306a36Sopenharmony_ci#include <linux/tcp.h> 7362306a36Sopenharmony_ci#include <linux/udp.h> 7462306a36Sopenharmony_ci#include <linux/crc-ccitt.h> 7562306a36Sopenharmony_ci#include <linux/crc32.h> 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#include "via-velocity.h" 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cienum velocity_bus_type { 8062306a36Sopenharmony_ci BUS_PCI, 8162306a36Sopenharmony_ci BUS_PLATFORM, 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int velocity_nics; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void velocity_set_power_state(struct velocity_info *vptr, char state) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci void *addr = vptr->mac_regs; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (vptr->pdev) 9162306a36Sopenharmony_ci pci_set_power_state(vptr->pdev, state); 9262306a36Sopenharmony_ci else 9362306a36Sopenharmony_ci writeb(state, addr + 0x154); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/** 9762306a36Sopenharmony_ci * mac_get_cam_mask - Read a CAM mask 9862306a36Sopenharmony_ci * @regs: register block for this velocity 9962306a36Sopenharmony_ci * @mask: buffer to store mask 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * Fetch the mask bits of the selected CAM and store them into the 10262306a36Sopenharmony_ci * provided mask buffer. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_cistatic void mac_get_cam_mask(struct mac_regs __iomem *regs, u8 *mask) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci int i; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Select CAM mask */ 10962306a36Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci writeb(0, ®s->CAMADDR); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* read mask */ 11462306a36Sopenharmony_ci for (i = 0; i < 8; i++) 11562306a36Sopenharmony_ci *mask++ = readb(&(regs->MARCAM[i])); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* disable CAMEN */ 11862306a36Sopenharmony_ci writeb(0, ®s->CAMADDR); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Select mar */ 12162306a36Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/** 12562306a36Sopenharmony_ci * mac_set_cam_mask - Set a CAM mask 12662306a36Sopenharmony_ci * @regs: register block for this velocity 12762306a36Sopenharmony_ci * @mask: CAM mask to load 12862306a36Sopenharmony_ci * 12962306a36Sopenharmony_ci * Store a new mask into a CAM 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_cistatic void mac_set_cam_mask(struct mac_regs __iomem *regs, u8 *mask) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci int i; 13462306a36Sopenharmony_ci /* Select CAM mask */ 13562306a36Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci writeb(CAMADDR_CAMEN, ®s->CAMADDR); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci for (i = 0; i < 8; i++) 14062306a36Sopenharmony_ci writeb(*mask++, &(regs->MARCAM[i])); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* disable CAMEN */ 14362306a36Sopenharmony_ci writeb(0, ®s->CAMADDR); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* Select mar */ 14662306a36Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic void mac_set_vlan_cam_mask(struct mac_regs __iomem *regs, u8 *mask) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci int i; 15262306a36Sopenharmony_ci /* Select CAM mask */ 15362306a36Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL, ®s->CAMADDR); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci for (i = 0; i < 8; i++) 15862306a36Sopenharmony_ci writeb(*mask++, &(regs->MARCAM[i])); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* disable CAMEN */ 16162306a36Sopenharmony_ci writeb(0, ®s->CAMADDR); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* Select mar */ 16462306a36Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/** 16862306a36Sopenharmony_ci * mac_set_cam - set CAM data 16962306a36Sopenharmony_ci * @regs: register block of this velocity 17062306a36Sopenharmony_ci * @idx: Cam index 17162306a36Sopenharmony_ci * @addr: 2 or 6 bytes of CAM data 17262306a36Sopenharmony_ci * 17362306a36Sopenharmony_ci * Load an address or vlan tag into a CAM 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_cistatic void mac_set_cam(struct mac_regs __iomem *regs, int idx, const u8 *addr) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci int i; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* Select CAM mask */ 18062306a36Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci idx &= (64 - 1); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci writeb(CAMADDR_CAMEN | idx, ®s->CAMADDR); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci for (i = 0; i < 6; i++) 18762306a36Sopenharmony_ci writeb(*addr++, &(regs->MARCAM[i])); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci BYTE_REG_BITS_ON(CAMCR_CAMWR, ®s->CAMCR); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci udelay(10); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci writeb(0, ®s->CAMADDR); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* Select mar */ 19662306a36Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic void mac_set_vlan_cam(struct mac_regs __iomem *regs, int idx, 20062306a36Sopenharmony_ci const u8 *addr) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Select CAM mask */ 20462306a36Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci idx &= (64 - 1); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL | idx, ®s->CAMADDR); 20962306a36Sopenharmony_ci writew(*((u16 *) addr), ®s->MARCAM[0]); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci BYTE_REG_BITS_ON(CAMCR_CAMWR, ®s->CAMCR); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci udelay(10); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci writeb(0, ®s->CAMADDR); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Select mar */ 21862306a36Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/** 22362306a36Sopenharmony_ci * mac_wol_reset - reset WOL after exiting low power 22462306a36Sopenharmony_ci * @regs: register block of this velocity 22562306a36Sopenharmony_ci * 22662306a36Sopenharmony_ci * Called after we drop out of wake on lan mode in order to 22762306a36Sopenharmony_ci * reset the Wake on lan features. This function doesn't restore 22862306a36Sopenharmony_ci * the rest of the logic from the result of sleep/wakeup 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_cistatic void mac_wol_reset(struct mac_regs __iomem *regs) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Turn off SWPTAG right after leaving power mode */ 23462306a36Sopenharmony_ci BYTE_REG_BITS_OFF(STICKHW_SWPTAG, ®s->STICKHW); 23562306a36Sopenharmony_ci /* clear sticky bits */ 23662306a36Sopenharmony_ci BYTE_REG_BITS_OFF((STICKHW_DS1 | STICKHW_DS0), ®s->STICKHW); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci BYTE_REG_BITS_OFF(CHIPGCR_FCGMII, ®s->CHIPGCR); 23962306a36Sopenharmony_ci BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, ®s->CHIPGCR); 24062306a36Sopenharmony_ci /* disable force PME-enable */ 24162306a36Sopenharmony_ci writeb(WOLCFG_PMEOVR, ®s->WOLCFGClr); 24262306a36Sopenharmony_ci /* disable power-event config bit */ 24362306a36Sopenharmony_ci writew(0xFFFF, ®s->WOLCRClr); 24462306a36Sopenharmony_ci /* clear power status */ 24562306a36Sopenharmony_ci writew(0xFFFF, ®s->WOLSRClr); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic const struct ethtool_ops velocity_ethtool_ops; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* 25162306a36Sopenharmony_ci Define module options 25262306a36Sopenharmony_ci*/ 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ciMODULE_AUTHOR("VIA Networking Technologies, Inc."); 25562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 25662306a36Sopenharmony_ciMODULE_DESCRIPTION("VIA Networking Velocity Family Gigabit Ethernet Adapter Driver"); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci#define VELOCITY_PARAM(N, D) \ 25962306a36Sopenharmony_ci static int N[MAX_UNITS] = OPTION_DEFAULT;\ 26062306a36Sopenharmony_ci module_param_array(N, int, NULL, 0); \ 26162306a36Sopenharmony_ci MODULE_PARM_DESC(N, D); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci#define RX_DESC_MIN 64 26462306a36Sopenharmony_ci#define RX_DESC_MAX 255 26562306a36Sopenharmony_ci#define RX_DESC_DEF 64 26662306a36Sopenharmony_ciVELOCITY_PARAM(RxDescriptors, "Number of receive descriptors"); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci#define TX_DESC_MIN 16 26962306a36Sopenharmony_ci#define TX_DESC_MAX 256 27062306a36Sopenharmony_ci#define TX_DESC_DEF 64 27162306a36Sopenharmony_ciVELOCITY_PARAM(TxDescriptors, "Number of transmit descriptors"); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci#define RX_THRESH_MIN 0 27462306a36Sopenharmony_ci#define RX_THRESH_MAX 3 27562306a36Sopenharmony_ci#define RX_THRESH_DEF 0 27662306a36Sopenharmony_ci/* rx_thresh[] is used for controlling the receive fifo threshold. 27762306a36Sopenharmony_ci 0: indicate the rxfifo threshold is 128 bytes. 27862306a36Sopenharmony_ci 1: indicate the rxfifo threshold is 512 bytes. 27962306a36Sopenharmony_ci 2: indicate the rxfifo threshold is 1024 bytes. 28062306a36Sopenharmony_ci 3: indicate the rxfifo threshold is store & forward. 28162306a36Sopenharmony_ci*/ 28262306a36Sopenharmony_ciVELOCITY_PARAM(rx_thresh, "Receive fifo threshold"); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci#define DMA_LENGTH_MIN 0 28562306a36Sopenharmony_ci#define DMA_LENGTH_MAX 7 28662306a36Sopenharmony_ci#define DMA_LENGTH_DEF 6 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* DMA_length[] is used for controlling the DMA length 28962306a36Sopenharmony_ci 0: 8 DWORDs 29062306a36Sopenharmony_ci 1: 16 DWORDs 29162306a36Sopenharmony_ci 2: 32 DWORDs 29262306a36Sopenharmony_ci 3: 64 DWORDs 29362306a36Sopenharmony_ci 4: 128 DWORDs 29462306a36Sopenharmony_ci 5: 256 DWORDs 29562306a36Sopenharmony_ci 6: SF(flush till emply) 29662306a36Sopenharmony_ci 7: SF(flush till emply) 29762306a36Sopenharmony_ci*/ 29862306a36Sopenharmony_ciVELOCITY_PARAM(DMA_length, "DMA length"); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci#define IP_ALIG_DEF 0 30162306a36Sopenharmony_ci/* IP_byte_align[] is used for IP header DWORD byte aligned 30262306a36Sopenharmony_ci 0: indicate the IP header won't be DWORD byte aligned.(Default) . 30362306a36Sopenharmony_ci 1: indicate the IP header will be DWORD byte aligned. 30462306a36Sopenharmony_ci In some environment, the IP header should be DWORD byte aligned, 30562306a36Sopenharmony_ci or the packet will be droped when we receive it. (eg: IPVS) 30662306a36Sopenharmony_ci*/ 30762306a36Sopenharmony_ciVELOCITY_PARAM(IP_byte_align, "Enable IP header dword aligned"); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci#define FLOW_CNTL_DEF 1 31062306a36Sopenharmony_ci#define FLOW_CNTL_MIN 1 31162306a36Sopenharmony_ci#define FLOW_CNTL_MAX 5 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci/* flow_control[] is used for setting the flow control ability of NIC. 31462306a36Sopenharmony_ci 1: hardware deafult - AUTO (default). Use Hardware default value in ANAR. 31562306a36Sopenharmony_ci 2: enable TX flow control. 31662306a36Sopenharmony_ci 3: enable RX flow control. 31762306a36Sopenharmony_ci 4: enable RX/TX flow control. 31862306a36Sopenharmony_ci 5: disable 31962306a36Sopenharmony_ci*/ 32062306a36Sopenharmony_ciVELOCITY_PARAM(flow_control, "Enable flow control ability"); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci#define MED_LNK_DEF 0 32362306a36Sopenharmony_ci#define MED_LNK_MIN 0 32462306a36Sopenharmony_ci#define MED_LNK_MAX 5 32562306a36Sopenharmony_ci/* speed_duplex[] is used for setting the speed and duplex mode of NIC. 32662306a36Sopenharmony_ci 0: indicate autonegotiation for both speed and duplex mode 32762306a36Sopenharmony_ci 1: indicate 100Mbps half duplex mode 32862306a36Sopenharmony_ci 2: indicate 100Mbps full duplex mode 32962306a36Sopenharmony_ci 3: indicate 10Mbps half duplex mode 33062306a36Sopenharmony_ci 4: indicate 10Mbps full duplex mode 33162306a36Sopenharmony_ci 5: indicate 1000Mbps full duplex mode 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci Note: 33462306a36Sopenharmony_ci if EEPROM have been set to the force mode, this option is ignored 33562306a36Sopenharmony_ci by driver. 33662306a36Sopenharmony_ci*/ 33762306a36Sopenharmony_ciVELOCITY_PARAM(speed_duplex, "Setting the speed and duplex mode"); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci#define WOL_OPT_DEF 0 34062306a36Sopenharmony_ci#define WOL_OPT_MIN 0 34162306a36Sopenharmony_ci#define WOL_OPT_MAX 7 34262306a36Sopenharmony_ci/* wol_opts[] is used for controlling wake on lan behavior. 34362306a36Sopenharmony_ci 0: Wake up if recevied a magic packet. (Default) 34462306a36Sopenharmony_ci 1: Wake up if link status is on/off. 34562306a36Sopenharmony_ci 2: Wake up if recevied an arp packet. 34662306a36Sopenharmony_ci 4: Wake up if recevied any unicast packet. 34762306a36Sopenharmony_ci Those value can be sumed up to support more than one option. 34862306a36Sopenharmony_ci*/ 34962306a36Sopenharmony_ciVELOCITY_PARAM(wol_opts, "Wake On Lan options"); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic int rx_copybreak = 200; 35262306a36Sopenharmony_cimodule_param(rx_copybreak, int, 0644); 35362306a36Sopenharmony_ciMODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames"); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci/* 35662306a36Sopenharmony_ci * Internal board variants. At the moment we have only one 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_cistatic struct velocity_info_tbl chip_info_table[] = { 35962306a36Sopenharmony_ci {CHIP_TYPE_VT6110, "VIA Networking Velocity Family Gigabit Ethernet Adapter", 1, 0x00FFFFFFUL}, 36062306a36Sopenharmony_ci { } 36162306a36Sopenharmony_ci}; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/* 36462306a36Sopenharmony_ci * Describe the PCI device identifiers that we support in this 36562306a36Sopenharmony_ci * device driver. Used for hotplug autoloading. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic const struct pci_device_id velocity_pci_id_table[] = { 36962306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_612X) }, 37062306a36Sopenharmony_ci { } 37162306a36Sopenharmony_ci}; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, velocity_pci_id_table); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/* 37662306a36Sopenharmony_ci * Describe the OF device identifiers that we support in this 37762306a36Sopenharmony_ci * device driver. Used for devicetree nodes. 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_cistatic const struct of_device_id velocity_of_ids[] = { 38062306a36Sopenharmony_ci { .compatible = "via,velocity-vt6110", .data = &chip_info_table[0] }, 38162306a36Sopenharmony_ci { /* Sentinel */ }, 38262306a36Sopenharmony_ci}; 38362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, velocity_of_ids); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci/** 38662306a36Sopenharmony_ci * get_chip_name - identifier to name 38762306a36Sopenharmony_ci * @chip_id: chip identifier 38862306a36Sopenharmony_ci * 38962306a36Sopenharmony_ci * Given a chip identifier return a suitable description. Returns 39062306a36Sopenharmony_ci * a pointer a static string valid while the driver is loaded. 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_cistatic const char *get_chip_name(enum chip_type chip_id) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci int i; 39562306a36Sopenharmony_ci for (i = 0; chip_info_table[i].name != NULL; i++) 39662306a36Sopenharmony_ci if (chip_info_table[i].chip_id == chip_id) 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci return chip_info_table[i].name; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci/** 40262306a36Sopenharmony_ci * velocity_set_int_opt - parser for integer options 40362306a36Sopenharmony_ci * @opt: pointer to option value 40462306a36Sopenharmony_ci * @val: value the user requested (or -1 for default) 40562306a36Sopenharmony_ci * @min: lowest value allowed 40662306a36Sopenharmony_ci * @max: highest value allowed 40762306a36Sopenharmony_ci * @def: default value 40862306a36Sopenharmony_ci * @name: property name 40962306a36Sopenharmony_ci * 41062306a36Sopenharmony_ci * Set an integer property in the module options. This function does 41162306a36Sopenharmony_ci * all the verification and checking as well as reporting so that 41262306a36Sopenharmony_ci * we don't duplicate code for each option. 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_cistatic void velocity_set_int_opt(int *opt, int val, int min, int max, int def, 41562306a36Sopenharmony_ci char *name) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci if (val == -1) 41862306a36Sopenharmony_ci *opt = def; 41962306a36Sopenharmony_ci else if (val < min || val > max) { 42062306a36Sopenharmony_ci pr_notice("the value of parameter %s is invalid, the valid range is (%d-%d)\n", 42162306a36Sopenharmony_ci name, min, max); 42262306a36Sopenharmony_ci *opt = def; 42362306a36Sopenharmony_ci } else { 42462306a36Sopenharmony_ci pr_info("set value of parameter %s to %d\n", name, val); 42562306a36Sopenharmony_ci *opt = val; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci/** 43062306a36Sopenharmony_ci * velocity_set_bool_opt - parser for boolean options 43162306a36Sopenharmony_ci * @opt: pointer to option value 43262306a36Sopenharmony_ci * @val: value the user requested (or -1 for default) 43362306a36Sopenharmony_ci * @def: default value (yes/no) 43462306a36Sopenharmony_ci * @flag: numeric value to set for true. 43562306a36Sopenharmony_ci * @name: property name 43662306a36Sopenharmony_ci * 43762306a36Sopenharmony_ci * Set a boolean property in the module options. This function does 43862306a36Sopenharmony_ci * all the verification and checking as well as reporting so that 43962306a36Sopenharmony_ci * we don't duplicate code for each option. 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_cistatic void velocity_set_bool_opt(u32 *opt, int val, int def, u32 flag, 44262306a36Sopenharmony_ci char *name) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci (*opt) &= (~flag); 44562306a36Sopenharmony_ci if (val == -1) 44662306a36Sopenharmony_ci *opt |= (def ? flag : 0); 44762306a36Sopenharmony_ci else if (val < 0 || val > 1) { 44862306a36Sopenharmony_ci pr_notice("the value of parameter %s is invalid, the valid range is (%d-%d)\n", 44962306a36Sopenharmony_ci name, 0, 1); 45062306a36Sopenharmony_ci *opt |= (def ? flag : 0); 45162306a36Sopenharmony_ci } else { 45262306a36Sopenharmony_ci pr_info("set parameter %s to %s\n", 45362306a36Sopenharmony_ci name, val ? "TRUE" : "FALSE"); 45462306a36Sopenharmony_ci *opt |= (val ? flag : 0); 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci/** 45962306a36Sopenharmony_ci * velocity_get_options - set options on device 46062306a36Sopenharmony_ci * @opts: option structure for the device 46162306a36Sopenharmony_ci * @index: index of option to use in module options array 46262306a36Sopenharmony_ci * 46362306a36Sopenharmony_ci * Turn the module and command options into a single structure 46462306a36Sopenharmony_ci * for the current device 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_cistatic void velocity_get_options(struct velocity_opt *opts, int index) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci velocity_set_int_opt(&opts->rx_thresh, rx_thresh[index], 47062306a36Sopenharmony_ci RX_THRESH_MIN, RX_THRESH_MAX, RX_THRESH_DEF, 47162306a36Sopenharmony_ci "rx_thresh"); 47262306a36Sopenharmony_ci velocity_set_int_opt(&opts->DMA_length, DMA_length[index], 47362306a36Sopenharmony_ci DMA_LENGTH_MIN, DMA_LENGTH_MAX, DMA_LENGTH_DEF, 47462306a36Sopenharmony_ci "DMA_length"); 47562306a36Sopenharmony_ci velocity_set_int_opt(&opts->numrx, RxDescriptors[index], 47662306a36Sopenharmony_ci RX_DESC_MIN, RX_DESC_MAX, RX_DESC_DEF, 47762306a36Sopenharmony_ci "RxDescriptors"); 47862306a36Sopenharmony_ci velocity_set_int_opt(&opts->numtx, TxDescriptors[index], 47962306a36Sopenharmony_ci TX_DESC_MIN, TX_DESC_MAX, TX_DESC_DEF, 48062306a36Sopenharmony_ci "TxDescriptors"); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci velocity_set_int_opt(&opts->flow_cntl, flow_control[index], 48362306a36Sopenharmony_ci FLOW_CNTL_MIN, FLOW_CNTL_MAX, FLOW_CNTL_DEF, 48462306a36Sopenharmony_ci "flow_control"); 48562306a36Sopenharmony_ci velocity_set_bool_opt(&opts->flags, IP_byte_align[index], 48662306a36Sopenharmony_ci IP_ALIG_DEF, VELOCITY_FLAGS_IP_ALIGN, 48762306a36Sopenharmony_ci "IP_byte_align"); 48862306a36Sopenharmony_ci velocity_set_int_opt((int *) &opts->spd_dpx, speed_duplex[index], 48962306a36Sopenharmony_ci MED_LNK_MIN, MED_LNK_MAX, MED_LNK_DEF, 49062306a36Sopenharmony_ci "Media link mode"); 49162306a36Sopenharmony_ci velocity_set_int_opt(&opts->wol_opts, wol_opts[index], 49262306a36Sopenharmony_ci WOL_OPT_MIN, WOL_OPT_MAX, WOL_OPT_DEF, 49362306a36Sopenharmony_ci "Wake On Lan options"); 49462306a36Sopenharmony_ci opts->numrx = (opts->numrx & ~3); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci/** 49862306a36Sopenharmony_ci * velocity_init_cam_filter - initialise CAM 49962306a36Sopenharmony_ci * @vptr: velocity to program 50062306a36Sopenharmony_ci * 50162306a36Sopenharmony_ci * Initialize the content addressable memory used for filters. Load 50262306a36Sopenharmony_ci * appropriately according to the presence of VLAN 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_cistatic void velocity_init_cam_filter(struct velocity_info *vptr) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 50762306a36Sopenharmony_ci unsigned int vid, i = 0; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* Turn on MCFG_PQEN, turn off MCFG_RTGOPT */ 51062306a36Sopenharmony_ci WORD_REG_BITS_SET(MCFG_PQEN, MCFG_RTGOPT, ®s->MCFG); 51162306a36Sopenharmony_ci WORD_REG_BITS_ON(MCFG_VIDFR, ®s->MCFG); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* Disable all CAMs */ 51462306a36Sopenharmony_ci memset(vptr->vCAMmask, 0, sizeof(u8) * 8); 51562306a36Sopenharmony_ci memset(vptr->mCAMmask, 0, sizeof(u8) * 8); 51662306a36Sopenharmony_ci mac_set_vlan_cam_mask(regs, vptr->vCAMmask); 51762306a36Sopenharmony_ci mac_set_cam_mask(regs, vptr->mCAMmask); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* Enable VCAMs */ 52062306a36Sopenharmony_ci for_each_set_bit(vid, vptr->active_vlans, VLAN_N_VID) { 52162306a36Sopenharmony_ci mac_set_vlan_cam(regs, i, (u8 *) &vid); 52262306a36Sopenharmony_ci vptr->vCAMmask[i / 8] |= 0x1 << (i % 8); 52362306a36Sopenharmony_ci if (++i >= VCAM_SIZE) 52462306a36Sopenharmony_ci break; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci mac_set_vlan_cam_mask(regs, vptr->vCAMmask); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic int velocity_vlan_rx_add_vid(struct net_device *dev, 53062306a36Sopenharmony_ci __be16 proto, u16 vid) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci spin_lock_irq(&vptr->lock); 53562306a36Sopenharmony_ci set_bit(vid, vptr->active_vlans); 53662306a36Sopenharmony_ci velocity_init_cam_filter(vptr); 53762306a36Sopenharmony_ci spin_unlock_irq(&vptr->lock); 53862306a36Sopenharmony_ci return 0; 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic int velocity_vlan_rx_kill_vid(struct net_device *dev, 54262306a36Sopenharmony_ci __be16 proto, u16 vid) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci spin_lock_irq(&vptr->lock); 54762306a36Sopenharmony_ci clear_bit(vid, vptr->active_vlans); 54862306a36Sopenharmony_ci velocity_init_cam_filter(vptr); 54962306a36Sopenharmony_ci spin_unlock_irq(&vptr->lock); 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic void velocity_init_rx_ring_indexes(struct velocity_info *vptr) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci vptr->rx.dirty = vptr->rx.filled = vptr->rx.curr = 0; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci/** 55962306a36Sopenharmony_ci * velocity_rx_reset - handle a receive reset 56062306a36Sopenharmony_ci * @vptr: velocity we are resetting 56162306a36Sopenharmony_ci * 56262306a36Sopenharmony_ci * Reset the ownership and status for the receive ring side. 56362306a36Sopenharmony_ci * Hand all the receive queue to the NIC. 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_cistatic void velocity_rx_reset(struct velocity_info *vptr) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 56962306a36Sopenharmony_ci int i; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci velocity_init_rx_ring_indexes(vptr); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* 57462306a36Sopenharmony_ci * Init state, all RD entries belong to the NIC 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ci for (i = 0; i < vptr->options.numrx; ++i) 57762306a36Sopenharmony_ci vptr->rx.ring[i].rdesc0.len |= OWNED_BY_NIC; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci writew(vptr->options.numrx, ®s->RBRDU); 58062306a36Sopenharmony_ci writel(vptr->rx.pool_dma, ®s->RDBaseLo); 58162306a36Sopenharmony_ci writew(0, ®s->RDIdx); 58262306a36Sopenharmony_ci writew(vptr->options.numrx - 1, ®s->RDCSize); 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci/** 58662306a36Sopenharmony_ci * velocity_get_opt_media_mode - get media selection 58762306a36Sopenharmony_ci * @vptr: velocity adapter 58862306a36Sopenharmony_ci * 58962306a36Sopenharmony_ci * Get the media mode stored in EEPROM or module options and load 59062306a36Sopenharmony_ci * mii_status accordingly. The requested link state information 59162306a36Sopenharmony_ci * is also returned. 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_cistatic u32 velocity_get_opt_media_mode(struct velocity_info *vptr) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci u32 status = 0; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci switch (vptr->options.spd_dpx) { 59862306a36Sopenharmony_ci case SPD_DPX_AUTO: 59962306a36Sopenharmony_ci status = VELOCITY_AUTONEG_ENABLE; 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci case SPD_DPX_100_FULL: 60262306a36Sopenharmony_ci status = VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL; 60362306a36Sopenharmony_ci break; 60462306a36Sopenharmony_ci case SPD_DPX_10_FULL: 60562306a36Sopenharmony_ci status = VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL; 60662306a36Sopenharmony_ci break; 60762306a36Sopenharmony_ci case SPD_DPX_100_HALF: 60862306a36Sopenharmony_ci status = VELOCITY_SPEED_100; 60962306a36Sopenharmony_ci break; 61062306a36Sopenharmony_ci case SPD_DPX_10_HALF: 61162306a36Sopenharmony_ci status = VELOCITY_SPEED_10; 61262306a36Sopenharmony_ci break; 61362306a36Sopenharmony_ci case SPD_DPX_1000_FULL: 61462306a36Sopenharmony_ci status = VELOCITY_SPEED_1000 | VELOCITY_DUPLEX_FULL; 61562306a36Sopenharmony_ci break; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci vptr->mii_status = status; 61862306a36Sopenharmony_ci return status; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci/** 62262306a36Sopenharmony_ci * safe_disable_mii_autopoll - autopoll off 62362306a36Sopenharmony_ci * @regs: velocity registers 62462306a36Sopenharmony_ci * 62562306a36Sopenharmony_ci * Turn off the autopoll and wait for it to disable on the chip 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_cistatic void safe_disable_mii_autopoll(struct mac_regs __iomem *regs) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci u16 ww; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* turn off MAUTO */ 63262306a36Sopenharmony_ci writeb(0, ®s->MIICR); 63362306a36Sopenharmony_ci for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { 63462306a36Sopenharmony_ci udelay(1); 63562306a36Sopenharmony_ci if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) 63662306a36Sopenharmony_ci break; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci/** 64162306a36Sopenharmony_ci * enable_mii_autopoll - turn on autopolling 64262306a36Sopenharmony_ci * @regs: velocity registers 64362306a36Sopenharmony_ci * 64462306a36Sopenharmony_ci * Enable the MII link status autopoll feature on the Velocity 64562306a36Sopenharmony_ci * hardware. Wait for it to enable. 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_cistatic void enable_mii_autopoll(struct mac_regs __iomem *regs) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci int ii; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci writeb(0, &(regs->MIICR)); 65262306a36Sopenharmony_ci writeb(MIIADR_SWMPL, ®s->MIIADR); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { 65562306a36Sopenharmony_ci udelay(1); 65662306a36Sopenharmony_ci if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci writeb(MIICR_MAUTO, ®s->MIICR); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { 66362306a36Sopenharmony_ci udelay(1); 66462306a36Sopenharmony_ci if (!BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) 66562306a36Sopenharmony_ci break; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci/** 67162306a36Sopenharmony_ci * velocity_mii_read - read MII data 67262306a36Sopenharmony_ci * @regs: velocity registers 67362306a36Sopenharmony_ci * @index: MII register index 67462306a36Sopenharmony_ci * @data: buffer for received data 67562306a36Sopenharmony_ci * 67662306a36Sopenharmony_ci * Perform a single read of an MII 16bit register. Returns zero 67762306a36Sopenharmony_ci * on success or -ETIMEDOUT if the PHY did not respond. 67862306a36Sopenharmony_ci */ 67962306a36Sopenharmony_cistatic int velocity_mii_read(struct mac_regs __iomem *regs, u8 index, u16 *data) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci u16 ww; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci /* 68462306a36Sopenharmony_ci * Disable MIICR_MAUTO, so that mii addr can be set normally 68562306a36Sopenharmony_ci */ 68662306a36Sopenharmony_ci safe_disable_mii_autopoll(regs); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci writeb(index, ®s->MIIADR); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci BYTE_REG_BITS_ON(MIICR_RCMD, ®s->MIICR); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { 69362306a36Sopenharmony_ci if (!(readb(®s->MIICR) & MIICR_RCMD)) 69462306a36Sopenharmony_ci break; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci *data = readw(®s->MIIDATA); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci enable_mii_autopoll(regs); 70062306a36Sopenharmony_ci if (ww == W_MAX_TIMEOUT) 70162306a36Sopenharmony_ci return -ETIMEDOUT; 70262306a36Sopenharmony_ci return 0; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci/** 70662306a36Sopenharmony_ci * mii_check_media_mode - check media state 70762306a36Sopenharmony_ci * @regs: velocity registers 70862306a36Sopenharmony_ci * 70962306a36Sopenharmony_ci * Check the current MII status and determine the link status 71062306a36Sopenharmony_ci * accordingly 71162306a36Sopenharmony_ci */ 71262306a36Sopenharmony_cistatic u32 mii_check_media_mode(struct mac_regs __iomem *regs) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci u32 status = 0; 71562306a36Sopenharmony_ci u16 ANAR; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (!MII_REG_BITS_IS_ON(BMSR_LSTATUS, MII_BMSR, regs)) 71862306a36Sopenharmony_ci status |= VELOCITY_LINK_FAIL; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (MII_REG_BITS_IS_ON(ADVERTISE_1000FULL, MII_CTRL1000, regs)) 72162306a36Sopenharmony_ci status |= VELOCITY_SPEED_1000 | VELOCITY_DUPLEX_FULL; 72262306a36Sopenharmony_ci else if (MII_REG_BITS_IS_ON(ADVERTISE_1000HALF, MII_CTRL1000, regs)) 72362306a36Sopenharmony_ci status |= (VELOCITY_SPEED_1000); 72462306a36Sopenharmony_ci else { 72562306a36Sopenharmony_ci velocity_mii_read(regs, MII_ADVERTISE, &ANAR); 72662306a36Sopenharmony_ci if (ANAR & ADVERTISE_100FULL) 72762306a36Sopenharmony_ci status |= (VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL); 72862306a36Sopenharmony_ci else if (ANAR & ADVERTISE_100HALF) 72962306a36Sopenharmony_ci status |= VELOCITY_SPEED_100; 73062306a36Sopenharmony_ci else if (ANAR & ADVERTISE_10FULL) 73162306a36Sopenharmony_ci status |= (VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL); 73262306a36Sopenharmony_ci else 73362306a36Sopenharmony_ci status |= (VELOCITY_SPEED_10); 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (MII_REG_BITS_IS_ON(BMCR_ANENABLE, MII_BMCR, regs)) { 73762306a36Sopenharmony_ci velocity_mii_read(regs, MII_ADVERTISE, &ANAR); 73862306a36Sopenharmony_ci if ((ANAR & (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF)) 73962306a36Sopenharmony_ci == (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF)) { 74062306a36Sopenharmony_ci if (MII_REG_BITS_IS_ON(ADVERTISE_1000HALF | ADVERTISE_1000FULL, MII_CTRL1000, regs)) 74162306a36Sopenharmony_ci status |= VELOCITY_AUTONEG_ENABLE; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci return status; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci/** 74962306a36Sopenharmony_ci * velocity_mii_write - write MII data 75062306a36Sopenharmony_ci * @regs: velocity registers 75162306a36Sopenharmony_ci * @mii_addr: MII register index 75262306a36Sopenharmony_ci * @data: 16bit data for the MII register 75362306a36Sopenharmony_ci * 75462306a36Sopenharmony_ci * Perform a single write to an MII 16bit register. Returns zero 75562306a36Sopenharmony_ci * on success or -ETIMEDOUT if the PHY did not respond. 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_cistatic int velocity_mii_write(struct mac_regs __iomem *regs, u8 mii_addr, u16 data) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci u16 ww; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* 76262306a36Sopenharmony_ci * Disable MIICR_MAUTO, so that mii addr can be set normally 76362306a36Sopenharmony_ci */ 76462306a36Sopenharmony_ci safe_disable_mii_autopoll(regs); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci /* MII reg offset */ 76762306a36Sopenharmony_ci writeb(mii_addr, ®s->MIIADR); 76862306a36Sopenharmony_ci /* set MII data */ 76962306a36Sopenharmony_ci writew(data, ®s->MIIDATA); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* turn on MIICR_WCMD */ 77262306a36Sopenharmony_ci BYTE_REG_BITS_ON(MIICR_WCMD, ®s->MIICR); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* W_MAX_TIMEOUT is the timeout period */ 77562306a36Sopenharmony_ci for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { 77662306a36Sopenharmony_ci udelay(5); 77762306a36Sopenharmony_ci if (!(readb(®s->MIICR) & MIICR_WCMD)) 77862306a36Sopenharmony_ci break; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci enable_mii_autopoll(regs); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (ww == W_MAX_TIMEOUT) 78362306a36Sopenharmony_ci return -ETIMEDOUT; 78462306a36Sopenharmony_ci return 0; 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci/** 78862306a36Sopenharmony_ci * set_mii_flow_control - flow control setup 78962306a36Sopenharmony_ci * @vptr: velocity interface 79062306a36Sopenharmony_ci * 79162306a36Sopenharmony_ci * Set up the flow control on this interface according to 79262306a36Sopenharmony_ci * the supplied user/eeprom options. 79362306a36Sopenharmony_ci */ 79462306a36Sopenharmony_cistatic void set_mii_flow_control(struct velocity_info *vptr) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci /*Enable or Disable PAUSE in ANAR */ 79762306a36Sopenharmony_ci switch (vptr->options.flow_cntl) { 79862306a36Sopenharmony_ci case FLOW_CNTL_TX: 79962306a36Sopenharmony_ci MII_REG_BITS_OFF(ADVERTISE_PAUSE_CAP, MII_ADVERTISE, vptr->mac_regs); 80062306a36Sopenharmony_ci MII_REG_BITS_ON(ADVERTISE_PAUSE_ASYM, MII_ADVERTISE, vptr->mac_regs); 80162306a36Sopenharmony_ci break; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci case FLOW_CNTL_RX: 80462306a36Sopenharmony_ci MII_REG_BITS_ON(ADVERTISE_PAUSE_CAP, MII_ADVERTISE, vptr->mac_regs); 80562306a36Sopenharmony_ci MII_REG_BITS_ON(ADVERTISE_PAUSE_ASYM, MII_ADVERTISE, vptr->mac_regs); 80662306a36Sopenharmony_ci break; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci case FLOW_CNTL_TX_RX: 80962306a36Sopenharmony_ci MII_REG_BITS_ON(ADVERTISE_PAUSE_CAP, MII_ADVERTISE, vptr->mac_regs); 81062306a36Sopenharmony_ci MII_REG_BITS_OFF(ADVERTISE_PAUSE_ASYM, MII_ADVERTISE, vptr->mac_regs); 81162306a36Sopenharmony_ci break; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci case FLOW_CNTL_DISABLE: 81462306a36Sopenharmony_ci MII_REG_BITS_OFF(ADVERTISE_PAUSE_CAP, MII_ADVERTISE, vptr->mac_regs); 81562306a36Sopenharmony_ci MII_REG_BITS_OFF(ADVERTISE_PAUSE_ASYM, MII_ADVERTISE, vptr->mac_regs); 81662306a36Sopenharmony_ci break; 81762306a36Sopenharmony_ci default: 81862306a36Sopenharmony_ci break; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci/** 82362306a36Sopenharmony_ci * mii_set_auto_on - autonegotiate on 82462306a36Sopenharmony_ci * @vptr: velocity 82562306a36Sopenharmony_ci * 82662306a36Sopenharmony_ci * Enable autonegotation on this interface 82762306a36Sopenharmony_ci */ 82862306a36Sopenharmony_cistatic void mii_set_auto_on(struct velocity_info *vptr) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci if (MII_REG_BITS_IS_ON(BMCR_ANENABLE, MII_BMCR, vptr->mac_regs)) 83162306a36Sopenharmony_ci MII_REG_BITS_ON(BMCR_ANRESTART, MII_BMCR, vptr->mac_regs); 83262306a36Sopenharmony_ci else 83362306a36Sopenharmony_ci MII_REG_BITS_ON(BMCR_ANENABLE, MII_BMCR, vptr->mac_regs); 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic u32 check_connection_type(struct mac_regs __iomem *regs) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci u32 status = 0; 83962306a36Sopenharmony_ci u8 PHYSR0; 84062306a36Sopenharmony_ci u16 ANAR; 84162306a36Sopenharmony_ci PHYSR0 = readb(®s->PHYSR0); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci /* 84462306a36Sopenharmony_ci if (!(PHYSR0 & PHYSR0_LINKGD)) 84562306a36Sopenharmony_ci status|=VELOCITY_LINK_FAIL; 84662306a36Sopenharmony_ci */ 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (PHYSR0 & PHYSR0_FDPX) 84962306a36Sopenharmony_ci status |= VELOCITY_DUPLEX_FULL; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (PHYSR0 & PHYSR0_SPDG) 85262306a36Sopenharmony_ci status |= VELOCITY_SPEED_1000; 85362306a36Sopenharmony_ci else if (PHYSR0 & PHYSR0_SPD10) 85462306a36Sopenharmony_ci status |= VELOCITY_SPEED_10; 85562306a36Sopenharmony_ci else 85662306a36Sopenharmony_ci status |= VELOCITY_SPEED_100; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (MII_REG_BITS_IS_ON(BMCR_ANENABLE, MII_BMCR, regs)) { 85962306a36Sopenharmony_ci velocity_mii_read(regs, MII_ADVERTISE, &ANAR); 86062306a36Sopenharmony_ci if ((ANAR & (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF)) 86162306a36Sopenharmony_ci == (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF)) { 86262306a36Sopenharmony_ci if (MII_REG_BITS_IS_ON(ADVERTISE_1000HALF | ADVERTISE_1000FULL, MII_CTRL1000, regs)) 86362306a36Sopenharmony_ci status |= VELOCITY_AUTONEG_ENABLE; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci return status; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci/** 87162306a36Sopenharmony_ci * velocity_set_media_mode - set media mode 87262306a36Sopenharmony_ci * @vptr: velocity adapter 87362306a36Sopenharmony_ci * @mii_status: old MII link state 87462306a36Sopenharmony_ci * 87562306a36Sopenharmony_ci * Check the media link state and configure the flow control 87662306a36Sopenharmony_ci * PHY and also velocity hardware setup accordingly. In particular 87762306a36Sopenharmony_ci * we need to set up CD polling and frame bursting. 87862306a36Sopenharmony_ci */ 87962306a36Sopenharmony_cistatic int velocity_set_media_mode(struct velocity_info *vptr, u32 mii_status) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci vptr->mii_status = mii_check_media_mode(vptr->mac_regs); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* Set mii link status */ 88662306a36Sopenharmony_ci set_mii_flow_control(vptr); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201) 88962306a36Sopenharmony_ci MII_REG_BITS_ON(AUXCR_MDPPS, MII_NCONFIG, vptr->mac_regs); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci /* 89262306a36Sopenharmony_ci * If connection type is AUTO 89362306a36Sopenharmony_ci */ 89462306a36Sopenharmony_ci if (mii_status & VELOCITY_AUTONEG_ENABLE) { 89562306a36Sopenharmony_ci netdev_info(vptr->netdev, "Velocity is in AUTO mode\n"); 89662306a36Sopenharmony_ci /* clear force MAC mode bit */ 89762306a36Sopenharmony_ci BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, ®s->CHIPGCR); 89862306a36Sopenharmony_ci /* set duplex mode of MAC according to duplex mode of MII */ 89962306a36Sopenharmony_ci MII_REG_BITS_ON(ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF, MII_ADVERTISE, vptr->mac_regs); 90062306a36Sopenharmony_ci MII_REG_BITS_ON(ADVERTISE_1000FULL | ADVERTISE_1000HALF, MII_CTRL1000, vptr->mac_regs); 90162306a36Sopenharmony_ci MII_REG_BITS_ON(BMCR_SPEED1000, MII_BMCR, vptr->mac_regs); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci /* enable AUTO-NEGO mode */ 90462306a36Sopenharmony_ci mii_set_auto_on(vptr); 90562306a36Sopenharmony_ci } else { 90662306a36Sopenharmony_ci u16 CTRL1000; 90762306a36Sopenharmony_ci u16 ANAR; 90862306a36Sopenharmony_ci u8 CHIPGCR; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci /* 91162306a36Sopenharmony_ci * 1. if it's 3119, disable frame bursting in halfduplex mode 91262306a36Sopenharmony_ci * and enable it in fullduplex mode 91362306a36Sopenharmony_ci * 2. set correct MII/GMII and half/full duplex mode in CHIPGCR 91462306a36Sopenharmony_ci * 3. only enable CD heart beat counter in 10HD mode 91562306a36Sopenharmony_ci */ 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci /* set force MAC mode bit */ 91862306a36Sopenharmony_ci BYTE_REG_BITS_ON(CHIPGCR_FCMODE, ®s->CHIPGCR); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci CHIPGCR = readb(®s->CHIPGCR); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci if (mii_status & VELOCITY_SPEED_1000) 92362306a36Sopenharmony_ci CHIPGCR |= CHIPGCR_FCGMII; 92462306a36Sopenharmony_ci else 92562306a36Sopenharmony_ci CHIPGCR &= ~CHIPGCR_FCGMII; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (mii_status & VELOCITY_DUPLEX_FULL) { 92862306a36Sopenharmony_ci CHIPGCR |= CHIPGCR_FCFDX; 92962306a36Sopenharmony_ci writeb(CHIPGCR, ®s->CHIPGCR); 93062306a36Sopenharmony_ci netdev_info(vptr->netdev, 93162306a36Sopenharmony_ci "set Velocity to forced full mode\n"); 93262306a36Sopenharmony_ci if (vptr->rev_id < REV_ID_VT3216_A0) 93362306a36Sopenharmony_ci BYTE_REG_BITS_OFF(TCR_TB2BDIS, ®s->TCR); 93462306a36Sopenharmony_ci } else { 93562306a36Sopenharmony_ci CHIPGCR &= ~CHIPGCR_FCFDX; 93662306a36Sopenharmony_ci netdev_info(vptr->netdev, 93762306a36Sopenharmony_ci "set Velocity to forced half mode\n"); 93862306a36Sopenharmony_ci writeb(CHIPGCR, ®s->CHIPGCR); 93962306a36Sopenharmony_ci if (vptr->rev_id < REV_ID_VT3216_A0) 94062306a36Sopenharmony_ci BYTE_REG_BITS_ON(TCR_TB2BDIS, ®s->TCR); 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci velocity_mii_read(vptr->mac_regs, MII_CTRL1000, &CTRL1000); 94462306a36Sopenharmony_ci CTRL1000 &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); 94562306a36Sopenharmony_ci if ((mii_status & VELOCITY_SPEED_1000) && 94662306a36Sopenharmony_ci (mii_status & VELOCITY_DUPLEX_FULL)) { 94762306a36Sopenharmony_ci CTRL1000 |= ADVERTISE_1000FULL; 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci velocity_mii_write(vptr->mac_regs, MII_CTRL1000, CTRL1000); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci if (!(mii_status & VELOCITY_DUPLEX_FULL) && (mii_status & VELOCITY_SPEED_10)) 95262306a36Sopenharmony_ci BYTE_REG_BITS_OFF(TESTCFG_HBDIS, ®s->TESTCFG); 95362306a36Sopenharmony_ci else 95462306a36Sopenharmony_ci BYTE_REG_BITS_ON(TESTCFG_HBDIS, ®s->TESTCFG); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci /* MII_REG_BITS_OFF(BMCR_SPEED1000, MII_BMCR, vptr->mac_regs); */ 95762306a36Sopenharmony_ci velocity_mii_read(vptr->mac_regs, MII_ADVERTISE, &ANAR); 95862306a36Sopenharmony_ci ANAR &= (~(ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF)); 95962306a36Sopenharmony_ci if (mii_status & VELOCITY_SPEED_100) { 96062306a36Sopenharmony_ci if (mii_status & VELOCITY_DUPLEX_FULL) 96162306a36Sopenharmony_ci ANAR |= ADVERTISE_100FULL; 96262306a36Sopenharmony_ci else 96362306a36Sopenharmony_ci ANAR |= ADVERTISE_100HALF; 96462306a36Sopenharmony_ci } else if (mii_status & VELOCITY_SPEED_10) { 96562306a36Sopenharmony_ci if (mii_status & VELOCITY_DUPLEX_FULL) 96662306a36Sopenharmony_ci ANAR |= ADVERTISE_10FULL; 96762306a36Sopenharmony_ci else 96862306a36Sopenharmony_ci ANAR |= ADVERTISE_10HALF; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci velocity_mii_write(vptr->mac_regs, MII_ADVERTISE, ANAR); 97162306a36Sopenharmony_ci /* enable AUTO-NEGO mode */ 97262306a36Sopenharmony_ci mii_set_auto_on(vptr); 97362306a36Sopenharmony_ci /* MII_REG_BITS_ON(BMCR_ANENABLE, MII_BMCR, vptr->mac_regs); */ 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci /* vptr->mii_status=mii_check_media_mode(vptr->mac_regs); */ 97662306a36Sopenharmony_ci /* vptr->mii_status=check_connection_type(vptr->mac_regs); */ 97762306a36Sopenharmony_ci return VELOCITY_LINK_CHANGE; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci/** 98162306a36Sopenharmony_ci * velocity_print_link_status - link status reporting 98262306a36Sopenharmony_ci * @vptr: velocity to report on 98362306a36Sopenharmony_ci * 98462306a36Sopenharmony_ci * Turn the link status of the velocity card into a kernel log 98562306a36Sopenharmony_ci * description of the new link state, detailing speed and duplex 98662306a36Sopenharmony_ci * status 98762306a36Sopenharmony_ci */ 98862306a36Sopenharmony_cistatic void velocity_print_link_status(struct velocity_info *vptr) 98962306a36Sopenharmony_ci{ 99062306a36Sopenharmony_ci const char *link; 99162306a36Sopenharmony_ci const char *speed; 99262306a36Sopenharmony_ci const char *duplex; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (vptr->mii_status & VELOCITY_LINK_FAIL) { 99562306a36Sopenharmony_ci netdev_notice(vptr->netdev, "failed to detect cable link\n"); 99662306a36Sopenharmony_ci return; 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (vptr->options.spd_dpx == SPD_DPX_AUTO) { 100062306a36Sopenharmony_ci link = "auto-negotiation"; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci if (vptr->mii_status & VELOCITY_SPEED_1000) 100362306a36Sopenharmony_ci speed = "1000"; 100462306a36Sopenharmony_ci else if (vptr->mii_status & VELOCITY_SPEED_100) 100562306a36Sopenharmony_ci speed = "100"; 100662306a36Sopenharmony_ci else 100762306a36Sopenharmony_ci speed = "10"; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci if (vptr->mii_status & VELOCITY_DUPLEX_FULL) 101062306a36Sopenharmony_ci duplex = "full"; 101162306a36Sopenharmony_ci else 101262306a36Sopenharmony_ci duplex = "half"; 101362306a36Sopenharmony_ci } else { 101462306a36Sopenharmony_ci link = "forced"; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci switch (vptr->options.spd_dpx) { 101762306a36Sopenharmony_ci case SPD_DPX_1000_FULL: 101862306a36Sopenharmony_ci speed = "1000"; 101962306a36Sopenharmony_ci duplex = "full"; 102062306a36Sopenharmony_ci break; 102162306a36Sopenharmony_ci case SPD_DPX_100_HALF: 102262306a36Sopenharmony_ci speed = "100"; 102362306a36Sopenharmony_ci duplex = "half"; 102462306a36Sopenharmony_ci break; 102562306a36Sopenharmony_ci case SPD_DPX_100_FULL: 102662306a36Sopenharmony_ci speed = "100"; 102762306a36Sopenharmony_ci duplex = "full"; 102862306a36Sopenharmony_ci break; 102962306a36Sopenharmony_ci case SPD_DPX_10_HALF: 103062306a36Sopenharmony_ci speed = "10"; 103162306a36Sopenharmony_ci duplex = "half"; 103262306a36Sopenharmony_ci break; 103362306a36Sopenharmony_ci case SPD_DPX_10_FULL: 103462306a36Sopenharmony_ci speed = "10"; 103562306a36Sopenharmony_ci duplex = "full"; 103662306a36Sopenharmony_ci break; 103762306a36Sopenharmony_ci default: 103862306a36Sopenharmony_ci speed = "unknown"; 103962306a36Sopenharmony_ci duplex = "unknown"; 104062306a36Sopenharmony_ci break; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci netdev_notice(vptr->netdev, "Link %s speed %sM bps %s duplex\n", 104462306a36Sopenharmony_ci link, speed, duplex); 104562306a36Sopenharmony_ci} 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci/** 104862306a36Sopenharmony_ci * enable_flow_control_ability - flow control 104962306a36Sopenharmony_ci * @vptr: veloity to configure 105062306a36Sopenharmony_ci * 105162306a36Sopenharmony_ci * Set up flow control according to the flow control options 105262306a36Sopenharmony_ci * determined by the eeprom/configuration. 105362306a36Sopenharmony_ci */ 105462306a36Sopenharmony_cistatic void enable_flow_control_ability(struct velocity_info *vptr) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci switch (vptr->options.flow_cntl) { 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci case FLOW_CNTL_DEFAULT: 106262306a36Sopenharmony_ci if (BYTE_REG_BITS_IS_ON(PHYSR0_RXFLC, ®s->PHYSR0)) 106362306a36Sopenharmony_ci writel(CR0_FDXRFCEN, ®s->CR0Set); 106462306a36Sopenharmony_ci else 106562306a36Sopenharmony_ci writel(CR0_FDXRFCEN, ®s->CR0Clr); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (BYTE_REG_BITS_IS_ON(PHYSR0_TXFLC, ®s->PHYSR0)) 106862306a36Sopenharmony_ci writel(CR0_FDXTFCEN, ®s->CR0Set); 106962306a36Sopenharmony_ci else 107062306a36Sopenharmony_ci writel(CR0_FDXTFCEN, ®s->CR0Clr); 107162306a36Sopenharmony_ci break; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci case FLOW_CNTL_TX: 107462306a36Sopenharmony_ci writel(CR0_FDXTFCEN, ®s->CR0Set); 107562306a36Sopenharmony_ci writel(CR0_FDXRFCEN, ®s->CR0Clr); 107662306a36Sopenharmony_ci break; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci case FLOW_CNTL_RX: 107962306a36Sopenharmony_ci writel(CR0_FDXRFCEN, ®s->CR0Set); 108062306a36Sopenharmony_ci writel(CR0_FDXTFCEN, ®s->CR0Clr); 108162306a36Sopenharmony_ci break; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci case FLOW_CNTL_TX_RX: 108462306a36Sopenharmony_ci writel(CR0_FDXTFCEN, ®s->CR0Set); 108562306a36Sopenharmony_ci writel(CR0_FDXRFCEN, ®s->CR0Set); 108662306a36Sopenharmony_ci break; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci case FLOW_CNTL_DISABLE: 108962306a36Sopenharmony_ci writel(CR0_FDXRFCEN, ®s->CR0Clr); 109062306a36Sopenharmony_ci writel(CR0_FDXTFCEN, ®s->CR0Clr); 109162306a36Sopenharmony_ci break; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci default: 109462306a36Sopenharmony_ci break; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci} 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci/** 110062306a36Sopenharmony_ci * velocity_soft_reset - soft reset 110162306a36Sopenharmony_ci * @vptr: velocity to reset 110262306a36Sopenharmony_ci * 110362306a36Sopenharmony_ci * Kick off a soft reset of the velocity adapter and then poll 110462306a36Sopenharmony_ci * until the reset sequence has completed before returning. 110562306a36Sopenharmony_ci */ 110662306a36Sopenharmony_cistatic int velocity_soft_reset(struct velocity_info *vptr) 110762306a36Sopenharmony_ci{ 110862306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 110962306a36Sopenharmony_ci int i = 0; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci writel(CR0_SFRST, ®s->CR0Set); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci for (i = 0; i < W_MAX_TIMEOUT; i++) { 111462306a36Sopenharmony_ci udelay(5); 111562306a36Sopenharmony_ci if (!DWORD_REG_BITS_IS_ON(CR0_SFRST, ®s->CR0Set)) 111662306a36Sopenharmony_ci break; 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci if (i == W_MAX_TIMEOUT) { 112062306a36Sopenharmony_ci writel(CR0_FORSRST, ®s->CR0Set); 112162306a36Sopenharmony_ci /* FIXME: PCI POSTING */ 112262306a36Sopenharmony_ci /* delay 2ms */ 112362306a36Sopenharmony_ci mdelay(2); 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci return 0; 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci/** 112962306a36Sopenharmony_ci * velocity_set_multi - filter list change callback 113062306a36Sopenharmony_ci * @dev: network device 113162306a36Sopenharmony_ci * 113262306a36Sopenharmony_ci * Called by the network layer when the filter lists need to change 113362306a36Sopenharmony_ci * for a velocity adapter. Reload the CAMs with the new address 113462306a36Sopenharmony_ci * filter ruleset. 113562306a36Sopenharmony_ci */ 113662306a36Sopenharmony_cistatic void velocity_set_multi(struct net_device *dev) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 113962306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 114062306a36Sopenharmony_ci u8 rx_mode; 114162306a36Sopenharmony_ci int i; 114262306a36Sopenharmony_ci struct netdev_hw_addr *ha; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ 114562306a36Sopenharmony_ci writel(0xffffffff, ®s->MARCAM[0]); 114662306a36Sopenharmony_ci writel(0xffffffff, ®s->MARCAM[4]); 114762306a36Sopenharmony_ci rx_mode = (RCR_AM | RCR_AB | RCR_PROM); 114862306a36Sopenharmony_ci } else if ((netdev_mc_count(dev) > vptr->multicast_limit) || 114962306a36Sopenharmony_ci (dev->flags & IFF_ALLMULTI)) { 115062306a36Sopenharmony_ci writel(0xffffffff, ®s->MARCAM[0]); 115162306a36Sopenharmony_ci writel(0xffffffff, ®s->MARCAM[4]); 115262306a36Sopenharmony_ci rx_mode = (RCR_AM | RCR_AB); 115362306a36Sopenharmony_ci } else { 115462306a36Sopenharmony_ci int offset = MCAM_SIZE - vptr->multicast_limit; 115562306a36Sopenharmony_ci mac_get_cam_mask(regs, vptr->mCAMmask); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci i = 0; 115862306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 115962306a36Sopenharmony_ci mac_set_cam(regs, i + offset, ha->addr); 116062306a36Sopenharmony_ci vptr->mCAMmask[(offset + i) / 8] |= 1 << ((offset + i) & 7); 116162306a36Sopenharmony_ci i++; 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci mac_set_cam_mask(regs, vptr->mCAMmask); 116562306a36Sopenharmony_ci rx_mode = RCR_AM | RCR_AB | RCR_AP; 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci if (dev->mtu > 1500) 116862306a36Sopenharmony_ci rx_mode |= RCR_AL; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci BYTE_REG_BITS_ON(rx_mode, ®s->RCR); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci} 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci/* 117562306a36Sopenharmony_ci * MII access , media link mode setting functions 117662306a36Sopenharmony_ci */ 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci/** 117962306a36Sopenharmony_ci * mii_init - set up MII 118062306a36Sopenharmony_ci * @vptr: velocity adapter 118162306a36Sopenharmony_ci * @mii_status: links tatus 118262306a36Sopenharmony_ci * 118362306a36Sopenharmony_ci * Set up the PHY for the current link state. 118462306a36Sopenharmony_ci */ 118562306a36Sopenharmony_cistatic void mii_init(struct velocity_info *vptr, u32 mii_status) 118662306a36Sopenharmony_ci{ 118762306a36Sopenharmony_ci u16 BMCR; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci switch (PHYID_GET_PHY_ID(vptr->phy_id)) { 119062306a36Sopenharmony_ci case PHYID_ICPLUS_IP101A: 119162306a36Sopenharmony_ci MII_REG_BITS_ON((ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP), 119262306a36Sopenharmony_ci MII_ADVERTISE, vptr->mac_regs); 119362306a36Sopenharmony_ci if (vptr->mii_status & VELOCITY_DUPLEX_FULL) 119462306a36Sopenharmony_ci MII_REG_BITS_ON(TCSR_ECHODIS, MII_SREVISION, 119562306a36Sopenharmony_ci vptr->mac_regs); 119662306a36Sopenharmony_ci else 119762306a36Sopenharmony_ci MII_REG_BITS_OFF(TCSR_ECHODIS, MII_SREVISION, 119862306a36Sopenharmony_ci vptr->mac_regs); 119962306a36Sopenharmony_ci MII_REG_BITS_ON(PLED_LALBE, MII_TPISTATUS, vptr->mac_regs); 120062306a36Sopenharmony_ci break; 120162306a36Sopenharmony_ci case PHYID_CICADA_CS8201: 120262306a36Sopenharmony_ci /* 120362306a36Sopenharmony_ci * Reset to hardware default 120462306a36Sopenharmony_ci */ 120562306a36Sopenharmony_ci MII_REG_BITS_OFF((ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP), MII_ADVERTISE, vptr->mac_regs); 120662306a36Sopenharmony_ci /* 120762306a36Sopenharmony_ci * Turn on ECHODIS bit in NWay-forced full mode and turn it 120862306a36Sopenharmony_ci * off it in NWay-forced half mode for NWay-forced v.s. 120962306a36Sopenharmony_ci * legacy-forced issue. 121062306a36Sopenharmony_ci */ 121162306a36Sopenharmony_ci if (vptr->mii_status & VELOCITY_DUPLEX_FULL) 121262306a36Sopenharmony_ci MII_REG_BITS_ON(TCSR_ECHODIS, MII_SREVISION, vptr->mac_regs); 121362306a36Sopenharmony_ci else 121462306a36Sopenharmony_ci MII_REG_BITS_OFF(TCSR_ECHODIS, MII_SREVISION, vptr->mac_regs); 121562306a36Sopenharmony_ci /* 121662306a36Sopenharmony_ci * Turn on Link/Activity LED enable bit for CIS8201 121762306a36Sopenharmony_ci */ 121862306a36Sopenharmony_ci MII_REG_BITS_ON(PLED_LALBE, MII_TPISTATUS, vptr->mac_regs); 121962306a36Sopenharmony_ci break; 122062306a36Sopenharmony_ci case PHYID_VT3216_32BIT: 122162306a36Sopenharmony_ci case PHYID_VT3216_64BIT: 122262306a36Sopenharmony_ci /* 122362306a36Sopenharmony_ci * Reset to hardware default 122462306a36Sopenharmony_ci */ 122562306a36Sopenharmony_ci MII_REG_BITS_ON((ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP), MII_ADVERTISE, vptr->mac_regs); 122662306a36Sopenharmony_ci /* 122762306a36Sopenharmony_ci * Turn on ECHODIS bit in NWay-forced full mode and turn it 122862306a36Sopenharmony_ci * off it in NWay-forced half mode for NWay-forced v.s. 122962306a36Sopenharmony_ci * legacy-forced issue 123062306a36Sopenharmony_ci */ 123162306a36Sopenharmony_ci if (vptr->mii_status & VELOCITY_DUPLEX_FULL) 123262306a36Sopenharmony_ci MII_REG_BITS_ON(TCSR_ECHODIS, MII_SREVISION, vptr->mac_regs); 123362306a36Sopenharmony_ci else 123462306a36Sopenharmony_ci MII_REG_BITS_OFF(TCSR_ECHODIS, MII_SREVISION, vptr->mac_regs); 123562306a36Sopenharmony_ci break; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci case PHYID_MARVELL_1000: 123862306a36Sopenharmony_ci case PHYID_MARVELL_1000S: 123962306a36Sopenharmony_ci /* 124062306a36Sopenharmony_ci * Assert CRS on Transmit 124162306a36Sopenharmony_ci */ 124262306a36Sopenharmony_ci MII_REG_BITS_ON(PSCR_ACRSTX, MII_REG_PSCR, vptr->mac_regs); 124362306a36Sopenharmony_ci /* 124462306a36Sopenharmony_ci * Reset to hardware default 124562306a36Sopenharmony_ci */ 124662306a36Sopenharmony_ci MII_REG_BITS_ON((ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP), MII_ADVERTISE, vptr->mac_regs); 124762306a36Sopenharmony_ci break; 124862306a36Sopenharmony_ci default: 124962306a36Sopenharmony_ci ; 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci velocity_mii_read(vptr->mac_regs, MII_BMCR, &BMCR); 125262306a36Sopenharmony_ci if (BMCR & BMCR_ISOLATE) { 125362306a36Sopenharmony_ci BMCR &= ~BMCR_ISOLATE; 125462306a36Sopenharmony_ci velocity_mii_write(vptr->mac_regs, MII_BMCR, BMCR); 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci/** 125962306a36Sopenharmony_ci * setup_queue_timers - Setup interrupt timers 126062306a36Sopenharmony_ci * @vptr: velocity adapter 126162306a36Sopenharmony_ci * 126262306a36Sopenharmony_ci * Setup interrupt frequency during suppression (timeout if the frame 126362306a36Sopenharmony_ci * count isn't filled). 126462306a36Sopenharmony_ci */ 126562306a36Sopenharmony_cistatic void setup_queue_timers(struct velocity_info *vptr) 126662306a36Sopenharmony_ci{ 126762306a36Sopenharmony_ci /* Only for newer revisions */ 126862306a36Sopenharmony_ci if (vptr->rev_id >= REV_ID_VT3216_A0) { 126962306a36Sopenharmony_ci u8 txqueue_timer = 0; 127062306a36Sopenharmony_ci u8 rxqueue_timer = 0; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci if (vptr->mii_status & (VELOCITY_SPEED_1000 | 127362306a36Sopenharmony_ci VELOCITY_SPEED_100)) { 127462306a36Sopenharmony_ci txqueue_timer = vptr->options.txqueue_timer; 127562306a36Sopenharmony_ci rxqueue_timer = vptr->options.rxqueue_timer; 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci writeb(txqueue_timer, &vptr->mac_regs->TQETMR); 127962306a36Sopenharmony_ci writeb(rxqueue_timer, &vptr->mac_regs->RQETMR); 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci} 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci/** 128462306a36Sopenharmony_ci * setup_adaptive_interrupts - Setup interrupt suppression 128562306a36Sopenharmony_ci * @vptr: velocity adapter 128662306a36Sopenharmony_ci * 128762306a36Sopenharmony_ci * The velocity is able to suppress interrupt during high interrupt load. 128862306a36Sopenharmony_ci * This function turns on that feature. 128962306a36Sopenharmony_ci */ 129062306a36Sopenharmony_cistatic void setup_adaptive_interrupts(struct velocity_info *vptr) 129162306a36Sopenharmony_ci{ 129262306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 129362306a36Sopenharmony_ci u16 tx_intsup = vptr->options.tx_intsup; 129462306a36Sopenharmony_ci u16 rx_intsup = vptr->options.rx_intsup; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci /* Setup default interrupt mask (will be changed below) */ 129762306a36Sopenharmony_ci vptr->int_mask = INT_MASK_DEF; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci /* Set Tx Interrupt Suppression Threshold */ 130062306a36Sopenharmony_ci writeb(CAMCR_PS0, ®s->CAMCR); 130162306a36Sopenharmony_ci if (tx_intsup != 0) { 130262306a36Sopenharmony_ci vptr->int_mask &= ~(ISR_PTXI | ISR_PTX0I | ISR_PTX1I | 130362306a36Sopenharmony_ci ISR_PTX2I | ISR_PTX3I); 130462306a36Sopenharmony_ci writew(tx_intsup, ®s->ISRCTL); 130562306a36Sopenharmony_ci } else 130662306a36Sopenharmony_ci writew(ISRCTL_TSUPDIS, ®s->ISRCTL); 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci /* Set Rx Interrupt Suppression Threshold */ 130962306a36Sopenharmony_ci writeb(CAMCR_PS1, ®s->CAMCR); 131062306a36Sopenharmony_ci if (rx_intsup != 0) { 131162306a36Sopenharmony_ci vptr->int_mask &= ~ISR_PRXI; 131262306a36Sopenharmony_ci writew(rx_intsup, ®s->ISRCTL); 131362306a36Sopenharmony_ci } else 131462306a36Sopenharmony_ci writew(ISRCTL_RSUPDIS, ®s->ISRCTL); 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci /* Select page to interrupt hold timer */ 131762306a36Sopenharmony_ci writeb(0, ®s->CAMCR); 131862306a36Sopenharmony_ci} 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci/** 132162306a36Sopenharmony_ci * velocity_init_registers - initialise MAC registers 132262306a36Sopenharmony_ci * @vptr: velocity to init 132362306a36Sopenharmony_ci * @type: type of initialisation (hot or cold) 132462306a36Sopenharmony_ci * 132562306a36Sopenharmony_ci * Initialise the MAC on a reset or on first set up on the 132662306a36Sopenharmony_ci * hardware. 132762306a36Sopenharmony_ci */ 132862306a36Sopenharmony_cistatic void velocity_init_registers(struct velocity_info *vptr, 132962306a36Sopenharmony_ci enum velocity_init_type type) 133062306a36Sopenharmony_ci{ 133162306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 133262306a36Sopenharmony_ci struct net_device *netdev = vptr->netdev; 133362306a36Sopenharmony_ci int i, mii_status; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci mac_wol_reset(regs); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci switch (type) { 133862306a36Sopenharmony_ci case VELOCITY_INIT_RESET: 133962306a36Sopenharmony_ci case VELOCITY_INIT_WOL: 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci netif_stop_queue(netdev); 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci /* 134462306a36Sopenharmony_ci * Reset RX to prevent RX pointer not on the 4X location 134562306a36Sopenharmony_ci */ 134662306a36Sopenharmony_ci velocity_rx_reset(vptr); 134762306a36Sopenharmony_ci mac_rx_queue_run(regs); 134862306a36Sopenharmony_ci mac_rx_queue_wake(regs); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci mii_status = velocity_get_opt_media_mode(vptr); 135162306a36Sopenharmony_ci if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) { 135262306a36Sopenharmony_ci velocity_print_link_status(vptr); 135362306a36Sopenharmony_ci if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) 135462306a36Sopenharmony_ci netif_wake_queue(netdev); 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci enable_flow_control_ability(vptr); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci mac_clear_isr(regs); 136062306a36Sopenharmony_ci writel(CR0_STOP, ®s->CR0Clr); 136162306a36Sopenharmony_ci writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), 136262306a36Sopenharmony_ci ®s->CR0Set); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci break; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci case VELOCITY_INIT_COLD: 136762306a36Sopenharmony_ci default: 136862306a36Sopenharmony_ci /* 136962306a36Sopenharmony_ci * Do reset 137062306a36Sopenharmony_ci */ 137162306a36Sopenharmony_ci velocity_soft_reset(vptr); 137262306a36Sopenharmony_ci mdelay(5); 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci if (!vptr->no_eeprom) { 137562306a36Sopenharmony_ci mac_eeprom_reload(regs); 137662306a36Sopenharmony_ci for (i = 0; i < 6; i++) 137762306a36Sopenharmony_ci writeb(netdev->dev_addr[i], regs->PAR + i); 137862306a36Sopenharmony_ci } 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci /* 138162306a36Sopenharmony_ci * clear Pre_ACPI bit. 138262306a36Sopenharmony_ci */ 138362306a36Sopenharmony_ci BYTE_REG_BITS_OFF(CFGA_PACPI, &(regs->CFGA)); 138462306a36Sopenharmony_ci mac_set_rx_thresh(regs, vptr->options.rx_thresh); 138562306a36Sopenharmony_ci mac_set_dma_length(regs, vptr->options.DMA_length); 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci writeb(WOLCFG_SAM | WOLCFG_SAB, ®s->WOLCFGSet); 138862306a36Sopenharmony_ci /* 138962306a36Sopenharmony_ci * Back off algorithm use original IEEE standard 139062306a36Sopenharmony_ci */ 139162306a36Sopenharmony_ci BYTE_REG_BITS_SET(CFGB_OFSET, (CFGB_CRANDOM | CFGB_CAP | CFGB_MBA | CFGB_BAKOPT), ®s->CFGB); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci /* 139462306a36Sopenharmony_ci * Init CAM filter 139562306a36Sopenharmony_ci */ 139662306a36Sopenharmony_ci velocity_init_cam_filter(vptr); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci /* 139962306a36Sopenharmony_ci * Set packet filter: Receive directed and broadcast address 140062306a36Sopenharmony_ci */ 140162306a36Sopenharmony_ci velocity_set_multi(netdev); 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci /* 140462306a36Sopenharmony_ci * Enable MII auto-polling 140562306a36Sopenharmony_ci */ 140662306a36Sopenharmony_ci enable_mii_autopoll(regs); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci setup_adaptive_interrupts(vptr); 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci writel(vptr->rx.pool_dma, ®s->RDBaseLo); 141162306a36Sopenharmony_ci writew(vptr->options.numrx - 1, ®s->RDCSize); 141262306a36Sopenharmony_ci mac_rx_queue_run(regs); 141362306a36Sopenharmony_ci mac_rx_queue_wake(regs); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci writew(vptr->options.numtx - 1, ®s->TDCSize); 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci for (i = 0; i < vptr->tx.numq; i++) { 141862306a36Sopenharmony_ci writel(vptr->tx.pool_dma[i], ®s->TDBaseLo[i]); 141962306a36Sopenharmony_ci mac_tx_queue_run(regs, i); 142062306a36Sopenharmony_ci } 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci init_flow_control_register(vptr); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci writel(CR0_STOP, ®s->CR0Clr); 142562306a36Sopenharmony_ci writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), ®s->CR0Set); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci mii_status = velocity_get_opt_media_mode(vptr); 142862306a36Sopenharmony_ci netif_stop_queue(netdev); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci mii_init(vptr, mii_status); 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) { 143362306a36Sopenharmony_ci velocity_print_link_status(vptr); 143462306a36Sopenharmony_ci if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) 143562306a36Sopenharmony_ci netif_wake_queue(netdev); 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci enable_flow_control_ability(vptr); 143962306a36Sopenharmony_ci mac_hw_mibs_init(regs); 144062306a36Sopenharmony_ci mac_write_int_mask(vptr->int_mask, regs); 144162306a36Sopenharmony_ci mac_clear_isr(regs); 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci} 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_cistatic void velocity_give_many_rx_descs(struct velocity_info *vptr) 144762306a36Sopenharmony_ci{ 144862306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 144962306a36Sopenharmony_ci int avail, dirty, unusable; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci /* 145262306a36Sopenharmony_ci * RD number must be equal to 4X per hardware spec 145362306a36Sopenharmony_ci * (programming guide rev 1.20, p.13) 145462306a36Sopenharmony_ci */ 145562306a36Sopenharmony_ci if (vptr->rx.filled < 4) 145662306a36Sopenharmony_ci return; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci wmb(); 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci unusable = vptr->rx.filled & 0x0003; 146162306a36Sopenharmony_ci dirty = vptr->rx.dirty - unusable; 146262306a36Sopenharmony_ci for (avail = vptr->rx.filled & 0xfffc; avail; avail--) { 146362306a36Sopenharmony_ci dirty = (dirty > 0) ? dirty - 1 : vptr->options.numrx - 1; 146462306a36Sopenharmony_ci vptr->rx.ring[dirty].rdesc0.len |= OWNED_BY_NIC; 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci writew(vptr->rx.filled & 0xfffc, ®s->RBRDU); 146862306a36Sopenharmony_ci vptr->rx.filled = unusable; 146962306a36Sopenharmony_ci} 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci/** 147262306a36Sopenharmony_ci * velocity_init_dma_rings - set up DMA rings 147362306a36Sopenharmony_ci * @vptr: Velocity to set up 147462306a36Sopenharmony_ci * 147562306a36Sopenharmony_ci * Allocate PCI mapped DMA rings for the receive and transmit layer 147662306a36Sopenharmony_ci * to use. 147762306a36Sopenharmony_ci */ 147862306a36Sopenharmony_cistatic int velocity_init_dma_rings(struct velocity_info *vptr) 147962306a36Sopenharmony_ci{ 148062306a36Sopenharmony_ci struct velocity_opt *opt = &vptr->options; 148162306a36Sopenharmony_ci const unsigned int rx_ring_size = opt->numrx * sizeof(struct rx_desc); 148262306a36Sopenharmony_ci const unsigned int tx_ring_size = opt->numtx * sizeof(struct tx_desc); 148362306a36Sopenharmony_ci dma_addr_t pool_dma; 148462306a36Sopenharmony_ci void *pool; 148562306a36Sopenharmony_ci unsigned int i; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci /* 148862306a36Sopenharmony_ci * Allocate all RD/TD rings a single pool. 148962306a36Sopenharmony_ci * 149062306a36Sopenharmony_ci * dma_alloc_coherent() fulfills the requirement for 64 bytes 149162306a36Sopenharmony_ci * alignment 149262306a36Sopenharmony_ci */ 149362306a36Sopenharmony_ci pool = dma_alloc_coherent(vptr->dev, tx_ring_size * vptr->tx.numq + 149462306a36Sopenharmony_ci rx_ring_size, &pool_dma, GFP_ATOMIC); 149562306a36Sopenharmony_ci if (!pool) { 149662306a36Sopenharmony_ci dev_err(vptr->dev, "%s : DMA memory allocation failed.\n", 149762306a36Sopenharmony_ci vptr->netdev->name); 149862306a36Sopenharmony_ci return -ENOMEM; 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci vptr->rx.ring = pool; 150262306a36Sopenharmony_ci vptr->rx.pool_dma = pool_dma; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci pool += rx_ring_size; 150562306a36Sopenharmony_ci pool_dma += rx_ring_size; 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci for (i = 0; i < vptr->tx.numq; i++) { 150862306a36Sopenharmony_ci vptr->tx.rings[i] = pool; 150962306a36Sopenharmony_ci vptr->tx.pool_dma[i] = pool_dma; 151062306a36Sopenharmony_ci pool += tx_ring_size; 151162306a36Sopenharmony_ci pool_dma += tx_ring_size; 151262306a36Sopenharmony_ci } 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci return 0; 151562306a36Sopenharmony_ci} 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_cistatic void velocity_set_rxbufsize(struct velocity_info *vptr, int mtu) 151862306a36Sopenharmony_ci{ 151962306a36Sopenharmony_ci vptr->rx.buf_sz = (mtu <= ETH_DATA_LEN) ? PKT_BUF_SZ : mtu + 32; 152062306a36Sopenharmony_ci} 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci/** 152362306a36Sopenharmony_ci * velocity_alloc_rx_buf - allocate aligned receive buffer 152462306a36Sopenharmony_ci * @vptr: velocity 152562306a36Sopenharmony_ci * @idx: ring index 152662306a36Sopenharmony_ci * 152762306a36Sopenharmony_ci * Allocate a new full sized buffer for the reception of a frame and 152862306a36Sopenharmony_ci * map it into PCI space for the hardware to use. The hardware 152962306a36Sopenharmony_ci * requires *64* byte alignment of the buffer which makes life 153062306a36Sopenharmony_ci * less fun than would be ideal. 153162306a36Sopenharmony_ci */ 153262306a36Sopenharmony_cistatic int velocity_alloc_rx_buf(struct velocity_info *vptr, int idx) 153362306a36Sopenharmony_ci{ 153462306a36Sopenharmony_ci struct rx_desc *rd = &(vptr->rx.ring[idx]); 153562306a36Sopenharmony_ci struct velocity_rd_info *rd_info = &(vptr->rx.info[idx]); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci rd_info->skb = netdev_alloc_skb(vptr->netdev, vptr->rx.buf_sz + 64); 153862306a36Sopenharmony_ci if (rd_info->skb == NULL) 153962306a36Sopenharmony_ci return -ENOMEM; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci /* 154262306a36Sopenharmony_ci * Do the gymnastics to get the buffer head for data at 154362306a36Sopenharmony_ci * 64byte alignment. 154462306a36Sopenharmony_ci */ 154562306a36Sopenharmony_ci skb_reserve(rd_info->skb, 154662306a36Sopenharmony_ci 64 - ((unsigned long) rd_info->skb->data & 63)); 154762306a36Sopenharmony_ci rd_info->skb_dma = dma_map_single(vptr->dev, rd_info->skb->data, 154862306a36Sopenharmony_ci vptr->rx.buf_sz, DMA_FROM_DEVICE); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci /* 155162306a36Sopenharmony_ci * Fill in the descriptor to match 155262306a36Sopenharmony_ci */ 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci *((u32 *) & (rd->rdesc0)) = 0; 155562306a36Sopenharmony_ci rd->size = cpu_to_le16(vptr->rx.buf_sz) | RX_INTEN; 155662306a36Sopenharmony_ci rd->pa_low = cpu_to_le32(rd_info->skb_dma); 155762306a36Sopenharmony_ci rd->pa_high = 0; 155862306a36Sopenharmony_ci return 0; 155962306a36Sopenharmony_ci} 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_cistatic int velocity_rx_refill(struct velocity_info *vptr) 156362306a36Sopenharmony_ci{ 156462306a36Sopenharmony_ci int dirty = vptr->rx.dirty, done = 0; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci do { 156762306a36Sopenharmony_ci struct rx_desc *rd = vptr->rx.ring + dirty; 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci /* Fine for an all zero Rx desc at init time as well */ 157062306a36Sopenharmony_ci if (rd->rdesc0.len & OWNED_BY_NIC) 157162306a36Sopenharmony_ci break; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci if (!vptr->rx.info[dirty].skb) { 157462306a36Sopenharmony_ci if (velocity_alloc_rx_buf(vptr, dirty) < 0) 157562306a36Sopenharmony_ci break; 157662306a36Sopenharmony_ci } 157762306a36Sopenharmony_ci done++; 157862306a36Sopenharmony_ci dirty = (dirty < vptr->options.numrx - 1) ? dirty + 1 : 0; 157962306a36Sopenharmony_ci } while (dirty != vptr->rx.curr); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci if (done) { 158262306a36Sopenharmony_ci vptr->rx.dirty = dirty; 158362306a36Sopenharmony_ci vptr->rx.filled += done; 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci return done; 158762306a36Sopenharmony_ci} 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci/** 159062306a36Sopenharmony_ci * velocity_free_rd_ring - free receive ring 159162306a36Sopenharmony_ci * @vptr: velocity to clean up 159262306a36Sopenharmony_ci * 159362306a36Sopenharmony_ci * Free the receive buffers for each ring slot and any 159462306a36Sopenharmony_ci * attached socket buffers that need to go away. 159562306a36Sopenharmony_ci */ 159662306a36Sopenharmony_cistatic void velocity_free_rd_ring(struct velocity_info *vptr) 159762306a36Sopenharmony_ci{ 159862306a36Sopenharmony_ci int i; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci if (vptr->rx.info == NULL) 160162306a36Sopenharmony_ci return; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci for (i = 0; i < vptr->options.numrx; i++) { 160462306a36Sopenharmony_ci struct velocity_rd_info *rd_info = &(vptr->rx.info[i]); 160562306a36Sopenharmony_ci struct rx_desc *rd = vptr->rx.ring + i; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci memset(rd, 0, sizeof(*rd)); 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci if (!rd_info->skb) 161062306a36Sopenharmony_ci continue; 161162306a36Sopenharmony_ci dma_unmap_single(vptr->dev, rd_info->skb_dma, vptr->rx.buf_sz, 161262306a36Sopenharmony_ci DMA_FROM_DEVICE); 161362306a36Sopenharmony_ci rd_info->skb_dma = 0; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci dev_kfree_skb(rd_info->skb); 161662306a36Sopenharmony_ci rd_info->skb = NULL; 161762306a36Sopenharmony_ci } 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci kfree(vptr->rx.info); 162062306a36Sopenharmony_ci vptr->rx.info = NULL; 162162306a36Sopenharmony_ci} 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci/** 162462306a36Sopenharmony_ci * velocity_init_rd_ring - set up receive ring 162562306a36Sopenharmony_ci * @vptr: velocity to configure 162662306a36Sopenharmony_ci * 162762306a36Sopenharmony_ci * Allocate and set up the receive buffers for each ring slot and 162862306a36Sopenharmony_ci * assign them to the network adapter. 162962306a36Sopenharmony_ci */ 163062306a36Sopenharmony_cistatic int velocity_init_rd_ring(struct velocity_info *vptr) 163162306a36Sopenharmony_ci{ 163262306a36Sopenharmony_ci int ret = -ENOMEM; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci vptr->rx.info = kcalloc(vptr->options.numrx, 163562306a36Sopenharmony_ci sizeof(struct velocity_rd_info), GFP_KERNEL); 163662306a36Sopenharmony_ci if (!vptr->rx.info) 163762306a36Sopenharmony_ci goto out; 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci velocity_init_rx_ring_indexes(vptr); 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci if (velocity_rx_refill(vptr) != vptr->options.numrx) { 164262306a36Sopenharmony_ci netdev_err(vptr->netdev, "failed to allocate RX buffer\n"); 164362306a36Sopenharmony_ci velocity_free_rd_ring(vptr); 164462306a36Sopenharmony_ci goto out; 164562306a36Sopenharmony_ci } 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci ret = 0; 164862306a36Sopenharmony_ciout: 164962306a36Sopenharmony_ci return ret; 165062306a36Sopenharmony_ci} 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci/** 165362306a36Sopenharmony_ci * velocity_init_td_ring - set up transmit ring 165462306a36Sopenharmony_ci * @vptr: velocity 165562306a36Sopenharmony_ci * 165662306a36Sopenharmony_ci * Set up the transmit ring and chain the ring pointers together. 165762306a36Sopenharmony_ci * Returns zero on success or a negative posix errno code for 165862306a36Sopenharmony_ci * failure. 165962306a36Sopenharmony_ci */ 166062306a36Sopenharmony_cistatic int velocity_init_td_ring(struct velocity_info *vptr) 166162306a36Sopenharmony_ci{ 166262306a36Sopenharmony_ci int j; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci /* Init the TD ring entries */ 166562306a36Sopenharmony_ci for (j = 0; j < vptr->tx.numq; j++) { 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci vptr->tx.infos[j] = kcalloc(vptr->options.numtx, 166862306a36Sopenharmony_ci sizeof(struct velocity_td_info), 166962306a36Sopenharmony_ci GFP_KERNEL); 167062306a36Sopenharmony_ci if (!vptr->tx.infos[j]) { 167162306a36Sopenharmony_ci while (--j >= 0) 167262306a36Sopenharmony_ci kfree(vptr->tx.infos[j]); 167362306a36Sopenharmony_ci return -ENOMEM; 167462306a36Sopenharmony_ci } 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci vptr->tx.tail[j] = vptr->tx.curr[j] = vptr->tx.used[j] = 0; 167762306a36Sopenharmony_ci } 167862306a36Sopenharmony_ci return 0; 167962306a36Sopenharmony_ci} 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci/** 168262306a36Sopenharmony_ci * velocity_free_dma_rings - free PCI ring pointers 168362306a36Sopenharmony_ci * @vptr: Velocity to free from 168462306a36Sopenharmony_ci * 168562306a36Sopenharmony_ci * Clean up the PCI ring buffers allocated to this velocity. 168662306a36Sopenharmony_ci */ 168762306a36Sopenharmony_cistatic void velocity_free_dma_rings(struct velocity_info *vptr) 168862306a36Sopenharmony_ci{ 168962306a36Sopenharmony_ci const int size = vptr->options.numrx * sizeof(struct rx_desc) + 169062306a36Sopenharmony_ci vptr->options.numtx * sizeof(struct tx_desc) * vptr->tx.numq; 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci dma_free_coherent(vptr->dev, size, vptr->rx.ring, vptr->rx.pool_dma); 169362306a36Sopenharmony_ci} 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_cistatic int velocity_init_rings(struct velocity_info *vptr, int mtu) 169662306a36Sopenharmony_ci{ 169762306a36Sopenharmony_ci int ret; 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci velocity_set_rxbufsize(vptr, mtu); 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci ret = velocity_init_dma_rings(vptr); 170262306a36Sopenharmony_ci if (ret < 0) 170362306a36Sopenharmony_ci goto out; 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci ret = velocity_init_rd_ring(vptr); 170662306a36Sopenharmony_ci if (ret < 0) 170762306a36Sopenharmony_ci goto err_free_dma_rings_0; 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci ret = velocity_init_td_ring(vptr); 171062306a36Sopenharmony_ci if (ret < 0) 171162306a36Sopenharmony_ci goto err_free_rd_ring_1; 171262306a36Sopenharmony_ciout: 171362306a36Sopenharmony_ci return ret; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_cierr_free_rd_ring_1: 171662306a36Sopenharmony_ci velocity_free_rd_ring(vptr); 171762306a36Sopenharmony_cierr_free_dma_rings_0: 171862306a36Sopenharmony_ci velocity_free_dma_rings(vptr); 171962306a36Sopenharmony_ci goto out; 172062306a36Sopenharmony_ci} 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci/** 172362306a36Sopenharmony_ci * velocity_free_tx_buf - free transmit buffer 172462306a36Sopenharmony_ci * @vptr: velocity 172562306a36Sopenharmony_ci * @tdinfo: buffer 172662306a36Sopenharmony_ci * @td: transmit descriptor to free 172762306a36Sopenharmony_ci * 172862306a36Sopenharmony_ci * Release an transmit buffer. If the buffer was preallocated then 172962306a36Sopenharmony_ci * recycle it, if not then unmap the buffer. 173062306a36Sopenharmony_ci */ 173162306a36Sopenharmony_cistatic void velocity_free_tx_buf(struct velocity_info *vptr, 173262306a36Sopenharmony_ci struct velocity_td_info *tdinfo, struct tx_desc *td) 173362306a36Sopenharmony_ci{ 173462306a36Sopenharmony_ci struct sk_buff *skb = tdinfo->skb; 173562306a36Sopenharmony_ci int i; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci /* 173862306a36Sopenharmony_ci * Don't unmap the pre-allocated tx_bufs 173962306a36Sopenharmony_ci */ 174062306a36Sopenharmony_ci for (i = 0; i < tdinfo->nskb_dma; i++) { 174162306a36Sopenharmony_ci size_t pktlen = max_t(size_t, skb->len, ETH_ZLEN); 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci /* For scatter-gather */ 174462306a36Sopenharmony_ci if (skb_shinfo(skb)->nr_frags > 0) 174562306a36Sopenharmony_ci pktlen = max_t(size_t, pktlen, 174662306a36Sopenharmony_ci td->td_buf[i].size & ~TD_QUEUE); 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci dma_unmap_single(vptr->dev, tdinfo->skb_dma[i], 174962306a36Sopenharmony_ci le16_to_cpu(pktlen), DMA_TO_DEVICE); 175062306a36Sopenharmony_ci } 175162306a36Sopenharmony_ci dev_consume_skb_irq(skb); 175262306a36Sopenharmony_ci tdinfo->skb = NULL; 175362306a36Sopenharmony_ci} 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci/* 175662306a36Sopenharmony_ci * FIXME: could we merge this with velocity_free_tx_buf ? 175762306a36Sopenharmony_ci */ 175862306a36Sopenharmony_cistatic void velocity_free_td_ring_entry(struct velocity_info *vptr, 175962306a36Sopenharmony_ci int q, int n) 176062306a36Sopenharmony_ci{ 176162306a36Sopenharmony_ci struct velocity_td_info *td_info = &(vptr->tx.infos[q][n]); 176262306a36Sopenharmony_ci int i; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci if (td_info == NULL) 176562306a36Sopenharmony_ci return; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci if (td_info->skb) { 176862306a36Sopenharmony_ci for (i = 0; i < td_info->nskb_dma; i++) { 176962306a36Sopenharmony_ci if (td_info->skb_dma[i]) { 177062306a36Sopenharmony_ci dma_unmap_single(vptr->dev, td_info->skb_dma[i], 177162306a36Sopenharmony_ci td_info->skb->len, DMA_TO_DEVICE); 177262306a36Sopenharmony_ci td_info->skb_dma[i] = 0; 177362306a36Sopenharmony_ci } 177462306a36Sopenharmony_ci } 177562306a36Sopenharmony_ci dev_kfree_skb(td_info->skb); 177662306a36Sopenharmony_ci td_info->skb = NULL; 177762306a36Sopenharmony_ci } 177862306a36Sopenharmony_ci} 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci/** 178162306a36Sopenharmony_ci * velocity_free_td_ring - free td ring 178262306a36Sopenharmony_ci * @vptr: velocity 178362306a36Sopenharmony_ci * 178462306a36Sopenharmony_ci * Free up the transmit ring for this particular velocity adapter. 178562306a36Sopenharmony_ci * We free the ring contents but not the ring itself. 178662306a36Sopenharmony_ci */ 178762306a36Sopenharmony_cistatic void velocity_free_td_ring(struct velocity_info *vptr) 178862306a36Sopenharmony_ci{ 178962306a36Sopenharmony_ci int i, j; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci for (j = 0; j < vptr->tx.numq; j++) { 179262306a36Sopenharmony_ci if (vptr->tx.infos[j] == NULL) 179362306a36Sopenharmony_ci continue; 179462306a36Sopenharmony_ci for (i = 0; i < vptr->options.numtx; i++) 179562306a36Sopenharmony_ci velocity_free_td_ring_entry(vptr, j, i); 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci kfree(vptr->tx.infos[j]); 179862306a36Sopenharmony_ci vptr->tx.infos[j] = NULL; 179962306a36Sopenharmony_ci } 180062306a36Sopenharmony_ci} 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_cistatic void velocity_free_rings(struct velocity_info *vptr) 180362306a36Sopenharmony_ci{ 180462306a36Sopenharmony_ci velocity_free_td_ring(vptr); 180562306a36Sopenharmony_ci velocity_free_rd_ring(vptr); 180662306a36Sopenharmony_ci velocity_free_dma_rings(vptr); 180762306a36Sopenharmony_ci} 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci/** 181062306a36Sopenharmony_ci * velocity_error - handle error from controller 181162306a36Sopenharmony_ci * @vptr: velocity 181262306a36Sopenharmony_ci * @status: card status 181362306a36Sopenharmony_ci * 181462306a36Sopenharmony_ci * Process an error report from the hardware and attempt to recover 181562306a36Sopenharmony_ci * the card itself. At the moment we cannot recover from some 181662306a36Sopenharmony_ci * theoretically impossible errors but this could be fixed using 181762306a36Sopenharmony_ci * the pci_device_failed logic to bounce the hardware 181862306a36Sopenharmony_ci * 181962306a36Sopenharmony_ci */ 182062306a36Sopenharmony_cistatic void velocity_error(struct velocity_info *vptr, int status) 182162306a36Sopenharmony_ci{ 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci if (status & ISR_TXSTLI) { 182462306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci netdev_err(vptr->netdev, "TD structure error TDindex=%hx\n", 182762306a36Sopenharmony_ci readw(®s->TDIdx[0])); 182862306a36Sopenharmony_ci BYTE_REG_BITS_ON(TXESR_TDSTR, ®s->TXESR); 182962306a36Sopenharmony_ci writew(TRDCSR_RUN, ®s->TDCSRClr); 183062306a36Sopenharmony_ci netif_stop_queue(vptr->netdev); 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci /* FIXME: port over the pci_device_failed code and use it 183362306a36Sopenharmony_ci here */ 183462306a36Sopenharmony_ci } 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci if (status & ISR_SRCI) { 183762306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 183862306a36Sopenharmony_ci int linked; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci if (vptr->options.spd_dpx == SPD_DPX_AUTO) { 184162306a36Sopenharmony_ci vptr->mii_status = check_connection_type(regs); 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci /* 184462306a36Sopenharmony_ci * If it is a 3119, disable frame bursting in 184562306a36Sopenharmony_ci * halfduplex mode and enable it in fullduplex 184662306a36Sopenharmony_ci * mode 184762306a36Sopenharmony_ci */ 184862306a36Sopenharmony_ci if (vptr->rev_id < REV_ID_VT3216_A0) { 184962306a36Sopenharmony_ci if (vptr->mii_status & VELOCITY_DUPLEX_FULL) 185062306a36Sopenharmony_ci BYTE_REG_BITS_ON(TCR_TB2BDIS, ®s->TCR); 185162306a36Sopenharmony_ci else 185262306a36Sopenharmony_ci BYTE_REG_BITS_OFF(TCR_TB2BDIS, ®s->TCR); 185362306a36Sopenharmony_ci } 185462306a36Sopenharmony_ci /* 185562306a36Sopenharmony_ci * Only enable CD heart beat counter in 10HD mode 185662306a36Sopenharmony_ci */ 185762306a36Sopenharmony_ci if (!(vptr->mii_status & VELOCITY_DUPLEX_FULL) && (vptr->mii_status & VELOCITY_SPEED_10)) 185862306a36Sopenharmony_ci BYTE_REG_BITS_OFF(TESTCFG_HBDIS, ®s->TESTCFG); 185962306a36Sopenharmony_ci else 186062306a36Sopenharmony_ci BYTE_REG_BITS_ON(TESTCFG_HBDIS, ®s->TESTCFG); 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci setup_queue_timers(vptr); 186362306a36Sopenharmony_ci } 186462306a36Sopenharmony_ci /* 186562306a36Sopenharmony_ci * Get link status from PHYSR0 186662306a36Sopenharmony_ci */ 186762306a36Sopenharmony_ci linked = readb(®s->PHYSR0) & PHYSR0_LINKGD; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci if (linked) { 187062306a36Sopenharmony_ci vptr->mii_status &= ~VELOCITY_LINK_FAIL; 187162306a36Sopenharmony_ci netif_carrier_on(vptr->netdev); 187262306a36Sopenharmony_ci } else { 187362306a36Sopenharmony_ci vptr->mii_status |= VELOCITY_LINK_FAIL; 187462306a36Sopenharmony_ci netif_carrier_off(vptr->netdev); 187562306a36Sopenharmony_ci } 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci velocity_print_link_status(vptr); 187862306a36Sopenharmony_ci enable_flow_control_ability(vptr); 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci /* 188162306a36Sopenharmony_ci * Re-enable auto-polling because SRCI will disable 188262306a36Sopenharmony_ci * auto-polling 188362306a36Sopenharmony_ci */ 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci enable_mii_autopoll(regs); 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci if (vptr->mii_status & VELOCITY_LINK_FAIL) 188862306a36Sopenharmony_ci netif_stop_queue(vptr->netdev); 188962306a36Sopenharmony_ci else 189062306a36Sopenharmony_ci netif_wake_queue(vptr->netdev); 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci } 189362306a36Sopenharmony_ci if (status & ISR_MIBFI) 189462306a36Sopenharmony_ci velocity_update_hw_mibs(vptr); 189562306a36Sopenharmony_ci if (status & ISR_LSTEI) 189662306a36Sopenharmony_ci mac_rx_queue_wake(vptr->mac_regs); 189762306a36Sopenharmony_ci} 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci/** 190062306a36Sopenharmony_ci * velocity_tx_srv - transmit interrupt service 190162306a36Sopenharmony_ci * @vptr: Velocity 190262306a36Sopenharmony_ci * 190362306a36Sopenharmony_ci * Scan the queues looking for transmitted packets that 190462306a36Sopenharmony_ci * we can complete and clean up. Update any statistics as 190562306a36Sopenharmony_ci * necessary/ 190662306a36Sopenharmony_ci */ 190762306a36Sopenharmony_cistatic int velocity_tx_srv(struct velocity_info *vptr) 190862306a36Sopenharmony_ci{ 190962306a36Sopenharmony_ci struct tx_desc *td; 191062306a36Sopenharmony_ci int qnum; 191162306a36Sopenharmony_ci int full = 0; 191262306a36Sopenharmony_ci int idx; 191362306a36Sopenharmony_ci int works = 0; 191462306a36Sopenharmony_ci struct velocity_td_info *tdinfo; 191562306a36Sopenharmony_ci struct net_device_stats *stats = &vptr->netdev->stats; 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci for (qnum = 0; qnum < vptr->tx.numq; qnum++) { 191862306a36Sopenharmony_ci for (idx = vptr->tx.tail[qnum]; vptr->tx.used[qnum] > 0; 191962306a36Sopenharmony_ci idx = (idx + 1) % vptr->options.numtx) { 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci /* 192262306a36Sopenharmony_ci * Get Tx Descriptor 192362306a36Sopenharmony_ci */ 192462306a36Sopenharmony_ci td = &(vptr->tx.rings[qnum][idx]); 192562306a36Sopenharmony_ci tdinfo = &(vptr->tx.infos[qnum][idx]); 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci if (td->tdesc0.len & OWNED_BY_NIC) 192862306a36Sopenharmony_ci break; 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci if ((works++ > 15)) 193162306a36Sopenharmony_ci break; 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci if (td->tdesc0.TSR & TSR0_TERR) { 193462306a36Sopenharmony_ci stats->tx_errors++; 193562306a36Sopenharmony_ci stats->tx_dropped++; 193662306a36Sopenharmony_ci if (td->tdesc0.TSR & TSR0_CDH) 193762306a36Sopenharmony_ci stats->tx_heartbeat_errors++; 193862306a36Sopenharmony_ci if (td->tdesc0.TSR & TSR0_CRS) 193962306a36Sopenharmony_ci stats->tx_carrier_errors++; 194062306a36Sopenharmony_ci if (td->tdesc0.TSR & TSR0_ABT) 194162306a36Sopenharmony_ci stats->tx_aborted_errors++; 194262306a36Sopenharmony_ci if (td->tdesc0.TSR & TSR0_OWC) 194362306a36Sopenharmony_ci stats->tx_window_errors++; 194462306a36Sopenharmony_ci } else { 194562306a36Sopenharmony_ci stats->tx_packets++; 194662306a36Sopenharmony_ci stats->tx_bytes += tdinfo->skb->len; 194762306a36Sopenharmony_ci } 194862306a36Sopenharmony_ci velocity_free_tx_buf(vptr, tdinfo, td); 194962306a36Sopenharmony_ci vptr->tx.used[qnum]--; 195062306a36Sopenharmony_ci } 195162306a36Sopenharmony_ci vptr->tx.tail[qnum] = idx; 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci if (AVAIL_TD(vptr, qnum) < 1) 195462306a36Sopenharmony_ci full = 1; 195562306a36Sopenharmony_ci } 195662306a36Sopenharmony_ci /* 195762306a36Sopenharmony_ci * Look to see if we should kick the transmit network 195862306a36Sopenharmony_ci * layer for more work. 195962306a36Sopenharmony_ci */ 196062306a36Sopenharmony_ci if (netif_queue_stopped(vptr->netdev) && (full == 0) && 196162306a36Sopenharmony_ci (!(vptr->mii_status & VELOCITY_LINK_FAIL))) { 196262306a36Sopenharmony_ci netif_wake_queue(vptr->netdev); 196362306a36Sopenharmony_ci } 196462306a36Sopenharmony_ci return works; 196562306a36Sopenharmony_ci} 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci/** 196862306a36Sopenharmony_ci * velocity_rx_csum - checksum process 196962306a36Sopenharmony_ci * @rd: receive packet descriptor 197062306a36Sopenharmony_ci * @skb: network layer packet buffer 197162306a36Sopenharmony_ci * 197262306a36Sopenharmony_ci * Process the status bits for the received packet and determine 197362306a36Sopenharmony_ci * if the checksum was computed and verified by the hardware 197462306a36Sopenharmony_ci */ 197562306a36Sopenharmony_cistatic inline void velocity_rx_csum(struct rx_desc *rd, struct sk_buff *skb) 197662306a36Sopenharmony_ci{ 197762306a36Sopenharmony_ci skb_checksum_none_assert(skb); 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci if (rd->rdesc1.CSM & CSM_IPKT) { 198062306a36Sopenharmony_ci if (rd->rdesc1.CSM & CSM_IPOK) { 198162306a36Sopenharmony_ci if ((rd->rdesc1.CSM & CSM_TCPKT) || 198262306a36Sopenharmony_ci (rd->rdesc1.CSM & CSM_UDPKT)) { 198362306a36Sopenharmony_ci if (!(rd->rdesc1.CSM & CSM_TUPOK)) 198462306a36Sopenharmony_ci return; 198562306a36Sopenharmony_ci } 198662306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 198762306a36Sopenharmony_ci } 198862306a36Sopenharmony_ci } 198962306a36Sopenharmony_ci} 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci/** 199262306a36Sopenharmony_ci * velocity_rx_copy - in place Rx copy for small packets 199362306a36Sopenharmony_ci * @rx_skb: network layer packet buffer candidate 199462306a36Sopenharmony_ci * @pkt_size: received data size 199562306a36Sopenharmony_ci * @vptr: velocity adapter 199662306a36Sopenharmony_ci * 199762306a36Sopenharmony_ci * Replace the current skb that is scheduled for Rx processing by a 199862306a36Sopenharmony_ci * shorter, immediately allocated skb, if the received packet is small 199962306a36Sopenharmony_ci * enough. This function returns a negative value if the received 200062306a36Sopenharmony_ci * packet is too big or if memory is exhausted. 200162306a36Sopenharmony_ci */ 200262306a36Sopenharmony_cistatic int velocity_rx_copy(struct sk_buff **rx_skb, int pkt_size, 200362306a36Sopenharmony_ci struct velocity_info *vptr) 200462306a36Sopenharmony_ci{ 200562306a36Sopenharmony_ci int ret = -1; 200662306a36Sopenharmony_ci if (pkt_size < rx_copybreak) { 200762306a36Sopenharmony_ci struct sk_buff *new_skb; 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci new_skb = netdev_alloc_skb_ip_align(vptr->netdev, pkt_size); 201062306a36Sopenharmony_ci if (new_skb) { 201162306a36Sopenharmony_ci new_skb->ip_summed = rx_skb[0]->ip_summed; 201262306a36Sopenharmony_ci skb_copy_from_linear_data(*rx_skb, new_skb->data, pkt_size); 201362306a36Sopenharmony_ci *rx_skb = new_skb; 201462306a36Sopenharmony_ci ret = 0; 201562306a36Sopenharmony_ci } 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci } 201862306a36Sopenharmony_ci return ret; 201962306a36Sopenharmony_ci} 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci/** 202262306a36Sopenharmony_ci * velocity_iph_realign - IP header alignment 202362306a36Sopenharmony_ci * @vptr: velocity we are handling 202462306a36Sopenharmony_ci * @skb: network layer packet buffer 202562306a36Sopenharmony_ci * @pkt_size: received data size 202662306a36Sopenharmony_ci * 202762306a36Sopenharmony_ci * Align IP header on a 2 bytes boundary. This behavior can be 202862306a36Sopenharmony_ci * configured by the user. 202962306a36Sopenharmony_ci */ 203062306a36Sopenharmony_cistatic inline void velocity_iph_realign(struct velocity_info *vptr, 203162306a36Sopenharmony_ci struct sk_buff *skb, int pkt_size) 203262306a36Sopenharmony_ci{ 203362306a36Sopenharmony_ci if (vptr->flags & VELOCITY_FLAGS_IP_ALIGN) { 203462306a36Sopenharmony_ci memmove(skb->data + 2, skb->data, pkt_size); 203562306a36Sopenharmony_ci skb_reserve(skb, 2); 203662306a36Sopenharmony_ci } 203762306a36Sopenharmony_ci} 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci/** 204062306a36Sopenharmony_ci * velocity_receive_frame - received packet processor 204162306a36Sopenharmony_ci * @vptr: velocity we are handling 204262306a36Sopenharmony_ci * @idx: ring index 204362306a36Sopenharmony_ci * 204462306a36Sopenharmony_ci * A packet has arrived. We process the packet and if appropriate 204562306a36Sopenharmony_ci * pass the frame up the network stack 204662306a36Sopenharmony_ci */ 204762306a36Sopenharmony_cistatic int velocity_receive_frame(struct velocity_info *vptr, int idx) 204862306a36Sopenharmony_ci{ 204962306a36Sopenharmony_ci struct net_device_stats *stats = &vptr->netdev->stats; 205062306a36Sopenharmony_ci struct velocity_rd_info *rd_info = &(vptr->rx.info[idx]); 205162306a36Sopenharmony_ci struct rx_desc *rd = &(vptr->rx.ring[idx]); 205262306a36Sopenharmony_ci int pkt_len = le16_to_cpu(rd->rdesc0.len) & 0x3fff; 205362306a36Sopenharmony_ci struct sk_buff *skb; 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci if (unlikely(rd->rdesc0.RSR & (RSR_STP | RSR_EDP | RSR_RL))) { 205662306a36Sopenharmony_ci if (rd->rdesc0.RSR & (RSR_STP | RSR_EDP)) 205762306a36Sopenharmony_ci netdev_err(vptr->netdev, "received frame spans multiple RDs\n"); 205862306a36Sopenharmony_ci stats->rx_length_errors++; 205962306a36Sopenharmony_ci return -EINVAL; 206062306a36Sopenharmony_ci } 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci if (rd->rdesc0.RSR & RSR_MAR) 206362306a36Sopenharmony_ci stats->multicast++; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci skb = rd_info->skb; 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci dma_sync_single_for_cpu(vptr->dev, rd_info->skb_dma, 206862306a36Sopenharmony_ci vptr->rx.buf_sz, DMA_FROM_DEVICE); 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci velocity_rx_csum(rd, skb); 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci if (velocity_rx_copy(&skb, pkt_len, vptr) < 0) { 207362306a36Sopenharmony_ci velocity_iph_realign(vptr, skb, pkt_len); 207462306a36Sopenharmony_ci rd_info->skb = NULL; 207562306a36Sopenharmony_ci dma_unmap_single(vptr->dev, rd_info->skb_dma, vptr->rx.buf_sz, 207662306a36Sopenharmony_ci DMA_FROM_DEVICE); 207762306a36Sopenharmony_ci } else { 207862306a36Sopenharmony_ci dma_sync_single_for_device(vptr->dev, rd_info->skb_dma, 207962306a36Sopenharmony_ci vptr->rx.buf_sz, DMA_FROM_DEVICE); 208062306a36Sopenharmony_ci } 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci skb_put(skb, pkt_len - 4); 208362306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, vptr->netdev); 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci if (rd->rdesc0.RSR & RSR_DETAG) { 208662306a36Sopenharmony_ci u16 vid = swab16(le16_to_cpu(rd->rdesc1.PQTAG)); 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid); 208962306a36Sopenharmony_ci } 209062306a36Sopenharmony_ci netif_receive_skb(skb); 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci stats->rx_bytes += pkt_len; 209362306a36Sopenharmony_ci stats->rx_packets++; 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci return 0; 209662306a36Sopenharmony_ci} 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci/** 209962306a36Sopenharmony_ci * velocity_rx_srv - service RX interrupt 210062306a36Sopenharmony_ci * @vptr: velocity 210162306a36Sopenharmony_ci * @budget_left: remaining budget 210262306a36Sopenharmony_ci * 210362306a36Sopenharmony_ci * Walk the receive ring of the velocity adapter and remove 210462306a36Sopenharmony_ci * any received packets from the receive queue. Hand the ring 210562306a36Sopenharmony_ci * slots back to the adapter for reuse. 210662306a36Sopenharmony_ci */ 210762306a36Sopenharmony_cistatic int velocity_rx_srv(struct velocity_info *vptr, int budget_left) 210862306a36Sopenharmony_ci{ 210962306a36Sopenharmony_ci struct net_device_stats *stats = &vptr->netdev->stats; 211062306a36Sopenharmony_ci int rd_curr = vptr->rx.curr; 211162306a36Sopenharmony_ci int works = 0; 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci while (works < budget_left) { 211462306a36Sopenharmony_ci struct rx_desc *rd = vptr->rx.ring + rd_curr; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci if (!vptr->rx.info[rd_curr].skb) 211762306a36Sopenharmony_ci break; 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci if (rd->rdesc0.len & OWNED_BY_NIC) 212062306a36Sopenharmony_ci break; 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci rmb(); 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci /* 212562306a36Sopenharmony_ci * Don't drop CE or RL error frame although RXOK is off 212662306a36Sopenharmony_ci */ 212762306a36Sopenharmony_ci if (rd->rdesc0.RSR & (RSR_RXOK | RSR_CE | RSR_RL)) { 212862306a36Sopenharmony_ci if (velocity_receive_frame(vptr, rd_curr) < 0) 212962306a36Sopenharmony_ci stats->rx_dropped++; 213062306a36Sopenharmony_ci } else { 213162306a36Sopenharmony_ci if (rd->rdesc0.RSR & RSR_CRC) 213262306a36Sopenharmony_ci stats->rx_crc_errors++; 213362306a36Sopenharmony_ci if (rd->rdesc0.RSR & RSR_FAE) 213462306a36Sopenharmony_ci stats->rx_frame_errors++; 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci stats->rx_dropped++; 213762306a36Sopenharmony_ci } 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci rd->size |= RX_INTEN; 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci rd_curr++; 214262306a36Sopenharmony_ci if (rd_curr >= vptr->options.numrx) 214362306a36Sopenharmony_ci rd_curr = 0; 214462306a36Sopenharmony_ci works++; 214562306a36Sopenharmony_ci } 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci vptr->rx.curr = rd_curr; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci if ((works > 0) && (velocity_rx_refill(vptr) > 0)) 215062306a36Sopenharmony_ci velocity_give_many_rx_descs(vptr); 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci VAR_USED(stats); 215362306a36Sopenharmony_ci return works; 215462306a36Sopenharmony_ci} 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_cistatic int velocity_poll(struct napi_struct *napi, int budget) 215762306a36Sopenharmony_ci{ 215862306a36Sopenharmony_ci struct velocity_info *vptr = container_of(napi, 215962306a36Sopenharmony_ci struct velocity_info, napi); 216062306a36Sopenharmony_ci unsigned int rx_done; 216162306a36Sopenharmony_ci unsigned long flags; 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci /* 216462306a36Sopenharmony_ci * Do rx and tx twice for performance (taken from the VIA 216562306a36Sopenharmony_ci * out-of-tree driver). 216662306a36Sopenharmony_ci */ 216762306a36Sopenharmony_ci rx_done = velocity_rx_srv(vptr, budget); 216862306a36Sopenharmony_ci spin_lock_irqsave(&vptr->lock, flags); 216962306a36Sopenharmony_ci velocity_tx_srv(vptr); 217062306a36Sopenharmony_ci /* If budget not fully consumed, exit the polling mode */ 217162306a36Sopenharmony_ci if (rx_done < budget) { 217262306a36Sopenharmony_ci napi_complete_done(napi, rx_done); 217362306a36Sopenharmony_ci mac_enable_int(vptr->mac_regs); 217462306a36Sopenharmony_ci } 217562306a36Sopenharmony_ci spin_unlock_irqrestore(&vptr->lock, flags); 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci return rx_done; 217862306a36Sopenharmony_ci} 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci/** 218162306a36Sopenharmony_ci * velocity_intr - interrupt callback 218262306a36Sopenharmony_ci * @irq: interrupt number 218362306a36Sopenharmony_ci * @dev_instance: interrupting device 218462306a36Sopenharmony_ci * 218562306a36Sopenharmony_ci * Called whenever an interrupt is generated by the velocity 218662306a36Sopenharmony_ci * adapter IRQ line. We may not be the source of the interrupt 218762306a36Sopenharmony_ci * and need to identify initially if we are, and if not exit as 218862306a36Sopenharmony_ci * efficiently as possible. 218962306a36Sopenharmony_ci */ 219062306a36Sopenharmony_cistatic irqreturn_t velocity_intr(int irq, void *dev_instance) 219162306a36Sopenharmony_ci{ 219262306a36Sopenharmony_ci struct net_device *dev = dev_instance; 219362306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 219462306a36Sopenharmony_ci u32 isr_status; 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci spin_lock(&vptr->lock); 219762306a36Sopenharmony_ci isr_status = mac_read_isr(vptr->mac_regs); 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci /* Not us ? */ 220062306a36Sopenharmony_ci if (isr_status == 0) { 220162306a36Sopenharmony_ci spin_unlock(&vptr->lock); 220262306a36Sopenharmony_ci return IRQ_NONE; 220362306a36Sopenharmony_ci } 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci /* Ack the interrupt */ 220662306a36Sopenharmony_ci mac_write_isr(vptr->mac_regs, isr_status); 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci if (likely(napi_schedule_prep(&vptr->napi))) { 220962306a36Sopenharmony_ci mac_disable_int(vptr->mac_regs); 221062306a36Sopenharmony_ci __napi_schedule(&vptr->napi); 221162306a36Sopenharmony_ci } 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci if (isr_status & (~(ISR_PRXI | ISR_PPRXI | ISR_PTXI | ISR_PPTXI))) 221462306a36Sopenharmony_ci velocity_error(vptr, isr_status); 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci spin_unlock(&vptr->lock); 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci return IRQ_HANDLED; 221962306a36Sopenharmony_ci} 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ci/** 222262306a36Sopenharmony_ci * velocity_open - interface activation callback 222362306a36Sopenharmony_ci * @dev: network layer device to open 222462306a36Sopenharmony_ci * 222562306a36Sopenharmony_ci * Called when the network layer brings the interface up. Returns 222662306a36Sopenharmony_ci * a negative posix error code on failure, or zero on success. 222762306a36Sopenharmony_ci * 222862306a36Sopenharmony_ci * All the ring allocation and set up is done on open for this 222962306a36Sopenharmony_ci * adapter to minimise memory usage when inactive 223062306a36Sopenharmony_ci */ 223162306a36Sopenharmony_cistatic int velocity_open(struct net_device *dev) 223262306a36Sopenharmony_ci{ 223362306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 223462306a36Sopenharmony_ci int ret; 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci ret = velocity_init_rings(vptr, dev->mtu); 223762306a36Sopenharmony_ci if (ret < 0) 223862306a36Sopenharmony_ci goto out; 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci /* Ensure chip is running */ 224162306a36Sopenharmony_ci velocity_set_power_state(vptr, PCI_D0); 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci velocity_init_registers(vptr, VELOCITY_INIT_COLD); 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci ret = request_irq(dev->irq, velocity_intr, IRQF_SHARED, 224662306a36Sopenharmony_ci dev->name, dev); 224762306a36Sopenharmony_ci if (ret < 0) { 224862306a36Sopenharmony_ci /* Power down the chip */ 224962306a36Sopenharmony_ci velocity_set_power_state(vptr, PCI_D3hot); 225062306a36Sopenharmony_ci velocity_free_rings(vptr); 225162306a36Sopenharmony_ci goto out; 225262306a36Sopenharmony_ci } 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci velocity_give_many_rx_descs(vptr); 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci mac_enable_int(vptr->mac_regs); 225762306a36Sopenharmony_ci netif_start_queue(dev); 225862306a36Sopenharmony_ci napi_enable(&vptr->napi); 225962306a36Sopenharmony_ci vptr->flags |= VELOCITY_FLAGS_OPENED; 226062306a36Sopenharmony_ciout: 226162306a36Sopenharmony_ci return ret; 226262306a36Sopenharmony_ci} 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci/** 226562306a36Sopenharmony_ci * velocity_shutdown - shut down the chip 226662306a36Sopenharmony_ci * @vptr: velocity to deactivate 226762306a36Sopenharmony_ci * 226862306a36Sopenharmony_ci * Shuts down the internal operations of the velocity and 226962306a36Sopenharmony_ci * disables interrupts, autopolling, transmit and receive 227062306a36Sopenharmony_ci */ 227162306a36Sopenharmony_cistatic void velocity_shutdown(struct velocity_info *vptr) 227262306a36Sopenharmony_ci{ 227362306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 227462306a36Sopenharmony_ci mac_disable_int(regs); 227562306a36Sopenharmony_ci writel(CR0_STOP, ®s->CR0Set); 227662306a36Sopenharmony_ci writew(0xFFFF, ®s->TDCSRClr); 227762306a36Sopenharmony_ci writeb(0xFF, ®s->RDCSRClr); 227862306a36Sopenharmony_ci safe_disable_mii_autopoll(regs); 227962306a36Sopenharmony_ci mac_clear_isr(regs); 228062306a36Sopenharmony_ci} 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci/** 228362306a36Sopenharmony_ci * velocity_change_mtu - MTU change callback 228462306a36Sopenharmony_ci * @dev: network device 228562306a36Sopenharmony_ci * @new_mtu: desired MTU 228662306a36Sopenharmony_ci * 228762306a36Sopenharmony_ci * Handle requests from the networking layer for MTU change on 228862306a36Sopenharmony_ci * this interface. It gets called on a change by the network layer. 228962306a36Sopenharmony_ci * Return zero for success or negative posix error code. 229062306a36Sopenharmony_ci */ 229162306a36Sopenharmony_cistatic int velocity_change_mtu(struct net_device *dev, int new_mtu) 229262306a36Sopenharmony_ci{ 229362306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 229462306a36Sopenharmony_ci int ret = 0; 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci if (!netif_running(dev)) { 229762306a36Sopenharmony_ci dev->mtu = new_mtu; 229862306a36Sopenharmony_ci goto out_0; 229962306a36Sopenharmony_ci } 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci if (dev->mtu != new_mtu) { 230262306a36Sopenharmony_ci struct velocity_info *tmp_vptr; 230362306a36Sopenharmony_ci unsigned long flags; 230462306a36Sopenharmony_ci struct rx_info rx; 230562306a36Sopenharmony_ci struct tx_info tx; 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci tmp_vptr = kzalloc(sizeof(*tmp_vptr), GFP_KERNEL); 230862306a36Sopenharmony_ci if (!tmp_vptr) { 230962306a36Sopenharmony_ci ret = -ENOMEM; 231062306a36Sopenharmony_ci goto out_0; 231162306a36Sopenharmony_ci } 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci tmp_vptr->netdev = dev; 231462306a36Sopenharmony_ci tmp_vptr->pdev = vptr->pdev; 231562306a36Sopenharmony_ci tmp_vptr->dev = vptr->dev; 231662306a36Sopenharmony_ci tmp_vptr->options = vptr->options; 231762306a36Sopenharmony_ci tmp_vptr->tx.numq = vptr->tx.numq; 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci ret = velocity_init_rings(tmp_vptr, new_mtu); 232062306a36Sopenharmony_ci if (ret < 0) 232162306a36Sopenharmony_ci goto out_free_tmp_vptr_1; 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci napi_disable(&vptr->napi); 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci spin_lock_irqsave(&vptr->lock, flags); 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ci netif_stop_queue(dev); 232862306a36Sopenharmony_ci velocity_shutdown(vptr); 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci rx = vptr->rx; 233162306a36Sopenharmony_ci tx = vptr->tx; 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ci vptr->rx = tmp_vptr->rx; 233462306a36Sopenharmony_ci vptr->tx = tmp_vptr->tx; 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci tmp_vptr->rx = rx; 233762306a36Sopenharmony_ci tmp_vptr->tx = tx; 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci dev->mtu = new_mtu; 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci velocity_init_registers(vptr, VELOCITY_INIT_COLD); 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci velocity_give_many_rx_descs(vptr); 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci napi_enable(&vptr->napi); 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci mac_enable_int(vptr->mac_regs); 234862306a36Sopenharmony_ci netif_start_queue(dev); 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ci spin_unlock_irqrestore(&vptr->lock, flags); 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci velocity_free_rings(tmp_vptr); 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ciout_free_tmp_vptr_1: 235562306a36Sopenharmony_ci kfree(tmp_vptr); 235662306a36Sopenharmony_ci } 235762306a36Sopenharmony_ciout_0: 235862306a36Sopenharmony_ci return ret; 235962306a36Sopenharmony_ci} 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 236262306a36Sopenharmony_ci/** 236362306a36Sopenharmony_ci * velocity_poll_controller - Velocity Poll controller function 236462306a36Sopenharmony_ci * @dev: network device 236562306a36Sopenharmony_ci * 236662306a36Sopenharmony_ci * 236762306a36Sopenharmony_ci * Used by NETCONSOLE and other diagnostic tools to allow network I/P 236862306a36Sopenharmony_ci * with interrupts disabled. 236962306a36Sopenharmony_ci */ 237062306a36Sopenharmony_cistatic void velocity_poll_controller(struct net_device *dev) 237162306a36Sopenharmony_ci{ 237262306a36Sopenharmony_ci disable_irq(dev->irq); 237362306a36Sopenharmony_ci velocity_intr(dev->irq, dev); 237462306a36Sopenharmony_ci enable_irq(dev->irq); 237562306a36Sopenharmony_ci} 237662306a36Sopenharmony_ci#endif 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci/** 237962306a36Sopenharmony_ci * velocity_mii_ioctl - MII ioctl handler 238062306a36Sopenharmony_ci * @dev: network device 238162306a36Sopenharmony_ci * @ifr: the ifreq block for the ioctl 238262306a36Sopenharmony_ci * @cmd: the command 238362306a36Sopenharmony_ci * 238462306a36Sopenharmony_ci * Process MII requests made via ioctl from the network layer. These 238562306a36Sopenharmony_ci * are used by tools like kudzu to interrogate the link state of the 238662306a36Sopenharmony_ci * hardware 238762306a36Sopenharmony_ci */ 238862306a36Sopenharmony_cistatic int velocity_mii_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 238962306a36Sopenharmony_ci{ 239062306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 239162306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 239262306a36Sopenharmony_ci unsigned long flags; 239362306a36Sopenharmony_ci struct mii_ioctl_data *miidata = if_mii(ifr); 239462306a36Sopenharmony_ci int err; 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_ci switch (cmd) { 239762306a36Sopenharmony_ci case SIOCGMIIPHY: 239862306a36Sopenharmony_ci miidata->phy_id = readb(®s->MIIADR) & 0x1f; 239962306a36Sopenharmony_ci break; 240062306a36Sopenharmony_ci case SIOCGMIIREG: 240162306a36Sopenharmony_ci if (velocity_mii_read(vptr->mac_regs, miidata->reg_num & 0x1f, &(miidata->val_out)) < 0) 240262306a36Sopenharmony_ci return -ETIMEDOUT; 240362306a36Sopenharmony_ci break; 240462306a36Sopenharmony_ci case SIOCSMIIREG: 240562306a36Sopenharmony_ci spin_lock_irqsave(&vptr->lock, flags); 240662306a36Sopenharmony_ci err = velocity_mii_write(vptr->mac_regs, miidata->reg_num & 0x1f, miidata->val_in); 240762306a36Sopenharmony_ci spin_unlock_irqrestore(&vptr->lock, flags); 240862306a36Sopenharmony_ci check_connection_type(vptr->mac_regs); 240962306a36Sopenharmony_ci if (err) 241062306a36Sopenharmony_ci return err; 241162306a36Sopenharmony_ci break; 241262306a36Sopenharmony_ci default: 241362306a36Sopenharmony_ci return -EOPNOTSUPP; 241462306a36Sopenharmony_ci } 241562306a36Sopenharmony_ci return 0; 241662306a36Sopenharmony_ci} 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci/** 241962306a36Sopenharmony_ci * velocity_ioctl - ioctl entry point 242062306a36Sopenharmony_ci * @dev: network device 242162306a36Sopenharmony_ci * @rq: interface request ioctl 242262306a36Sopenharmony_ci * @cmd: command code 242362306a36Sopenharmony_ci * 242462306a36Sopenharmony_ci * Called when the user issues an ioctl request to the network 242562306a36Sopenharmony_ci * device in question. The velocity interface supports MII. 242662306a36Sopenharmony_ci */ 242762306a36Sopenharmony_cistatic int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 242862306a36Sopenharmony_ci{ 242962306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 243062306a36Sopenharmony_ci int ret; 243162306a36Sopenharmony_ci 243262306a36Sopenharmony_ci /* If we are asked for information and the device is power 243362306a36Sopenharmony_ci saving then we need to bring the device back up to talk to it */ 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci if (!netif_running(dev)) 243662306a36Sopenharmony_ci velocity_set_power_state(vptr, PCI_D0); 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci switch (cmd) { 243962306a36Sopenharmony_ci case SIOCGMIIPHY: /* Get address of MII PHY in use. */ 244062306a36Sopenharmony_ci case SIOCGMIIREG: /* Read MII PHY register. */ 244162306a36Sopenharmony_ci case SIOCSMIIREG: /* Write to MII PHY register. */ 244262306a36Sopenharmony_ci ret = velocity_mii_ioctl(dev, rq, cmd); 244362306a36Sopenharmony_ci break; 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci default: 244662306a36Sopenharmony_ci ret = -EOPNOTSUPP; 244762306a36Sopenharmony_ci } 244862306a36Sopenharmony_ci if (!netif_running(dev)) 244962306a36Sopenharmony_ci velocity_set_power_state(vptr, PCI_D3hot); 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci return ret; 245362306a36Sopenharmony_ci} 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci/** 245662306a36Sopenharmony_ci * velocity_get_stats - statistics callback 245762306a36Sopenharmony_ci * @dev: network device 245862306a36Sopenharmony_ci * 245962306a36Sopenharmony_ci * Callback from the network layer to allow driver statistics 246062306a36Sopenharmony_ci * to be resynchronized with hardware collected state. In the 246162306a36Sopenharmony_ci * case of the velocity we need to pull the MIB counters from 246262306a36Sopenharmony_ci * the hardware into the counters before letting the network 246362306a36Sopenharmony_ci * layer display them. 246462306a36Sopenharmony_ci */ 246562306a36Sopenharmony_cistatic struct net_device_stats *velocity_get_stats(struct net_device *dev) 246662306a36Sopenharmony_ci{ 246762306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci /* If the hardware is down, don't touch MII */ 247062306a36Sopenharmony_ci if (!netif_running(dev)) 247162306a36Sopenharmony_ci return &dev->stats; 247262306a36Sopenharmony_ci 247362306a36Sopenharmony_ci spin_lock_irq(&vptr->lock); 247462306a36Sopenharmony_ci velocity_update_hw_mibs(vptr); 247562306a36Sopenharmony_ci spin_unlock_irq(&vptr->lock); 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_ci dev->stats.rx_packets = vptr->mib_counter[HW_MIB_ifRxAllPkts]; 247862306a36Sopenharmony_ci dev->stats.rx_errors = vptr->mib_counter[HW_MIB_ifRxErrorPkts]; 247962306a36Sopenharmony_ci dev->stats.rx_length_errors = vptr->mib_counter[HW_MIB_ifInRangeLengthErrors]; 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci// unsigned long rx_dropped; /* no space in linux buffers */ 248262306a36Sopenharmony_ci dev->stats.collisions = vptr->mib_counter[HW_MIB_ifTxEtherCollisions]; 248362306a36Sopenharmony_ci /* detailed rx_errors: */ 248462306a36Sopenharmony_ci// unsigned long rx_length_errors; 248562306a36Sopenharmony_ci// unsigned long rx_over_errors; /* receiver ring buff overflow */ 248662306a36Sopenharmony_ci dev->stats.rx_crc_errors = vptr->mib_counter[HW_MIB_ifRxPktCRCE]; 248762306a36Sopenharmony_ci// unsigned long rx_frame_errors; /* recv'd frame alignment error */ 248862306a36Sopenharmony_ci// unsigned long rx_fifo_errors; /* recv'r fifo overrun */ 248962306a36Sopenharmony_ci// unsigned long rx_missed_errors; /* receiver missed packet */ 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci /* detailed tx_errors */ 249262306a36Sopenharmony_ci// unsigned long tx_fifo_errors; 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci return &dev->stats; 249562306a36Sopenharmony_ci} 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_ci/** 249862306a36Sopenharmony_ci * velocity_close - close adapter callback 249962306a36Sopenharmony_ci * @dev: network device 250062306a36Sopenharmony_ci * 250162306a36Sopenharmony_ci * Callback from the network layer when the velocity is being 250262306a36Sopenharmony_ci * deactivated by the network layer 250362306a36Sopenharmony_ci */ 250462306a36Sopenharmony_cistatic int velocity_close(struct net_device *dev) 250562306a36Sopenharmony_ci{ 250662306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 250762306a36Sopenharmony_ci 250862306a36Sopenharmony_ci napi_disable(&vptr->napi); 250962306a36Sopenharmony_ci netif_stop_queue(dev); 251062306a36Sopenharmony_ci velocity_shutdown(vptr); 251162306a36Sopenharmony_ci 251262306a36Sopenharmony_ci if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED) 251362306a36Sopenharmony_ci velocity_get_ip(vptr); 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci free_irq(dev->irq, dev); 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_ci velocity_free_rings(vptr); 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci vptr->flags &= (~VELOCITY_FLAGS_OPENED); 252062306a36Sopenharmony_ci return 0; 252162306a36Sopenharmony_ci} 252262306a36Sopenharmony_ci 252362306a36Sopenharmony_ci/** 252462306a36Sopenharmony_ci * velocity_xmit - transmit packet callback 252562306a36Sopenharmony_ci * @skb: buffer to transmit 252662306a36Sopenharmony_ci * @dev: network device 252762306a36Sopenharmony_ci * 252862306a36Sopenharmony_ci * Called by the network layer to request a packet is queued to 252962306a36Sopenharmony_ci * the velocity. Returns zero on success. 253062306a36Sopenharmony_ci */ 253162306a36Sopenharmony_cistatic netdev_tx_t velocity_xmit(struct sk_buff *skb, 253262306a36Sopenharmony_ci struct net_device *dev) 253362306a36Sopenharmony_ci{ 253462306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 253562306a36Sopenharmony_ci int qnum = 0; 253662306a36Sopenharmony_ci struct tx_desc *td_ptr; 253762306a36Sopenharmony_ci struct velocity_td_info *tdinfo; 253862306a36Sopenharmony_ci unsigned long flags; 253962306a36Sopenharmony_ci int pktlen; 254062306a36Sopenharmony_ci int index, prev; 254162306a36Sopenharmony_ci int i = 0; 254262306a36Sopenharmony_ci 254362306a36Sopenharmony_ci if (skb_padto(skb, ETH_ZLEN)) 254462306a36Sopenharmony_ci goto out; 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_ci /* The hardware can handle at most 7 memory segments, so merge 254762306a36Sopenharmony_ci * the skb if there are more */ 254862306a36Sopenharmony_ci if (skb_shinfo(skb)->nr_frags > 6 && __skb_linearize(skb)) { 254962306a36Sopenharmony_ci dev_kfree_skb_any(skb); 255062306a36Sopenharmony_ci return NETDEV_TX_OK; 255162306a36Sopenharmony_ci } 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci pktlen = skb_shinfo(skb)->nr_frags == 0 ? 255462306a36Sopenharmony_ci max_t(unsigned int, skb->len, ETH_ZLEN) : 255562306a36Sopenharmony_ci skb_headlen(skb); 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci spin_lock_irqsave(&vptr->lock, flags); 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci index = vptr->tx.curr[qnum]; 256062306a36Sopenharmony_ci td_ptr = &(vptr->tx.rings[qnum][index]); 256162306a36Sopenharmony_ci tdinfo = &(vptr->tx.infos[qnum][index]); 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci td_ptr->tdesc1.TCR = TCR0_TIC; 256462306a36Sopenharmony_ci td_ptr->td_buf[0].size &= ~TD_QUEUE; 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci /* 256762306a36Sopenharmony_ci * Map the linear network buffer into PCI space and 256862306a36Sopenharmony_ci * add it to the transmit ring. 256962306a36Sopenharmony_ci */ 257062306a36Sopenharmony_ci tdinfo->skb = skb; 257162306a36Sopenharmony_ci tdinfo->skb_dma[0] = dma_map_single(vptr->dev, skb->data, pktlen, 257262306a36Sopenharmony_ci DMA_TO_DEVICE); 257362306a36Sopenharmony_ci td_ptr->tdesc0.len = cpu_to_le16(pktlen); 257462306a36Sopenharmony_ci td_ptr->td_buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]); 257562306a36Sopenharmony_ci td_ptr->td_buf[0].pa_high = 0; 257662306a36Sopenharmony_ci td_ptr->td_buf[0].size = cpu_to_le16(pktlen); 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci /* Handle fragments */ 257962306a36Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 258062306a36Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci tdinfo->skb_dma[i + 1] = skb_frag_dma_map(vptr->dev, 258362306a36Sopenharmony_ci frag, 0, 258462306a36Sopenharmony_ci skb_frag_size(frag), 258562306a36Sopenharmony_ci DMA_TO_DEVICE); 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci td_ptr->td_buf[i + 1].pa_low = cpu_to_le32(tdinfo->skb_dma[i + 1]); 258862306a36Sopenharmony_ci td_ptr->td_buf[i + 1].pa_high = 0; 258962306a36Sopenharmony_ci td_ptr->td_buf[i + 1].size = cpu_to_le16(skb_frag_size(frag)); 259062306a36Sopenharmony_ci } 259162306a36Sopenharmony_ci tdinfo->nskb_dma = i + 1; 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci td_ptr->tdesc1.cmd = TCPLS_NORMAL + (tdinfo->nskb_dma + 1) * 16; 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 259662306a36Sopenharmony_ci td_ptr->tdesc1.vlan = cpu_to_le16(skb_vlan_tag_get(skb)); 259762306a36Sopenharmony_ci td_ptr->tdesc1.TCR |= TCR0_VETAG; 259862306a36Sopenharmony_ci } 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_ci /* 260162306a36Sopenharmony_ci * Handle hardware checksum 260262306a36Sopenharmony_ci */ 260362306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 260462306a36Sopenharmony_ci const struct iphdr *ip = ip_hdr(skb); 260562306a36Sopenharmony_ci if (ip->protocol == IPPROTO_TCP) 260662306a36Sopenharmony_ci td_ptr->tdesc1.TCR |= TCR0_TCPCK; 260762306a36Sopenharmony_ci else if (ip->protocol == IPPROTO_UDP) 260862306a36Sopenharmony_ci td_ptr->tdesc1.TCR |= (TCR0_UDPCK); 260962306a36Sopenharmony_ci td_ptr->tdesc1.TCR |= TCR0_IPCK; 261062306a36Sopenharmony_ci } 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci prev = index - 1; 261362306a36Sopenharmony_ci if (prev < 0) 261462306a36Sopenharmony_ci prev = vptr->options.numtx - 1; 261562306a36Sopenharmony_ci td_ptr->tdesc0.len |= OWNED_BY_NIC; 261662306a36Sopenharmony_ci vptr->tx.used[qnum]++; 261762306a36Sopenharmony_ci vptr->tx.curr[qnum] = (index + 1) % vptr->options.numtx; 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_ci if (AVAIL_TD(vptr, qnum) < 1) 262062306a36Sopenharmony_ci netif_stop_queue(dev); 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_ci td_ptr = &(vptr->tx.rings[qnum][prev]); 262362306a36Sopenharmony_ci td_ptr->td_buf[0].size |= TD_QUEUE; 262462306a36Sopenharmony_ci mac_tx_queue_wake(vptr->mac_regs, qnum); 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_ci spin_unlock_irqrestore(&vptr->lock, flags); 262762306a36Sopenharmony_ciout: 262862306a36Sopenharmony_ci return NETDEV_TX_OK; 262962306a36Sopenharmony_ci} 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_cistatic const struct net_device_ops velocity_netdev_ops = { 263262306a36Sopenharmony_ci .ndo_open = velocity_open, 263362306a36Sopenharmony_ci .ndo_stop = velocity_close, 263462306a36Sopenharmony_ci .ndo_start_xmit = velocity_xmit, 263562306a36Sopenharmony_ci .ndo_get_stats = velocity_get_stats, 263662306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 263762306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 263862306a36Sopenharmony_ci .ndo_set_rx_mode = velocity_set_multi, 263962306a36Sopenharmony_ci .ndo_change_mtu = velocity_change_mtu, 264062306a36Sopenharmony_ci .ndo_eth_ioctl = velocity_ioctl, 264162306a36Sopenharmony_ci .ndo_vlan_rx_add_vid = velocity_vlan_rx_add_vid, 264262306a36Sopenharmony_ci .ndo_vlan_rx_kill_vid = velocity_vlan_rx_kill_vid, 264362306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 264462306a36Sopenharmony_ci .ndo_poll_controller = velocity_poll_controller, 264562306a36Sopenharmony_ci#endif 264662306a36Sopenharmony_ci}; 264762306a36Sopenharmony_ci 264862306a36Sopenharmony_ci/** 264962306a36Sopenharmony_ci * velocity_init_info - init private data 265062306a36Sopenharmony_ci * @vptr: Velocity info 265162306a36Sopenharmony_ci * @info: Board type 265262306a36Sopenharmony_ci * 265362306a36Sopenharmony_ci * Set up the initial velocity_info struct for the device that has been 265462306a36Sopenharmony_ci * discovered. 265562306a36Sopenharmony_ci */ 265662306a36Sopenharmony_cistatic void velocity_init_info(struct velocity_info *vptr, 265762306a36Sopenharmony_ci const struct velocity_info_tbl *info) 265862306a36Sopenharmony_ci{ 265962306a36Sopenharmony_ci vptr->chip_id = info->chip_id; 266062306a36Sopenharmony_ci vptr->tx.numq = info->txqueue; 266162306a36Sopenharmony_ci vptr->multicast_limit = MCAM_SIZE; 266262306a36Sopenharmony_ci spin_lock_init(&vptr->lock); 266362306a36Sopenharmony_ci} 266462306a36Sopenharmony_ci 266562306a36Sopenharmony_ci/** 266662306a36Sopenharmony_ci * velocity_get_pci_info - retrieve PCI info for device 266762306a36Sopenharmony_ci * @vptr: velocity device 266862306a36Sopenharmony_ci * 266962306a36Sopenharmony_ci * Retrieve the PCI configuration space data that interests us from 267062306a36Sopenharmony_ci * the kernel PCI layer 267162306a36Sopenharmony_ci */ 267262306a36Sopenharmony_cistatic int velocity_get_pci_info(struct velocity_info *vptr) 267362306a36Sopenharmony_ci{ 267462306a36Sopenharmony_ci struct pci_dev *pdev = vptr->pdev; 267562306a36Sopenharmony_ci 267662306a36Sopenharmony_ci pci_set_master(pdev); 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_ci vptr->ioaddr = pci_resource_start(pdev, 0); 267962306a36Sopenharmony_ci vptr->memaddr = pci_resource_start(pdev, 1); 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci if (!(pci_resource_flags(pdev, 0) & IORESOURCE_IO)) { 268262306a36Sopenharmony_ci dev_err(&pdev->dev, 268362306a36Sopenharmony_ci "region #0 is not an I/O resource, aborting.\n"); 268462306a36Sopenharmony_ci return -EINVAL; 268562306a36Sopenharmony_ci } 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci if ((pci_resource_flags(pdev, 1) & IORESOURCE_IO)) { 268862306a36Sopenharmony_ci dev_err(&pdev->dev, 268962306a36Sopenharmony_ci "region #1 is an I/O resource, aborting.\n"); 269062306a36Sopenharmony_ci return -EINVAL; 269162306a36Sopenharmony_ci } 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci if (pci_resource_len(pdev, 1) < VELOCITY_IO_SIZE) { 269462306a36Sopenharmony_ci dev_err(&pdev->dev, "region #1 is too small.\n"); 269562306a36Sopenharmony_ci return -EINVAL; 269662306a36Sopenharmony_ci } 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ci return 0; 269962306a36Sopenharmony_ci} 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ci/** 270262306a36Sopenharmony_ci * velocity_get_platform_info - retrieve platform info for device 270362306a36Sopenharmony_ci * @vptr: velocity device 270462306a36Sopenharmony_ci * 270562306a36Sopenharmony_ci * Retrieve the Platform configuration data that interests us 270662306a36Sopenharmony_ci */ 270762306a36Sopenharmony_cistatic int velocity_get_platform_info(struct velocity_info *vptr) 270862306a36Sopenharmony_ci{ 270962306a36Sopenharmony_ci struct resource res; 271062306a36Sopenharmony_ci int ret; 271162306a36Sopenharmony_ci 271262306a36Sopenharmony_ci vptr->no_eeprom = of_property_read_bool(vptr->dev->of_node, "no-eeprom"); 271362306a36Sopenharmony_ci 271462306a36Sopenharmony_ci ret = of_address_to_resource(vptr->dev->of_node, 0, &res); 271562306a36Sopenharmony_ci if (ret) { 271662306a36Sopenharmony_ci dev_err(vptr->dev, "unable to find memory address\n"); 271762306a36Sopenharmony_ci return ret; 271862306a36Sopenharmony_ci } 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_ci vptr->memaddr = res.start; 272162306a36Sopenharmony_ci 272262306a36Sopenharmony_ci if (resource_size(&res) < VELOCITY_IO_SIZE) { 272362306a36Sopenharmony_ci dev_err(vptr->dev, "memory region is too small.\n"); 272462306a36Sopenharmony_ci return -EINVAL; 272562306a36Sopenharmony_ci } 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci return 0; 272862306a36Sopenharmony_ci} 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci/** 273162306a36Sopenharmony_ci * velocity_print_info - per driver data 273262306a36Sopenharmony_ci * @vptr: velocity 273362306a36Sopenharmony_ci * 273462306a36Sopenharmony_ci * Print per driver data as the kernel driver finds Velocity 273562306a36Sopenharmony_ci * hardware 273662306a36Sopenharmony_ci */ 273762306a36Sopenharmony_cistatic void velocity_print_info(struct velocity_info *vptr) 273862306a36Sopenharmony_ci{ 273962306a36Sopenharmony_ci netdev_info(vptr->netdev, "%s - Ethernet Address: %pM\n", 274062306a36Sopenharmony_ci get_chip_name(vptr->chip_id), vptr->netdev->dev_addr); 274162306a36Sopenharmony_ci} 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_cistatic u32 velocity_get_link(struct net_device *dev) 274462306a36Sopenharmony_ci{ 274562306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 274662306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 274762306a36Sopenharmony_ci return BYTE_REG_BITS_IS_ON(PHYSR0_LINKGD, ®s->PHYSR0) ? 1 : 0; 274862306a36Sopenharmony_ci} 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci/** 275162306a36Sopenharmony_ci * velocity_probe - set up discovered velocity device 275262306a36Sopenharmony_ci * @dev: PCI device 275362306a36Sopenharmony_ci * @info: table of match 275462306a36Sopenharmony_ci * @irq: interrupt info 275562306a36Sopenharmony_ci * @bustype: bus that device is connected to 275662306a36Sopenharmony_ci * 275762306a36Sopenharmony_ci * Configure a discovered adapter from scratch. Return a negative 275862306a36Sopenharmony_ci * errno error code on failure paths. 275962306a36Sopenharmony_ci */ 276062306a36Sopenharmony_cistatic int velocity_probe(struct device *dev, int irq, 276162306a36Sopenharmony_ci const struct velocity_info_tbl *info, 276262306a36Sopenharmony_ci enum velocity_bus_type bustype) 276362306a36Sopenharmony_ci{ 276462306a36Sopenharmony_ci struct net_device *netdev; 276562306a36Sopenharmony_ci int i; 276662306a36Sopenharmony_ci struct velocity_info *vptr; 276762306a36Sopenharmony_ci struct mac_regs __iomem *regs; 276862306a36Sopenharmony_ci int ret = -ENOMEM; 276962306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 277062306a36Sopenharmony_ci 277162306a36Sopenharmony_ci /* FIXME: this driver, like almost all other ethernet drivers, 277262306a36Sopenharmony_ci * can support more than MAX_UNITS. 277362306a36Sopenharmony_ci */ 277462306a36Sopenharmony_ci if (velocity_nics >= MAX_UNITS) { 277562306a36Sopenharmony_ci dev_notice(dev, "already found %d NICs.\n", velocity_nics); 277662306a36Sopenharmony_ci return -ENODEV; 277762306a36Sopenharmony_ci } 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_ci netdev = alloc_etherdev(sizeof(struct velocity_info)); 278062306a36Sopenharmony_ci if (!netdev) 278162306a36Sopenharmony_ci goto out; 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ci /* Chain it all together */ 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, dev); 278662306a36Sopenharmony_ci vptr = netdev_priv(netdev); 278762306a36Sopenharmony_ci 278862306a36Sopenharmony_ci pr_info_once("%s Ver. %s\n", VELOCITY_FULL_DRV_NAM, VELOCITY_VERSION); 278962306a36Sopenharmony_ci pr_info_once("Copyright (c) 2002, 2003 VIA Networking Technologies, Inc.\n"); 279062306a36Sopenharmony_ci pr_info_once("Copyright (c) 2004 Red Hat Inc.\n"); 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_ci netdev->irq = irq; 279362306a36Sopenharmony_ci vptr->netdev = netdev; 279462306a36Sopenharmony_ci vptr->dev = dev; 279562306a36Sopenharmony_ci 279662306a36Sopenharmony_ci velocity_init_info(vptr, info); 279762306a36Sopenharmony_ci 279862306a36Sopenharmony_ci if (bustype == BUS_PCI) { 279962306a36Sopenharmony_ci vptr->pdev = to_pci_dev(dev); 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci ret = velocity_get_pci_info(vptr); 280262306a36Sopenharmony_ci if (ret < 0) 280362306a36Sopenharmony_ci goto err_free_dev; 280462306a36Sopenharmony_ci } else { 280562306a36Sopenharmony_ci vptr->pdev = NULL; 280662306a36Sopenharmony_ci ret = velocity_get_platform_info(vptr); 280762306a36Sopenharmony_ci if (ret < 0) 280862306a36Sopenharmony_ci goto err_free_dev; 280962306a36Sopenharmony_ci } 281062306a36Sopenharmony_ci 281162306a36Sopenharmony_ci regs = ioremap(vptr->memaddr, VELOCITY_IO_SIZE); 281262306a36Sopenharmony_ci if (regs == NULL) { 281362306a36Sopenharmony_ci ret = -EIO; 281462306a36Sopenharmony_ci goto err_free_dev; 281562306a36Sopenharmony_ci } 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci vptr->mac_regs = regs; 281862306a36Sopenharmony_ci vptr->rev_id = readb(®s->rev_id); 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci mac_wol_reset(regs); 282162306a36Sopenharmony_ci 282262306a36Sopenharmony_ci for (i = 0; i < 6; i++) 282362306a36Sopenharmony_ci addr[i] = readb(®s->PAR[i]); 282462306a36Sopenharmony_ci eth_hw_addr_set(netdev, addr); 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci 282762306a36Sopenharmony_ci velocity_get_options(&vptr->options, velocity_nics); 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci /* 283062306a36Sopenharmony_ci * Mask out the options cannot be set to the chip 283162306a36Sopenharmony_ci */ 283262306a36Sopenharmony_ci 283362306a36Sopenharmony_ci vptr->options.flags &= info->flags; 283462306a36Sopenharmony_ci 283562306a36Sopenharmony_ci /* 283662306a36Sopenharmony_ci * Enable the chip specified capbilities 283762306a36Sopenharmony_ci */ 283862306a36Sopenharmony_ci 283962306a36Sopenharmony_ci vptr->flags = vptr->options.flags | (info->flags & 0xFF000000UL); 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci vptr->wol_opts = vptr->options.wol_opts; 284262306a36Sopenharmony_ci vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ci vptr->phy_id = MII_GET_PHY_ID(vptr->mac_regs); 284562306a36Sopenharmony_ci 284662306a36Sopenharmony_ci netdev->netdev_ops = &velocity_netdev_ops; 284762306a36Sopenharmony_ci netdev->ethtool_ops = &velocity_ethtool_ops; 284862306a36Sopenharmony_ci netif_napi_add(netdev, &vptr->napi, velocity_poll); 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_ci netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | 285162306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_TX; 285262306a36Sopenharmony_ci netdev->features |= NETIF_F_HW_VLAN_CTAG_TX | 285362306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX | 285462306a36Sopenharmony_ci NETIF_F_IP_CSUM; 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_ci /* MTU range: 64 - 9000 */ 285762306a36Sopenharmony_ci netdev->min_mtu = VELOCITY_MIN_MTU; 285862306a36Sopenharmony_ci netdev->max_mtu = VELOCITY_MAX_MTU; 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_ci ret = register_netdev(netdev); 286162306a36Sopenharmony_ci if (ret < 0) 286262306a36Sopenharmony_ci goto err_iounmap; 286362306a36Sopenharmony_ci 286462306a36Sopenharmony_ci if (!velocity_get_link(netdev)) { 286562306a36Sopenharmony_ci netif_carrier_off(netdev); 286662306a36Sopenharmony_ci vptr->mii_status |= VELOCITY_LINK_FAIL; 286762306a36Sopenharmony_ci } 286862306a36Sopenharmony_ci 286962306a36Sopenharmony_ci velocity_print_info(vptr); 287062306a36Sopenharmony_ci dev_set_drvdata(vptr->dev, netdev); 287162306a36Sopenharmony_ci 287262306a36Sopenharmony_ci /* and leave the chip powered down */ 287362306a36Sopenharmony_ci 287462306a36Sopenharmony_ci velocity_set_power_state(vptr, PCI_D3hot); 287562306a36Sopenharmony_ci velocity_nics++; 287662306a36Sopenharmony_ciout: 287762306a36Sopenharmony_ci return ret; 287862306a36Sopenharmony_ci 287962306a36Sopenharmony_cierr_iounmap: 288062306a36Sopenharmony_ci netif_napi_del(&vptr->napi); 288162306a36Sopenharmony_ci iounmap(regs); 288262306a36Sopenharmony_cierr_free_dev: 288362306a36Sopenharmony_ci free_netdev(netdev); 288462306a36Sopenharmony_ci goto out; 288562306a36Sopenharmony_ci} 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_ci/** 288862306a36Sopenharmony_ci * velocity_remove - device unplug 288962306a36Sopenharmony_ci * @dev: device being removed 289062306a36Sopenharmony_ci * 289162306a36Sopenharmony_ci * Device unload callback. Called on an unplug or on module 289262306a36Sopenharmony_ci * unload for each active device that is present. Disconnects 289362306a36Sopenharmony_ci * the device from the network layer and frees all the resources 289462306a36Sopenharmony_ci */ 289562306a36Sopenharmony_cistatic int velocity_remove(struct device *dev) 289662306a36Sopenharmony_ci{ 289762306a36Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(dev); 289862306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(netdev); 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_ci unregister_netdev(netdev); 290162306a36Sopenharmony_ci netif_napi_del(&vptr->napi); 290262306a36Sopenharmony_ci iounmap(vptr->mac_regs); 290362306a36Sopenharmony_ci free_netdev(netdev); 290462306a36Sopenharmony_ci velocity_nics--; 290562306a36Sopenharmony_ci 290662306a36Sopenharmony_ci return 0; 290762306a36Sopenharmony_ci} 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_cistatic int velocity_pci_probe(struct pci_dev *pdev, 291062306a36Sopenharmony_ci const struct pci_device_id *ent) 291162306a36Sopenharmony_ci{ 291262306a36Sopenharmony_ci const struct velocity_info_tbl *info = 291362306a36Sopenharmony_ci &chip_info_table[ent->driver_data]; 291462306a36Sopenharmony_ci int ret; 291562306a36Sopenharmony_ci 291662306a36Sopenharmony_ci ret = pci_enable_device(pdev); 291762306a36Sopenharmony_ci if (ret < 0) 291862306a36Sopenharmony_ci return ret; 291962306a36Sopenharmony_ci 292062306a36Sopenharmony_ci ret = pci_request_regions(pdev, VELOCITY_NAME); 292162306a36Sopenharmony_ci if (ret < 0) { 292262306a36Sopenharmony_ci dev_err(&pdev->dev, "No PCI resources.\n"); 292362306a36Sopenharmony_ci goto fail1; 292462306a36Sopenharmony_ci } 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_ci ret = velocity_probe(&pdev->dev, pdev->irq, info, BUS_PCI); 292762306a36Sopenharmony_ci if (ret == 0) 292862306a36Sopenharmony_ci return 0; 292962306a36Sopenharmony_ci 293062306a36Sopenharmony_ci pci_release_regions(pdev); 293162306a36Sopenharmony_cifail1: 293262306a36Sopenharmony_ci pci_disable_device(pdev); 293362306a36Sopenharmony_ci return ret; 293462306a36Sopenharmony_ci} 293562306a36Sopenharmony_ci 293662306a36Sopenharmony_cistatic void velocity_pci_remove(struct pci_dev *pdev) 293762306a36Sopenharmony_ci{ 293862306a36Sopenharmony_ci velocity_remove(&pdev->dev); 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci pci_release_regions(pdev); 294162306a36Sopenharmony_ci pci_disable_device(pdev); 294262306a36Sopenharmony_ci} 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_cistatic int velocity_platform_probe(struct platform_device *pdev) 294562306a36Sopenharmony_ci{ 294662306a36Sopenharmony_ci const struct velocity_info_tbl *info; 294762306a36Sopenharmony_ci int irq; 294862306a36Sopenharmony_ci 294962306a36Sopenharmony_ci info = of_device_get_match_data(&pdev->dev); 295062306a36Sopenharmony_ci if (!info) 295162306a36Sopenharmony_ci return -EINVAL; 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_ci irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 295462306a36Sopenharmony_ci if (!irq) 295562306a36Sopenharmony_ci return -EINVAL; 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_ci return velocity_probe(&pdev->dev, irq, info, BUS_PLATFORM); 295862306a36Sopenharmony_ci} 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_cistatic int velocity_platform_remove(struct platform_device *pdev) 296162306a36Sopenharmony_ci{ 296262306a36Sopenharmony_ci velocity_remove(&pdev->dev); 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci return 0; 296562306a36Sopenharmony_ci} 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 296862306a36Sopenharmony_ci/** 296962306a36Sopenharmony_ci * wol_calc_crc - WOL CRC 297062306a36Sopenharmony_ci * @size: size of the wake mask 297162306a36Sopenharmony_ci * @pattern: data pattern 297262306a36Sopenharmony_ci * @mask_pattern: mask 297362306a36Sopenharmony_ci * 297462306a36Sopenharmony_ci * Compute the wake on lan crc hashes for the packet header 297562306a36Sopenharmony_ci * we are interested in. 297662306a36Sopenharmony_ci */ 297762306a36Sopenharmony_cistatic u16 wol_calc_crc(int size, u8 *pattern, u8 *mask_pattern) 297862306a36Sopenharmony_ci{ 297962306a36Sopenharmony_ci u16 crc = 0xFFFF; 298062306a36Sopenharmony_ci u8 mask; 298162306a36Sopenharmony_ci int i, j; 298262306a36Sopenharmony_ci 298362306a36Sopenharmony_ci for (i = 0; i < size; i++) { 298462306a36Sopenharmony_ci mask = mask_pattern[i]; 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci /* Skip this loop if the mask equals to zero */ 298762306a36Sopenharmony_ci if (mask == 0x00) 298862306a36Sopenharmony_ci continue; 298962306a36Sopenharmony_ci 299062306a36Sopenharmony_ci for (j = 0; j < 8; j++) { 299162306a36Sopenharmony_ci if ((mask & 0x01) == 0) { 299262306a36Sopenharmony_ci mask >>= 1; 299362306a36Sopenharmony_ci continue; 299462306a36Sopenharmony_ci } 299562306a36Sopenharmony_ci mask >>= 1; 299662306a36Sopenharmony_ci crc = crc_ccitt(crc, &(pattern[i * 8 + j]), 1); 299762306a36Sopenharmony_ci } 299862306a36Sopenharmony_ci } 299962306a36Sopenharmony_ci /* Finally, invert the result once to get the correct data */ 300062306a36Sopenharmony_ci crc = ~crc; 300162306a36Sopenharmony_ci return bitrev32(crc) >> 16; 300262306a36Sopenharmony_ci} 300362306a36Sopenharmony_ci 300462306a36Sopenharmony_ci/** 300562306a36Sopenharmony_ci * velocity_set_wol - set up for wake on lan 300662306a36Sopenharmony_ci * @vptr: velocity to set WOL status on 300762306a36Sopenharmony_ci * 300862306a36Sopenharmony_ci * Set a card up for wake on lan either by unicast or by 300962306a36Sopenharmony_ci * ARP packet. 301062306a36Sopenharmony_ci * 301162306a36Sopenharmony_ci * FIXME: check static buffer is safe here 301262306a36Sopenharmony_ci */ 301362306a36Sopenharmony_cistatic int velocity_set_wol(struct velocity_info *vptr) 301462306a36Sopenharmony_ci{ 301562306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 301662306a36Sopenharmony_ci enum speed_opt spd_dpx = vptr->options.spd_dpx; 301762306a36Sopenharmony_ci static u8 buf[256]; 301862306a36Sopenharmony_ci int i; 301962306a36Sopenharmony_ci 302062306a36Sopenharmony_ci static u32 mask_pattern[2][4] = { 302162306a36Sopenharmony_ci {0x00203000, 0x000003C0, 0x00000000, 0x0000000}, /* ARP */ 302262306a36Sopenharmony_ci {0xfffff000, 0xffffffff, 0xffffffff, 0x000ffff} /* Magic Packet */ 302362306a36Sopenharmony_ci }; 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_ci writew(0xFFFF, ®s->WOLCRClr); 302662306a36Sopenharmony_ci writeb(WOLCFG_SAB | WOLCFG_SAM, ®s->WOLCFGSet); 302762306a36Sopenharmony_ci writew(WOLCR_MAGIC_EN, ®s->WOLCRSet); 302862306a36Sopenharmony_ci 302962306a36Sopenharmony_ci /* 303062306a36Sopenharmony_ci if (vptr->wol_opts & VELOCITY_WOL_PHY) 303162306a36Sopenharmony_ci writew((WOLCR_LINKON_EN|WOLCR_LINKOFF_EN), ®s->WOLCRSet); 303262306a36Sopenharmony_ci */ 303362306a36Sopenharmony_ci 303462306a36Sopenharmony_ci if (vptr->wol_opts & VELOCITY_WOL_UCAST) 303562306a36Sopenharmony_ci writew(WOLCR_UNICAST_EN, ®s->WOLCRSet); 303662306a36Sopenharmony_ci 303762306a36Sopenharmony_ci if (vptr->wol_opts & VELOCITY_WOL_ARP) { 303862306a36Sopenharmony_ci struct arp_packet *arp = (struct arp_packet *) buf; 303962306a36Sopenharmony_ci u16 crc; 304062306a36Sopenharmony_ci memset(buf, 0, sizeof(struct arp_packet) + 7); 304162306a36Sopenharmony_ci 304262306a36Sopenharmony_ci for (i = 0; i < 4; i++) 304362306a36Sopenharmony_ci writel(mask_pattern[0][i], ®s->ByteMask[0][i]); 304462306a36Sopenharmony_ci 304562306a36Sopenharmony_ci arp->type = htons(ETH_P_ARP); 304662306a36Sopenharmony_ci arp->ar_op = htons(1); 304762306a36Sopenharmony_ci 304862306a36Sopenharmony_ci memcpy(arp->ar_tip, vptr->ip_addr, 4); 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_ci crc = wol_calc_crc((sizeof(struct arp_packet) + 7) / 8, buf, 305162306a36Sopenharmony_ci (u8 *) & mask_pattern[0][0]); 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_ci writew(crc, ®s->PatternCRC[0]); 305462306a36Sopenharmony_ci writew(WOLCR_ARP_EN, ®s->WOLCRSet); 305562306a36Sopenharmony_ci } 305662306a36Sopenharmony_ci 305762306a36Sopenharmony_ci BYTE_REG_BITS_ON(PWCFG_WOLTYPE, ®s->PWCFGSet); 305862306a36Sopenharmony_ci BYTE_REG_BITS_ON(PWCFG_LEGACY_WOLEN, ®s->PWCFGSet); 305962306a36Sopenharmony_ci 306062306a36Sopenharmony_ci writew(0x0FFF, ®s->WOLSRClr); 306162306a36Sopenharmony_ci 306262306a36Sopenharmony_ci if (spd_dpx == SPD_DPX_1000_FULL) 306362306a36Sopenharmony_ci goto mac_done; 306462306a36Sopenharmony_ci 306562306a36Sopenharmony_ci if (spd_dpx != SPD_DPX_AUTO) 306662306a36Sopenharmony_ci goto advertise_done; 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_ci if (vptr->mii_status & VELOCITY_AUTONEG_ENABLE) { 306962306a36Sopenharmony_ci if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201) 307062306a36Sopenharmony_ci MII_REG_BITS_ON(AUXCR_MDPPS, MII_NCONFIG, vptr->mac_regs); 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_ci MII_REG_BITS_OFF(ADVERTISE_1000FULL | ADVERTISE_1000HALF, MII_CTRL1000, vptr->mac_regs); 307362306a36Sopenharmony_ci } 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci if (vptr->mii_status & VELOCITY_SPEED_1000) 307662306a36Sopenharmony_ci MII_REG_BITS_ON(BMCR_ANRESTART, MII_BMCR, vptr->mac_regs); 307762306a36Sopenharmony_ci 307862306a36Sopenharmony_ciadvertise_done: 307962306a36Sopenharmony_ci BYTE_REG_BITS_ON(CHIPGCR_FCMODE, ®s->CHIPGCR); 308062306a36Sopenharmony_ci 308162306a36Sopenharmony_ci { 308262306a36Sopenharmony_ci u8 GCR; 308362306a36Sopenharmony_ci GCR = readb(®s->CHIPGCR); 308462306a36Sopenharmony_ci GCR = (GCR & ~CHIPGCR_FCGMII) | CHIPGCR_FCFDX; 308562306a36Sopenharmony_ci writeb(GCR, ®s->CHIPGCR); 308662306a36Sopenharmony_ci } 308762306a36Sopenharmony_ci 308862306a36Sopenharmony_cimac_done: 308962306a36Sopenharmony_ci BYTE_REG_BITS_OFF(ISR_PWEI, ®s->ISR); 309062306a36Sopenharmony_ci /* Turn on SWPTAG just before entering power mode */ 309162306a36Sopenharmony_ci BYTE_REG_BITS_ON(STICKHW_SWPTAG, ®s->STICKHW); 309262306a36Sopenharmony_ci /* Go to bed ..... */ 309362306a36Sopenharmony_ci BYTE_REG_BITS_ON((STICKHW_DS1 | STICKHW_DS0), ®s->STICKHW); 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ci return 0; 309662306a36Sopenharmony_ci} 309762306a36Sopenharmony_ci 309862306a36Sopenharmony_ci/** 309962306a36Sopenharmony_ci * velocity_save_context - save registers 310062306a36Sopenharmony_ci * @vptr: velocity 310162306a36Sopenharmony_ci * @context: buffer for stored context 310262306a36Sopenharmony_ci * 310362306a36Sopenharmony_ci * Retrieve the current configuration from the velocity hardware 310462306a36Sopenharmony_ci * and stash it in the context structure, for use by the context 310562306a36Sopenharmony_ci * restore functions. This allows us to save things we need across 310662306a36Sopenharmony_ci * power down states 310762306a36Sopenharmony_ci */ 310862306a36Sopenharmony_cistatic void velocity_save_context(struct velocity_info *vptr, struct velocity_context *context) 310962306a36Sopenharmony_ci{ 311062306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 311162306a36Sopenharmony_ci u16 i; 311262306a36Sopenharmony_ci u8 __iomem *ptr = (u8 __iomem *)regs; 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_ci for (i = MAC_REG_PAR; i < MAC_REG_CR0_CLR; i += 4) 311562306a36Sopenharmony_ci *((u32 *) (context->mac_reg + i)) = readl(ptr + i); 311662306a36Sopenharmony_ci 311762306a36Sopenharmony_ci for (i = MAC_REG_MAR; i < MAC_REG_TDCSR_CLR; i += 4) 311862306a36Sopenharmony_ci *((u32 *) (context->mac_reg + i)) = readl(ptr + i); 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ci for (i = MAC_REG_RDBASE_LO; i < MAC_REG_FIFO_TEST0; i += 4) 312162306a36Sopenharmony_ci *((u32 *) (context->mac_reg + i)) = readl(ptr + i); 312262306a36Sopenharmony_ci 312362306a36Sopenharmony_ci} 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_cistatic int velocity_suspend(struct device *dev) 312662306a36Sopenharmony_ci{ 312762306a36Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(dev); 312862306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(netdev); 312962306a36Sopenharmony_ci unsigned long flags; 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_ci if (!netif_running(vptr->netdev)) 313262306a36Sopenharmony_ci return 0; 313362306a36Sopenharmony_ci 313462306a36Sopenharmony_ci netif_device_detach(vptr->netdev); 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci spin_lock_irqsave(&vptr->lock, flags); 313762306a36Sopenharmony_ci if (vptr->pdev) 313862306a36Sopenharmony_ci pci_save_state(vptr->pdev); 313962306a36Sopenharmony_ci 314062306a36Sopenharmony_ci if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED) { 314162306a36Sopenharmony_ci velocity_get_ip(vptr); 314262306a36Sopenharmony_ci velocity_save_context(vptr, &vptr->context); 314362306a36Sopenharmony_ci velocity_shutdown(vptr); 314462306a36Sopenharmony_ci velocity_set_wol(vptr); 314562306a36Sopenharmony_ci if (vptr->pdev) 314662306a36Sopenharmony_ci pci_enable_wake(vptr->pdev, PCI_D3hot, 1); 314762306a36Sopenharmony_ci velocity_set_power_state(vptr, PCI_D3hot); 314862306a36Sopenharmony_ci } else { 314962306a36Sopenharmony_ci velocity_save_context(vptr, &vptr->context); 315062306a36Sopenharmony_ci velocity_shutdown(vptr); 315162306a36Sopenharmony_ci if (vptr->pdev) 315262306a36Sopenharmony_ci pci_disable_device(vptr->pdev); 315362306a36Sopenharmony_ci velocity_set_power_state(vptr, PCI_D3hot); 315462306a36Sopenharmony_ci } 315562306a36Sopenharmony_ci 315662306a36Sopenharmony_ci spin_unlock_irqrestore(&vptr->lock, flags); 315762306a36Sopenharmony_ci return 0; 315862306a36Sopenharmony_ci} 315962306a36Sopenharmony_ci 316062306a36Sopenharmony_ci/** 316162306a36Sopenharmony_ci * velocity_restore_context - restore registers 316262306a36Sopenharmony_ci * @vptr: velocity 316362306a36Sopenharmony_ci * @context: buffer for stored context 316462306a36Sopenharmony_ci * 316562306a36Sopenharmony_ci * Reload the register configuration from the velocity context 316662306a36Sopenharmony_ci * created by velocity_save_context. 316762306a36Sopenharmony_ci */ 316862306a36Sopenharmony_cistatic void velocity_restore_context(struct velocity_info *vptr, struct velocity_context *context) 316962306a36Sopenharmony_ci{ 317062306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 317162306a36Sopenharmony_ci int i; 317262306a36Sopenharmony_ci u8 __iomem *ptr = (u8 __iomem *)regs; 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci for (i = MAC_REG_PAR; i < MAC_REG_CR0_SET; i += 4) 317562306a36Sopenharmony_ci writel(*((u32 *) (context->mac_reg + i)), ptr + i); 317662306a36Sopenharmony_ci 317762306a36Sopenharmony_ci /* Just skip cr0 */ 317862306a36Sopenharmony_ci for (i = MAC_REG_CR1_SET; i < MAC_REG_CR0_CLR; i++) { 317962306a36Sopenharmony_ci /* Clear */ 318062306a36Sopenharmony_ci writeb(~(*((u8 *) (context->mac_reg + i))), ptr + i + 4); 318162306a36Sopenharmony_ci /* Set */ 318262306a36Sopenharmony_ci writeb(*((u8 *) (context->mac_reg + i)), ptr + i); 318362306a36Sopenharmony_ci } 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_ci for (i = MAC_REG_MAR; i < MAC_REG_IMR; i += 4) 318662306a36Sopenharmony_ci writel(*((u32 *) (context->mac_reg + i)), ptr + i); 318762306a36Sopenharmony_ci 318862306a36Sopenharmony_ci for (i = MAC_REG_RDBASE_LO; i < MAC_REG_FIFO_TEST0; i += 4) 318962306a36Sopenharmony_ci writel(*((u32 *) (context->mac_reg + i)), ptr + i); 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_ci for (i = MAC_REG_TDCSR_SET; i <= MAC_REG_RDCSR_SET; i++) 319262306a36Sopenharmony_ci writeb(*((u8 *) (context->mac_reg + i)), ptr + i); 319362306a36Sopenharmony_ci} 319462306a36Sopenharmony_ci 319562306a36Sopenharmony_cistatic int velocity_resume(struct device *dev) 319662306a36Sopenharmony_ci{ 319762306a36Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(dev); 319862306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(netdev); 319962306a36Sopenharmony_ci unsigned long flags; 320062306a36Sopenharmony_ci int i; 320162306a36Sopenharmony_ci 320262306a36Sopenharmony_ci if (!netif_running(vptr->netdev)) 320362306a36Sopenharmony_ci return 0; 320462306a36Sopenharmony_ci 320562306a36Sopenharmony_ci velocity_set_power_state(vptr, PCI_D0); 320662306a36Sopenharmony_ci 320762306a36Sopenharmony_ci if (vptr->pdev) { 320862306a36Sopenharmony_ci pci_enable_wake(vptr->pdev, PCI_D0, 0); 320962306a36Sopenharmony_ci pci_restore_state(vptr->pdev); 321062306a36Sopenharmony_ci } 321162306a36Sopenharmony_ci 321262306a36Sopenharmony_ci mac_wol_reset(vptr->mac_regs); 321362306a36Sopenharmony_ci 321462306a36Sopenharmony_ci spin_lock_irqsave(&vptr->lock, flags); 321562306a36Sopenharmony_ci velocity_restore_context(vptr, &vptr->context); 321662306a36Sopenharmony_ci velocity_init_registers(vptr, VELOCITY_INIT_WOL); 321762306a36Sopenharmony_ci mac_disable_int(vptr->mac_regs); 321862306a36Sopenharmony_ci 321962306a36Sopenharmony_ci velocity_tx_srv(vptr); 322062306a36Sopenharmony_ci 322162306a36Sopenharmony_ci for (i = 0; i < vptr->tx.numq; i++) { 322262306a36Sopenharmony_ci if (vptr->tx.used[i]) 322362306a36Sopenharmony_ci mac_tx_queue_wake(vptr->mac_regs, i); 322462306a36Sopenharmony_ci } 322562306a36Sopenharmony_ci 322662306a36Sopenharmony_ci mac_enable_int(vptr->mac_regs); 322762306a36Sopenharmony_ci spin_unlock_irqrestore(&vptr->lock, flags); 322862306a36Sopenharmony_ci netif_device_attach(vptr->netdev); 322962306a36Sopenharmony_ci 323062306a36Sopenharmony_ci return 0; 323162306a36Sopenharmony_ci} 323262306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(velocity_pm_ops, velocity_suspend, velocity_resume); 323562306a36Sopenharmony_ci 323662306a36Sopenharmony_ci/* 323762306a36Sopenharmony_ci * Definition for our device driver. The PCI layer interface 323862306a36Sopenharmony_ci * uses this to handle all our card discover and plugging 323962306a36Sopenharmony_ci */ 324062306a36Sopenharmony_cistatic struct pci_driver velocity_pci_driver = { 324162306a36Sopenharmony_ci .name = VELOCITY_NAME, 324262306a36Sopenharmony_ci .id_table = velocity_pci_id_table, 324362306a36Sopenharmony_ci .probe = velocity_pci_probe, 324462306a36Sopenharmony_ci .remove = velocity_pci_remove, 324562306a36Sopenharmony_ci .driver = { 324662306a36Sopenharmony_ci .pm = &velocity_pm_ops, 324762306a36Sopenharmony_ci }, 324862306a36Sopenharmony_ci}; 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_cistatic struct platform_driver velocity_platform_driver = { 325162306a36Sopenharmony_ci .probe = velocity_platform_probe, 325262306a36Sopenharmony_ci .remove = velocity_platform_remove, 325362306a36Sopenharmony_ci .driver = { 325462306a36Sopenharmony_ci .name = "via-velocity", 325562306a36Sopenharmony_ci .of_match_table = velocity_of_ids, 325662306a36Sopenharmony_ci .pm = &velocity_pm_ops, 325762306a36Sopenharmony_ci }, 325862306a36Sopenharmony_ci}; 325962306a36Sopenharmony_ci 326062306a36Sopenharmony_ci/** 326162306a36Sopenharmony_ci * velocity_ethtool_up - pre hook for ethtool 326262306a36Sopenharmony_ci * @dev: network device 326362306a36Sopenharmony_ci * 326462306a36Sopenharmony_ci * Called before an ethtool operation. We need to make sure the 326562306a36Sopenharmony_ci * chip is out of D3 state before we poke at it. In case of ethtool 326662306a36Sopenharmony_ci * ops nesting, only wake the device up in the outermost block. 326762306a36Sopenharmony_ci */ 326862306a36Sopenharmony_cistatic int velocity_ethtool_up(struct net_device *dev) 326962306a36Sopenharmony_ci{ 327062306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 327162306a36Sopenharmony_ci 327262306a36Sopenharmony_ci if (vptr->ethtool_ops_nesting == U32_MAX) 327362306a36Sopenharmony_ci return -EBUSY; 327462306a36Sopenharmony_ci if (!vptr->ethtool_ops_nesting++ && !netif_running(dev)) 327562306a36Sopenharmony_ci velocity_set_power_state(vptr, PCI_D0); 327662306a36Sopenharmony_ci return 0; 327762306a36Sopenharmony_ci} 327862306a36Sopenharmony_ci 327962306a36Sopenharmony_ci/** 328062306a36Sopenharmony_ci * velocity_ethtool_down - post hook for ethtool 328162306a36Sopenharmony_ci * @dev: network device 328262306a36Sopenharmony_ci * 328362306a36Sopenharmony_ci * Called after an ethtool operation. Restore the chip back to D3 328462306a36Sopenharmony_ci * state if it isn't running. In case of ethtool ops nesting, only 328562306a36Sopenharmony_ci * put the device to sleep in the outermost block. 328662306a36Sopenharmony_ci */ 328762306a36Sopenharmony_cistatic void velocity_ethtool_down(struct net_device *dev) 328862306a36Sopenharmony_ci{ 328962306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_ci if (!--vptr->ethtool_ops_nesting && !netif_running(dev)) 329262306a36Sopenharmony_ci velocity_set_power_state(vptr, PCI_D3hot); 329362306a36Sopenharmony_ci} 329462306a36Sopenharmony_ci 329562306a36Sopenharmony_cistatic int velocity_get_link_ksettings(struct net_device *dev, 329662306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 329762306a36Sopenharmony_ci{ 329862306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 329962306a36Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 330062306a36Sopenharmony_ci u32 status; 330162306a36Sopenharmony_ci u32 supported, advertising; 330262306a36Sopenharmony_ci 330362306a36Sopenharmony_ci status = check_connection_type(vptr->mac_regs); 330462306a36Sopenharmony_ci 330562306a36Sopenharmony_ci supported = SUPPORTED_TP | 330662306a36Sopenharmony_ci SUPPORTED_Autoneg | 330762306a36Sopenharmony_ci SUPPORTED_10baseT_Half | 330862306a36Sopenharmony_ci SUPPORTED_10baseT_Full | 330962306a36Sopenharmony_ci SUPPORTED_100baseT_Half | 331062306a36Sopenharmony_ci SUPPORTED_100baseT_Full | 331162306a36Sopenharmony_ci SUPPORTED_1000baseT_Half | 331262306a36Sopenharmony_ci SUPPORTED_1000baseT_Full; 331362306a36Sopenharmony_ci 331462306a36Sopenharmony_ci advertising = ADVERTISED_TP | ADVERTISED_Autoneg; 331562306a36Sopenharmony_ci if (vptr->options.spd_dpx == SPD_DPX_AUTO) { 331662306a36Sopenharmony_ci advertising |= 331762306a36Sopenharmony_ci ADVERTISED_10baseT_Half | 331862306a36Sopenharmony_ci ADVERTISED_10baseT_Full | 331962306a36Sopenharmony_ci ADVERTISED_100baseT_Half | 332062306a36Sopenharmony_ci ADVERTISED_100baseT_Full | 332162306a36Sopenharmony_ci ADVERTISED_1000baseT_Half | 332262306a36Sopenharmony_ci ADVERTISED_1000baseT_Full; 332362306a36Sopenharmony_ci } else { 332462306a36Sopenharmony_ci switch (vptr->options.spd_dpx) { 332562306a36Sopenharmony_ci case SPD_DPX_1000_FULL: 332662306a36Sopenharmony_ci advertising |= ADVERTISED_1000baseT_Full; 332762306a36Sopenharmony_ci break; 332862306a36Sopenharmony_ci case SPD_DPX_100_HALF: 332962306a36Sopenharmony_ci advertising |= ADVERTISED_100baseT_Half; 333062306a36Sopenharmony_ci break; 333162306a36Sopenharmony_ci case SPD_DPX_100_FULL: 333262306a36Sopenharmony_ci advertising |= ADVERTISED_100baseT_Full; 333362306a36Sopenharmony_ci break; 333462306a36Sopenharmony_ci case SPD_DPX_10_HALF: 333562306a36Sopenharmony_ci advertising |= ADVERTISED_10baseT_Half; 333662306a36Sopenharmony_ci break; 333762306a36Sopenharmony_ci case SPD_DPX_10_FULL: 333862306a36Sopenharmony_ci advertising |= ADVERTISED_10baseT_Full; 333962306a36Sopenharmony_ci break; 334062306a36Sopenharmony_ci default: 334162306a36Sopenharmony_ci break; 334262306a36Sopenharmony_ci } 334362306a36Sopenharmony_ci } 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_ci if (status & VELOCITY_SPEED_1000) 334662306a36Sopenharmony_ci cmd->base.speed = SPEED_1000; 334762306a36Sopenharmony_ci else if (status & VELOCITY_SPEED_100) 334862306a36Sopenharmony_ci cmd->base.speed = SPEED_100; 334962306a36Sopenharmony_ci else 335062306a36Sopenharmony_ci cmd->base.speed = SPEED_10; 335162306a36Sopenharmony_ci 335262306a36Sopenharmony_ci cmd->base.autoneg = (status & VELOCITY_AUTONEG_ENABLE) ? 335362306a36Sopenharmony_ci AUTONEG_ENABLE : AUTONEG_DISABLE; 335462306a36Sopenharmony_ci cmd->base.port = PORT_TP; 335562306a36Sopenharmony_ci cmd->base.phy_address = readb(®s->MIIADR) & 0x1F; 335662306a36Sopenharmony_ci 335762306a36Sopenharmony_ci if (status & VELOCITY_DUPLEX_FULL) 335862306a36Sopenharmony_ci cmd->base.duplex = DUPLEX_FULL; 335962306a36Sopenharmony_ci else 336062306a36Sopenharmony_ci cmd->base.duplex = DUPLEX_HALF; 336162306a36Sopenharmony_ci 336262306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 336362306a36Sopenharmony_ci supported); 336462306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 336562306a36Sopenharmony_ci advertising); 336662306a36Sopenharmony_ci 336762306a36Sopenharmony_ci return 0; 336862306a36Sopenharmony_ci} 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_cistatic int velocity_set_link_ksettings(struct net_device *dev, 337162306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 337262306a36Sopenharmony_ci{ 337362306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 337462306a36Sopenharmony_ci u32 speed = cmd->base.speed; 337562306a36Sopenharmony_ci u32 curr_status; 337662306a36Sopenharmony_ci u32 new_status = 0; 337762306a36Sopenharmony_ci int ret = 0; 337862306a36Sopenharmony_ci 337962306a36Sopenharmony_ci curr_status = check_connection_type(vptr->mac_regs); 338062306a36Sopenharmony_ci curr_status &= (~VELOCITY_LINK_FAIL); 338162306a36Sopenharmony_ci 338262306a36Sopenharmony_ci new_status |= ((cmd->base.autoneg) ? VELOCITY_AUTONEG_ENABLE : 0); 338362306a36Sopenharmony_ci new_status |= ((speed == SPEED_1000) ? VELOCITY_SPEED_1000 : 0); 338462306a36Sopenharmony_ci new_status |= ((speed == SPEED_100) ? VELOCITY_SPEED_100 : 0); 338562306a36Sopenharmony_ci new_status |= ((speed == SPEED_10) ? VELOCITY_SPEED_10 : 0); 338662306a36Sopenharmony_ci new_status |= ((cmd->base.duplex == DUPLEX_FULL) ? 338762306a36Sopenharmony_ci VELOCITY_DUPLEX_FULL : 0); 338862306a36Sopenharmony_ci 338962306a36Sopenharmony_ci if ((new_status & VELOCITY_AUTONEG_ENABLE) && 339062306a36Sopenharmony_ci (new_status != (curr_status | VELOCITY_AUTONEG_ENABLE))) { 339162306a36Sopenharmony_ci ret = -EINVAL; 339262306a36Sopenharmony_ci } else { 339362306a36Sopenharmony_ci enum speed_opt spd_dpx; 339462306a36Sopenharmony_ci 339562306a36Sopenharmony_ci if (new_status & VELOCITY_AUTONEG_ENABLE) 339662306a36Sopenharmony_ci spd_dpx = SPD_DPX_AUTO; 339762306a36Sopenharmony_ci else if ((new_status & VELOCITY_SPEED_1000) && 339862306a36Sopenharmony_ci (new_status & VELOCITY_DUPLEX_FULL)) { 339962306a36Sopenharmony_ci spd_dpx = SPD_DPX_1000_FULL; 340062306a36Sopenharmony_ci } else if (new_status & VELOCITY_SPEED_100) 340162306a36Sopenharmony_ci spd_dpx = (new_status & VELOCITY_DUPLEX_FULL) ? 340262306a36Sopenharmony_ci SPD_DPX_100_FULL : SPD_DPX_100_HALF; 340362306a36Sopenharmony_ci else if (new_status & VELOCITY_SPEED_10) 340462306a36Sopenharmony_ci spd_dpx = (new_status & VELOCITY_DUPLEX_FULL) ? 340562306a36Sopenharmony_ci SPD_DPX_10_FULL : SPD_DPX_10_HALF; 340662306a36Sopenharmony_ci else 340762306a36Sopenharmony_ci return -EOPNOTSUPP; 340862306a36Sopenharmony_ci 340962306a36Sopenharmony_ci vptr->options.spd_dpx = spd_dpx; 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_ci velocity_set_media_mode(vptr, new_status); 341262306a36Sopenharmony_ci } 341362306a36Sopenharmony_ci 341462306a36Sopenharmony_ci return ret; 341562306a36Sopenharmony_ci} 341662306a36Sopenharmony_ci 341762306a36Sopenharmony_cistatic void velocity_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 341862306a36Sopenharmony_ci{ 341962306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 342062306a36Sopenharmony_ci 342162306a36Sopenharmony_ci strscpy(info->driver, VELOCITY_NAME, sizeof(info->driver)); 342262306a36Sopenharmony_ci strscpy(info->version, VELOCITY_VERSION, sizeof(info->version)); 342362306a36Sopenharmony_ci if (vptr->pdev) 342462306a36Sopenharmony_ci strscpy(info->bus_info, pci_name(vptr->pdev), 342562306a36Sopenharmony_ci sizeof(info->bus_info)); 342662306a36Sopenharmony_ci else 342762306a36Sopenharmony_ci strscpy(info->bus_info, "platform", sizeof(info->bus_info)); 342862306a36Sopenharmony_ci} 342962306a36Sopenharmony_ci 343062306a36Sopenharmony_cistatic void velocity_ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 343162306a36Sopenharmony_ci{ 343262306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 343362306a36Sopenharmony_ci wol->supported = WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP; 343462306a36Sopenharmony_ci wol->wolopts |= WAKE_MAGIC; 343562306a36Sopenharmony_ci /* 343662306a36Sopenharmony_ci if (vptr->wol_opts & VELOCITY_WOL_PHY) 343762306a36Sopenharmony_ci wol.wolopts|=WAKE_PHY; 343862306a36Sopenharmony_ci */ 343962306a36Sopenharmony_ci if (vptr->wol_opts & VELOCITY_WOL_UCAST) 344062306a36Sopenharmony_ci wol->wolopts |= WAKE_UCAST; 344162306a36Sopenharmony_ci if (vptr->wol_opts & VELOCITY_WOL_ARP) 344262306a36Sopenharmony_ci wol->wolopts |= WAKE_ARP; 344362306a36Sopenharmony_ci memcpy(&wol->sopass, vptr->wol_passwd, 6); 344462306a36Sopenharmony_ci} 344562306a36Sopenharmony_ci 344662306a36Sopenharmony_cistatic int velocity_ethtool_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 344762306a36Sopenharmony_ci{ 344862306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 344962306a36Sopenharmony_ci 345062306a36Sopenharmony_ci if (!(wol->wolopts & (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP))) 345162306a36Sopenharmony_ci return -EFAULT; 345262306a36Sopenharmony_ci vptr->wol_opts = VELOCITY_WOL_MAGIC; 345362306a36Sopenharmony_ci 345462306a36Sopenharmony_ci /* 345562306a36Sopenharmony_ci if (wol.wolopts & WAKE_PHY) { 345662306a36Sopenharmony_ci vptr->wol_opts|=VELOCITY_WOL_PHY; 345762306a36Sopenharmony_ci vptr->flags |=VELOCITY_FLAGS_WOL_ENABLED; 345862306a36Sopenharmony_ci } 345962306a36Sopenharmony_ci */ 346062306a36Sopenharmony_ci 346162306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) { 346262306a36Sopenharmony_ci vptr->wol_opts |= VELOCITY_WOL_MAGIC; 346362306a36Sopenharmony_ci vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; 346462306a36Sopenharmony_ci } 346562306a36Sopenharmony_ci if (wol->wolopts & WAKE_UCAST) { 346662306a36Sopenharmony_ci vptr->wol_opts |= VELOCITY_WOL_UCAST; 346762306a36Sopenharmony_ci vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; 346862306a36Sopenharmony_ci } 346962306a36Sopenharmony_ci if (wol->wolopts & WAKE_ARP) { 347062306a36Sopenharmony_ci vptr->wol_opts |= VELOCITY_WOL_ARP; 347162306a36Sopenharmony_ci vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; 347262306a36Sopenharmony_ci } 347362306a36Sopenharmony_ci memcpy(vptr->wol_passwd, wol->sopass, 6); 347462306a36Sopenharmony_ci return 0; 347562306a36Sopenharmony_ci} 347662306a36Sopenharmony_ci 347762306a36Sopenharmony_cistatic int get_pending_timer_val(int val) 347862306a36Sopenharmony_ci{ 347962306a36Sopenharmony_ci int mult_bits = val >> 6; 348062306a36Sopenharmony_ci int mult = 1; 348162306a36Sopenharmony_ci 348262306a36Sopenharmony_ci switch (mult_bits) 348362306a36Sopenharmony_ci { 348462306a36Sopenharmony_ci case 1: 348562306a36Sopenharmony_ci mult = 4; break; 348662306a36Sopenharmony_ci case 2: 348762306a36Sopenharmony_ci mult = 16; break; 348862306a36Sopenharmony_ci case 3: 348962306a36Sopenharmony_ci mult = 64; break; 349062306a36Sopenharmony_ci case 0: 349162306a36Sopenharmony_ci default: 349262306a36Sopenharmony_ci break; 349362306a36Sopenharmony_ci } 349462306a36Sopenharmony_ci 349562306a36Sopenharmony_ci return (val & 0x3f) * mult; 349662306a36Sopenharmony_ci} 349762306a36Sopenharmony_ci 349862306a36Sopenharmony_cistatic void set_pending_timer_val(int *val, u32 us) 349962306a36Sopenharmony_ci{ 350062306a36Sopenharmony_ci u8 mult = 0; 350162306a36Sopenharmony_ci u8 shift = 0; 350262306a36Sopenharmony_ci 350362306a36Sopenharmony_ci if (us >= 0x3f) { 350462306a36Sopenharmony_ci mult = 1; /* mult with 4 */ 350562306a36Sopenharmony_ci shift = 2; 350662306a36Sopenharmony_ci } 350762306a36Sopenharmony_ci if (us >= 0x3f * 4) { 350862306a36Sopenharmony_ci mult = 2; /* mult with 16 */ 350962306a36Sopenharmony_ci shift = 4; 351062306a36Sopenharmony_ci } 351162306a36Sopenharmony_ci if (us >= 0x3f * 16) { 351262306a36Sopenharmony_ci mult = 3; /* mult with 64 */ 351362306a36Sopenharmony_ci shift = 6; 351462306a36Sopenharmony_ci } 351562306a36Sopenharmony_ci 351662306a36Sopenharmony_ci *val = (mult << 6) | ((us >> shift) & 0x3f); 351762306a36Sopenharmony_ci} 351862306a36Sopenharmony_ci 351962306a36Sopenharmony_ci 352062306a36Sopenharmony_cistatic int velocity_get_coalesce(struct net_device *dev, 352162306a36Sopenharmony_ci struct ethtool_coalesce *ecmd, 352262306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 352362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 352462306a36Sopenharmony_ci{ 352562306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 352662306a36Sopenharmony_ci 352762306a36Sopenharmony_ci ecmd->tx_max_coalesced_frames = vptr->options.tx_intsup; 352862306a36Sopenharmony_ci ecmd->rx_max_coalesced_frames = vptr->options.rx_intsup; 352962306a36Sopenharmony_ci 353062306a36Sopenharmony_ci ecmd->rx_coalesce_usecs = get_pending_timer_val(vptr->options.rxqueue_timer); 353162306a36Sopenharmony_ci ecmd->tx_coalesce_usecs = get_pending_timer_val(vptr->options.txqueue_timer); 353262306a36Sopenharmony_ci 353362306a36Sopenharmony_ci return 0; 353462306a36Sopenharmony_ci} 353562306a36Sopenharmony_ci 353662306a36Sopenharmony_cistatic int velocity_set_coalesce(struct net_device *dev, 353762306a36Sopenharmony_ci struct ethtool_coalesce *ecmd, 353862306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 353962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 354062306a36Sopenharmony_ci{ 354162306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 354262306a36Sopenharmony_ci int max_us = 0x3f * 64; 354362306a36Sopenharmony_ci unsigned long flags; 354462306a36Sopenharmony_ci 354562306a36Sopenharmony_ci /* 6 bits of */ 354662306a36Sopenharmony_ci if (ecmd->tx_coalesce_usecs > max_us) 354762306a36Sopenharmony_ci return -EINVAL; 354862306a36Sopenharmony_ci if (ecmd->rx_coalesce_usecs > max_us) 354962306a36Sopenharmony_ci return -EINVAL; 355062306a36Sopenharmony_ci 355162306a36Sopenharmony_ci if (ecmd->tx_max_coalesced_frames > 0xff) 355262306a36Sopenharmony_ci return -EINVAL; 355362306a36Sopenharmony_ci if (ecmd->rx_max_coalesced_frames > 0xff) 355462306a36Sopenharmony_ci return -EINVAL; 355562306a36Sopenharmony_ci 355662306a36Sopenharmony_ci vptr->options.rx_intsup = ecmd->rx_max_coalesced_frames; 355762306a36Sopenharmony_ci vptr->options.tx_intsup = ecmd->tx_max_coalesced_frames; 355862306a36Sopenharmony_ci 355962306a36Sopenharmony_ci set_pending_timer_val(&vptr->options.rxqueue_timer, 356062306a36Sopenharmony_ci ecmd->rx_coalesce_usecs); 356162306a36Sopenharmony_ci set_pending_timer_val(&vptr->options.txqueue_timer, 356262306a36Sopenharmony_ci ecmd->tx_coalesce_usecs); 356362306a36Sopenharmony_ci 356462306a36Sopenharmony_ci /* Setup the interrupt suppression and queue timers */ 356562306a36Sopenharmony_ci spin_lock_irqsave(&vptr->lock, flags); 356662306a36Sopenharmony_ci mac_disable_int(vptr->mac_regs); 356762306a36Sopenharmony_ci setup_adaptive_interrupts(vptr); 356862306a36Sopenharmony_ci setup_queue_timers(vptr); 356962306a36Sopenharmony_ci 357062306a36Sopenharmony_ci mac_write_int_mask(vptr->int_mask, vptr->mac_regs); 357162306a36Sopenharmony_ci mac_clear_isr(vptr->mac_regs); 357262306a36Sopenharmony_ci mac_enable_int(vptr->mac_regs); 357362306a36Sopenharmony_ci spin_unlock_irqrestore(&vptr->lock, flags); 357462306a36Sopenharmony_ci 357562306a36Sopenharmony_ci return 0; 357662306a36Sopenharmony_ci} 357762306a36Sopenharmony_ci 357862306a36Sopenharmony_cistatic const char velocity_gstrings[][ETH_GSTRING_LEN] = { 357962306a36Sopenharmony_ci "rx_all", 358062306a36Sopenharmony_ci "rx_ok", 358162306a36Sopenharmony_ci "tx_ok", 358262306a36Sopenharmony_ci "rx_error", 358362306a36Sopenharmony_ci "rx_runt_ok", 358462306a36Sopenharmony_ci "rx_runt_err", 358562306a36Sopenharmony_ci "rx_64", 358662306a36Sopenharmony_ci "tx_64", 358762306a36Sopenharmony_ci "rx_65_to_127", 358862306a36Sopenharmony_ci "tx_65_to_127", 358962306a36Sopenharmony_ci "rx_128_to_255", 359062306a36Sopenharmony_ci "tx_128_to_255", 359162306a36Sopenharmony_ci "rx_256_to_511", 359262306a36Sopenharmony_ci "tx_256_to_511", 359362306a36Sopenharmony_ci "rx_512_to_1023", 359462306a36Sopenharmony_ci "tx_512_to_1023", 359562306a36Sopenharmony_ci "rx_1024_to_1518", 359662306a36Sopenharmony_ci "tx_1024_to_1518", 359762306a36Sopenharmony_ci "tx_ether_collisions", 359862306a36Sopenharmony_ci "rx_crc_errors", 359962306a36Sopenharmony_ci "rx_jumbo", 360062306a36Sopenharmony_ci "tx_jumbo", 360162306a36Sopenharmony_ci "rx_mac_control_frames", 360262306a36Sopenharmony_ci "tx_mac_control_frames", 360362306a36Sopenharmony_ci "rx_frame_alignment_errors", 360462306a36Sopenharmony_ci "rx_long_ok", 360562306a36Sopenharmony_ci "rx_long_err", 360662306a36Sopenharmony_ci "tx_sqe_errors", 360762306a36Sopenharmony_ci "rx_no_buf", 360862306a36Sopenharmony_ci "rx_symbol_errors", 360962306a36Sopenharmony_ci "in_range_length_errors", 361062306a36Sopenharmony_ci "late_collisions" 361162306a36Sopenharmony_ci}; 361262306a36Sopenharmony_ci 361362306a36Sopenharmony_cistatic void velocity_get_strings(struct net_device *dev, u32 sset, u8 *data) 361462306a36Sopenharmony_ci{ 361562306a36Sopenharmony_ci switch (sset) { 361662306a36Sopenharmony_ci case ETH_SS_STATS: 361762306a36Sopenharmony_ci memcpy(data, *velocity_gstrings, sizeof(velocity_gstrings)); 361862306a36Sopenharmony_ci break; 361962306a36Sopenharmony_ci } 362062306a36Sopenharmony_ci} 362162306a36Sopenharmony_ci 362262306a36Sopenharmony_cistatic int velocity_get_sset_count(struct net_device *dev, int sset) 362362306a36Sopenharmony_ci{ 362462306a36Sopenharmony_ci switch (sset) { 362562306a36Sopenharmony_ci case ETH_SS_STATS: 362662306a36Sopenharmony_ci return ARRAY_SIZE(velocity_gstrings); 362762306a36Sopenharmony_ci default: 362862306a36Sopenharmony_ci return -EOPNOTSUPP; 362962306a36Sopenharmony_ci } 363062306a36Sopenharmony_ci} 363162306a36Sopenharmony_ci 363262306a36Sopenharmony_cistatic void velocity_get_ethtool_stats(struct net_device *dev, 363362306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 363462306a36Sopenharmony_ci{ 363562306a36Sopenharmony_ci if (netif_running(dev)) { 363662306a36Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 363762306a36Sopenharmony_ci u32 *p = vptr->mib_counter; 363862306a36Sopenharmony_ci int i; 363962306a36Sopenharmony_ci 364062306a36Sopenharmony_ci spin_lock_irq(&vptr->lock); 364162306a36Sopenharmony_ci velocity_update_hw_mibs(vptr); 364262306a36Sopenharmony_ci spin_unlock_irq(&vptr->lock); 364362306a36Sopenharmony_ci 364462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(velocity_gstrings); i++) 364562306a36Sopenharmony_ci *data++ = *p++; 364662306a36Sopenharmony_ci } 364762306a36Sopenharmony_ci} 364862306a36Sopenharmony_ci 364962306a36Sopenharmony_cistatic const struct ethtool_ops velocity_ethtool_ops = { 365062306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 365162306a36Sopenharmony_ci ETHTOOL_COALESCE_MAX_FRAMES, 365262306a36Sopenharmony_ci .get_drvinfo = velocity_get_drvinfo, 365362306a36Sopenharmony_ci .get_wol = velocity_ethtool_get_wol, 365462306a36Sopenharmony_ci .set_wol = velocity_ethtool_set_wol, 365562306a36Sopenharmony_ci .get_link = velocity_get_link, 365662306a36Sopenharmony_ci .get_strings = velocity_get_strings, 365762306a36Sopenharmony_ci .get_sset_count = velocity_get_sset_count, 365862306a36Sopenharmony_ci .get_ethtool_stats = velocity_get_ethtool_stats, 365962306a36Sopenharmony_ci .get_coalesce = velocity_get_coalesce, 366062306a36Sopenharmony_ci .set_coalesce = velocity_set_coalesce, 366162306a36Sopenharmony_ci .begin = velocity_ethtool_up, 366262306a36Sopenharmony_ci .complete = velocity_ethtool_down, 366362306a36Sopenharmony_ci .get_link_ksettings = velocity_get_link_ksettings, 366462306a36Sopenharmony_ci .set_link_ksettings = velocity_set_link_ksettings, 366562306a36Sopenharmony_ci}; 366662306a36Sopenharmony_ci 366762306a36Sopenharmony_ci#if defined(CONFIG_PM) && defined(CONFIG_INET) 366862306a36Sopenharmony_cistatic int velocity_netdev_event(struct notifier_block *nb, unsigned long notification, void *ptr) 366962306a36Sopenharmony_ci{ 367062306a36Sopenharmony_ci struct in_ifaddr *ifa = ptr; 367162306a36Sopenharmony_ci struct net_device *dev = ifa->ifa_dev->dev; 367262306a36Sopenharmony_ci 367362306a36Sopenharmony_ci if (dev_net(dev) == &init_net && 367462306a36Sopenharmony_ci dev->netdev_ops == &velocity_netdev_ops) 367562306a36Sopenharmony_ci velocity_get_ip(netdev_priv(dev)); 367662306a36Sopenharmony_ci 367762306a36Sopenharmony_ci return NOTIFY_DONE; 367862306a36Sopenharmony_ci} 367962306a36Sopenharmony_ci 368062306a36Sopenharmony_cistatic struct notifier_block velocity_inetaddr_notifier = { 368162306a36Sopenharmony_ci .notifier_call = velocity_netdev_event, 368262306a36Sopenharmony_ci}; 368362306a36Sopenharmony_ci 368462306a36Sopenharmony_cistatic void velocity_register_notifier(void) 368562306a36Sopenharmony_ci{ 368662306a36Sopenharmony_ci register_inetaddr_notifier(&velocity_inetaddr_notifier); 368762306a36Sopenharmony_ci} 368862306a36Sopenharmony_ci 368962306a36Sopenharmony_cistatic void velocity_unregister_notifier(void) 369062306a36Sopenharmony_ci{ 369162306a36Sopenharmony_ci unregister_inetaddr_notifier(&velocity_inetaddr_notifier); 369262306a36Sopenharmony_ci} 369362306a36Sopenharmony_ci 369462306a36Sopenharmony_ci#else 369562306a36Sopenharmony_ci 369662306a36Sopenharmony_ci#define velocity_register_notifier() do {} while (0) 369762306a36Sopenharmony_ci#define velocity_unregister_notifier() do {} while (0) 369862306a36Sopenharmony_ci 369962306a36Sopenharmony_ci#endif /* defined(CONFIG_PM) && defined(CONFIG_INET) */ 370062306a36Sopenharmony_ci 370162306a36Sopenharmony_ci/** 370262306a36Sopenharmony_ci * velocity_init_module - load time function 370362306a36Sopenharmony_ci * 370462306a36Sopenharmony_ci * Called when the velocity module is loaded. The PCI driver 370562306a36Sopenharmony_ci * is registered with the PCI layer, and in turn will call 370662306a36Sopenharmony_ci * the probe functions for each velocity adapter installed 370762306a36Sopenharmony_ci * in the system. 370862306a36Sopenharmony_ci */ 370962306a36Sopenharmony_cistatic int __init velocity_init_module(void) 371062306a36Sopenharmony_ci{ 371162306a36Sopenharmony_ci int ret_pci, ret_platform; 371262306a36Sopenharmony_ci 371362306a36Sopenharmony_ci velocity_register_notifier(); 371462306a36Sopenharmony_ci 371562306a36Sopenharmony_ci ret_pci = pci_register_driver(&velocity_pci_driver); 371662306a36Sopenharmony_ci ret_platform = platform_driver_register(&velocity_platform_driver); 371762306a36Sopenharmony_ci 371862306a36Sopenharmony_ci /* if both_registers failed, remove the notifier */ 371962306a36Sopenharmony_ci if ((ret_pci < 0) && (ret_platform < 0)) { 372062306a36Sopenharmony_ci velocity_unregister_notifier(); 372162306a36Sopenharmony_ci return ret_pci; 372262306a36Sopenharmony_ci } 372362306a36Sopenharmony_ci 372462306a36Sopenharmony_ci return 0; 372562306a36Sopenharmony_ci} 372662306a36Sopenharmony_ci 372762306a36Sopenharmony_ci/** 372862306a36Sopenharmony_ci * velocity_cleanup_module - module unload 372962306a36Sopenharmony_ci * 373062306a36Sopenharmony_ci * When the velocity hardware is unloaded this function is called. 373162306a36Sopenharmony_ci * It will clean up the notifiers and the unregister the PCI 373262306a36Sopenharmony_ci * driver interface for this hardware. This in turn cleans up 373362306a36Sopenharmony_ci * all discovered interfaces before returning from the function 373462306a36Sopenharmony_ci */ 373562306a36Sopenharmony_cistatic void __exit velocity_cleanup_module(void) 373662306a36Sopenharmony_ci{ 373762306a36Sopenharmony_ci velocity_unregister_notifier(); 373862306a36Sopenharmony_ci 373962306a36Sopenharmony_ci pci_unregister_driver(&velocity_pci_driver); 374062306a36Sopenharmony_ci platform_driver_unregister(&velocity_platform_driver); 374162306a36Sopenharmony_ci} 374262306a36Sopenharmony_ci 374362306a36Sopenharmony_cimodule_init(velocity_init_module); 374462306a36Sopenharmony_cimodule_exit(velocity_cleanup_module); 3745