18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This code is derived from the VIA reference driver (copyright message 48c2ecf20Sopenharmony_ci * below) provided to Red Hat by VIA Networking Technologies, Inc. for 58c2ecf20Sopenharmony_ci * addition to the Linux kernel. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * The code has been merged into one source file, cleaned up to follow 88c2ecf20Sopenharmony_ci * Linux coding style, ported to the Linux 2.6 kernel tree and cleaned 98c2ecf20Sopenharmony_ci * for 64bit hardware platforms. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * TODO 128c2ecf20Sopenharmony_ci * rx_copybreak/alignment 138c2ecf20Sopenharmony_ci * More testing 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * The changes are (c) Copyright 2004, Red Hat Inc. <alan@lxorguk.ukuu.org.uk> 168c2ecf20Sopenharmony_ci * Additional fixes and clean up: Francois Romieu 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * This source has not been verified for use in safety critical systems. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Please direct queries about the revamped driver to the linux-kernel 218c2ecf20Sopenharmony_ci * list not VIA. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Original code: 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. 268c2ecf20Sopenharmony_ci * All rights reserved. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * Author: Chuang Liang-Shing, AJ Jiang 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * Date: Jan 24, 2003 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * MODULE_LICENSE("GPL"); 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <linux/module.h> 388c2ecf20Sopenharmony_ci#include <linux/types.h> 398c2ecf20Sopenharmony_ci#include <linux/bitops.h> 408c2ecf20Sopenharmony_ci#include <linux/init.h> 418c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 428c2ecf20Sopenharmony_ci#include <linux/mm.h> 438c2ecf20Sopenharmony_ci#include <linux/errno.h> 448c2ecf20Sopenharmony_ci#include <linux/ioport.h> 458c2ecf20Sopenharmony_ci#include <linux/pci.h> 468c2ecf20Sopenharmony_ci#include <linux/kernel.h> 478c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 488c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 498c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 508c2ecf20Sopenharmony_ci#include <linux/delay.h> 518c2ecf20Sopenharmony_ci#include <linux/timer.h> 528c2ecf20Sopenharmony_ci#include <linux/slab.h> 538c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 548c2ecf20Sopenharmony_ci#include <linux/string.h> 558c2ecf20Sopenharmony_ci#include <linux/wait.h> 568c2ecf20Sopenharmony_ci#include <linux/io.h> 578c2ecf20Sopenharmony_ci#include <linux/if.h> 588c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 598c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 608c2ecf20Sopenharmony_ci#include <linux/of_address.h> 618c2ecf20Sopenharmony_ci#include <linux/of_device.h> 628c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 638c2ecf20Sopenharmony_ci#include <linux/inetdevice.h> 648c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 658c2ecf20Sopenharmony_ci#include <linux/reboot.h> 668c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 678c2ecf20Sopenharmony_ci#include <linux/mii.h> 688c2ecf20Sopenharmony_ci#include <linux/in.h> 698c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 708c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 718c2ecf20Sopenharmony_ci#include <linux/ip.h> 728c2ecf20Sopenharmony_ci#include <linux/tcp.h> 738c2ecf20Sopenharmony_ci#include <linux/udp.h> 748c2ecf20Sopenharmony_ci#include <linux/crc-ccitt.h> 758c2ecf20Sopenharmony_ci#include <linux/crc32.h> 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#include "via-velocity.h" 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cienum velocity_bus_type { 808c2ecf20Sopenharmony_ci BUS_PCI, 818c2ecf20Sopenharmony_ci BUS_PLATFORM, 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int velocity_nics; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void velocity_set_power_state(struct velocity_info *vptr, char state) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci void *addr = vptr->mac_regs; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (vptr->pdev) 918c2ecf20Sopenharmony_ci pci_set_power_state(vptr->pdev, state); 928c2ecf20Sopenharmony_ci else 938c2ecf20Sopenharmony_ci writeb(state, addr + 0x154); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/** 978c2ecf20Sopenharmony_ci * mac_get_cam_mask - Read a CAM mask 988c2ecf20Sopenharmony_ci * @regs: register block for this velocity 998c2ecf20Sopenharmony_ci * @mask: buffer to store mask 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * Fetch the mask bits of the selected CAM and store them into the 1028c2ecf20Sopenharmony_ci * provided mask buffer. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistatic void mac_get_cam_mask(struct mac_regs __iomem *regs, u8 *mask) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci int i; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* Select CAM mask */ 1098c2ecf20Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci writeb(0, ®s->CAMADDR); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* read mask */ 1148c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 1158c2ecf20Sopenharmony_ci *mask++ = readb(&(regs->MARCAM[i])); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* disable CAMEN */ 1188c2ecf20Sopenharmony_ci writeb(0, ®s->CAMADDR); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Select mar */ 1218c2ecf20Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/** 1258c2ecf20Sopenharmony_ci * mac_set_cam_mask - Set a CAM mask 1268c2ecf20Sopenharmony_ci * @regs: register block for this velocity 1278c2ecf20Sopenharmony_ci * @mask: CAM mask to load 1288c2ecf20Sopenharmony_ci * 1298c2ecf20Sopenharmony_ci * Store a new mask into a CAM 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_cistatic void mac_set_cam_mask(struct mac_regs __iomem *regs, u8 *mask) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci int i; 1348c2ecf20Sopenharmony_ci /* Select CAM mask */ 1358c2ecf20Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci writeb(CAMADDR_CAMEN, ®s->CAMADDR); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 1408c2ecf20Sopenharmony_ci writeb(*mask++, &(regs->MARCAM[i])); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* disable CAMEN */ 1438c2ecf20Sopenharmony_ci writeb(0, ®s->CAMADDR); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* Select mar */ 1468c2ecf20Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic void mac_set_vlan_cam_mask(struct mac_regs __iomem *regs, u8 *mask) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci int i; 1528c2ecf20Sopenharmony_ci /* Select CAM mask */ 1538c2ecf20Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL, ®s->CAMADDR); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 1588c2ecf20Sopenharmony_ci writeb(*mask++, &(regs->MARCAM[i])); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* disable CAMEN */ 1618c2ecf20Sopenharmony_ci writeb(0, ®s->CAMADDR); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* Select mar */ 1648c2ecf20Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/** 1688c2ecf20Sopenharmony_ci * mac_set_cam - set CAM data 1698c2ecf20Sopenharmony_ci * @regs: register block of this velocity 1708c2ecf20Sopenharmony_ci * @idx: Cam index 1718c2ecf20Sopenharmony_ci * @addr: 2 or 6 bytes of CAM data 1728c2ecf20Sopenharmony_ci * 1738c2ecf20Sopenharmony_ci * Load an address or vlan tag into a CAM 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_cistatic void mac_set_cam(struct mac_regs __iomem *regs, int idx, const u8 *addr) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci int i; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Select CAM mask */ 1808c2ecf20Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci idx &= (64 - 1); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci writeb(CAMADDR_CAMEN | idx, ®s->CAMADDR); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 1878c2ecf20Sopenharmony_ci writeb(*addr++, &(regs->MARCAM[i])); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON(CAMCR_CAMWR, ®s->CAMCR); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci udelay(10); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci writeb(0, ®s->CAMADDR); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* Select mar */ 1968c2ecf20Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic void mac_set_vlan_cam(struct mac_regs __iomem *regs, int idx, 2008c2ecf20Sopenharmony_ci const u8 *addr) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* Select CAM mask */ 2048c2ecf20Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci idx &= (64 - 1); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL | idx, ®s->CAMADDR); 2098c2ecf20Sopenharmony_ci writew(*((u16 *) addr), ®s->MARCAM[0]); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON(CAMCR_CAMWR, ®s->CAMCR); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci udelay(10); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci writeb(0, ®s->CAMADDR); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* Select mar */ 2188c2ecf20Sopenharmony_ci BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci/** 2238c2ecf20Sopenharmony_ci * mac_wol_reset - reset WOL after exiting low power 2248c2ecf20Sopenharmony_ci * @regs: register block of this velocity 2258c2ecf20Sopenharmony_ci * 2268c2ecf20Sopenharmony_ci * Called after we drop out of wake on lan mode in order to 2278c2ecf20Sopenharmony_ci * reset the Wake on lan features. This function doesn't restore 2288c2ecf20Sopenharmony_ci * the rest of the logic from the result of sleep/wakeup 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_cistatic void mac_wol_reset(struct mac_regs __iomem *regs) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* Turn off SWPTAG right after leaving power mode */ 2348c2ecf20Sopenharmony_ci BYTE_REG_BITS_OFF(STICKHW_SWPTAG, ®s->STICKHW); 2358c2ecf20Sopenharmony_ci /* clear sticky bits */ 2368c2ecf20Sopenharmony_ci BYTE_REG_BITS_OFF((STICKHW_DS1 | STICKHW_DS0), ®s->STICKHW); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci BYTE_REG_BITS_OFF(CHIPGCR_FCGMII, ®s->CHIPGCR); 2398c2ecf20Sopenharmony_ci BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, ®s->CHIPGCR); 2408c2ecf20Sopenharmony_ci /* disable force PME-enable */ 2418c2ecf20Sopenharmony_ci writeb(WOLCFG_PMEOVR, ®s->WOLCFGClr); 2428c2ecf20Sopenharmony_ci /* disable power-event config bit */ 2438c2ecf20Sopenharmony_ci writew(0xFFFF, ®s->WOLCRClr); 2448c2ecf20Sopenharmony_ci /* clear power status */ 2458c2ecf20Sopenharmony_ci writew(0xFFFF, ®s->WOLSRClr); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic const struct ethtool_ops velocity_ethtool_ops; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/* 2518c2ecf20Sopenharmony_ci Define module options 2528c2ecf20Sopenharmony_ci*/ 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ciMODULE_AUTHOR("VIA Networking Technologies, Inc."); 2558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("VIA Networking Velocity Family Gigabit Ethernet Adapter Driver"); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci#define VELOCITY_PARAM(N, D) \ 2598c2ecf20Sopenharmony_ci static int N[MAX_UNITS] = OPTION_DEFAULT;\ 2608c2ecf20Sopenharmony_ci module_param_array(N, int, NULL, 0); \ 2618c2ecf20Sopenharmony_ci MODULE_PARM_DESC(N, D); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci#define RX_DESC_MIN 64 2648c2ecf20Sopenharmony_ci#define RX_DESC_MAX 255 2658c2ecf20Sopenharmony_ci#define RX_DESC_DEF 64 2668c2ecf20Sopenharmony_ciVELOCITY_PARAM(RxDescriptors, "Number of receive descriptors"); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci#define TX_DESC_MIN 16 2698c2ecf20Sopenharmony_ci#define TX_DESC_MAX 256 2708c2ecf20Sopenharmony_ci#define TX_DESC_DEF 64 2718c2ecf20Sopenharmony_ciVELOCITY_PARAM(TxDescriptors, "Number of transmit descriptors"); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci#define RX_THRESH_MIN 0 2748c2ecf20Sopenharmony_ci#define RX_THRESH_MAX 3 2758c2ecf20Sopenharmony_ci#define RX_THRESH_DEF 0 2768c2ecf20Sopenharmony_ci/* rx_thresh[] is used for controlling the receive fifo threshold. 2778c2ecf20Sopenharmony_ci 0: indicate the rxfifo threshold is 128 bytes. 2788c2ecf20Sopenharmony_ci 1: indicate the rxfifo threshold is 512 bytes. 2798c2ecf20Sopenharmony_ci 2: indicate the rxfifo threshold is 1024 bytes. 2808c2ecf20Sopenharmony_ci 3: indicate the rxfifo threshold is store & forward. 2818c2ecf20Sopenharmony_ci*/ 2828c2ecf20Sopenharmony_ciVELOCITY_PARAM(rx_thresh, "Receive fifo threshold"); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci#define DMA_LENGTH_MIN 0 2858c2ecf20Sopenharmony_ci#define DMA_LENGTH_MAX 7 2868c2ecf20Sopenharmony_ci#define DMA_LENGTH_DEF 6 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/* DMA_length[] is used for controlling the DMA length 2898c2ecf20Sopenharmony_ci 0: 8 DWORDs 2908c2ecf20Sopenharmony_ci 1: 16 DWORDs 2918c2ecf20Sopenharmony_ci 2: 32 DWORDs 2928c2ecf20Sopenharmony_ci 3: 64 DWORDs 2938c2ecf20Sopenharmony_ci 4: 128 DWORDs 2948c2ecf20Sopenharmony_ci 5: 256 DWORDs 2958c2ecf20Sopenharmony_ci 6: SF(flush till emply) 2968c2ecf20Sopenharmony_ci 7: SF(flush till emply) 2978c2ecf20Sopenharmony_ci*/ 2988c2ecf20Sopenharmony_ciVELOCITY_PARAM(DMA_length, "DMA length"); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci#define IP_ALIG_DEF 0 3018c2ecf20Sopenharmony_ci/* IP_byte_align[] is used for IP header DWORD byte aligned 3028c2ecf20Sopenharmony_ci 0: indicate the IP header won't be DWORD byte aligned.(Default) . 3038c2ecf20Sopenharmony_ci 1: indicate the IP header will be DWORD byte aligned. 3048c2ecf20Sopenharmony_ci In some environment, the IP header should be DWORD byte aligned, 3058c2ecf20Sopenharmony_ci or the packet will be droped when we receive it. (eg: IPVS) 3068c2ecf20Sopenharmony_ci*/ 3078c2ecf20Sopenharmony_ciVELOCITY_PARAM(IP_byte_align, "Enable IP header dword aligned"); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci#define FLOW_CNTL_DEF 1 3108c2ecf20Sopenharmony_ci#define FLOW_CNTL_MIN 1 3118c2ecf20Sopenharmony_ci#define FLOW_CNTL_MAX 5 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci/* flow_control[] is used for setting the flow control ability of NIC. 3148c2ecf20Sopenharmony_ci 1: hardware deafult - AUTO (default). Use Hardware default value in ANAR. 3158c2ecf20Sopenharmony_ci 2: enable TX flow control. 3168c2ecf20Sopenharmony_ci 3: enable RX flow control. 3178c2ecf20Sopenharmony_ci 4: enable RX/TX flow control. 3188c2ecf20Sopenharmony_ci 5: disable 3198c2ecf20Sopenharmony_ci*/ 3208c2ecf20Sopenharmony_ciVELOCITY_PARAM(flow_control, "Enable flow control ability"); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci#define MED_LNK_DEF 0 3238c2ecf20Sopenharmony_ci#define MED_LNK_MIN 0 3248c2ecf20Sopenharmony_ci#define MED_LNK_MAX 5 3258c2ecf20Sopenharmony_ci/* speed_duplex[] is used for setting the speed and duplex mode of NIC. 3268c2ecf20Sopenharmony_ci 0: indicate autonegotiation for both speed and duplex mode 3278c2ecf20Sopenharmony_ci 1: indicate 100Mbps half duplex mode 3288c2ecf20Sopenharmony_ci 2: indicate 100Mbps full duplex mode 3298c2ecf20Sopenharmony_ci 3: indicate 10Mbps half duplex mode 3308c2ecf20Sopenharmony_ci 4: indicate 10Mbps full duplex mode 3318c2ecf20Sopenharmony_ci 5: indicate 1000Mbps full duplex mode 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci Note: 3348c2ecf20Sopenharmony_ci if EEPROM have been set to the force mode, this option is ignored 3358c2ecf20Sopenharmony_ci by driver. 3368c2ecf20Sopenharmony_ci*/ 3378c2ecf20Sopenharmony_ciVELOCITY_PARAM(speed_duplex, "Setting the speed and duplex mode"); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci#define WOL_OPT_DEF 0 3408c2ecf20Sopenharmony_ci#define WOL_OPT_MIN 0 3418c2ecf20Sopenharmony_ci#define WOL_OPT_MAX 7 3428c2ecf20Sopenharmony_ci/* wol_opts[] is used for controlling wake on lan behavior. 3438c2ecf20Sopenharmony_ci 0: Wake up if recevied a magic packet. (Default) 3448c2ecf20Sopenharmony_ci 1: Wake up if link status is on/off. 3458c2ecf20Sopenharmony_ci 2: Wake up if recevied an arp packet. 3468c2ecf20Sopenharmony_ci 4: Wake up if recevied any unicast packet. 3478c2ecf20Sopenharmony_ci Those value can be sumed up to support more than one option. 3488c2ecf20Sopenharmony_ci*/ 3498c2ecf20Sopenharmony_ciVELOCITY_PARAM(wol_opts, "Wake On Lan options"); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int rx_copybreak = 200; 3528c2ecf20Sopenharmony_cimodule_param(rx_copybreak, int, 0644); 3538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames"); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/* 3568c2ecf20Sopenharmony_ci * Internal board variants. At the moment we have only one 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_cistatic struct velocity_info_tbl chip_info_table[] = { 3598c2ecf20Sopenharmony_ci {CHIP_TYPE_VT6110, "VIA Networking Velocity Family Gigabit Ethernet Adapter", 1, 0x00FFFFFFUL}, 3608c2ecf20Sopenharmony_ci { } 3618c2ecf20Sopenharmony_ci}; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci/* 3648c2ecf20Sopenharmony_ci * Describe the PCI device identifiers that we support in this 3658c2ecf20Sopenharmony_ci * device driver. Used for hotplug autoloading. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic const struct pci_device_id velocity_pci_id_table[] = { 3698c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_612X) }, 3708c2ecf20Sopenharmony_ci { } 3718c2ecf20Sopenharmony_ci}; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, velocity_pci_id_table); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci/* 3768c2ecf20Sopenharmony_ci * Describe the OF device identifiers that we support in this 3778c2ecf20Sopenharmony_ci * device driver. Used for devicetree nodes. 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_cistatic const struct of_device_id velocity_of_ids[] = { 3808c2ecf20Sopenharmony_ci { .compatible = "via,velocity-vt6110", .data = &chip_info_table[0] }, 3818c2ecf20Sopenharmony_ci { /* Sentinel */ }, 3828c2ecf20Sopenharmony_ci}; 3838c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, velocity_of_ids); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci/** 3868c2ecf20Sopenharmony_ci * get_chip_name - identifier to name 3878c2ecf20Sopenharmony_ci * @chip_id: chip identifier 3888c2ecf20Sopenharmony_ci * 3898c2ecf20Sopenharmony_ci * Given a chip identifier return a suitable description. Returns 3908c2ecf20Sopenharmony_ci * a pointer a static string valid while the driver is loaded. 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_cistatic const char *get_chip_name(enum chip_type chip_id) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci int i; 3958c2ecf20Sopenharmony_ci for (i = 0; chip_info_table[i].name != NULL; i++) 3968c2ecf20Sopenharmony_ci if (chip_info_table[i].chip_id == chip_id) 3978c2ecf20Sopenharmony_ci break; 3988c2ecf20Sopenharmony_ci return chip_info_table[i].name; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci/** 4028c2ecf20Sopenharmony_ci * velocity_set_int_opt - parser for integer options 4038c2ecf20Sopenharmony_ci * @opt: pointer to option value 4048c2ecf20Sopenharmony_ci * @val: value the user requested (or -1 for default) 4058c2ecf20Sopenharmony_ci * @min: lowest value allowed 4068c2ecf20Sopenharmony_ci * @max: highest value allowed 4078c2ecf20Sopenharmony_ci * @def: default value 4088c2ecf20Sopenharmony_ci * @name: property name 4098c2ecf20Sopenharmony_ci * 4108c2ecf20Sopenharmony_ci * Set an integer property in the module options. This function does 4118c2ecf20Sopenharmony_ci * all the verification and checking as well as reporting so that 4128c2ecf20Sopenharmony_ci * we don't duplicate code for each option. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_cistatic void velocity_set_int_opt(int *opt, int val, int min, int max, int def, 4158c2ecf20Sopenharmony_ci char *name) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci if (val == -1) 4188c2ecf20Sopenharmony_ci *opt = def; 4198c2ecf20Sopenharmony_ci else if (val < min || val > max) { 4208c2ecf20Sopenharmony_ci pr_notice("the value of parameter %s is invalid, the valid range is (%d-%d)\n", 4218c2ecf20Sopenharmony_ci name, min, max); 4228c2ecf20Sopenharmony_ci *opt = def; 4238c2ecf20Sopenharmony_ci } else { 4248c2ecf20Sopenharmony_ci pr_info("set value of parameter %s to %d\n", name, val); 4258c2ecf20Sopenharmony_ci *opt = val; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci/** 4308c2ecf20Sopenharmony_ci * velocity_set_bool_opt - parser for boolean options 4318c2ecf20Sopenharmony_ci * @opt: pointer to option value 4328c2ecf20Sopenharmony_ci * @val: value the user requested (or -1 for default) 4338c2ecf20Sopenharmony_ci * @def: default value (yes/no) 4348c2ecf20Sopenharmony_ci * @flag: numeric value to set for true. 4358c2ecf20Sopenharmony_ci * @name: property name 4368c2ecf20Sopenharmony_ci * 4378c2ecf20Sopenharmony_ci * Set a boolean property in the module options. This function does 4388c2ecf20Sopenharmony_ci * all the verification and checking as well as reporting so that 4398c2ecf20Sopenharmony_ci * we don't duplicate code for each option. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_cistatic void velocity_set_bool_opt(u32 *opt, int val, int def, u32 flag, 4428c2ecf20Sopenharmony_ci char *name) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci (*opt) &= (~flag); 4458c2ecf20Sopenharmony_ci if (val == -1) 4468c2ecf20Sopenharmony_ci *opt |= (def ? flag : 0); 4478c2ecf20Sopenharmony_ci else if (val < 0 || val > 1) { 4488c2ecf20Sopenharmony_ci pr_notice("the value of parameter %s is invalid, the valid range is (%d-%d)\n", 4498c2ecf20Sopenharmony_ci name, 0, 1); 4508c2ecf20Sopenharmony_ci *opt |= (def ? flag : 0); 4518c2ecf20Sopenharmony_ci } else { 4528c2ecf20Sopenharmony_ci pr_info("set parameter %s to %s\n", 4538c2ecf20Sopenharmony_ci name, val ? "TRUE" : "FALSE"); 4548c2ecf20Sopenharmony_ci *opt |= (val ? flag : 0); 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci/** 4598c2ecf20Sopenharmony_ci * velocity_get_options - set options on device 4608c2ecf20Sopenharmony_ci * @opts: option structure for the device 4618c2ecf20Sopenharmony_ci * @index: index of option to use in module options array 4628c2ecf20Sopenharmony_ci * 4638c2ecf20Sopenharmony_ci * Turn the module and command options into a single structure 4648c2ecf20Sopenharmony_ci * for the current device 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_cistatic void velocity_get_options(struct velocity_opt *opts, int index) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci velocity_set_int_opt(&opts->rx_thresh, rx_thresh[index], 4708c2ecf20Sopenharmony_ci RX_THRESH_MIN, RX_THRESH_MAX, RX_THRESH_DEF, 4718c2ecf20Sopenharmony_ci "rx_thresh"); 4728c2ecf20Sopenharmony_ci velocity_set_int_opt(&opts->DMA_length, DMA_length[index], 4738c2ecf20Sopenharmony_ci DMA_LENGTH_MIN, DMA_LENGTH_MAX, DMA_LENGTH_DEF, 4748c2ecf20Sopenharmony_ci "DMA_length"); 4758c2ecf20Sopenharmony_ci velocity_set_int_opt(&opts->numrx, RxDescriptors[index], 4768c2ecf20Sopenharmony_ci RX_DESC_MIN, RX_DESC_MAX, RX_DESC_DEF, 4778c2ecf20Sopenharmony_ci "RxDescriptors"); 4788c2ecf20Sopenharmony_ci velocity_set_int_opt(&opts->numtx, TxDescriptors[index], 4798c2ecf20Sopenharmony_ci TX_DESC_MIN, TX_DESC_MAX, TX_DESC_DEF, 4808c2ecf20Sopenharmony_ci "TxDescriptors"); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci velocity_set_int_opt(&opts->flow_cntl, flow_control[index], 4838c2ecf20Sopenharmony_ci FLOW_CNTL_MIN, FLOW_CNTL_MAX, FLOW_CNTL_DEF, 4848c2ecf20Sopenharmony_ci "flow_control"); 4858c2ecf20Sopenharmony_ci velocity_set_bool_opt(&opts->flags, IP_byte_align[index], 4868c2ecf20Sopenharmony_ci IP_ALIG_DEF, VELOCITY_FLAGS_IP_ALIGN, 4878c2ecf20Sopenharmony_ci "IP_byte_align"); 4888c2ecf20Sopenharmony_ci velocity_set_int_opt((int *) &opts->spd_dpx, speed_duplex[index], 4898c2ecf20Sopenharmony_ci MED_LNK_MIN, MED_LNK_MAX, MED_LNK_DEF, 4908c2ecf20Sopenharmony_ci "Media link mode"); 4918c2ecf20Sopenharmony_ci velocity_set_int_opt(&opts->wol_opts, wol_opts[index], 4928c2ecf20Sopenharmony_ci WOL_OPT_MIN, WOL_OPT_MAX, WOL_OPT_DEF, 4938c2ecf20Sopenharmony_ci "Wake On Lan options"); 4948c2ecf20Sopenharmony_ci opts->numrx = (opts->numrx & ~3); 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci/** 4988c2ecf20Sopenharmony_ci * velocity_init_cam_filter - initialise CAM 4998c2ecf20Sopenharmony_ci * @vptr: velocity to program 5008c2ecf20Sopenharmony_ci * 5018c2ecf20Sopenharmony_ci * Initialize the content addressable memory used for filters. Load 5028c2ecf20Sopenharmony_ci * appropriately according to the presence of VLAN 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_cistatic void velocity_init_cam_filter(struct velocity_info *vptr) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 5078c2ecf20Sopenharmony_ci unsigned int vid, i = 0; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* Turn on MCFG_PQEN, turn off MCFG_RTGOPT */ 5108c2ecf20Sopenharmony_ci WORD_REG_BITS_SET(MCFG_PQEN, MCFG_RTGOPT, ®s->MCFG); 5118c2ecf20Sopenharmony_ci WORD_REG_BITS_ON(MCFG_VIDFR, ®s->MCFG); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* Disable all CAMs */ 5148c2ecf20Sopenharmony_ci memset(vptr->vCAMmask, 0, sizeof(u8) * 8); 5158c2ecf20Sopenharmony_ci memset(vptr->mCAMmask, 0, sizeof(u8) * 8); 5168c2ecf20Sopenharmony_ci mac_set_vlan_cam_mask(regs, vptr->vCAMmask); 5178c2ecf20Sopenharmony_ci mac_set_cam_mask(regs, vptr->mCAMmask); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* Enable VCAMs */ 5208c2ecf20Sopenharmony_ci for_each_set_bit(vid, vptr->active_vlans, VLAN_N_VID) { 5218c2ecf20Sopenharmony_ci mac_set_vlan_cam(regs, i, (u8 *) &vid); 5228c2ecf20Sopenharmony_ci vptr->vCAMmask[i / 8] |= 0x1 << (i % 8); 5238c2ecf20Sopenharmony_ci if (++i >= VCAM_SIZE) 5248c2ecf20Sopenharmony_ci break; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci mac_set_vlan_cam_mask(regs, vptr->vCAMmask); 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic int velocity_vlan_rx_add_vid(struct net_device *dev, 5308c2ecf20Sopenharmony_ci __be16 proto, u16 vid) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci spin_lock_irq(&vptr->lock); 5358c2ecf20Sopenharmony_ci set_bit(vid, vptr->active_vlans); 5368c2ecf20Sopenharmony_ci velocity_init_cam_filter(vptr); 5378c2ecf20Sopenharmony_ci spin_unlock_irq(&vptr->lock); 5388c2ecf20Sopenharmony_ci return 0; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic int velocity_vlan_rx_kill_vid(struct net_device *dev, 5428c2ecf20Sopenharmony_ci __be16 proto, u16 vid) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci spin_lock_irq(&vptr->lock); 5478c2ecf20Sopenharmony_ci clear_bit(vid, vptr->active_vlans); 5488c2ecf20Sopenharmony_ci velocity_init_cam_filter(vptr); 5498c2ecf20Sopenharmony_ci spin_unlock_irq(&vptr->lock); 5508c2ecf20Sopenharmony_ci return 0; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic void velocity_init_rx_ring_indexes(struct velocity_info *vptr) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci vptr->rx.dirty = vptr->rx.filled = vptr->rx.curr = 0; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci/** 5598c2ecf20Sopenharmony_ci * velocity_rx_reset - handle a receive reset 5608c2ecf20Sopenharmony_ci * @vptr: velocity we are resetting 5618c2ecf20Sopenharmony_ci * 5628c2ecf20Sopenharmony_ci * Reset the ownership and status for the receive ring side. 5638c2ecf20Sopenharmony_ci * Hand all the receive queue to the NIC. 5648c2ecf20Sopenharmony_ci */ 5658c2ecf20Sopenharmony_cistatic void velocity_rx_reset(struct velocity_info *vptr) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 5698c2ecf20Sopenharmony_ci int i; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci velocity_init_rx_ring_indexes(vptr); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* 5748c2ecf20Sopenharmony_ci * Init state, all RD entries belong to the NIC 5758c2ecf20Sopenharmony_ci */ 5768c2ecf20Sopenharmony_ci for (i = 0; i < vptr->options.numrx; ++i) 5778c2ecf20Sopenharmony_ci vptr->rx.ring[i].rdesc0.len |= OWNED_BY_NIC; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci writew(vptr->options.numrx, ®s->RBRDU); 5808c2ecf20Sopenharmony_ci writel(vptr->rx.pool_dma, ®s->RDBaseLo); 5818c2ecf20Sopenharmony_ci writew(0, ®s->RDIdx); 5828c2ecf20Sopenharmony_ci writew(vptr->options.numrx - 1, ®s->RDCSize); 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci/** 5868c2ecf20Sopenharmony_ci * velocity_get_opt_media_mode - get media selection 5878c2ecf20Sopenharmony_ci * @vptr: velocity adapter 5888c2ecf20Sopenharmony_ci * 5898c2ecf20Sopenharmony_ci * Get the media mode stored in EEPROM or module options and load 5908c2ecf20Sopenharmony_ci * mii_status accordingly. The requested link state information 5918c2ecf20Sopenharmony_ci * is also returned. 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_cistatic u32 velocity_get_opt_media_mode(struct velocity_info *vptr) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci u32 status = 0; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci switch (vptr->options.spd_dpx) { 5988c2ecf20Sopenharmony_ci case SPD_DPX_AUTO: 5998c2ecf20Sopenharmony_ci status = VELOCITY_AUTONEG_ENABLE; 6008c2ecf20Sopenharmony_ci break; 6018c2ecf20Sopenharmony_ci case SPD_DPX_100_FULL: 6028c2ecf20Sopenharmony_ci status = VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL; 6038c2ecf20Sopenharmony_ci break; 6048c2ecf20Sopenharmony_ci case SPD_DPX_10_FULL: 6058c2ecf20Sopenharmony_ci status = VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL; 6068c2ecf20Sopenharmony_ci break; 6078c2ecf20Sopenharmony_ci case SPD_DPX_100_HALF: 6088c2ecf20Sopenharmony_ci status = VELOCITY_SPEED_100; 6098c2ecf20Sopenharmony_ci break; 6108c2ecf20Sopenharmony_ci case SPD_DPX_10_HALF: 6118c2ecf20Sopenharmony_ci status = VELOCITY_SPEED_10; 6128c2ecf20Sopenharmony_ci break; 6138c2ecf20Sopenharmony_ci case SPD_DPX_1000_FULL: 6148c2ecf20Sopenharmony_ci status = VELOCITY_SPEED_1000 | VELOCITY_DUPLEX_FULL; 6158c2ecf20Sopenharmony_ci break; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci vptr->mii_status = status; 6188c2ecf20Sopenharmony_ci return status; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci/** 6228c2ecf20Sopenharmony_ci * safe_disable_mii_autopoll - autopoll off 6238c2ecf20Sopenharmony_ci * @regs: velocity registers 6248c2ecf20Sopenharmony_ci * 6258c2ecf20Sopenharmony_ci * Turn off the autopoll and wait for it to disable on the chip 6268c2ecf20Sopenharmony_ci */ 6278c2ecf20Sopenharmony_cistatic void safe_disable_mii_autopoll(struct mac_regs __iomem *regs) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci u16 ww; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci /* turn off MAUTO */ 6328c2ecf20Sopenharmony_ci writeb(0, ®s->MIICR); 6338c2ecf20Sopenharmony_ci for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { 6348c2ecf20Sopenharmony_ci udelay(1); 6358c2ecf20Sopenharmony_ci if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci/** 6418c2ecf20Sopenharmony_ci * enable_mii_autopoll - turn on autopolling 6428c2ecf20Sopenharmony_ci * @regs: velocity registers 6438c2ecf20Sopenharmony_ci * 6448c2ecf20Sopenharmony_ci * Enable the MII link status autopoll feature on the Velocity 6458c2ecf20Sopenharmony_ci * hardware. Wait for it to enable. 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_cistatic void enable_mii_autopoll(struct mac_regs __iomem *regs) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci int ii; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci writeb(0, &(regs->MIICR)); 6528c2ecf20Sopenharmony_ci writeb(MIIADR_SWMPL, ®s->MIIADR); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { 6558c2ecf20Sopenharmony_ci udelay(1); 6568c2ecf20Sopenharmony_ci if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) 6578c2ecf20Sopenharmony_ci break; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci writeb(MIICR_MAUTO, ®s->MIICR); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { 6638c2ecf20Sopenharmony_ci udelay(1); 6648c2ecf20Sopenharmony_ci if (!BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) 6658c2ecf20Sopenharmony_ci break; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci/** 6718c2ecf20Sopenharmony_ci * velocity_mii_read - read MII data 6728c2ecf20Sopenharmony_ci * @regs: velocity registers 6738c2ecf20Sopenharmony_ci * @index: MII register index 6748c2ecf20Sopenharmony_ci * @data: buffer for received data 6758c2ecf20Sopenharmony_ci * 6768c2ecf20Sopenharmony_ci * Perform a single read of an MII 16bit register. Returns zero 6778c2ecf20Sopenharmony_ci * on success or -ETIMEDOUT if the PHY did not respond. 6788c2ecf20Sopenharmony_ci */ 6798c2ecf20Sopenharmony_cistatic int velocity_mii_read(struct mac_regs __iomem *regs, u8 index, u16 *data) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci u16 ww; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci /* 6848c2ecf20Sopenharmony_ci * Disable MIICR_MAUTO, so that mii addr can be set normally 6858c2ecf20Sopenharmony_ci */ 6868c2ecf20Sopenharmony_ci safe_disable_mii_autopoll(regs); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci writeb(index, ®s->MIIADR); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON(MIICR_RCMD, ®s->MIICR); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { 6938c2ecf20Sopenharmony_ci if (!(readb(®s->MIICR) & MIICR_RCMD)) 6948c2ecf20Sopenharmony_ci break; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci *data = readw(®s->MIIDATA); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci enable_mii_autopoll(regs); 7008c2ecf20Sopenharmony_ci if (ww == W_MAX_TIMEOUT) 7018c2ecf20Sopenharmony_ci return -ETIMEDOUT; 7028c2ecf20Sopenharmony_ci return 0; 7038c2ecf20Sopenharmony_ci} 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci/** 7068c2ecf20Sopenharmony_ci * mii_check_media_mode - check media state 7078c2ecf20Sopenharmony_ci * @regs: velocity registers 7088c2ecf20Sopenharmony_ci * 7098c2ecf20Sopenharmony_ci * Check the current MII status and determine the link status 7108c2ecf20Sopenharmony_ci * accordingly 7118c2ecf20Sopenharmony_ci */ 7128c2ecf20Sopenharmony_cistatic u32 mii_check_media_mode(struct mac_regs __iomem *regs) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci u32 status = 0; 7158c2ecf20Sopenharmony_ci u16 ANAR; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (!MII_REG_BITS_IS_ON(BMSR_LSTATUS, MII_BMSR, regs)) 7188c2ecf20Sopenharmony_ci status |= VELOCITY_LINK_FAIL; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (MII_REG_BITS_IS_ON(ADVERTISE_1000FULL, MII_CTRL1000, regs)) 7218c2ecf20Sopenharmony_ci status |= VELOCITY_SPEED_1000 | VELOCITY_DUPLEX_FULL; 7228c2ecf20Sopenharmony_ci else if (MII_REG_BITS_IS_ON(ADVERTISE_1000HALF, MII_CTRL1000, regs)) 7238c2ecf20Sopenharmony_ci status |= (VELOCITY_SPEED_1000); 7248c2ecf20Sopenharmony_ci else { 7258c2ecf20Sopenharmony_ci velocity_mii_read(regs, MII_ADVERTISE, &ANAR); 7268c2ecf20Sopenharmony_ci if (ANAR & ADVERTISE_100FULL) 7278c2ecf20Sopenharmony_ci status |= (VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL); 7288c2ecf20Sopenharmony_ci else if (ANAR & ADVERTISE_100HALF) 7298c2ecf20Sopenharmony_ci status |= VELOCITY_SPEED_100; 7308c2ecf20Sopenharmony_ci else if (ANAR & ADVERTISE_10FULL) 7318c2ecf20Sopenharmony_ci status |= (VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL); 7328c2ecf20Sopenharmony_ci else 7338c2ecf20Sopenharmony_ci status |= (VELOCITY_SPEED_10); 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (MII_REG_BITS_IS_ON(BMCR_ANENABLE, MII_BMCR, regs)) { 7378c2ecf20Sopenharmony_ci velocity_mii_read(regs, MII_ADVERTISE, &ANAR); 7388c2ecf20Sopenharmony_ci if ((ANAR & (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF)) 7398c2ecf20Sopenharmony_ci == (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF)) { 7408c2ecf20Sopenharmony_ci if (MII_REG_BITS_IS_ON(ADVERTISE_1000HALF | ADVERTISE_1000FULL, MII_CTRL1000, regs)) 7418c2ecf20Sopenharmony_ci status |= VELOCITY_AUTONEG_ENABLE; 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci return status; 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci/** 7498c2ecf20Sopenharmony_ci * velocity_mii_write - write MII data 7508c2ecf20Sopenharmony_ci * @regs: velocity registers 7518c2ecf20Sopenharmony_ci * @mii_addr: MII register index 7528c2ecf20Sopenharmony_ci * @data: 16bit data for the MII register 7538c2ecf20Sopenharmony_ci * 7548c2ecf20Sopenharmony_ci * Perform a single write to an MII 16bit register. Returns zero 7558c2ecf20Sopenharmony_ci * on success or -ETIMEDOUT if the PHY did not respond. 7568c2ecf20Sopenharmony_ci */ 7578c2ecf20Sopenharmony_cistatic int velocity_mii_write(struct mac_regs __iomem *regs, u8 mii_addr, u16 data) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci u16 ww; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* 7628c2ecf20Sopenharmony_ci * Disable MIICR_MAUTO, so that mii addr can be set normally 7638c2ecf20Sopenharmony_ci */ 7648c2ecf20Sopenharmony_ci safe_disable_mii_autopoll(regs); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci /* MII reg offset */ 7678c2ecf20Sopenharmony_ci writeb(mii_addr, ®s->MIIADR); 7688c2ecf20Sopenharmony_ci /* set MII data */ 7698c2ecf20Sopenharmony_ci writew(data, ®s->MIIDATA); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci /* turn on MIICR_WCMD */ 7728c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON(MIICR_WCMD, ®s->MIICR); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci /* W_MAX_TIMEOUT is the timeout period */ 7758c2ecf20Sopenharmony_ci for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { 7768c2ecf20Sopenharmony_ci udelay(5); 7778c2ecf20Sopenharmony_ci if (!(readb(®s->MIICR) & MIICR_WCMD)) 7788c2ecf20Sopenharmony_ci break; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci enable_mii_autopoll(regs); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (ww == W_MAX_TIMEOUT) 7838c2ecf20Sopenharmony_ci return -ETIMEDOUT; 7848c2ecf20Sopenharmony_ci return 0; 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci/** 7888c2ecf20Sopenharmony_ci * set_mii_flow_control - flow control setup 7898c2ecf20Sopenharmony_ci * @vptr: velocity interface 7908c2ecf20Sopenharmony_ci * 7918c2ecf20Sopenharmony_ci * Set up the flow control on this interface according to 7928c2ecf20Sopenharmony_ci * the supplied user/eeprom options. 7938c2ecf20Sopenharmony_ci */ 7948c2ecf20Sopenharmony_cistatic void set_mii_flow_control(struct velocity_info *vptr) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci /*Enable or Disable PAUSE in ANAR */ 7978c2ecf20Sopenharmony_ci switch (vptr->options.flow_cntl) { 7988c2ecf20Sopenharmony_ci case FLOW_CNTL_TX: 7998c2ecf20Sopenharmony_ci MII_REG_BITS_OFF(ADVERTISE_PAUSE_CAP, MII_ADVERTISE, vptr->mac_regs); 8008c2ecf20Sopenharmony_ci MII_REG_BITS_ON(ADVERTISE_PAUSE_ASYM, MII_ADVERTISE, vptr->mac_regs); 8018c2ecf20Sopenharmony_ci break; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci case FLOW_CNTL_RX: 8048c2ecf20Sopenharmony_ci MII_REG_BITS_ON(ADVERTISE_PAUSE_CAP, MII_ADVERTISE, vptr->mac_regs); 8058c2ecf20Sopenharmony_ci MII_REG_BITS_ON(ADVERTISE_PAUSE_ASYM, MII_ADVERTISE, vptr->mac_regs); 8068c2ecf20Sopenharmony_ci break; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci case FLOW_CNTL_TX_RX: 8098c2ecf20Sopenharmony_ci MII_REG_BITS_ON(ADVERTISE_PAUSE_CAP, MII_ADVERTISE, vptr->mac_regs); 8108c2ecf20Sopenharmony_ci MII_REG_BITS_OFF(ADVERTISE_PAUSE_ASYM, MII_ADVERTISE, vptr->mac_regs); 8118c2ecf20Sopenharmony_ci break; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci case FLOW_CNTL_DISABLE: 8148c2ecf20Sopenharmony_ci MII_REG_BITS_OFF(ADVERTISE_PAUSE_CAP, MII_ADVERTISE, vptr->mac_regs); 8158c2ecf20Sopenharmony_ci MII_REG_BITS_OFF(ADVERTISE_PAUSE_ASYM, MII_ADVERTISE, vptr->mac_regs); 8168c2ecf20Sopenharmony_ci break; 8178c2ecf20Sopenharmony_ci default: 8188c2ecf20Sopenharmony_ci break; 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci/** 8238c2ecf20Sopenharmony_ci * mii_set_auto_on - autonegotiate on 8248c2ecf20Sopenharmony_ci * @vptr: velocity 8258c2ecf20Sopenharmony_ci * 8268c2ecf20Sopenharmony_ci * Enable autonegotation on this interface 8278c2ecf20Sopenharmony_ci */ 8288c2ecf20Sopenharmony_cistatic void mii_set_auto_on(struct velocity_info *vptr) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci if (MII_REG_BITS_IS_ON(BMCR_ANENABLE, MII_BMCR, vptr->mac_regs)) 8318c2ecf20Sopenharmony_ci MII_REG_BITS_ON(BMCR_ANRESTART, MII_BMCR, vptr->mac_regs); 8328c2ecf20Sopenharmony_ci else 8338c2ecf20Sopenharmony_ci MII_REG_BITS_ON(BMCR_ANENABLE, MII_BMCR, vptr->mac_regs); 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cistatic u32 check_connection_type(struct mac_regs __iomem *regs) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci u32 status = 0; 8398c2ecf20Sopenharmony_ci u8 PHYSR0; 8408c2ecf20Sopenharmony_ci u16 ANAR; 8418c2ecf20Sopenharmony_ci PHYSR0 = readb(®s->PHYSR0); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci /* 8448c2ecf20Sopenharmony_ci if (!(PHYSR0 & PHYSR0_LINKGD)) 8458c2ecf20Sopenharmony_ci status|=VELOCITY_LINK_FAIL; 8468c2ecf20Sopenharmony_ci */ 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (PHYSR0 & PHYSR0_FDPX) 8498c2ecf20Sopenharmony_ci status |= VELOCITY_DUPLEX_FULL; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci if (PHYSR0 & PHYSR0_SPDG) 8528c2ecf20Sopenharmony_ci status |= VELOCITY_SPEED_1000; 8538c2ecf20Sopenharmony_ci else if (PHYSR0 & PHYSR0_SPD10) 8548c2ecf20Sopenharmony_ci status |= VELOCITY_SPEED_10; 8558c2ecf20Sopenharmony_ci else 8568c2ecf20Sopenharmony_ci status |= VELOCITY_SPEED_100; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (MII_REG_BITS_IS_ON(BMCR_ANENABLE, MII_BMCR, regs)) { 8598c2ecf20Sopenharmony_ci velocity_mii_read(regs, MII_ADVERTISE, &ANAR); 8608c2ecf20Sopenharmony_ci if ((ANAR & (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF)) 8618c2ecf20Sopenharmony_ci == (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF)) { 8628c2ecf20Sopenharmony_ci if (MII_REG_BITS_IS_ON(ADVERTISE_1000HALF | ADVERTISE_1000FULL, MII_CTRL1000, regs)) 8638c2ecf20Sopenharmony_ci status |= VELOCITY_AUTONEG_ENABLE; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci return status; 8688c2ecf20Sopenharmony_ci} 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci/** 8718c2ecf20Sopenharmony_ci * velocity_set_media_mode - set media mode 8728c2ecf20Sopenharmony_ci * @vptr: velocity adapter 8738c2ecf20Sopenharmony_ci * @mii_status: old MII link state 8748c2ecf20Sopenharmony_ci * 8758c2ecf20Sopenharmony_ci * Check the media link state and configure the flow control 8768c2ecf20Sopenharmony_ci * PHY and also velocity hardware setup accordingly. In particular 8778c2ecf20Sopenharmony_ci * we need to set up CD polling and frame bursting. 8788c2ecf20Sopenharmony_ci */ 8798c2ecf20Sopenharmony_cistatic int velocity_set_media_mode(struct velocity_info *vptr, u32 mii_status) 8808c2ecf20Sopenharmony_ci{ 8818c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci vptr->mii_status = mii_check_media_mode(vptr->mac_regs); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci /* Set mii link status */ 8868c2ecf20Sopenharmony_ci set_mii_flow_control(vptr); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201) 8898c2ecf20Sopenharmony_ci MII_REG_BITS_ON(AUXCR_MDPPS, MII_NCONFIG, vptr->mac_regs); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci /* 8928c2ecf20Sopenharmony_ci * If connection type is AUTO 8938c2ecf20Sopenharmony_ci */ 8948c2ecf20Sopenharmony_ci if (mii_status & VELOCITY_AUTONEG_ENABLE) { 8958c2ecf20Sopenharmony_ci netdev_info(vptr->netdev, "Velocity is in AUTO mode\n"); 8968c2ecf20Sopenharmony_ci /* clear force MAC mode bit */ 8978c2ecf20Sopenharmony_ci BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, ®s->CHIPGCR); 8988c2ecf20Sopenharmony_ci /* set duplex mode of MAC according to duplex mode of MII */ 8998c2ecf20Sopenharmony_ci MII_REG_BITS_ON(ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF, MII_ADVERTISE, vptr->mac_regs); 9008c2ecf20Sopenharmony_ci MII_REG_BITS_ON(ADVERTISE_1000FULL | ADVERTISE_1000HALF, MII_CTRL1000, vptr->mac_regs); 9018c2ecf20Sopenharmony_ci MII_REG_BITS_ON(BMCR_SPEED1000, MII_BMCR, vptr->mac_regs); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci /* enable AUTO-NEGO mode */ 9048c2ecf20Sopenharmony_ci mii_set_auto_on(vptr); 9058c2ecf20Sopenharmony_ci } else { 9068c2ecf20Sopenharmony_ci u16 CTRL1000; 9078c2ecf20Sopenharmony_ci u16 ANAR; 9088c2ecf20Sopenharmony_ci u8 CHIPGCR; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* 9118c2ecf20Sopenharmony_ci * 1. if it's 3119, disable frame bursting in halfduplex mode 9128c2ecf20Sopenharmony_ci * and enable it in fullduplex mode 9138c2ecf20Sopenharmony_ci * 2. set correct MII/GMII and half/full duplex mode in CHIPGCR 9148c2ecf20Sopenharmony_ci * 3. only enable CD heart beat counter in 10HD mode 9158c2ecf20Sopenharmony_ci */ 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci /* set force MAC mode bit */ 9188c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON(CHIPGCR_FCMODE, ®s->CHIPGCR); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci CHIPGCR = readb(®s->CHIPGCR); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci if (mii_status & VELOCITY_SPEED_1000) 9238c2ecf20Sopenharmony_ci CHIPGCR |= CHIPGCR_FCGMII; 9248c2ecf20Sopenharmony_ci else 9258c2ecf20Sopenharmony_ci CHIPGCR &= ~CHIPGCR_FCGMII; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (mii_status & VELOCITY_DUPLEX_FULL) { 9288c2ecf20Sopenharmony_ci CHIPGCR |= CHIPGCR_FCFDX; 9298c2ecf20Sopenharmony_ci writeb(CHIPGCR, ®s->CHIPGCR); 9308c2ecf20Sopenharmony_ci netdev_info(vptr->netdev, 9318c2ecf20Sopenharmony_ci "set Velocity to forced full mode\n"); 9328c2ecf20Sopenharmony_ci if (vptr->rev_id < REV_ID_VT3216_A0) 9338c2ecf20Sopenharmony_ci BYTE_REG_BITS_OFF(TCR_TB2BDIS, ®s->TCR); 9348c2ecf20Sopenharmony_ci } else { 9358c2ecf20Sopenharmony_ci CHIPGCR &= ~CHIPGCR_FCFDX; 9368c2ecf20Sopenharmony_ci netdev_info(vptr->netdev, 9378c2ecf20Sopenharmony_ci "set Velocity to forced half mode\n"); 9388c2ecf20Sopenharmony_ci writeb(CHIPGCR, ®s->CHIPGCR); 9398c2ecf20Sopenharmony_ci if (vptr->rev_id < REV_ID_VT3216_A0) 9408c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON(TCR_TB2BDIS, ®s->TCR); 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci velocity_mii_read(vptr->mac_regs, MII_CTRL1000, &CTRL1000); 9448c2ecf20Sopenharmony_ci CTRL1000 &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); 9458c2ecf20Sopenharmony_ci if ((mii_status & VELOCITY_SPEED_1000) && 9468c2ecf20Sopenharmony_ci (mii_status & VELOCITY_DUPLEX_FULL)) { 9478c2ecf20Sopenharmony_ci CTRL1000 |= ADVERTISE_1000FULL; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci velocity_mii_write(vptr->mac_regs, MII_CTRL1000, CTRL1000); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (!(mii_status & VELOCITY_DUPLEX_FULL) && (mii_status & VELOCITY_SPEED_10)) 9528c2ecf20Sopenharmony_ci BYTE_REG_BITS_OFF(TESTCFG_HBDIS, ®s->TESTCFG); 9538c2ecf20Sopenharmony_ci else 9548c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON(TESTCFG_HBDIS, ®s->TESTCFG); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci /* MII_REG_BITS_OFF(BMCR_SPEED1000, MII_BMCR, vptr->mac_regs); */ 9578c2ecf20Sopenharmony_ci velocity_mii_read(vptr->mac_regs, MII_ADVERTISE, &ANAR); 9588c2ecf20Sopenharmony_ci ANAR &= (~(ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF)); 9598c2ecf20Sopenharmony_ci if (mii_status & VELOCITY_SPEED_100) { 9608c2ecf20Sopenharmony_ci if (mii_status & VELOCITY_DUPLEX_FULL) 9618c2ecf20Sopenharmony_ci ANAR |= ADVERTISE_100FULL; 9628c2ecf20Sopenharmony_ci else 9638c2ecf20Sopenharmony_ci ANAR |= ADVERTISE_100HALF; 9648c2ecf20Sopenharmony_ci } else if (mii_status & VELOCITY_SPEED_10) { 9658c2ecf20Sopenharmony_ci if (mii_status & VELOCITY_DUPLEX_FULL) 9668c2ecf20Sopenharmony_ci ANAR |= ADVERTISE_10FULL; 9678c2ecf20Sopenharmony_ci else 9688c2ecf20Sopenharmony_ci ANAR |= ADVERTISE_10HALF; 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci velocity_mii_write(vptr->mac_regs, MII_ADVERTISE, ANAR); 9718c2ecf20Sopenharmony_ci /* enable AUTO-NEGO mode */ 9728c2ecf20Sopenharmony_ci mii_set_auto_on(vptr); 9738c2ecf20Sopenharmony_ci /* MII_REG_BITS_ON(BMCR_ANENABLE, MII_BMCR, vptr->mac_regs); */ 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci /* vptr->mii_status=mii_check_media_mode(vptr->mac_regs); */ 9768c2ecf20Sopenharmony_ci /* vptr->mii_status=check_connection_type(vptr->mac_regs); */ 9778c2ecf20Sopenharmony_ci return VELOCITY_LINK_CHANGE; 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci/** 9818c2ecf20Sopenharmony_ci * velocity_print_link_status - link status reporting 9828c2ecf20Sopenharmony_ci * @vptr: velocity to report on 9838c2ecf20Sopenharmony_ci * 9848c2ecf20Sopenharmony_ci * Turn the link status of the velocity card into a kernel log 9858c2ecf20Sopenharmony_ci * description of the new link state, detailing speed and duplex 9868c2ecf20Sopenharmony_ci * status 9878c2ecf20Sopenharmony_ci */ 9888c2ecf20Sopenharmony_cistatic void velocity_print_link_status(struct velocity_info *vptr) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci const char *link; 9918c2ecf20Sopenharmony_ci const char *speed; 9928c2ecf20Sopenharmony_ci const char *duplex; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci if (vptr->mii_status & VELOCITY_LINK_FAIL) { 9958c2ecf20Sopenharmony_ci netdev_notice(vptr->netdev, "failed to detect cable link\n"); 9968c2ecf20Sopenharmony_ci return; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (vptr->options.spd_dpx == SPD_DPX_AUTO) { 10008c2ecf20Sopenharmony_ci link = "auto-negotiation"; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci if (vptr->mii_status & VELOCITY_SPEED_1000) 10038c2ecf20Sopenharmony_ci speed = "1000"; 10048c2ecf20Sopenharmony_ci else if (vptr->mii_status & VELOCITY_SPEED_100) 10058c2ecf20Sopenharmony_ci speed = "100"; 10068c2ecf20Sopenharmony_ci else 10078c2ecf20Sopenharmony_ci speed = "10"; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci if (vptr->mii_status & VELOCITY_DUPLEX_FULL) 10108c2ecf20Sopenharmony_ci duplex = "full"; 10118c2ecf20Sopenharmony_ci else 10128c2ecf20Sopenharmony_ci duplex = "half"; 10138c2ecf20Sopenharmony_ci } else { 10148c2ecf20Sopenharmony_ci link = "forced"; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci switch (vptr->options.spd_dpx) { 10178c2ecf20Sopenharmony_ci case SPD_DPX_1000_FULL: 10188c2ecf20Sopenharmony_ci speed = "1000"; 10198c2ecf20Sopenharmony_ci duplex = "full"; 10208c2ecf20Sopenharmony_ci break; 10218c2ecf20Sopenharmony_ci case SPD_DPX_100_HALF: 10228c2ecf20Sopenharmony_ci speed = "100"; 10238c2ecf20Sopenharmony_ci duplex = "half"; 10248c2ecf20Sopenharmony_ci break; 10258c2ecf20Sopenharmony_ci case SPD_DPX_100_FULL: 10268c2ecf20Sopenharmony_ci speed = "100"; 10278c2ecf20Sopenharmony_ci duplex = "full"; 10288c2ecf20Sopenharmony_ci break; 10298c2ecf20Sopenharmony_ci case SPD_DPX_10_HALF: 10308c2ecf20Sopenharmony_ci speed = "10"; 10318c2ecf20Sopenharmony_ci duplex = "half"; 10328c2ecf20Sopenharmony_ci break; 10338c2ecf20Sopenharmony_ci case SPD_DPX_10_FULL: 10348c2ecf20Sopenharmony_ci speed = "10"; 10358c2ecf20Sopenharmony_ci duplex = "full"; 10368c2ecf20Sopenharmony_ci break; 10378c2ecf20Sopenharmony_ci default: 10388c2ecf20Sopenharmony_ci speed = "unknown"; 10398c2ecf20Sopenharmony_ci duplex = "unknown"; 10408c2ecf20Sopenharmony_ci break; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci netdev_notice(vptr->netdev, "Link %s speed %sM bps %s duplex\n", 10448c2ecf20Sopenharmony_ci link, speed, duplex); 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci/** 10488c2ecf20Sopenharmony_ci * enable_flow_control_ability - flow control 10498c2ecf20Sopenharmony_ci * @vptr: veloity to configure 10508c2ecf20Sopenharmony_ci * 10518c2ecf20Sopenharmony_ci * Set up flow control according to the flow control options 10528c2ecf20Sopenharmony_ci * determined by the eeprom/configuration. 10538c2ecf20Sopenharmony_ci */ 10548c2ecf20Sopenharmony_cistatic void enable_flow_control_ability(struct velocity_info *vptr) 10558c2ecf20Sopenharmony_ci{ 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci switch (vptr->options.flow_cntl) { 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci case FLOW_CNTL_DEFAULT: 10628c2ecf20Sopenharmony_ci if (BYTE_REG_BITS_IS_ON(PHYSR0_RXFLC, ®s->PHYSR0)) 10638c2ecf20Sopenharmony_ci writel(CR0_FDXRFCEN, ®s->CR0Set); 10648c2ecf20Sopenharmony_ci else 10658c2ecf20Sopenharmony_ci writel(CR0_FDXRFCEN, ®s->CR0Clr); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (BYTE_REG_BITS_IS_ON(PHYSR0_TXFLC, ®s->PHYSR0)) 10688c2ecf20Sopenharmony_ci writel(CR0_FDXTFCEN, ®s->CR0Set); 10698c2ecf20Sopenharmony_ci else 10708c2ecf20Sopenharmony_ci writel(CR0_FDXTFCEN, ®s->CR0Clr); 10718c2ecf20Sopenharmony_ci break; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci case FLOW_CNTL_TX: 10748c2ecf20Sopenharmony_ci writel(CR0_FDXTFCEN, ®s->CR0Set); 10758c2ecf20Sopenharmony_ci writel(CR0_FDXRFCEN, ®s->CR0Clr); 10768c2ecf20Sopenharmony_ci break; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci case FLOW_CNTL_RX: 10798c2ecf20Sopenharmony_ci writel(CR0_FDXRFCEN, ®s->CR0Set); 10808c2ecf20Sopenharmony_ci writel(CR0_FDXTFCEN, ®s->CR0Clr); 10818c2ecf20Sopenharmony_ci break; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci case FLOW_CNTL_TX_RX: 10848c2ecf20Sopenharmony_ci writel(CR0_FDXTFCEN, ®s->CR0Set); 10858c2ecf20Sopenharmony_ci writel(CR0_FDXRFCEN, ®s->CR0Set); 10868c2ecf20Sopenharmony_ci break; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci case FLOW_CNTL_DISABLE: 10898c2ecf20Sopenharmony_ci writel(CR0_FDXRFCEN, ®s->CR0Clr); 10908c2ecf20Sopenharmony_ci writel(CR0_FDXTFCEN, ®s->CR0Clr); 10918c2ecf20Sopenharmony_ci break; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci default: 10948c2ecf20Sopenharmony_ci break; 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci} 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci/** 11008c2ecf20Sopenharmony_ci * velocity_soft_reset - soft reset 11018c2ecf20Sopenharmony_ci * @vptr: velocity to reset 11028c2ecf20Sopenharmony_ci * 11038c2ecf20Sopenharmony_ci * Kick off a soft reset of the velocity adapter and then poll 11048c2ecf20Sopenharmony_ci * until the reset sequence has completed before returning. 11058c2ecf20Sopenharmony_ci */ 11068c2ecf20Sopenharmony_cistatic int velocity_soft_reset(struct velocity_info *vptr) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 11098c2ecf20Sopenharmony_ci int i = 0; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci writel(CR0_SFRST, ®s->CR0Set); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci for (i = 0; i < W_MAX_TIMEOUT; i++) { 11148c2ecf20Sopenharmony_ci udelay(5); 11158c2ecf20Sopenharmony_ci if (!DWORD_REG_BITS_IS_ON(CR0_SFRST, ®s->CR0Set)) 11168c2ecf20Sopenharmony_ci break; 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if (i == W_MAX_TIMEOUT) { 11208c2ecf20Sopenharmony_ci writel(CR0_FORSRST, ®s->CR0Set); 11218c2ecf20Sopenharmony_ci /* FIXME: PCI POSTING */ 11228c2ecf20Sopenharmony_ci /* delay 2ms */ 11238c2ecf20Sopenharmony_ci mdelay(2); 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci return 0; 11268c2ecf20Sopenharmony_ci} 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci/** 11298c2ecf20Sopenharmony_ci * velocity_set_multi - filter list change callback 11308c2ecf20Sopenharmony_ci * @dev: network device 11318c2ecf20Sopenharmony_ci * 11328c2ecf20Sopenharmony_ci * Called by the network layer when the filter lists need to change 11338c2ecf20Sopenharmony_ci * for a velocity adapter. Reload the CAMs with the new address 11348c2ecf20Sopenharmony_ci * filter ruleset. 11358c2ecf20Sopenharmony_ci */ 11368c2ecf20Sopenharmony_cistatic void velocity_set_multi(struct net_device *dev) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 11398c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 11408c2ecf20Sopenharmony_ci u8 rx_mode; 11418c2ecf20Sopenharmony_ci int i; 11428c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ 11458c2ecf20Sopenharmony_ci writel(0xffffffff, ®s->MARCAM[0]); 11468c2ecf20Sopenharmony_ci writel(0xffffffff, ®s->MARCAM[4]); 11478c2ecf20Sopenharmony_ci rx_mode = (RCR_AM | RCR_AB | RCR_PROM); 11488c2ecf20Sopenharmony_ci } else if ((netdev_mc_count(dev) > vptr->multicast_limit) || 11498c2ecf20Sopenharmony_ci (dev->flags & IFF_ALLMULTI)) { 11508c2ecf20Sopenharmony_ci writel(0xffffffff, ®s->MARCAM[0]); 11518c2ecf20Sopenharmony_ci writel(0xffffffff, ®s->MARCAM[4]); 11528c2ecf20Sopenharmony_ci rx_mode = (RCR_AM | RCR_AB); 11538c2ecf20Sopenharmony_ci } else { 11548c2ecf20Sopenharmony_ci int offset = MCAM_SIZE - vptr->multicast_limit; 11558c2ecf20Sopenharmony_ci mac_get_cam_mask(regs, vptr->mCAMmask); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci i = 0; 11588c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 11598c2ecf20Sopenharmony_ci mac_set_cam(regs, i + offset, ha->addr); 11608c2ecf20Sopenharmony_ci vptr->mCAMmask[(offset + i) / 8] |= 1 << ((offset + i) & 7); 11618c2ecf20Sopenharmony_ci i++; 11628c2ecf20Sopenharmony_ci } 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci mac_set_cam_mask(regs, vptr->mCAMmask); 11658c2ecf20Sopenharmony_ci rx_mode = RCR_AM | RCR_AB | RCR_AP; 11668c2ecf20Sopenharmony_ci } 11678c2ecf20Sopenharmony_ci if (dev->mtu > 1500) 11688c2ecf20Sopenharmony_ci rx_mode |= RCR_AL; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON(rx_mode, ®s->RCR); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci} 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci/* 11758c2ecf20Sopenharmony_ci * MII access , media link mode setting functions 11768c2ecf20Sopenharmony_ci */ 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci/** 11798c2ecf20Sopenharmony_ci * mii_init - set up MII 11808c2ecf20Sopenharmony_ci * @vptr: velocity adapter 11818c2ecf20Sopenharmony_ci * @mii_status: links tatus 11828c2ecf20Sopenharmony_ci * 11838c2ecf20Sopenharmony_ci * Set up the PHY for the current link state. 11848c2ecf20Sopenharmony_ci */ 11858c2ecf20Sopenharmony_cistatic void mii_init(struct velocity_info *vptr, u32 mii_status) 11868c2ecf20Sopenharmony_ci{ 11878c2ecf20Sopenharmony_ci u16 BMCR; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci switch (PHYID_GET_PHY_ID(vptr->phy_id)) { 11908c2ecf20Sopenharmony_ci case PHYID_ICPLUS_IP101A: 11918c2ecf20Sopenharmony_ci MII_REG_BITS_ON((ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP), 11928c2ecf20Sopenharmony_ci MII_ADVERTISE, vptr->mac_regs); 11938c2ecf20Sopenharmony_ci if (vptr->mii_status & VELOCITY_DUPLEX_FULL) 11948c2ecf20Sopenharmony_ci MII_REG_BITS_ON(TCSR_ECHODIS, MII_SREVISION, 11958c2ecf20Sopenharmony_ci vptr->mac_regs); 11968c2ecf20Sopenharmony_ci else 11978c2ecf20Sopenharmony_ci MII_REG_BITS_OFF(TCSR_ECHODIS, MII_SREVISION, 11988c2ecf20Sopenharmony_ci vptr->mac_regs); 11998c2ecf20Sopenharmony_ci MII_REG_BITS_ON(PLED_LALBE, MII_TPISTATUS, vptr->mac_regs); 12008c2ecf20Sopenharmony_ci break; 12018c2ecf20Sopenharmony_ci case PHYID_CICADA_CS8201: 12028c2ecf20Sopenharmony_ci /* 12038c2ecf20Sopenharmony_ci * Reset to hardware default 12048c2ecf20Sopenharmony_ci */ 12058c2ecf20Sopenharmony_ci MII_REG_BITS_OFF((ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP), MII_ADVERTISE, vptr->mac_regs); 12068c2ecf20Sopenharmony_ci /* 12078c2ecf20Sopenharmony_ci * Turn on ECHODIS bit in NWay-forced full mode and turn it 12088c2ecf20Sopenharmony_ci * off it in NWay-forced half mode for NWay-forced v.s. 12098c2ecf20Sopenharmony_ci * legacy-forced issue. 12108c2ecf20Sopenharmony_ci */ 12118c2ecf20Sopenharmony_ci if (vptr->mii_status & VELOCITY_DUPLEX_FULL) 12128c2ecf20Sopenharmony_ci MII_REG_BITS_ON(TCSR_ECHODIS, MII_SREVISION, vptr->mac_regs); 12138c2ecf20Sopenharmony_ci else 12148c2ecf20Sopenharmony_ci MII_REG_BITS_OFF(TCSR_ECHODIS, MII_SREVISION, vptr->mac_regs); 12158c2ecf20Sopenharmony_ci /* 12168c2ecf20Sopenharmony_ci * Turn on Link/Activity LED enable bit for CIS8201 12178c2ecf20Sopenharmony_ci */ 12188c2ecf20Sopenharmony_ci MII_REG_BITS_ON(PLED_LALBE, MII_TPISTATUS, vptr->mac_regs); 12198c2ecf20Sopenharmony_ci break; 12208c2ecf20Sopenharmony_ci case PHYID_VT3216_32BIT: 12218c2ecf20Sopenharmony_ci case PHYID_VT3216_64BIT: 12228c2ecf20Sopenharmony_ci /* 12238c2ecf20Sopenharmony_ci * Reset to hardware default 12248c2ecf20Sopenharmony_ci */ 12258c2ecf20Sopenharmony_ci MII_REG_BITS_ON((ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP), MII_ADVERTISE, vptr->mac_regs); 12268c2ecf20Sopenharmony_ci /* 12278c2ecf20Sopenharmony_ci * Turn on ECHODIS bit in NWay-forced full mode and turn it 12288c2ecf20Sopenharmony_ci * off it in NWay-forced half mode for NWay-forced v.s. 12298c2ecf20Sopenharmony_ci * legacy-forced issue 12308c2ecf20Sopenharmony_ci */ 12318c2ecf20Sopenharmony_ci if (vptr->mii_status & VELOCITY_DUPLEX_FULL) 12328c2ecf20Sopenharmony_ci MII_REG_BITS_ON(TCSR_ECHODIS, MII_SREVISION, vptr->mac_regs); 12338c2ecf20Sopenharmony_ci else 12348c2ecf20Sopenharmony_ci MII_REG_BITS_OFF(TCSR_ECHODIS, MII_SREVISION, vptr->mac_regs); 12358c2ecf20Sopenharmony_ci break; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci case PHYID_MARVELL_1000: 12388c2ecf20Sopenharmony_ci case PHYID_MARVELL_1000S: 12398c2ecf20Sopenharmony_ci /* 12408c2ecf20Sopenharmony_ci * Assert CRS on Transmit 12418c2ecf20Sopenharmony_ci */ 12428c2ecf20Sopenharmony_ci MII_REG_BITS_ON(PSCR_ACRSTX, MII_REG_PSCR, vptr->mac_regs); 12438c2ecf20Sopenharmony_ci /* 12448c2ecf20Sopenharmony_ci * Reset to hardware default 12458c2ecf20Sopenharmony_ci */ 12468c2ecf20Sopenharmony_ci MII_REG_BITS_ON((ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP), MII_ADVERTISE, vptr->mac_regs); 12478c2ecf20Sopenharmony_ci break; 12488c2ecf20Sopenharmony_ci default: 12498c2ecf20Sopenharmony_ci ; 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci velocity_mii_read(vptr->mac_regs, MII_BMCR, &BMCR); 12528c2ecf20Sopenharmony_ci if (BMCR & BMCR_ISOLATE) { 12538c2ecf20Sopenharmony_ci BMCR &= ~BMCR_ISOLATE; 12548c2ecf20Sopenharmony_ci velocity_mii_write(vptr->mac_regs, MII_BMCR, BMCR); 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci} 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci/** 12598c2ecf20Sopenharmony_ci * setup_queue_timers - Setup interrupt timers 12608c2ecf20Sopenharmony_ci * @vptr: velocity adapter 12618c2ecf20Sopenharmony_ci * 12628c2ecf20Sopenharmony_ci * Setup interrupt frequency during suppression (timeout if the frame 12638c2ecf20Sopenharmony_ci * count isn't filled). 12648c2ecf20Sopenharmony_ci */ 12658c2ecf20Sopenharmony_cistatic void setup_queue_timers(struct velocity_info *vptr) 12668c2ecf20Sopenharmony_ci{ 12678c2ecf20Sopenharmony_ci /* Only for newer revisions */ 12688c2ecf20Sopenharmony_ci if (vptr->rev_id >= REV_ID_VT3216_A0) { 12698c2ecf20Sopenharmony_ci u8 txqueue_timer = 0; 12708c2ecf20Sopenharmony_ci u8 rxqueue_timer = 0; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci if (vptr->mii_status & (VELOCITY_SPEED_1000 | 12738c2ecf20Sopenharmony_ci VELOCITY_SPEED_100)) { 12748c2ecf20Sopenharmony_ci txqueue_timer = vptr->options.txqueue_timer; 12758c2ecf20Sopenharmony_ci rxqueue_timer = vptr->options.rxqueue_timer; 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci writeb(txqueue_timer, &vptr->mac_regs->TQETMR); 12798c2ecf20Sopenharmony_ci writeb(rxqueue_timer, &vptr->mac_regs->RQETMR); 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci/** 12848c2ecf20Sopenharmony_ci * setup_adaptive_interrupts - Setup interrupt suppression 12858c2ecf20Sopenharmony_ci * @vptr: velocity adapter 12868c2ecf20Sopenharmony_ci * 12878c2ecf20Sopenharmony_ci * The velocity is able to suppress interrupt during high interrupt load. 12888c2ecf20Sopenharmony_ci * This function turns on that feature. 12898c2ecf20Sopenharmony_ci */ 12908c2ecf20Sopenharmony_cistatic void setup_adaptive_interrupts(struct velocity_info *vptr) 12918c2ecf20Sopenharmony_ci{ 12928c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 12938c2ecf20Sopenharmony_ci u16 tx_intsup = vptr->options.tx_intsup; 12948c2ecf20Sopenharmony_ci u16 rx_intsup = vptr->options.rx_intsup; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci /* Setup default interrupt mask (will be changed below) */ 12978c2ecf20Sopenharmony_ci vptr->int_mask = INT_MASK_DEF; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci /* Set Tx Interrupt Suppression Threshold */ 13008c2ecf20Sopenharmony_ci writeb(CAMCR_PS0, ®s->CAMCR); 13018c2ecf20Sopenharmony_ci if (tx_intsup != 0) { 13028c2ecf20Sopenharmony_ci vptr->int_mask &= ~(ISR_PTXI | ISR_PTX0I | ISR_PTX1I | 13038c2ecf20Sopenharmony_ci ISR_PTX2I | ISR_PTX3I); 13048c2ecf20Sopenharmony_ci writew(tx_intsup, ®s->ISRCTL); 13058c2ecf20Sopenharmony_ci } else 13068c2ecf20Sopenharmony_ci writew(ISRCTL_TSUPDIS, ®s->ISRCTL); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci /* Set Rx Interrupt Suppression Threshold */ 13098c2ecf20Sopenharmony_ci writeb(CAMCR_PS1, ®s->CAMCR); 13108c2ecf20Sopenharmony_ci if (rx_intsup != 0) { 13118c2ecf20Sopenharmony_ci vptr->int_mask &= ~ISR_PRXI; 13128c2ecf20Sopenharmony_ci writew(rx_intsup, ®s->ISRCTL); 13138c2ecf20Sopenharmony_ci } else 13148c2ecf20Sopenharmony_ci writew(ISRCTL_RSUPDIS, ®s->ISRCTL); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci /* Select page to interrupt hold timer */ 13178c2ecf20Sopenharmony_ci writeb(0, ®s->CAMCR); 13188c2ecf20Sopenharmony_ci} 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci/** 13218c2ecf20Sopenharmony_ci * velocity_init_registers - initialise MAC registers 13228c2ecf20Sopenharmony_ci * @vptr: velocity to init 13238c2ecf20Sopenharmony_ci * @type: type of initialisation (hot or cold) 13248c2ecf20Sopenharmony_ci * 13258c2ecf20Sopenharmony_ci * Initialise the MAC on a reset or on first set up on the 13268c2ecf20Sopenharmony_ci * hardware. 13278c2ecf20Sopenharmony_ci */ 13288c2ecf20Sopenharmony_cistatic void velocity_init_registers(struct velocity_info *vptr, 13298c2ecf20Sopenharmony_ci enum velocity_init_type type) 13308c2ecf20Sopenharmony_ci{ 13318c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 13328c2ecf20Sopenharmony_ci struct net_device *netdev = vptr->netdev; 13338c2ecf20Sopenharmony_ci int i, mii_status; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci mac_wol_reset(regs); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci switch (type) { 13388c2ecf20Sopenharmony_ci case VELOCITY_INIT_RESET: 13398c2ecf20Sopenharmony_ci case VELOCITY_INIT_WOL: 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci /* 13448c2ecf20Sopenharmony_ci * Reset RX to prevent RX pointer not on the 4X location 13458c2ecf20Sopenharmony_ci */ 13468c2ecf20Sopenharmony_ci velocity_rx_reset(vptr); 13478c2ecf20Sopenharmony_ci mac_rx_queue_run(regs); 13488c2ecf20Sopenharmony_ci mac_rx_queue_wake(regs); 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci mii_status = velocity_get_opt_media_mode(vptr); 13518c2ecf20Sopenharmony_ci if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) { 13528c2ecf20Sopenharmony_ci velocity_print_link_status(vptr); 13538c2ecf20Sopenharmony_ci if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) 13548c2ecf20Sopenharmony_ci netif_wake_queue(netdev); 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci enable_flow_control_ability(vptr); 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci mac_clear_isr(regs); 13608c2ecf20Sopenharmony_ci writel(CR0_STOP, ®s->CR0Clr); 13618c2ecf20Sopenharmony_ci writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), 13628c2ecf20Sopenharmony_ci ®s->CR0Set); 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci break; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci case VELOCITY_INIT_COLD: 13678c2ecf20Sopenharmony_ci default: 13688c2ecf20Sopenharmony_ci /* 13698c2ecf20Sopenharmony_ci * Do reset 13708c2ecf20Sopenharmony_ci */ 13718c2ecf20Sopenharmony_ci velocity_soft_reset(vptr); 13728c2ecf20Sopenharmony_ci mdelay(5); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci if (!vptr->no_eeprom) { 13758c2ecf20Sopenharmony_ci mac_eeprom_reload(regs); 13768c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 13778c2ecf20Sopenharmony_ci writeb(netdev->dev_addr[i], regs->PAR + i); 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci /* 13818c2ecf20Sopenharmony_ci * clear Pre_ACPI bit. 13828c2ecf20Sopenharmony_ci */ 13838c2ecf20Sopenharmony_ci BYTE_REG_BITS_OFF(CFGA_PACPI, &(regs->CFGA)); 13848c2ecf20Sopenharmony_ci mac_set_rx_thresh(regs, vptr->options.rx_thresh); 13858c2ecf20Sopenharmony_ci mac_set_dma_length(regs, vptr->options.DMA_length); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci writeb(WOLCFG_SAM | WOLCFG_SAB, ®s->WOLCFGSet); 13888c2ecf20Sopenharmony_ci /* 13898c2ecf20Sopenharmony_ci * Back off algorithm use original IEEE standard 13908c2ecf20Sopenharmony_ci */ 13918c2ecf20Sopenharmony_ci BYTE_REG_BITS_SET(CFGB_OFSET, (CFGB_CRANDOM | CFGB_CAP | CFGB_MBA | CFGB_BAKOPT), ®s->CFGB); 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci /* 13948c2ecf20Sopenharmony_ci * Init CAM filter 13958c2ecf20Sopenharmony_ci */ 13968c2ecf20Sopenharmony_ci velocity_init_cam_filter(vptr); 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci /* 13998c2ecf20Sopenharmony_ci * Set packet filter: Receive directed and broadcast address 14008c2ecf20Sopenharmony_ci */ 14018c2ecf20Sopenharmony_ci velocity_set_multi(netdev); 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci /* 14048c2ecf20Sopenharmony_ci * Enable MII auto-polling 14058c2ecf20Sopenharmony_ci */ 14068c2ecf20Sopenharmony_ci enable_mii_autopoll(regs); 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci setup_adaptive_interrupts(vptr); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci writel(vptr->rx.pool_dma, ®s->RDBaseLo); 14118c2ecf20Sopenharmony_ci writew(vptr->options.numrx - 1, ®s->RDCSize); 14128c2ecf20Sopenharmony_ci mac_rx_queue_run(regs); 14138c2ecf20Sopenharmony_ci mac_rx_queue_wake(regs); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci writew(vptr->options.numtx - 1, ®s->TDCSize); 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci for (i = 0; i < vptr->tx.numq; i++) { 14188c2ecf20Sopenharmony_ci writel(vptr->tx.pool_dma[i], ®s->TDBaseLo[i]); 14198c2ecf20Sopenharmony_ci mac_tx_queue_run(regs, i); 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci init_flow_control_register(vptr); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci writel(CR0_STOP, ®s->CR0Clr); 14258c2ecf20Sopenharmony_ci writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), ®s->CR0Set); 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci mii_status = velocity_get_opt_media_mode(vptr); 14288c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci mii_init(vptr, mii_status); 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) { 14338c2ecf20Sopenharmony_ci velocity_print_link_status(vptr); 14348c2ecf20Sopenharmony_ci if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) 14358c2ecf20Sopenharmony_ci netif_wake_queue(netdev); 14368c2ecf20Sopenharmony_ci } 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci enable_flow_control_ability(vptr); 14398c2ecf20Sopenharmony_ci mac_hw_mibs_init(regs); 14408c2ecf20Sopenharmony_ci mac_write_int_mask(vptr->int_mask, regs); 14418c2ecf20Sopenharmony_ci mac_clear_isr(regs); 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci } 14448c2ecf20Sopenharmony_ci} 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_cistatic void velocity_give_many_rx_descs(struct velocity_info *vptr) 14478c2ecf20Sopenharmony_ci{ 14488c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 14498c2ecf20Sopenharmony_ci int avail, dirty, unusable; 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci /* 14528c2ecf20Sopenharmony_ci * RD number must be equal to 4X per hardware spec 14538c2ecf20Sopenharmony_ci * (programming guide rev 1.20, p.13) 14548c2ecf20Sopenharmony_ci */ 14558c2ecf20Sopenharmony_ci if (vptr->rx.filled < 4) 14568c2ecf20Sopenharmony_ci return; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci wmb(); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci unusable = vptr->rx.filled & 0x0003; 14618c2ecf20Sopenharmony_ci dirty = vptr->rx.dirty - unusable; 14628c2ecf20Sopenharmony_ci for (avail = vptr->rx.filled & 0xfffc; avail; avail--) { 14638c2ecf20Sopenharmony_ci dirty = (dirty > 0) ? dirty - 1 : vptr->options.numrx - 1; 14648c2ecf20Sopenharmony_ci vptr->rx.ring[dirty].rdesc0.len |= OWNED_BY_NIC; 14658c2ecf20Sopenharmony_ci } 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci writew(vptr->rx.filled & 0xfffc, ®s->RBRDU); 14688c2ecf20Sopenharmony_ci vptr->rx.filled = unusable; 14698c2ecf20Sopenharmony_ci} 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci/** 14728c2ecf20Sopenharmony_ci * velocity_init_dma_rings - set up DMA rings 14738c2ecf20Sopenharmony_ci * @vptr: Velocity to set up 14748c2ecf20Sopenharmony_ci * 14758c2ecf20Sopenharmony_ci * Allocate PCI mapped DMA rings for the receive and transmit layer 14768c2ecf20Sopenharmony_ci * to use. 14778c2ecf20Sopenharmony_ci */ 14788c2ecf20Sopenharmony_cistatic int velocity_init_dma_rings(struct velocity_info *vptr) 14798c2ecf20Sopenharmony_ci{ 14808c2ecf20Sopenharmony_ci struct velocity_opt *opt = &vptr->options; 14818c2ecf20Sopenharmony_ci const unsigned int rx_ring_size = opt->numrx * sizeof(struct rx_desc); 14828c2ecf20Sopenharmony_ci const unsigned int tx_ring_size = opt->numtx * sizeof(struct tx_desc); 14838c2ecf20Sopenharmony_ci dma_addr_t pool_dma; 14848c2ecf20Sopenharmony_ci void *pool; 14858c2ecf20Sopenharmony_ci unsigned int i; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci /* 14888c2ecf20Sopenharmony_ci * Allocate all RD/TD rings a single pool. 14898c2ecf20Sopenharmony_ci * 14908c2ecf20Sopenharmony_ci * dma_alloc_coherent() fulfills the requirement for 64 bytes 14918c2ecf20Sopenharmony_ci * alignment 14928c2ecf20Sopenharmony_ci */ 14938c2ecf20Sopenharmony_ci pool = dma_alloc_coherent(vptr->dev, tx_ring_size * vptr->tx.numq + 14948c2ecf20Sopenharmony_ci rx_ring_size, &pool_dma, GFP_ATOMIC); 14958c2ecf20Sopenharmony_ci if (!pool) { 14968c2ecf20Sopenharmony_ci dev_err(vptr->dev, "%s : DMA memory allocation failed.\n", 14978c2ecf20Sopenharmony_ci vptr->netdev->name); 14988c2ecf20Sopenharmony_ci return -ENOMEM; 14998c2ecf20Sopenharmony_ci } 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci vptr->rx.ring = pool; 15028c2ecf20Sopenharmony_ci vptr->rx.pool_dma = pool_dma; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci pool += rx_ring_size; 15058c2ecf20Sopenharmony_ci pool_dma += rx_ring_size; 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci for (i = 0; i < vptr->tx.numq; i++) { 15088c2ecf20Sopenharmony_ci vptr->tx.rings[i] = pool; 15098c2ecf20Sopenharmony_ci vptr->tx.pool_dma[i] = pool_dma; 15108c2ecf20Sopenharmony_ci pool += tx_ring_size; 15118c2ecf20Sopenharmony_ci pool_dma += tx_ring_size; 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci return 0; 15158c2ecf20Sopenharmony_ci} 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_cistatic void velocity_set_rxbufsize(struct velocity_info *vptr, int mtu) 15188c2ecf20Sopenharmony_ci{ 15198c2ecf20Sopenharmony_ci vptr->rx.buf_sz = (mtu <= ETH_DATA_LEN) ? PKT_BUF_SZ : mtu + 32; 15208c2ecf20Sopenharmony_ci} 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci/** 15238c2ecf20Sopenharmony_ci * velocity_alloc_rx_buf - allocate aligned receive buffer 15248c2ecf20Sopenharmony_ci * @vptr: velocity 15258c2ecf20Sopenharmony_ci * @idx: ring index 15268c2ecf20Sopenharmony_ci * 15278c2ecf20Sopenharmony_ci * Allocate a new full sized buffer for the reception of a frame and 15288c2ecf20Sopenharmony_ci * map it into PCI space for the hardware to use. The hardware 15298c2ecf20Sopenharmony_ci * requires *64* byte alignment of the buffer which makes life 15308c2ecf20Sopenharmony_ci * less fun than would be ideal. 15318c2ecf20Sopenharmony_ci */ 15328c2ecf20Sopenharmony_cistatic int velocity_alloc_rx_buf(struct velocity_info *vptr, int idx) 15338c2ecf20Sopenharmony_ci{ 15348c2ecf20Sopenharmony_ci struct rx_desc *rd = &(vptr->rx.ring[idx]); 15358c2ecf20Sopenharmony_ci struct velocity_rd_info *rd_info = &(vptr->rx.info[idx]); 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci rd_info->skb = netdev_alloc_skb(vptr->netdev, vptr->rx.buf_sz + 64); 15388c2ecf20Sopenharmony_ci if (rd_info->skb == NULL) 15398c2ecf20Sopenharmony_ci return -ENOMEM; 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci /* 15428c2ecf20Sopenharmony_ci * Do the gymnastics to get the buffer head for data at 15438c2ecf20Sopenharmony_ci * 64byte alignment. 15448c2ecf20Sopenharmony_ci */ 15458c2ecf20Sopenharmony_ci skb_reserve(rd_info->skb, 15468c2ecf20Sopenharmony_ci 64 - ((unsigned long) rd_info->skb->data & 63)); 15478c2ecf20Sopenharmony_ci rd_info->skb_dma = dma_map_single(vptr->dev, rd_info->skb->data, 15488c2ecf20Sopenharmony_ci vptr->rx.buf_sz, DMA_FROM_DEVICE); 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci /* 15518c2ecf20Sopenharmony_ci * Fill in the descriptor to match 15528c2ecf20Sopenharmony_ci */ 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci *((u32 *) & (rd->rdesc0)) = 0; 15558c2ecf20Sopenharmony_ci rd->size = cpu_to_le16(vptr->rx.buf_sz) | RX_INTEN; 15568c2ecf20Sopenharmony_ci rd->pa_low = cpu_to_le32(rd_info->skb_dma); 15578c2ecf20Sopenharmony_ci rd->pa_high = 0; 15588c2ecf20Sopenharmony_ci return 0; 15598c2ecf20Sopenharmony_ci} 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_cistatic int velocity_rx_refill(struct velocity_info *vptr) 15638c2ecf20Sopenharmony_ci{ 15648c2ecf20Sopenharmony_ci int dirty = vptr->rx.dirty, done = 0; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci do { 15678c2ecf20Sopenharmony_ci struct rx_desc *rd = vptr->rx.ring + dirty; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci /* Fine for an all zero Rx desc at init time as well */ 15708c2ecf20Sopenharmony_ci if (rd->rdesc0.len & OWNED_BY_NIC) 15718c2ecf20Sopenharmony_ci break; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci if (!vptr->rx.info[dirty].skb) { 15748c2ecf20Sopenharmony_ci if (velocity_alloc_rx_buf(vptr, dirty) < 0) 15758c2ecf20Sopenharmony_ci break; 15768c2ecf20Sopenharmony_ci } 15778c2ecf20Sopenharmony_ci done++; 15788c2ecf20Sopenharmony_ci dirty = (dirty < vptr->options.numrx - 1) ? dirty + 1 : 0; 15798c2ecf20Sopenharmony_ci } while (dirty != vptr->rx.curr); 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci if (done) { 15828c2ecf20Sopenharmony_ci vptr->rx.dirty = dirty; 15838c2ecf20Sopenharmony_ci vptr->rx.filled += done; 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci return done; 15878c2ecf20Sopenharmony_ci} 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci/** 15908c2ecf20Sopenharmony_ci * velocity_free_rd_ring - free receive ring 15918c2ecf20Sopenharmony_ci * @vptr: velocity to clean up 15928c2ecf20Sopenharmony_ci * 15938c2ecf20Sopenharmony_ci * Free the receive buffers for each ring slot and any 15948c2ecf20Sopenharmony_ci * attached socket buffers that need to go away. 15958c2ecf20Sopenharmony_ci */ 15968c2ecf20Sopenharmony_cistatic void velocity_free_rd_ring(struct velocity_info *vptr) 15978c2ecf20Sopenharmony_ci{ 15988c2ecf20Sopenharmony_ci int i; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci if (vptr->rx.info == NULL) 16018c2ecf20Sopenharmony_ci return; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci for (i = 0; i < vptr->options.numrx; i++) { 16048c2ecf20Sopenharmony_ci struct velocity_rd_info *rd_info = &(vptr->rx.info[i]); 16058c2ecf20Sopenharmony_ci struct rx_desc *rd = vptr->rx.ring + i; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci memset(rd, 0, sizeof(*rd)); 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci if (!rd_info->skb) 16108c2ecf20Sopenharmony_ci continue; 16118c2ecf20Sopenharmony_ci dma_unmap_single(vptr->dev, rd_info->skb_dma, vptr->rx.buf_sz, 16128c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 16138c2ecf20Sopenharmony_ci rd_info->skb_dma = 0; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci dev_kfree_skb(rd_info->skb); 16168c2ecf20Sopenharmony_ci rd_info->skb = NULL; 16178c2ecf20Sopenharmony_ci } 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci kfree(vptr->rx.info); 16208c2ecf20Sopenharmony_ci vptr->rx.info = NULL; 16218c2ecf20Sopenharmony_ci} 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci/** 16248c2ecf20Sopenharmony_ci * velocity_init_rd_ring - set up receive ring 16258c2ecf20Sopenharmony_ci * @vptr: velocity to configure 16268c2ecf20Sopenharmony_ci * 16278c2ecf20Sopenharmony_ci * Allocate and set up the receive buffers for each ring slot and 16288c2ecf20Sopenharmony_ci * assign them to the network adapter. 16298c2ecf20Sopenharmony_ci */ 16308c2ecf20Sopenharmony_cistatic int velocity_init_rd_ring(struct velocity_info *vptr) 16318c2ecf20Sopenharmony_ci{ 16328c2ecf20Sopenharmony_ci int ret = -ENOMEM; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci vptr->rx.info = kcalloc(vptr->options.numrx, 16358c2ecf20Sopenharmony_ci sizeof(struct velocity_rd_info), GFP_KERNEL); 16368c2ecf20Sopenharmony_ci if (!vptr->rx.info) 16378c2ecf20Sopenharmony_ci goto out; 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci velocity_init_rx_ring_indexes(vptr); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci if (velocity_rx_refill(vptr) != vptr->options.numrx) { 16428c2ecf20Sopenharmony_ci netdev_err(vptr->netdev, "failed to allocate RX buffer\n"); 16438c2ecf20Sopenharmony_ci velocity_free_rd_ring(vptr); 16448c2ecf20Sopenharmony_ci goto out; 16458c2ecf20Sopenharmony_ci } 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci ret = 0; 16488c2ecf20Sopenharmony_ciout: 16498c2ecf20Sopenharmony_ci return ret; 16508c2ecf20Sopenharmony_ci} 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci/** 16538c2ecf20Sopenharmony_ci * velocity_init_td_ring - set up transmit ring 16548c2ecf20Sopenharmony_ci * @vptr: velocity 16558c2ecf20Sopenharmony_ci * 16568c2ecf20Sopenharmony_ci * Set up the transmit ring and chain the ring pointers together. 16578c2ecf20Sopenharmony_ci * Returns zero on success or a negative posix errno code for 16588c2ecf20Sopenharmony_ci * failure. 16598c2ecf20Sopenharmony_ci */ 16608c2ecf20Sopenharmony_cistatic int velocity_init_td_ring(struct velocity_info *vptr) 16618c2ecf20Sopenharmony_ci{ 16628c2ecf20Sopenharmony_ci int j; 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci /* Init the TD ring entries */ 16658c2ecf20Sopenharmony_ci for (j = 0; j < vptr->tx.numq; j++) { 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci vptr->tx.infos[j] = kcalloc(vptr->options.numtx, 16688c2ecf20Sopenharmony_ci sizeof(struct velocity_td_info), 16698c2ecf20Sopenharmony_ci GFP_KERNEL); 16708c2ecf20Sopenharmony_ci if (!vptr->tx.infos[j]) { 16718c2ecf20Sopenharmony_ci while (--j >= 0) 16728c2ecf20Sopenharmony_ci kfree(vptr->tx.infos[j]); 16738c2ecf20Sopenharmony_ci return -ENOMEM; 16748c2ecf20Sopenharmony_ci } 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci vptr->tx.tail[j] = vptr->tx.curr[j] = vptr->tx.used[j] = 0; 16778c2ecf20Sopenharmony_ci } 16788c2ecf20Sopenharmony_ci return 0; 16798c2ecf20Sopenharmony_ci} 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci/** 16828c2ecf20Sopenharmony_ci * velocity_free_dma_rings - free PCI ring pointers 16838c2ecf20Sopenharmony_ci * @vptr: Velocity to free from 16848c2ecf20Sopenharmony_ci * 16858c2ecf20Sopenharmony_ci * Clean up the PCI ring buffers allocated to this velocity. 16868c2ecf20Sopenharmony_ci */ 16878c2ecf20Sopenharmony_cistatic void velocity_free_dma_rings(struct velocity_info *vptr) 16888c2ecf20Sopenharmony_ci{ 16898c2ecf20Sopenharmony_ci const int size = vptr->options.numrx * sizeof(struct rx_desc) + 16908c2ecf20Sopenharmony_ci vptr->options.numtx * sizeof(struct tx_desc) * vptr->tx.numq; 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci dma_free_coherent(vptr->dev, size, vptr->rx.ring, vptr->rx.pool_dma); 16938c2ecf20Sopenharmony_ci} 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_cistatic int velocity_init_rings(struct velocity_info *vptr, int mtu) 16968c2ecf20Sopenharmony_ci{ 16978c2ecf20Sopenharmony_ci int ret; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci velocity_set_rxbufsize(vptr, mtu); 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci ret = velocity_init_dma_rings(vptr); 17028c2ecf20Sopenharmony_ci if (ret < 0) 17038c2ecf20Sopenharmony_ci goto out; 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci ret = velocity_init_rd_ring(vptr); 17068c2ecf20Sopenharmony_ci if (ret < 0) 17078c2ecf20Sopenharmony_ci goto err_free_dma_rings_0; 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci ret = velocity_init_td_ring(vptr); 17108c2ecf20Sopenharmony_ci if (ret < 0) 17118c2ecf20Sopenharmony_ci goto err_free_rd_ring_1; 17128c2ecf20Sopenharmony_ciout: 17138c2ecf20Sopenharmony_ci return ret; 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_cierr_free_rd_ring_1: 17168c2ecf20Sopenharmony_ci velocity_free_rd_ring(vptr); 17178c2ecf20Sopenharmony_cierr_free_dma_rings_0: 17188c2ecf20Sopenharmony_ci velocity_free_dma_rings(vptr); 17198c2ecf20Sopenharmony_ci goto out; 17208c2ecf20Sopenharmony_ci} 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci/** 17238c2ecf20Sopenharmony_ci * velocity_free_tx_buf - free transmit buffer 17248c2ecf20Sopenharmony_ci * @vptr: velocity 17258c2ecf20Sopenharmony_ci * @tdinfo: buffer 17268c2ecf20Sopenharmony_ci * @td: transmit descriptor to free 17278c2ecf20Sopenharmony_ci * 17288c2ecf20Sopenharmony_ci * Release an transmit buffer. If the buffer was preallocated then 17298c2ecf20Sopenharmony_ci * recycle it, if not then unmap the buffer. 17308c2ecf20Sopenharmony_ci */ 17318c2ecf20Sopenharmony_cistatic void velocity_free_tx_buf(struct velocity_info *vptr, 17328c2ecf20Sopenharmony_ci struct velocity_td_info *tdinfo, struct tx_desc *td) 17338c2ecf20Sopenharmony_ci{ 17348c2ecf20Sopenharmony_ci struct sk_buff *skb = tdinfo->skb; 17358c2ecf20Sopenharmony_ci int i; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci /* 17388c2ecf20Sopenharmony_ci * Don't unmap the pre-allocated tx_bufs 17398c2ecf20Sopenharmony_ci */ 17408c2ecf20Sopenharmony_ci for (i = 0; i < tdinfo->nskb_dma; i++) { 17418c2ecf20Sopenharmony_ci size_t pktlen = max_t(size_t, skb->len, ETH_ZLEN); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci /* For scatter-gather */ 17448c2ecf20Sopenharmony_ci if (skb_shinfo(skb)->nr_frags > 0) 17458c2ecf20Sopenharmony_ci pktlen = max_t(size_t, pktlen, 17468c2ecf20Sopenharmony_ci td->td_buf[i].size & ~TD_QUEUE); 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci dma_unmap_single(vptr->dev, tdinfo->skb_dma[i], 17498c2ecf20Sopenharmony_ci le16_to_cpu(pktlen), DMA_TO_DEVICE); 17508c2ecf20Sopenharmony_ci } 17518c2ecf20Sopenharmony_ci dev_consume_skb_irq(skb); 17528c2ecf20Sopenharmony_ci tdinfo->skb = NULL; 17538c2ecf20Sopenharmony_ci} 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci/* 17568c2ecf20Sopenharmony_ci * FIXME: could we merge this with velocity_free_tx_buf ? 17578c2ecf20Sopenharmony_ci */ 17588c2ecf20Sopenharmony_cistatic void velocity_free_td_ring_entry(struct velocity_info *vptr, 17598c2ecf20Sopenharmony_ci int q, int n) 17608c2ecf20Sopenharmony_ci{ 17618c2ecf20Sopenharmony_ci struct velocity_td_info *td_info = &(vptr->tx.infos[q][n]); 17628c2ecf20Sopenharmony_ci int i; 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci if (td_info == NULL) 17658c2ecf20Sopenharmony_ci return; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci if (td_info->skb) { 17688c2ecf20Sopenharmony_ci for (i = 0; i < td_info->nskb_dma; i++) { 17698c2ecf20Sopenharmony_ci if (td_info->skb_dma[i]) { 17708c2ecf20Sopenharmony_ci dma_unmap_single(vptr->dev, td_info->skb_dma[i], 17718c2ecf20Sopenharmony_ci td_info->skb->len, DMA_TO_DEVICE); 17728c2ecf20Sopenharmony_ci td_info->skb_dma[i] = 0; 17738c2ecf20Sopenharmony_ci } 17748c2ecf20Sopenharmony_ci } 17758c2ecf20Sopenharmony_ci dev_kfree_skb(td_info->skb); 17768c2ecf20Sopenharmony_ci td_info->skb = NULL; 17778c2ecf20Sopenharmony_ci } 17788c2ecf20Sopenharmony_ci} 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci/** 17818c2ecf20Sopenharmony_ci * velocity_free_td_ring - free td ring 17828c2ecf20Sopenharmony_ci * @vptr: velocity 17838c2ecf20Sopenharmony_ci * 17848c2ecf20Sopenharmony_ci * Free up the transmit ring for this particular velocity adapter. 17858c2ecf20Sopenharmony_ci * We free the ring contents but not the ring itself. 17868c2ecf20Sopenharmony_ci */ 17878c2ecf20Sopenharmony_cistatic void velocity_free_td_ring(struct velocity_info *vptr) 17888c2ecf20Sopenharmony_ci{ 17898c2ecf20Sopenharmony_ci int i, j; 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci for (j = 0; j < vptr->tx.numq; j++) { 17928c2ecf20Sopenharmony_ci if (vptr->tx.infos[j] == NULL) 17938c2ecf20Sopenharmony_ci continue; 17948c2ecf20Sopenharmony_ci for (i = 0; i < vptr->options.numtx; i++) 17958c2ecf20Sopenharmony_ci velocity_free_td_ring_entry(vptr, j, i); 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci kfree(vptr->tx.infos[j]); 17988c2ecf20Sopenharmony_ci vptr->tx.infos[j] = NULL; 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci} 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_cistatic void velocity_free_rings(struct velocity_info *vptr) 18038c2ecf20Sopenharmony_ci{ 18048c2ecf20Sopenharmony_ci velocity_free_td_ring(vptr); 18058c2ecf20Sopenharmony_ci velocity_free_rd_ring(vptr); 18068c2ecf20Sopenharmony_ci velocity_free_dma_rings(vptr); 18078c2ecf20Sopenharmony_ci} 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci/** 18108c2ecf20Sopenharmony_ci * velocity_error - handle error from controller 18118c2ecf20Sopenharmony_ci * @vptr: velocity 18128c2ecf20Sopenharmony_ci * @status: card status 18138c2ecf20Sopenharmony_ci * 18148c2ecf20Sopenharmony_ci * Process an error report from the hardware and attempt to recover 18158c2ecf20Sopenharmony_ci * the card itself. At the moment we cannot recover from some 18168c2ecf20Sopenharmony_ci * theoretically impossible errors but this could be fixed using 18178c2ecf20Sopenharmony_ci * the pci_device_failed logic to bounce the hardware 18188c2ecf20Sopenharmony_ci * 18198c2ecf20Sopenharmony_ci */ 18208c2ecf20Sopenharmony_cistatic void velocity_error(struct velocity_info *vptr, int status) 18218c2ecf20Sopenharmony_ci{ 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci if (status & ISR_TXSTLI) { 18248c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci netdev_err(vptr->netdev, "TD structure error TDindex=%hx\n", 18278c2ecf20Sopenharmony_ci readw(®s->TDIdx[0])); 18288c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON(TXESR_TDSTR, ®s->TXESR); 18298c2ecf20Sopenharmony_ci writew(TRDCSR_RUN, ®s->TDCSRClr); 18308c2ecf20Sopenharmony_ci netif_stop_queue(vptr->netdev); 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci /* FIXME: port over the pci_device_failed code and use it 18338c2ecf20Sopenharmony_ci here */ 18348c2ecf20Sopenharmony_ci } 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci if (status & ISR_SRCI) { 18378c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 18388c2ecf20Sopenharmony_ci int linked; 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci if (vptr->options.spd_dpx == SPD_DPX_AUTO) { 18418c2ecf20Sopenharmony_ci vptr->mii_status = check_connection_type(regs); 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci /* 18448c2ecf20Sopenharmony_ci * If it is a 3119, disable frame bursting in 18458c2ecf20Sopenharmony_ci * halfduplex mode and enable it in fullduplex 18468c2ecf20Sopenharmony_ci * mode 18478c2ecf20Sopenharmony_ci */ 18488c2ecf20Sopenharmony_ci if (vptr->rev_id < REV_ID_VT3216_A0) { 18498c2ecf20Sopenharmony_ci if (vptr->mii_status & VELOCITY_DUPLEX_FULL) 18508c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON(TCR_TB2BDIS, ®s->TCR); 18518c2ecf20Sopenharmony_ci else 18528c2ecf20Sopenharmony_ci BYTE_REG_BITS_OFF(TCR_TB2BDIS, ®s->TCR); 18538c2ecf20Sopenharmony_ci } 18548c2ecf20Sopenharmony_ci /* 18558c2ecf20Sopenharmony_ci * Only enable CD heart beat counter in 10HD mode 18568c2ecf20Sopenharmony_ci */ 18578c2ecf20Sopenharmony_ci if (!(vptr->mii_status & VELOCITY_DUPLEX_FULL) && (vptr->mii_status & VELOCITY_SPEED_10)) 18588c2ecf20Sopenharmony_ci BYTE_REG_BITS_OFF(TESTCFG_HBDIS, ®s->TESTCFG); 18598c2ecf20Sopenharmony_ci else 18608c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON(TESTCFG_HBDIS, ®s->TESTCFG); 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci setup_queue_timers(vptr); 18638c2ecf20Sopenharmony_ci } 18648c2ecf20Sopenharmony_ci /* 18658c2ecf20Sopenharmony_ci * Get link status from PHYSR0 18668c2ecf20Sopenharmony_ci */ 18678c2ecf20Sopenharmony_ci linked = readb(®s->PHYSR0) & PHYSR0_LINKGD; 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci if (linked) { 18708c2ecf20Sopenharmony_ci vptr->mii_status &= ~VELOCITY_LINK_FAIL; 18718c2ecf20Sopenharmony_ci netif_carrier_on(vptr->netdev); 18728c2ecf20Sopenharmony_ci } else { 18738c2ecf20Sopenharmony_ci vptr->mii_status |= VELOCITY_LINK_FAIL; 18748c2ecf20Sopenharmony_ci netif_carrier_off(vptr->netdev); 18758c2ecf20Sopenharmony_ci } 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci velocity_print_link_status(vptr); 18788c2ecf20Sopenharmony_ci enable_flow_control_ability(vptr); 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci /* 18818c2ecf20Sopenharmony_ci * Re-enable auto-polling because SRCI will disable 18828c2ecf20Sopenharmony_ci * auto-polling 18838c2ecf20Sopenharmony_ci */ 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci enable_mii_autopoll(regs); 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci if (vptr->mii_status & VELOCITY_LINK_FAIL) 18888c2ecf20Sopenharmony_ci netif_stop_queue(vptr->netdev); 18898c2ecf20Sopenharmony_ci else 18908c2ecf20Sopenharmony_ci netif_wake_queue(vptr->netdev); 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci } 18938c2ecf20Sopenharmony_ci if (status & ISR_MIBFI) 18948c2ecf20Sopenharmony_ci velocity_update_hw_mibs(vptr); 18958c2ecf20Sopenharmony_ci if (status & ISR_LSTEI) 18968c2ecf20Sopenharmony_ci mac_rx_queue_wake(vptr->mac_regs); 18978c2ecf20Sopenharmony_ci} 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci/** 19008c2ecf20Sopenharmony_ci * tx_srv - transmit interrupt service 19018c2ecf20Sopenharmony_ci * @vptr: Velocity 19028c2ecf20Sopenharmony_ci * 19038c2ecf20Sopenharmony_ci * Scan the queues looking for transmitted packets that 19048c2ecf20Sopenharmony_ci * we can complete and clean up. Update any statistics as 19058c2ecf20Sopenharmony_ci * necessary/ 19068c2ecf20Sopenharmony_ci */ 19078c2ecf20Sopenharmony_cistatic int velocity_tx_srv(struct velocity_info *vptr) 19088c2ecf20Sopenharmony_ci{ 19098c2ecf20Sopenharmony_ci struct tx_desc *td; 19108c2ecf20Sopenharmony_ci int qnum; 19118c2ecf20Sopenharmony_ci int full = 0; 19128c2ecf20Sopenharmony_ci int idx; 19138c2ecf20Sopenharmony_ci int works = 0; 19148c2ecf20Sopenharmony_ci struct velocity_td_info *tdinfo; 19158c2ecf20Sopenharmony_ci struct net_device_stats *stats = &vptr->netdev->stats; 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci for (qnum = 0; qnum < vptr->tx.numq; qnum++) { 19188c2ecf20Sopenharmony_ci for (idx = vptr->tx.tail[qnum]; vptr->tx.used[qnum] > 0; 19198c2ecf20Sopenharmony_ci idx = (idx + 1) % vptr->options.numtx) { 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci /* 19228c2ecf20Sopenharmony_ci * Get Tx Descriptor 19238c2ecf20Sopenharmony_ci */ 19248c2ecf20Sopenharmony_ci td = &(vptr->tx.rings[qnum][idx]); 19258c2ecf20Sopenharmony_ci tdinfo = &(vptr->tx.infos[qnum][idx]); 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci if (td->tdesc0.len & OWNED_BY_NIC) 19288c2ecf20Sopenharmony_ci break; 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci if ((works++ > 15)) 19318c2ecf20Sopenharmony_ci break; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci if (td->tdesc0.TSR & TSR0_TERR) { 19348c2ecf20Sopenharmony_ci stats->tx_errors++; 19358c2ecf20Sopenharmony_ci stats->tx_dropped++; 19368c2ecf20Sopenharmony_ci if (td->tdesc0.TSR & TSR0_CDH) 19378c2ecf20Sopenharmony_ci stats->tx_heartbeat_errors++; 19388c2ecf20Sopenharmony_ci if (td->tdesc0.TSR & TSR0_CRS) 19398c2ecf20Sopenharmony_ci stats->tx_carrier_errors++; 19408c2ecf20Sopenharmony_ci if (td->tdesc0.TSR & TSR0_ABT) 19418c2ecf20Sopenharmony_ci stats->tx_aborted_errors++; 19428c2ecf20Sopenharmony_ci if (td->tdesc0.TSR & TSR0_OWC) 19438c2ecf20Sopenharmony_ci stats->tx_window_errors++; 19448c2ecf20Sopenharmony_ci } else { 19458c2ecf20Sopenharmony_ci stats->tx_packets++; 19468c2ecf20Sopenharmony_ci stats->tx_bytes += tdinfo->skb->len; 19478c2ecf20Sopenharmony_ci } 19488c2ecf20Sopenharmony_ci velocity_free_tx_buf(vptr, tdinfo, td); 19498c2ecf20Sopenharmony_ci vptr->tx.used[qnum]--; 19508c2ecf20Sopenharmony_ci } 19518c2ecf20Sopenharmony_ci vptr->tx.tail[qnum] = idx; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci if (AVAIL_TD(vptr, qnum) < 1) 19548c2ecf20Sopenharmony_ci full = 1; 19558c2ecf20Sopenharmony_ci } 19568c2ecf20Sopenharmony_ci /* 19578c2ecf20Sopenharmony_ci * Look to see if we should kick the transmit network 19588c2ecf20Sopenharmony_ci * layer for more work. 19598c2ecf20Sopenharmony_ci */ 19608c2ecf20Sopenharmony_ci if (netif_queue_stopped(vptr->netdev) && (full == 0) && 19618c2ecf20Sopenharmony_ci (!(vptr->mii_status & VELOCITY_LINK_FAIL))) { 19628c2ecf20Sopenharmony_ci netif_wake_queue(vptr->netdev); 19638c2ecf20Sopenharmony_ci } 19648c2ecf20Sopenharmony_ci return works; 19658c2ecf20Sopenharmony_ci} 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci/** 19688c2ecf20Sopenharmony_ci * velocity_rx_csum - checksum process 19698c2ecf20Sopenharmony_ci * @rd: receive packet descriptor 19708c2ecf20Sopenharmony_ci * @skb: network layer packet buffer 19718c2ecf20Sopenharmony_ci * 19728c2ecf20Sopenharmony_ci * Process the status bits for the received packet and determine 19738c2ecf20Sopenharmony_ci * if the checksum was computed and verified by the hardware 19748c2ecf20Sopenharmony_ci */ 19758c2ecf20Sopenharmony_cistatic inline void velocity_rx_csum(struct rx_desc *rd, struct sk_buff *skb) 19768c2ecf20Sopenharmony_ci{ 19778c2ecf20Sopenharmony_ci skb_checksum_none_assert(skb); 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci if (rd->rdesc1.CSM & CSM_IPKT) { 19808c2ecf20Sopenharmony_ci if (rd->rdesc1.CSM & CSM_IPOK) { 19818c2ecf20Sopenharmony_ci if ((rd->rdesc1.CSM & CSM_TCPKT) || 19828c2ecf20Sopenharmony_ci (rd->rdesc1.CSM & CSM_UDPKT)) { 19838c2ecf20Sopenharmony_ci if (!(rd->rdesc1.CSM & CSM_TUPOK)) 19848c2ecf20Sopenharmony_ci return; 19858c2ecf20Sopenharmony_ci } 19868c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 19878c2ecf20Sopenharmony_ci } 19888c2ecf20Sopenharmony_ci } 19898c2ecf20Sopenharmony_ci} 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci/** 19928c2ecf20Sopenharmony_ci * velocity_rx_copy - in place Rx copy for small packets 19938c2ecf20Sopenharmony_ci * @rx_skb: network layer packet buffer candidate 19948c2ecf20Sopenharmony_ci * @pkt_size: received data size 19958c2ecf20Sopenharmony_ci * @vptr: velocity adapter 19968c2ecf20Sopenharmony_ci * 19978c2ecf20Sopenharmony_ci * Replace the current skb that is scheduled for Rx processing by a 19988c2ecf20Sopenharmony_ci * shorter, immediately allocated skb, if the received packet is small 19998c2ecf20Sopenharmony_ci * enough. This function returns a negative value if the received 20008c2ecf20Sopenharmony_ci * packet is too big or if memory is exhausted. 20018c2ecf20Sopenharmony_ci */ 20028c2ecf20Sopenharmony_cistatic int velocity_rx_copy(struct sk_buff **rx_skb, int pkt_size, 20038c2ecf20Sopenharmony_ci struct velocity_info *vptr) 20048c2ecf20Sopenharmony_ci{ 20058c2ecf20Sopenharmony_ci int ret = -1; 20068c2ecf20Sopenharmony_ci if (pkt_size < rx_copybreak) { 20078c2ecf20Sopenharmony_ci struct sk_buff *new_skb; 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci new_skb = netdev_alloc_skb_ip_align(vptr->netdev, pkt_size); 20108c2ecf20Sopenharmony_ci if (new_skb) { 20118c2ecf20Sopenharmony_ci new_skb->ip_summed = rx_skb[0]->ip_summed; 20128c2ecf20Sopenharmony_ci skb_copy_from_linear_data(*rx_skb, new_skb->data, pkt_size); 20138c2ecf20Sopenharmony_ci *rx_skb = new_skb; 20148c2ecf20Sopenharmony_ci ret = 0; 20158c2ecf20Sopenharmony_ci } 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci } 20188c2ecf20Sopenharmony_ci return ret; 20198c2ecf20Sopenharmony_ci} 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci/** 20228c2ecf20Sopenharmony_ci * velocity_iph_realign - IP header alignment 20238c2ecf20Sopenharmony_ci * @vptr: velocity we are handling 20248c2ecf20Sopenharmony_ci * @skb: network layer packet buffer 20258c2ecf20Sopenharmony_ci * @pkt_size: received data size 20268c2ecf20Sopenharmony_ci * 20278c2ecf20Sopenharmony_ci * Align IP header on a 2 bytes boundary. This behavior can be 20288c2ecf20Sopenharmony_ci * configured by the user. 20298c2ecf20Sopenharmony_ci */ 20308c2ecf20Sopenharmony_cistatic inline void velocity_iph_realign(struct velocity_info *vptr, 20318c2ecf20Sopenharmony_ci struct sk_buff *skb, int pkt_size) 20328c2ecf20Sopenharmony_ci{ 20338c2ecf20Sopenharmony_ci if (vptr->flags & VELOCITY_FLAGS_IP_ALIGN) { 20348c2ecf20Sopenharmony_ci memmove(skb->data + 2, skb->data, pkt_size); 20358c2ecf20Sopenharmony_ci skb_reserve(skb, 2); 20368c2ecf20Sopenharmony_ci } 20378c2ecf20Sopenharmony_ci} 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci/** 20408c2ecf20Sopenharmony_ci * velocity_receive_frame - received packet processor 20418c2ecf20Sopenharmony_ci * @vptr: velocity we are handling 20428c2ecf20Sopenharmony_ci * @idx: ring index 20438c2ecf20Sopenharmony_ci * 20448c2ecf20Sopenharmony_ci * A packet has arrived. We process the packet and if appropriate 20458c2ecf20Sopenharmony_ci * pass the frame up the network stack 20468c2ecf20Sopenharmony_ci */ 20478c2ecf20Sopenharmony_cistatic int velocity_receive_frame(struct velocity_info *vptr, int idx) 20488c2ecf20Sopenharmony_ci{ 20498c2ecf20Sopenharmony_ci struct net_device_stats *stats = &vptr->netdev->stats; 20508c2ecf20Sopenharmony_ci struct velocity_rd_info *rd_info = &(vptr->rx.info[idx]); 20518c2ecf20Sopenharmony_ci struct rx_desc *rd = &(vptr->rx.ring[idx]); 20528c2ecf20Sopenharmony_ci int pkt_len = le16_to_cpu(rd->rdesc0.len) & 0x3fff; 20538c2ecf20Sopenharmony_ci struct sk_buff *skb; 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci if (unlikely(rd->rdesc0.RSR & (RSR_STP | RSR_EDP | RSR_RL))) { 20568c2ecf20Sopenharmony_ci if (rd->rdesc0.RSR & (RSR_STP | RSR_EDP)) 20578c2ecf20Sopenharmony_ci netdev_err(vptr->netdev, "received frame spans multiple RDs\n"); 20588c2ecf20Sopenharmony_ci stats->rx_length_errors++; 20598c2ecf20Sopenharmony_ci return -EINVAL; 20608c2ecf20Sopenharmony_ci } 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci if (rd->rdesc0.RSR & RSR_MAR) 20638c2ecf20Sopenharmony_ci stats->multicast++; 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci skb = rd_info->skb; 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(vptr->dev, rd_info->skb_dma, 20688c2ecf20Sopenharmony_ci vptr->rx.buf_sz, DMA_FROM_DEVICE); 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci velocity_rx_csum(rd, skb); 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci if (velocity_rx_copy(&skb, pkt_len, vptr) < 0) { 20738c2ecf20Sopenharmony_ci velocity_iph_realign(vptr, skb, pkt_len); 20748c2ecf20Sopenharmony_ci rd_info->skb = NULL; 20758c2ecf20Sopenharmony_ci dma_unmap_single(vptr->dev, rd_info->skb_dma, vptr->rx.buf_sz, 20768c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 20778c2ecf20Sopenharmony_ci } else { 20788c2ecf20Sopenharmony_ci dma_sync_single_for_device(vptr->dev, rd_info->skb_dma, 20798c2ecf20Sopenharmony_ci vptr->rx.buf_sz, DMA_FROM_DEVICE); 20808c2ecf20Sopenharmony_ci } 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci skb_put(skb, pkt_len - 4); 20838c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, vptr->netdev); 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci if (rd->rdesc0.RSR & RSR_DETAG) { 20868c2ecf20Sopenharmony_ci u16 vid = swab16(le16_to_cpu(rd->rdesc1.PQTAG)); 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid); 20898c2ecf20Sopenharmony_ci } 20908c2ecf20Sopenharmony_ci netif_receive_skb(skb); 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci stats->rx_bytes += pkt_len; 20938c2ecf20Sopenharmony_ci stats->rx_packets++; 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci return 0; 20968c2ecf20Sopenharmony_ci} 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci/** 20998c2ecf20Sopenharmony_ci * velocity_rx_srv - service RX interrupt 21008c2ecf20Sopenharmony_ci * @vptr: velocity 21018c2ecf20Sopenharmony_ci * @budget_left: remaining budget 21028c2ecf20Sopenharmony_ci * 21038c2ecf20Sopenharmony_ci * Walk the receive ring of the velocity adapter and remove 21048c2ecf20Sopenharmony_ci * any received packets from the receive queue. Hand the ring 21058c2ecf20Sopenharmony_ci * slots back to the adapter for reuse. 21068c2ecf20Sopenharmony_ci */ 21078c2ecf20Sopenharmony_cistatic int velocity_rx_srv(struct velocity_info *vptr, int budget_left) 21088c2ecf20Sopenharmony_ci{ 21098c2ecf20Sopenharmony_ci struct net_device_stats *stats = &vptr->netdev->stats; 21108c2ecf20Sopenharmony_ci int rd_curr = vptr->rx.curr; 21118c2ecf20Sopenharmony_ci int works = 0; 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci while (works < budget_left) { 21148c2ecf20Sopenharmony_ci struct rx_desc *rd = vptr->rx.ring + rd_curr; 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci if (!vptr->rx.info[rd_curr].skb) 21178c2ecf20Sopenharmony_ci break; 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_ci if (rd->rdesc0.len & OWNED_BY_NIC) 21208c2ecf20Sopenharmony_ci break; 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci rmb(); 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci /* 21258c2ecf20Sopenharmony_ci * Don't drop CE or RL error frame although RXOK is off 21268c2ecf20Sopenharmony_ci */ 21278c2ecf20Sopenharmony_ci if (rd->rdesc0.RSR & (RSR_RXOK | RSR_CE | RSR_RL)) { 21288c2ecf20Sopenharmony_ci if (velocity_receive_frame(vptr, rd_curr) < 0) 21298c2ecf20Sopenharmony_ci stats->rx_dropped++; 21308c2ecf20Sopenharmony_ci } else { 21318c2ecf20Sopenharmony_ci if (rd->rdesc0.RSR & RSR_CRC) 21328c2ecf20Sopenharmony_ci stats->rx_crc_errors++; 21338c2ecf20Sopenharmony_ci if (rd->rdesc0.RSR & RSR_FAE) 21348c2ecf20Sopenharmony_ci stats->rx_frame_errors++; 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci stats->rx_dropped++; 21378c2ecf20Sopenharmony_ci } 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci rd->size |= RX_INTEN; 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci rd_curr++; 21428c2ecf20Sopenharmony_ci if (rd_curr >= vptr->options.numrx) 21438c2ecf20Sopenharmony_ci rd_curr = 0; 21448c2ecf20Sopenharmony_ci works++; 21458c2ecf20Sopenharmony_ci } 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci vptr->rx.curr = rd_curr; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci if ((works > 0) && (velocity_rx_refill(vptr) > 0)) 21508c2ecf20Sopenharmony_ci velocity_give_many_rx_descs(vptr); 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci VAR_USED(stats); 21538c2ecf20Sopenharmony_ci return works; 21548c2ecf20Sopenharmony_ci} 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_cistatic int velocity_poll(struct napi_struct *napi, int budget) 21578c2ecf20Sopenharmony_ci{ 21588c2ecf20Sopenharmony_ci struct velocity_info *vptr = container_of(napi, 21598c2ecf20Sopenharmony_ci struct velocity_info, napi); 21608c2ecf20Sopenharmony_ci unsigned int rx_done; 21618c2ecf20Sopenharmony_ci unsigned long flags; 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci /* 21648c2ecf20Sopenharmony_ci * Do rx and tx twice for performance (taken from the VIA 21658c2ecf20Sopenharmony_ci * out-of-tree driver). 21668c2ecf20Sopenharmony_ci */ 21678c2ecf20Sopenharmony_ci rx_done = velocity_rx_srv(vptr, budget); 21688c2ecf20Sopenharmony_ci spin_lock_irqsave(&vptr->lock, flags); 21698c2ecf20Sopenharmony_ci velocity_tx_srv(vptr); 21708c2ecf20Sopenharmony_ci /* If budget not fully consumed, exit the polling mode */ 21718c2ecf20Sopenharmony_ci if (rx_done < budget) { 21728c2ecf20Sopenharmony_ci napi_complete_done(napi, rx_done); 21738c2ecf20Sopenharmony_ci mac_enable_int(vptr->mac_regs); 21748c2ecf20Sopenharmony_ci } 21758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vptr->lock, flags); 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci return rx_done; 21788c2ecf20Sopenharmony_ci} 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci/** 21818c2ecf20Sopenharmony_ci * velocity_intr - interrupt callback 21828c2ecf20Sopenharmony_ci * @irq: interrupt number 21838c2ecf20Sopenharmony_ci * @dev_instance: interrupting device 21848c2ecf20Sopenharmony_ci * 21858c2ecf20Sopenharmony_ci * Called whenever an interrupt is generated by the velocity 21868c2ecf20Sopenharmony_ci * adapter IRQ line. We may not be the source of the interrupt 21878c2ecf20Sopenharmony_ci * and need to identify initially if we are, and if not exit as 21888c2ecf20Sopenharmony_ci * efficiently as possible. 21898c2ecf20Sopenharmony_ci */ 21908c2ecf20Sopenharmony_cistatic irqreturn_t velocity_intr(int irq, void *dev_instance) 21918c2ecf20Sopenharmony_ci{ 21928c2ecf20Sopenharmony_ci struct net_device *dev = dev_instance; 21938c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 21948c2ecf20Sopenharmony_ci u32 isr_status; 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci spin_lock(&vptr->lock); 21978c2ecf20Sopenharmony_ci isr_status = mac_read_isr(vptr->mac_regs); 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci /* Not us ? */ 22008c2ecf20Sopenharmony_ci if (isr_status == 0) { 22018c2ecf20Sopenharmony_ci spin_unlock(&vptr->lock); 22028c2ecf20Sopenharmony_ci return IRQ_NONE; 22038c2ecf20Sopenharmony_ci } 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci /* Ack the interrupt */ 22068c2ecf20Sopenharmony_ci mac_write_isr(vptr->mac_regs, isr_status); 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci if (likely(napi_schedule_prep(&vptr->napi))) { 22098c2ecf20Sopenharmony_ci mac_disable_int(vptr->mac_regs); 22108c2ecf20Sopenharmony_ci __napi_schedule(&vptr->napi); 22118c2ecf20Sopenharmony_ci } 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci if (isr_status & (~(ISR_PRXI | ISR_PPRXI | ISR_PTXI | ISR_PPTXI))) 22148c2ecf20Sopenharmony_ci velocity_error(vptr, isr_status); 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci spin_unlock(&vptr->lock); 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci return IRQ_HANDLED; 22198c2ecf20Sopenharmony_ci} 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci/** 22228c2ecf20Sopenharmony_ci * velocity_open - interface activation callback 22238c2ecf20Sopenharmony_ci * @dev: network layer device to open 22248c2ecf20Sopenharmony_ci * 22258c2ecf20Sopenharmony_ci * Called when the network layer brings the interface up. Returns 22268c2ecf20Sopenharmony_ci * a negative posix error code on failure, or zero on success. 22278c2ecf20Sopenharmony_ci * 22288c2ecf20Sopenharmony_ci * All the ring allocation and set up is done on open for this 22298c2ecf20Sopenharmony_ci * adapter to minimise memory usage when inactive 22308c2ecf20Sopenharmony_ci */ 22318c2ecf20Sopenharmony_cistatic int velocity_open(struct net_device *dev) 22328c2ecf20Sopenharmony_ci{ 22338c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 22348c2ecf20Sopenharmony_ci int ret; 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci ret = velocity_init_rings(vptr, dev->mtu); 22378c2ecf20Sopenharmony_ci if (ret < 0) 22388c2ecf20Sopenharmony_ci goto out; 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci /* Ensure chip is running */ 22418c2ecf20Sopenharmony_ci velocity_set_power_state(vptr, PCI_D0); 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci velocity_init_registers(vptr, VELOCITY_INIT_COLD); 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_ci ret = request_irq(dev->irq, velocity_intr, IRQF_SHARED, 22468c2ecf20Sopenharmony_ci dev->name, dev); 22478c2ecf20Sopenharmony_ci if (ret < 0) { 22488c2ecf20Sopenharmony_ci /* Power down the chip */ 22498c2ecf20Sopenharmony_ci velocity_set_power_state(vptr, PCI_D3hot); 22508c2ecf20Sopenharmony_ci velocity_free_rings(vptr); 22518c2ecf20Sopenharmony_ci goto out; 22528c2ecf20Sopenharmony_ci } 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci velocity_give_many_rx_descs(vptr); 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci mac_enable_int(vptr->mac_regs); 22578c2ecf20Sopenharmony_ci netif_start_queue(dev); 22588c2ecf20Sopenharmony_ci napi_enable(&vptr->napi); 22598c2ecf20Sopenharmony_ci vptr->flags |= VELOCITY_FLAGS_OPENED; 22608c2ecf20Sopenharmony_ciout: 22618c2ecf20Sopenharmony_ci return ret; 22628c2ecf20Sopenharmony_ci} 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci/** 22658c2ecf20Sopenharmony_ci * velocity_shutdown - shut down the chip 22668c2ecf20Sopenharmony_ci * @vptr: velocity to deactivate 22678c2ecf20Sopenharmony_ci * 22688c2ecf20Sopenharmony_ci * Shuts down the internal operations of the velocity and 22698c2ecf20Sopenharmony_ci * disables interrupts, autopolling, transmit and receive 22708c2ecf20Sopenharmony_ci */ 22718c2ecf20Sopenharmony_cistatic void velocity_shutdown(struct velocity_info *vptr) 22728c2ecf20Sopenharmony_ci{ 22738c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 22748c2ecf20Sopenharmony_ci mac_disable_int(regs); 22758c2ecf20Sopenharmony_ci writel(CR0_STOP, ®s->CR0Set); 22768c2ecf20Sopenharmony_ci writew(0xFFFF, ®s->TDCSRClr); 22778c2ecf20Sopenharmony_ci writeb(0xFF, ®s->RDCSRClr); 22788c2ecf20Sopenharmony_ci safe_disable_mii_autopoll(regs); 22798c2ecf20Sopenharmony_ci mac_clear_isr(regs); 22808c2ecf20Sopenharmony_ci} 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci/** 22838c2ecf20Sopenharmony_ci * velocity_change_mtu - MTU change callback 22848c2ecf20Sopenharmony_ci * @dev: network device 22858c2ecf20Sopenharmony_ci * @new_mtu: desired MTU 22868c2ecf20Sopenharmony_ci * 22878c2ecf20Sopenharmony_ci * Handle requests from the networking layer for MTU change on 22888c2ecf20Sopenharmony_ci * this interface. It gets called on a change by the network layer. 22898c2ecf20Sopenharmony_ci * Return zero for success or negative posix error code. 22908c2ecf20Sopenharmony_ci */ 22918c2ecf20Sopenharmony_cistatic int velocity_change_mtu(struct net_device *dev, int new_mtu) 22928c2ecf20Sopenharmony_ci{ 22938c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 22948c2ecf20Sopenharmony_ci int ret = 0; 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci if (!netif_running(dev)) { 22978c2ecf20Sopenharmony_ci dev->mtu = new_mtu; 22988c2ecf20Sopenharmony_ci goto out_0; 22998c2ecf20Sopenharmony_ci } 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci if (dev->mtu != new_mtu) { 23028c2ecf20Sopenharmony_ci struct velocity_info *tmp_vptr; 23038c2ecf20Sopenharmony_ci unsigned long flags; 23048c2ecf20Sopenharmony_ci struct rx_info rx; 23058c2ecf20Sopenharmony_ci struct tx_info tx; 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_ci tmp_vptr = kzalloc(sizeof(*tmp_vptr), GFP_KERNEL); 23088c2ecf20Sopenharmony_ci if (!tmp_vptr) { 23098c2ecf20Sopenharmony_ci ret = -ENOMEM; 23108c2ecf20Sopenharmony_ci goto out_0; 23118c2ecf20Sopenharmony_ci } 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci tmp_vptr->netdev = dev; 23148c2ecf20Sopenharmony_ci tmp_vptr->pdev = vptr->pdev; 23158c2ecf20Sopenharmony_ci tmp_vptr->dev = vptr->dev; 23168c2ecf20Sopenharmony_ci tmp_vptr->options = vptr->options; 23178c2ecf20Sopenharmony_ci tmp_vptr->tx.numq = vptr->tx.numq; 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci ret = velocity_init_rings(tmp_vptr, new_mtu); 23208c2ecf20Sopenharmony_ci if (ret < 0) 23218c2ecf20Sopenharmony_ci goto out_free_tmp_vptr_1; 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci napi_disable(&vptr->napi); 23248c2ecf20Sopenharmony_ci 23258c2ecf20Sopenharmony_ci spin_lock_irqsave(&vptr->lock, flags); 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci netif_stop_queue(dev); 23288c2ecf20Sopenharmony_ci velocity_shutdown(vptr); 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci rx = vptr->rx; 23318c2ecf20Sopenharmony_ci tx = vptr->tx; 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci vptr->rx = tmp_vptr->rx; 23348c2ecf20Sopenharmony_ci vptr->tx = tmp_vptr->tx; 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci tmp_vptr->rx = rx; 23378c2ecf20Sopenharmony_ci tmp_vptr->tx = tx; 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci dev->mtu = new_mtu; 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci velocity_init_registers(vptr, VELOCITY_INIT_COLD); 23428c2ecf20Sopenharmony_ci 23438c2ecf20Sopenharmony_ci velocity_give_many_rx_descs(vptr); 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_ci napi_enable(&vptr->napi); 23468c2ecf20Sopenharmony_ci 23478c2ecf20Sopenharmony_ci mac_enable_int(vptr->mac_regs); 23488c2ecf20Sopenharmony_ci netif_start_queue(dev); 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vptr->lock, flags); 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci velocity_free_rings(tmp_vptr); 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ciout_free_tmp_vptr_1: 23558c2ecf20Sopenharmony_ci kfree(tmp_vptr); 23568c2ecf20Sopenharmony_ci } 23578c2ecf20Sopenharmony_ciout_0: 23588c2ecf20Sopenharmony_ci return ret; 23598c2ecf20Sopenharmony_ci} 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 23628c2ecf20Sopenharmony_ci/** 23638c2ecf20Sopenharmony_ci * velocity_poll_controller - Velocity Poll controller function 23648c2ecf20Sopenharmony_ci * @dev: network device 23658c2ecf20Sopenharmony_ci * 23668c2ecf20Sopenharmony_ci * 23678c2ecf20Sopenharmony_ci * Used by NETCONSOLE and other diagnostic tools to allow network I/P 23688c2ecf20Sopenharmony_ci * with interrupts disabled. 23698c2ecf20Sopenharmony_ci */ 23708c2ecf20Sopenharmony_cistatic void velocity_poll_controller(struct net_device *dev) 23718c2ecf20Sopenharmony_ci{ 23728c2ecf20Sopenharmony_ci disable_irq(dev->irq); 23738c2ecf20Sopenharmony_ci velocity_intr(dev->irq, dev); 23748c2ecf20Sopenharmony_ci enable_irq(dev->irq); 23758c2ecf20Sopenharmony_ci} 23768c2ecf20Sopenharmony_ci#endif 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci/** 23798c2ecf20Sopenharmony_ci * velocity_mii_ioctl - MII ioctl handler 23808c2ecf20Sopenharmony_ci * @dev: network device 23818c2ecf20Sopenharmony_ci * @ifr: the ifreq block for the ioctl 23828c2ecf20Sopenharmony_ci * @cmd: the command 23838c2ecf20Sopenharmony_ci * 23848c2ecf20Sopenharmony_ci * Process MII requests made via ioctl from the network layer. These 23858c2ecf20Sopenharmony_ci * are used by tools like kudzu to interrogate the link state of the 23868c2ecf20Sopenharmony_ci * hardware 23878c2ecf20Sopenharmony_ci */ 23888c2ecf20Sopenharmony_cistatic int velocity_mii_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 23898c2ecf20Sopenharmony_ci{ 23908c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 23918c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 23928c2ecf20Sopenharmony_ci unsigned long flags; 23938c2ecf20Sopenharmony_ci struct mii_ioctl_data *miidata = if_mii(ifr); 23948c2ecf20Sopenharmony_ci int err; 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_ci switch (cmd) { 23978c2ecf20Sopenharmony_ci case SIOCGMIIPHY: 23988c2ecf20Sopenharmony_ci miidata->phy_id = readb(®s->MIIADR) & 0x1f; 23998c2ecf20Sopenharmony_ci break; 24008c2ecf20Sopenharmony_ci case SIOCGMIIREG: 24018c2ecf20Sopenharmony_ci if (velocity_mii_read(vptr->mac_regs, miidata->reg_num & 0x1f, &(miidata->val_out)) < 0) 24028c2ecf20Sopenharmony_ci return -ETIMEDOUT; 24038c2ecf20Sopenharmony_ci break; 24048c2ecf20Sopenharmony_ci case SIOCSMIIREG: 24058c2ecf20Sopenharmony_ci spin_lock_irqsave(&vptr->lock, flags); 24068c2ecf20Sopenharmony_ci err = velocity_mii_write(vptr->mac_regs, miidata->reg_num & 0x1f, miidata->val_in); 24078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vptr->lock, flags); 24088c2ecf20Sopenharmony_ci check_connection_type(vptr->mac_regs); 24098c2ecf20Sopenharmony_ci if (err) 24108c2ecf20Sopenharmony_ci return err; 24118c2ecf20Sopenharmony_ci break; 24128c2ecf20Sopenharmony_ci default: 24138c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 24148c2ecf20Sopenharmony_ci } 24158c2ecf20Sopenharmony_ci return 0; 24168c2ecf20Sopenharmony_ci} 24178c2ecf20Sopenharmony_ci 24188c2ecf20Sopenharmony_ci/** 24198c2ecf20Sopenharmony_ci * velocity_ioctl - ioctl entry point 24208c2ecf20Sopenharmony_ci * @dev: network device 24218c2ecf20Sopenharmony_ci * @rq: interface request ioctl 24228c2ecf20Sopenharmony_ci * @cmd: command code 24238c2ecf20Sopenharmony_ci * 24248c2ecf20Sopenharmony_ci * Called when the user issues an ioctl request to the network 24258c2ecf20Sopenharmony_ci * device in question. The velocity interface supports MII. 24268c2ecf20Sopenharmony_ci */ 24278c2ecf20Sopenharmony_cistatic int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 24288c2ecf20Sopenharmony_ci{ 24298c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 24308c2ecf20Sopenharmony_ci int ret; 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci /* If we are asked for information and the device is power 24338c2ecf20Sopenharmony_ci saving then we need to bring the device back up to talk to it */ 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci if (!netif_running(dev)) 24368c2ecf20Sopenharmony_ci velocity_set_power_state(vptr, PCI_D0); 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci switch (cmd) { 24398c2ecf20Sopenharmony_ci case SIOCGMIIPHY: /* Get address of MII PHY in use. */ 24408c2ecf20Sopenharmony_ci case SIOCGMIIREG: /* Read MII PHY register. */ 24418c2ecf20Sopenharmony_ci case SIOCSMIIREG: /* Write to MII PHY register. */ 24428c2ecf20Sopenharmony_ci ret = velocity_mii_ioctl(dev, rq, cmd); 24438c2ecf20Sopenharmony_ci break; 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_ci default: 24468c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 24478c2ecf20Sopenharmony_ci } 24488c2ecf20Sopenharmony_ci if (!netif_running(dev)) 24498c2ecf20Sopenharmony_ci velocity_set_power_state(vptr, PCI_D3hot); 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci return ret; 24538c2ecf20Sopenharmony_ci} 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci/** 24568c2ecf20Sopenharmony_ci * velocity_get_status - statistics callback 24578c2ecf20Sopenharmony_ci * @dev: network device 24588c2ecf20Sopenharmony_ci * 24598c2ecf20Sopenharmony_ci * Callback from the network layer to allow driver statistics 24608c2ecf20Sopenharmony_ci * to be resynchronized with hardware collected state. In the 24618c2ecf20Sopenharmony_ci * case of the velocity we need to pull the MIB counters from 24628c2ecf20Sopenharmony_ci * the hardware into the counters before letting the network 24638c2ecf20Sopenharmony_ci * layer display them. 24648c2ecf20Sopenharmony_ci */ 24658c2ecf20Sopenharmony_cistatic struct net_device_stats *velocity_get_stats(struct net_device *dev) 24668c2ecf20Sopenharmony_ci{ 24678c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ci /* If the hardware is down, don't touch MII */ 24708c2ecf20Sopenharmony_ci if (!netif_running(dev)) 24718c2ecf20Sopenharmony_ci return &dev->stats; 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_ci spin_lock_irq(&vptr->lock); 24748c2ecf20Sopenharmony_ci velocity_update_hw_mibs(vptr); 24758c2ecf20Sopenharmony_ci spin_unlock_irq(&vptr->lock); 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_ci dev->stats.rx_packets = vptr->mib_counter[HW_MIB_ifRxAllPkts]; 24788c2ecf20Sopenharmony_ci dev->stats.rx_errors = vptr->mib_counter[HW_MIB_ifRxErrorPkts]; 24798c2ecf20Sopenharmony_ci dev->stats.rx_length_errors = vptr->mib_counter[HW_MIB_ifInRangeLengthErrors]; 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_ci// unsigned long rx_dropped; /* no space in linux buffers */ 24828c2ecf20Sopenharmony_ci dev->stats.collisions = vptr->mib_counter[HW_MIB_ifTxEtherCollisions]; 24838c2ecf20Sopenharmony_ci /* detailed rx_errors: */ 24848c2ecf20Sopenharmony_ci// unsigned long rx_length_errors; 24858c2ecf20Sopenharmony_ci// unsigned long rx_over_errors; /* receiver ring buff overflow */ 24868c2ecf20Sopenharmony_ci dev->stats.rx_crc_errors = vptr->mib_counter[HW_MIB_ifRxPktCRCE]; 24878c2ecf20Sopenharmony_ci// unsigned long rx_frame_errors; /* recv'd frame alignment error */ 24888c2ecf20Sopenharmony_ci// unsigned long rx_fifo_errors; /* recv'r fifo overrun */ 24898c2ecf20Sopenharmony_ci// unsigned long rx_missed_errors; /* receiver missed packet */ 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci /* detailed tx_errors */ 24928c2ecf20Sopenharmony_ci// unsigned long tx_fifo_errors; 24938c2ecf20Sopenharmony_ci 24948c2ecf20Sopenharmony_ci return &dev->stats; 24958c2ecf20Sopenharmony_ci} 24968c2ecf20Sopenharmony_ci 24978c2ecf20Sopenharmony_ci/** 24988c2ecf20Sopenharmony_ci * velocity_close - close adapter callback 24998c2ecf20Sopenharmony_ci * @dev: network device 25008c2ecf20Sopenharmony_ci * 25018c2ecf20Sopenharmony_ci * Callback from the network layer when the velocity is being 25028c2ecf20Sopenharmony_ci * deactivated by the network layer 25038c2ecf20Sopenharmony_ci */ 25048c2ecf20Sopenharmony_cistatic int velocity_close(struct net_device *dev) 25058c2ecf20Sopenharmony_ci{ 25068c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci napi_disable(&vptr->napi); 25098c2ecf20Sopenharmony_ci netif_stop_queue(dev); 25108c2ecf20Sopenharmony_ci velocity_shutdown(vptr); 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED) 25138c2ecf20Sopenharmony_ci velocity_get_ip(vptr); 25148c2ecf20Sopenharmony_ci 25158c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci velocity_free_rings(vptr); 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci vptr->flags &= (~VELOCITY_FLAGS_OPENED); 25208c2ecf20Sopenharmony_ci return 0; 25218c2ecf20Sopenharmony_ci} 25228c2ecf20Sopenharmony_ci 25238c2ecf20Sopenharmony_ci/** 25248c2ecf20Sopenharmony_ci * velocity_xmit - transmit packet callback 25258c2ecf20Sopenharmony_ci * @skb: buffer to transmit 25268c2ecf20Sopenharmony_ci * @dev: network device 25278c2ecf20Sopenharmony_ci * 25288c2ecf20Sopenharmony_ci * Called by the networ layer to request a packet is queued to 25298c2ecf20Sopenharmony_ci * the velocity. Returns zero on success. 25308c2ecf20Sopenharmony_ci */ 25318c2ecf20Sopenharmony_cistatic netdev_tx_t velocity_xmit(struct sk_buff *skb, 25328c2ecf20Sopenharmony_ci struct net_device *dev) 25338c2ecf20Sopenharmony_ci{ 25348c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 25358c2ecf20Sopenharmony_ci int qnum = 0; 25368c2ecf20Sopenharmony_ci struct tx_desc *td_ptr; 25378c2ecf20Sopenharmony_ci struct velocity_td_info *tdinfo; 25388c2ecf20Sopenharmony_ci unsigned long flags; 25398c2ecf20Sopenharmony_ci int pktlen; 25408c2ecf20Sopenharmony_ci int index, prev; 25418c2ecf20Sopenharmony_ci int i = 0; 25428c2ecf20Sopenharmony_ci 25438c2ecf20Sopenharmony_ci if (skb_padto(skb, ETH_ZLEN)) 25448c2ecf20Sopenharmony_ci goto out; 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci /* The hardware can handle at most 7 memory segments, so merge 25478c2ecf20Sopenharmony_ci * the skb if there are more */ 25488c2ecf20Sopenharmony_ci if (skb_shinfo(skb)->nr_frags > 6 && __skb_linearize(skb)) { 25498c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 25508c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 25518c2ecf20Sopenharmony_ci } 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci pktlen = skb_shinfo(skb)->nr_frags == 0 ? 25548c2ecf20Sopenharmony_ci max_t(unsigned int, skb->len, ETH_ZLEN) : 25558c2ecf20Sopenharmony_ci skb_headlen(skb); 25568c2ecf20Sopenharmony_ci 25578c2ecf20Sopenharmony_ci spin_lock_irqsave(&vptr->lock, flags); 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci index = vptr->tx.curr[qnum]; 25608c2ecf20Sopenharmony_ci td_ptr = &(vptr->tx.rings[qnum][index]); 25618c2ecf20Sopenharmony_ci tdinfo = &(vptr->tx.infos[qnum][index]); 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci td_ptr->tdesc1.TCR = TCR0_TIC; 25648c2ecf20Sopenharmony_ci td_ptr->td_buf[0].size &= ~TD_QUEUE; 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci /* 25678c2ecf20Sopenharmony_ci * Map the linear network buffer into PCI space and 25688c2ecf20Sopenharmony_ci * add it to the transmit ring. 25698c2ecf20Sopenharmony_ci */ 25708c2ecf20Sopenharmony_ci tdinfo->skb = skb; 25718c2ecf20Sopenharmony_ci tdinfo->skb_dma[0] = dma_map_single(vptr->dev, skb->data, pktlen, 25728c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 25738c2ecf20Sopenharmony_ci td_ptr->tdesc0.len = cpu_to_le16(pktlen); 25748c2ecf20Sopenharmony_ci td_ptr->td_buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]); 25758c2ecf20Sopenharmony_ci td_ptr->td_buf[0].pa_high = 0; 25768c2ecf20Sopenharmony_ci td_ptr->td_buf[0].size = cpu_to_le16(pktlen); 25778c2ecf20Sopenharmony_ci 25788c2ecf20Sopenharmony_ci /* Handle fragments */ 25798c2ecf20Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 25808c2ecf20Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci tdinfo->skb_dma[i + 1] = skb_frag_dma_map(vptr->dev, 25838c2ecf20Sopenharmony_ci frag, 0, 25848c2ecf20Sopenharmony_ci skb_frag_size(frag), 25858c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_ci td_ptr->td_buf[i + 1].pa_low = cpu_to_le32(tdinfo->skb_dma[i + 1]); 25888c2ecf20Sopenharmony_ci td_ptr->td_buf[i + 1].pa_high = 0; 25898c2ecf20Sopenharmony_ci td_ptr->td_buf[i + 1].size = cpu_to_le16(skb_frag_size(frag)); 25908c2ecf20Sopenharmony_ci } 25918c2ecf20Sopenharmony_ci tdinfo->nskb_dma = i + 1; 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci td_ptr->tdesc1.cmd = TCPLS_NORMAL + (tdinfo->nskb_dma + 1) * 16; 25948c2ecf20Sopenharmony_ci 25958c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 25968c2ecf20Sopenharmony_ci td_ptr->tdesc1.vlan = cpu_to_le16(skb_vlan_tag_get(skb)); 25978c2ecf20Sopenharmony_ci td_ptr->tdesc1.TCR |= TCR0_VETAG; 25988c2ecf20Sopenharmony_ci } 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_ci /* 26018c2ecf20Sopenharmony_ci * Handle hardware checksum 26028c2ecf20Sopenharmony_ci */ 26038c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 26048c2ecf20Sopenharmony_ci const struct iphdr *ip = ip_hdr(skb); 26058c2ecf20Sopenharmony_ci if (ip->protocol == IPPROTO_TCP) 26068c2ecf20Sopenharmony_ci td_ptr->tdesc1.TCR |= TCR0_TCPCK; 26078c2ecf20Sopenharmony_ci else if (ip->protocol == IPPROTO_UDP) 26088c2ecf20Sopenharmony_ci td_ptr->tdesc1.TCR |= (TCR0_UDPCK); 26098c2ecf20Sopenharmony_ci td_ptr->tdesc1.TCR |= TCR0_IPCK; 26108c2ecf20Sopenharmony_ci } 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci prev = index - 1; 26138c2ecf20Sopenharmony_ci if (prev < 0) 26148c2ecf20Sopenharmony_ci prev = vptr->options.numtx - 1; 26158c2ecf20Sopenharmony_ci td_ptr->tdesc0.len |= OWNED_BY_NIC; 26168c2ecf20Sopenharmony_ci vptr->tx.used[qnum]++; 26178c2ecf20Sopenharmony_ci vptr->tx.curr[qnum] = (index + 1) % vptr->options.numtx; 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci if (AVAIL_TD(vptr, qnum) < 1) 26208c2ecf20Sopenharmony_ci netif_stop_queue(dev); 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci td_ptr = &(vptr->tx.rings[qnum][prev]); 26238c2ecf20Sopenharmony_ci td_ptr->td_buf[0].size |= TD_QUEUE; 26248c2ecf20Sopenharmony_ci mac_tx_queue_wake(vptr->mac_regs, qnum); 26258c2ecf20Sopenharmony_ci 26268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vptr->lock, flags); 26278c2ecf20Sopenharmony_ciout: 26288c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 26298c2ecf20Sopenharmony_ci} 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_cistatic const struct net_device_ops velocity_netdev_ops = { 26328c2ecf20Sopenharmony_ci .ndo_open = velocity_open, 26338c2ecf20Sopenharmony_ci .ndo_stop = velocity_close, 26348c2ecf20Sopenharmony_ci .ndo_start_xmit = velocity_xmit, 26358c2ecf20Sopenharmony_ci .ndo_get_stats = velocity_get_stats, 26368c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 26378c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 26388c2ecf20Sopenharmony_ci .ndo_set_rx_mode = velocity_set_multi, 26398c2ecf20Sopenharmony_ci .ndo_change_mtu = velocity_change_mtu, 26408c2ecf20Sopenharmony_ci .ndo_do_ioctl = velocity_ioctl, 26418c2ecf20Sopenharmony_ci .ndo_vlan_rx_add_vid = velocity_vlan_rx_add_vid, 26428c2ecf20Sopenharmony_ci .ndo_vlan_rx_kill_vid = velocity_vlan_rx_kill_vid, 26438c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 26448c2ecf20Sopenharmony_ci .ndo_poll_controller = velocity_poll_controller, 26458c2ecf20Sopenharmony_ci#endif 26468c2ecf20Sopenharmony_ci}; 26478c2ecf20Sopenharmony_ci 26488c2ecf20Sopenharmony_ci/** 26498c2ecf20Sopenharmony_ci * velocity_init_info - init private data 26508c2ecf20Sopenharmony_ci * @vptr: Velocity info 26518c2ecf20Sopenharmony_ci * @info: Board type 26528c2ecf20Sopenharmony_ci * 26538c2ecf20Sopenharmony_ci * Set up the initial velocity_info struct for the device that has been 26548c2ecf20Sopenharmony_ci * discovered. 26558c2ecf20Sopenharmony_ci */ 26568c2ecf20Sopenharmony_cistatic void velocity_init_info(struct velocity_info *vptr, 26578c2ecf20Sopenharmony_ci const struct velocity_info_tbl *info) 26588c2ecf20Sopenharmony_ci{ 26598c2ecf20Sopenharmony_ci vptr->chip_id = info->chip_id; 26608c2ecf20Sopenharmony_ci vptr->tx.numq = info->txqueue; 26618c2ecf20Sopenharmony_ci vptr->multicast_limit = MCAM_SIZE; 26628c2ecf20Sopenharmony_ci spin_lock_init(&vptr->lock); 26638c2ecf20Sopenharmony_ci} 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ci/** 26668c2ecf20Sopenharmony_ci * velocity_get_pci_info - retrieve PCI info for device 26678c2ecf20Sopenharmony_ci * @vptr: velocity device 26688c2ecf20Sopenharmony_ci * 26698c2ecf20Sopenharmony_ci * Retrieve the PCI configuration space data that interests us from 26708c2ecf20Sopenharmony_ci * the kernel PCI layer 26718c2ecf20Sopenharmony_ci */ 26728c2ecf20Sopenharmony_cistatic int velocity_get_pci_info(struct velocity_info *vptr) 26738c2ecf20Sopenharmony_ci{ 26748c2ecf20Sopenharmony_ci struct pci_dev *pdev = vptr->pdev; 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_ci pci_set_master(pdev); 26778c2ecf20Sopenharmony_ci 26788c2ecf20Sopenharmony_ci vptr->ioaddr = pci_resource_start(pdev, 0); 26798c2ecf20Sopenharmony_ci vptr->memaddr = pci_resource_start(pdev, 1); 26808c2ecf20Sopenharmony_ci 26818c2ecf20Sopenharmony_ci if (!(pci_resource_flags(pdev, 0) & IORESOURCE_IO)) { 26828c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 26838c2ecf20Sopenharmony_ci "region #0 is not an I/O resource, aborting.\n"); 26848c2ecf20Sopenharmony_ci return -EINVAL; 26858c2ecf20Sopenharmony_ci } 26868c2ecf20Sopenharmony_ci 26878c2ecf20Sopenharmony_ci if ((pci_resource_flags(pdev, 1) & IORESOURCE_IO)) { 26888c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 26898c2ecf20Sopenharmony_ci "region #1 is an I/O resource, aborting.\n"); 26908c2ecf20Sopenharmony_ci return -EINVAL; 26918c2ecf20Sopenharmony_ci } 26928c2ecf20Sopenharmony_ci 26938c2ecf20Sopenharmony_ci if (pci_resource_len(pdev, 1) < VELOCITY_IO_SIZE) { 26948c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "region #1 is too small.\n"); 26958c2ecf20Sopenharmony_ci return -EINVAL; 26968c2ecf20Sopenharmony_ci } 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci return 0; 26998c2ecf20Sopenharmony_ci} 27008c2ecf20Sopenharmony_ci 27018c2ecf20Sopenharmony_ci/** 27028c2ecf20Sopenharmony_ci * velocity_get_platform_info - retrieve platform info for device 27038c2ecf20Sopenharmony_ci * @vptr: velocity device 27048c2ecf20Sopenharmony_ci * 27058c2ecf20Sopenharmony_ci * Retrieve the Platform configuration data that interests us 27068c2ecf20Sopenharmony_ci */ 27078c2ecf20Sopenharmony_cistatic int velocity_get_platform_info(struct velocity_info *vptr) 27088c2ecf20Sopenharmony_ci{ 27098c2ecf20Sopenharmony_ci struct resource res; 27108c2ecf20Sopenharmony_ci int ret; 27118c2ecf20Sopenharmony_ci 27128c2ecf20Sopenharmony_ci if (of_get_property(vptr->dev->of_node, "no-eeprom", NULL)) 27138c2ecf20Sopenharmony_ci vptr->no_eeprom = 1; 27148c2ecf20Sopenharmony_ci 27158c2ecf20Sopenharmony_ci ret = of_address_to_resource(vptr->dev->of_node, 0, &res); 27168c2ecf20Sopenharmony_ci if (ret) { 27178c2ecf20Sopenharmony_ci dev_err(vptr->dev, "unable to find memory address\n"); 27188c2ecf20Sopenharmony_ci return ret; 27198c2ecf20Sopenharmony_ci } 27208c2ecf20Sopenharmony_ci 27218c2ecf20Sopenharmony_ci vptr->memaddr = res.start; 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_ci if (resource_size(&res) < VELOCITY_IO_SIZE) { 27248c2ecf20Sopenharmony_ci dev_err(vptr->dev, "memory region is too small.\n"); 27258c2ecf20Sopenharmony_ci return -EINVAL; 27268c2ecf20Sopenharmony_ci } 27278c2ecf20Sopenharmony_ci 27288c2ecf20Sopenharmony_ci return 0; 27298c2ecf20Sopenharmony_ci} 27308c2ecf20Sopenharmony_ci 27318c2ecf20Sopenharmony_ci/** 27328c2ecf20Sopenharmony_ci * velocity_print_info - per driver data 27338c2ecf20Sopenharmony_ci * @vptr: velocity 27348c2ecf20Sopenharmony_ci * 27358c2ecf20Sopenharmony_ci * Print per driver data as the kernel driver finds Velocity 27368c2ecf20Sopenharmony_ci * hardware 27378c2ecf20Sopenharmony_ci */ 27388c2ecf20Sopenharmony_cistatic void velocity_print_info(struct velocity_info *vptr) 27398c2ecf20Sopenharmony_ci{ 27408c2ecf20Sopenharmony_ci netdev_info(vptr->netdev, "%s - Ethernet Address: %pM\n", 27418c2ecf20Sopenharmony_ci get_chip_name(vptr->chip_id), vptr->netdev->dev_addr); 27428c2ecf20Sopenharmony_ci} 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_cistatic u32 velocity_get_link(struct net_device *dev) 27458c2ecf20Sopenharmony_ci{ 27468c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 27478c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 27488c2ecf20Sopenharmony_ci return BYTE_REG_BITS_IS_ON(PHYSR0_LINKGD, ®s->PHYSR0) ? 1 : 0; 27498c2ecf20Sopenharmony_ci} 27508c2ecf20Sopenharmony_ci 27518c2ecf20Sopenharmony_ci/** 27528c2ecf20Sopenharmony_ci * velocity_probe - set up discovered velocity device 27538c2ecf20Sopenharmony_ci * @dev: PCI device 27548c2ecf20Sopenharmony_ci * @info: table of match 27558c2ecf20Sopenharmony_ci * @irq: interrupt info 27568c2ecf20Sopenharmony_ci * @bustype: bus that device is connected to 27578c2ecf20Sopenharmony_ci * 27588c2ecf20Sopenharmony_ci * Configure a discovered adapter from scratch. Return a negative 27598c2ecf20Sopenharmony_ci * errno error code on failure paths. 27608c2ecf20Sopenharmony_ci */ 27618c2ecf20Sopenharmony_cistatic int velocity_probe(struct device *dev, int irq, 27628c2ecf20Sopenharmony_ci const struct velocity_info_tbl *info, 27638c2ecf20Sopenharmony_ci enum velocity_bus_type bustype) 27648c2ecf20Sopenharmony_ci{ 27658c2ecf20Sopenharmony_ci struct net_device *netdev; 27668c2ecf20Sopenharmony_ci int i; 27678c2ecf20Sopenharmony_ci struct velocity_info *vptr; 27688c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs; 27698c2ecf20Sopenharmony_ci int ret = -ENOMEM; 27708c2ecf20Sopenharmony_ci 27718c2ecf20Sopenharmony_ci /* FIXME: this driver, like almost all other ethernet drivers, 27728c2ecf20Sopenharmony_ci * can support more than MAX_UNITS. 27738c2ecf20Sopenharmony_ci */ 27748c2ecf20Sopenharmony_ci if (velocity_nics >= MAX_UNITS) { 27758c2ecf20Sopenharmony_ci dev_notice(dev, "already found %d NICs.\n", velocity_nics); 27768c2ecf20Sopenharmony_ci return -ENODEV; 27778c2ecf20Sopenharmony_ci } 27788c2ecf20Sopenharmony_ci 27798c2ecf20Sopenharmony_ci netdev = alloc_etherdev(sizeof(struct velocity_info)); 27808c2ecf20Sopenharmony_ci if (!netdev) 27818c2ecf20Sopenharmony_ci goto out; 27828c2ecf20Sopenharmony_ci 27838c2ecf20Sopenharmony_ci /* Chain it all together */ 27848c2ecf20Sopenharmony_ci 27858c2ecf20Sopenharmony_ci SET_NETDEV_DEV(netdev, dev); 27868c2ecf20Sopenharmony_ci vptr = netdev_priv(netdev); 27878c2ecf20Sopenharmony_ci 27888c2ecf20Sopenharmony_ci pr_info_once("%s Ver. %s\n", VELOCITY_FULL_DRV_NAM, VELOCITY_VERSION); 27898c2ecf20Sopenharmony_ci pr_info_once("Copyright (c) 2002, 2003 VIA Networking Technologies, Inc.\n"); 27908c2ecf20Sopenharmony_ci pr_info_once("Copyright (c) 2004 Red Hat Inc.\n"); 27918c2ecf20Sopenharmony_ci 27928c2ecf20Sopenharmony_ci netdev->irq = irq; 27938c2ecf20Sopenharmony_ci vptr->netdev = netdev; 27948c2ecf20Sopenharmony_ci vptr->dev = dev; 27958c2ecf20Sopenharmony_ci 27968c2ecf20Sopenharmony_ci velocity_init_info(vptr, info); 27978c2ecf20Sopenharmony_ci 27988c2ecf20Sopenharmony_ci if (bustype == BUS_PCI) { 27998c2ecf20Sopenharmony_ci vptr->pdev = to_pci_dev(dev); 28008c2ecf20Sopenharmony_ci 28018c2ecf20Sopenharmony_ci ret = velocity_get_pci_info(vptr); 28028c2ecf20Sopenharmony_ci if (ret < 0) 28038c2ecf20Sopenharmony_ci goto err_free_dev; 28048c2ecf20Sopenharmony_ci } else { 28058c2ecf20Sopenharmony_ci vptr->pdev = NULL; 28068c2ecf20Sopenharmony_ci ret = velocity_get_platform_info(vptr); 28078c2ecf20Sopenharmony_ci if (ret < 0) 28088c2ecf20Sopenharmony_ci goto err_free_dev; 28098c2ecf20Sopenharmony_ci } 28108c2ecf20Sopenharmony_ci 28118c2ecf20Sopenharmony_ci regs = ioremap(vptr->memaddr, VELOCITY_IO_SIZE); 28128c2ecf20Sopenharmony_ci if (regs == NULL) { 28138c2ecf20Sopenharmony_ci ret = -EIO; 28148c2ecf20Sopenharmony_ci goto err_free_dev; 28158c2ecf20Sopenharmony_ci } 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_ci vptr->mac_regs = regs; 28188c2ecf20Sopenharmony_ci vptr->rev_id = readb(®s->rev_id); 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ci mac_wol_reset(regs); 28218c2ecf20Sopenharmony_ci 28228c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 28238c2ecf20Sopenharmony_ci netdev->dev_addr[i] = readb(®s->PAR[i]); 28248c2ecf20Sopenharmony_ci 28258c2ecf20Sopenharmony_ci 28268c2ecf20Sopenharmony_ci velocity_get_options(&vptr->options, velocity_nics); 28278c2ecf20Sopenharmony_ci 28288c2ecf20Sopenharmony_ci /* 28298c2ecf20Sopenharmony_ci * Mask out the options cannot be set to the chip 28308c2ecf20Sopenharmony_ci */ 28318c2ecf20Sopenharmony_ci 28328c2ecf20Sopenharmony_ci vptr->options.flags &= info->flags; 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_ci /* 28358c2ecf20Sopenharmony_ci * Enable the chip specified capbilities 28368c2ecf20Sopenharmony_ci */ 28378c2ecf20Sopenharmony_ci 28388c2ecf20Sopenharmony_ci vptr->flags = vptr->options.flags | (info->flags & 0xFF000000UL); 28398c2ecf20Sopenharmony_ci 28408c2ecf20Sopenharmony_ci vptr->wol_opts = vptr->options.wol_opts; 28418c2ecf20Sopenharmony_ci vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci vptr->phy_id = MII_GET_PHY_ID(vptr->mac_regs); 28448c2ecf20Sopenharmony_ci 28458c2ecf20Sopenharmony_ci netdev->netdev_ops = &velocity_netdev_ops; 28468c2ecf20Sopenharmony_ci netdev->ethtool_ops = &velocity_ethtool_ops; 28478c2ecf20Sopenharmony_ci netif_napi_add(netdev, &vptr->napi, velocity_poll, 28488c2ecf20Sopenharmony_ci VELOCITY_NAPI_WEIGHT); 28498c2ecf20Sopenharmony_ci 28508c2ecf20Sopenharmony_ci netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | 28518c2ecf20Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_TX; 28528c2ecf20Sopenharmony_ci netdev->features |= NETIF_F_HW_VLAN_CTAG_TX | 28538c2ecf20Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX | 28548c2ecf20Sopenharmony_ci NETIF_F_IP_CSUM; 28558c2ecf20Sopenharmony_ci 28568c2ecf20Sopenharmony_ci /* MTU range: 64 - 9000 */ 28578c2ecf20Sopenharmony_ci netdev->min_mtu = VELOCITY_MIN_MTU; 28588c2ecf20Sopenharmony_ci netdev->max_mtu = VELOCITY_MAX_MTU; 28598c2ecf20Sopenharmony_ci 28608c2ecf20Sopenharmony_ci ret = register_netdev(netdev); 28618c2ecf20Sopenharmony_ci if (ret < 0) 28628c2ecf20Sopenharmony_ci goto err_iounmap; 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci if (!velocity_get_link(netdev)) { 28658c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 28668c2ecf20Sopenharmony_ci vptr->mii_status |= VELOCITY_LINK_FAIL; 28678c2ecf20Sopenharmony_ci } 28688c2ecf20Sopenharmony_ci 28698c2ecf20Sopenharmony_ci velocity_print_info(vptr); 28708c2ecf20Sopenharmony_ci dev_set_drvdata(vptr->dev, netdev); 28718c2ecf20Sopenharmony_ci 28728c2ecf20Sopenharmony_ci /* and leave the chip powered down */ 28738c2ecf20Sopenharmony_ci 28748c2ecf20Sopenharmony_ci velocity_set_power_state(vptr, PCI_D3hot); 28758c2ecf20Sopenharmony_ci velocity_nics++; 28768c2ecf20Sopenharmony_ciout: 28778c2ecf20Sopenharmony_ci return ret; 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_cierr_iounmap: 28808c2ecf20Sopenharmony_ci netif_napi_del(&vptr->napi); 28818c2ecf20Sopenharmony_ci iounmap(regs); 28828c2ecf20Sopenharmony_cierr_free_dev: 28838c2ecf20Sopenharmony_ci free_netdev(netdev); 28848c2ecf20Sopenharmony_ci goto out; 28858c2ecf20Sopenharmony_ci} 28868c2ecf20Sopenharmony_ci 28878c2ecf20Sopenharmony_ci/** 28888c2ecf20Sopenharmony_ci * velocity_remove - device unplug 28898c2ecf20Sopenharmony_ci * @dev: device being removed 28908c2ecf20Sopenharmony_ci * 28918c2ecf20Sopenharmony_ci * Device unload callback. Called on an unplug or on module 28928c2ecf20Sopenharmony_ci * unload for each active device that is present. Disconnects 28938c2ecf20Sopenharmony_ci * the device from the network layer and frees all the resources 28948c2ecf20Sopenharmony_ci */ 28958c2ecf20Sopenharmony_cistatic int velocity_remove(struct device *dev) 28968c2ecf20Sopenharmony_ci{ 28978c2ecf20Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(dev); 28988c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(netdev); 28998c2ecf20Sopenharmony_ci 29008c2ecf20Sopenharmony_ci unregister_netdev(netdev); 29018c2ecf20Sopenharmony_ci netif_napi_del(&vptr->napi); 29028c2ecf20Sopenharmony_ci iounmap(vptr->mac_regs); 29038c2ecf20Sopenharmony_ci free_netdev(netdev); 29048c2ecf20Sopenharmony_ci velocity_nics--; 29058c2ecf20Sopenharmony_ci 29068c2ecf20Sopenharmony_ci return 0; 29078c2ecf20Sopenharmony_ci} 29088c2ecf20Sopenharmony_ci 29098c2ecf20Sopenharmony_cistatic int velocity_pci_probe(struct pci_dev *pdev, 29108c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 29118c2ecf20Sopenharmony_ci{ 29128c2ecf20Sopenharmony_ci const struct velocity_info_tbl *info = 29138c2ecf20Sopenharmony_ci &chip_info_table[ent->driver_data]; 29148c2ecf20Sopenharmony_ci int ret; 29158c2ecf20Sopenharmony_ci 29168c2ecf20Sopenharmony_ci ret = pci_enable_device(pdev); 29178c2ecf20Sopenharmony_ci if (ret < 0) 29188c2ecf20Sopenharmony_ci return ret; 29198c2ecf20Sopenharmony_ci 29208c2ecf20Sopenharmony_ci ret = pci_request_regions(pdev, VELOCITY_NAME); 29218c2ecf20Sopenharmony_ci if (ret < 0) { 29228c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No PCI resources.\n"); 29238c2ecf20Sopenharmony_ci goto fail1; 29248c2ecf20Sopenharmony_ci } 29258c2ecf20Sopenharmony_ci 29268c2ecf20Sopenharmony_ci ret = velocity_probe(&pdev->dev, pdev->irq, info, BUS_PCI); 29278c2ecf20Sopenharmony_ci if (ret == 0) 29288c2ecf20Sopenharmony_ci return 0; 29298c2ecf20Sopenharmony_ci 29308c2ecf20Sopenharmony_ci pci_release_regions(pdev); 29318c2ecf20Sopenharmony_cifail1: 29328c2ecf20Sopenharmony_ci pci_disable_device(pdev); 29338c2ecf20Sopenharmony_ci return ret; 29348c2ecf20Sopenharmony_ci} 29358c2ecf20Sopenharmony_ci 29368c2ecf20Sopenharmony_cistatic void velocity_pci_remove(struct pci_dev *pdev) 29378c2ecf20Sopenharmony_ci{ 29388c2ecf20Sopenharmony_ci velocity_remove(&pdev->dev); 29398c2ecf20Sopenharmony_ci 29408c2ecf20Sopenharmony_ci pci_release_regions(pdev); 29418c2ecf20Sopenharmony_ci pci_disable_device(pdev); 29428c2ecf20Sopenharmony_ci} 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_cistatic int velocity_platform_probe(struct platform_device *pdev) 29458c2ecf20Sopenharmony_ci{ 29468c2ecf20Sopenharmony_ci const struct of_device_id *of_id; 29478c2ecf20Sopenharmony_ci const struct velocity_info_tbl *info; 29488c2ecf20Sopenharmony_ci int irq; 29498c2ecf20Sopenharmony_ci 29508c2ecf20Sopenharmony_ci of_id = of_match_device(velocity_of_ids, &pdev->dev); 29518c2ecf20Sopenharmony_ci if (!of_id) 29528c2ecf20Sopenharmony_ci return -EINVAL; 29538c2ecf20Sopenharmony_ci info = of_id->data; 29548c2ecf20Sopenharmony_ci 29558c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 29568c2ecf20Sopenharmony_ci if (!irq) 29578c2ecf20Sopenharmony_ci return -EINVAL; 29588c2ecf20Sopenharmony_ci 29598c2ecf20Sopenharmony_ci return velocity_probe(&pdev->dev, irq, info, BUS_PLATFORM); 29608c2ecf20Sopenharmony_ci} 29618c2ecf20Sopenharmony_ci 29628c2ecf20Sopenharmony_cistatic int velocity_platform_remove(struct platform_device *pdev) 29638c2ecf20Sopenharmony_ci{ 29648c2ecf20Sopenharmony_ci velocity_remove(&pdev->dev); 29658c2ecf20Sopenharmony_ci 29668c2ecf20Sopenharmony_ci return 0; 29678c2ecf20Sopenharmony_ci} 29688c2ecf20Sopenharmony_ci 29698c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 29708c2ecf20Sopenharmony_ci/** 29718c2ecf20Sopenharmony_ci * wol_calc_crc - WOL CRC 29728c2ecf20Sopenharmony_ci * @size: size of the wake mask 29738c2ecf20Sopenharmony_ci * @pattern: data pattern 29748c2ecf20Sopenharmony_ci * @mask_pattern: mask 29758c2ecf20Sopenharmony_ci * 29768c2ecf20Sopenharmony_ci * Compute the wake on lan crc hashes for the packet header 29778c2ecf20Sopenharmony_ci * we are interested in. 29788c2ecf20Sopenharmony_ci */ 29798c2ecf20Sopenharmony_cistatic u16 wol_calc_crc(int size, u8 *pattern, u8 *mask_pattern) 29808c2ecf20Sopenharmony_ci{ 29818c2ecf20Sopenharmony_ci u16 crc = 0xFFFF; 29828c2ecf20Sopenharmony_ci u8 mask; 29838c2ecf20Sopenharmony_ci int i, j; 29848c2ecf20Sopenharmony_ci 29858c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 29868c2ecf20Sopenharmony_ci mask = mask_pattern[i]; 29878c2ecf20Sopenharmony_ci 29888c2ecf20Sopenharmony_ci /* Skip this loop if the mask equals to zero */ 29898c2ecf20Sopenharmony_ci if (mask == 0x00) 29908c2ecf20Sopenharmony_ci continue; 29918c2ecf20Sopenharmony_ci 29928c2ecf20Sopenharmony_ci for (j = 0; j < 8; j++) { 29938c2ecf20Sopenharmony_ci if ((mask & 0x01) == 0) { 29948c2ecf20Sopenharmony_ci mask >>= 1; 29958c2ecf20Sopenharmony_ci continue; 29968c2ecf20Sopenharmony_ci } 29978c2ecf20Sopenharmony_ci mask >>= 1; 29988c2ecf20Sopenharmony_ci crc = crc_ccitt(crc, &(pattern[i * 8 + j]), 1); 29998c2ecf20Sopenharmony_ci } 30008c2ecf20Sopenharmony_ci } 30018c2ecf20Sopenharmony_ci /* Finally, invert the result once to get the correct data */ 30028c2ecf20Sopenharmony_ci crc = ~crc; 30038c2ecf20Sopenharmony_ci return bitrev32(crc) >> 16; 30048c2ecf20Sopenharmony_ci} 30058c2ecf20Sopenharmony_ci 30068c2ecf20Sopenharmony_ci/** 30078c2ecf20Sopenharmony_ci * velocity_set_wol - set up for wake on lan 30088c2ecf20Sopenharmony_ci * @vptr: velocity to set WOL status on 30098c2ecf20Sopenharmony_ci * 30108c2ecf20Sopenharmony_ci * Set a card up for wake on lan either by unicast or by 30118c2ecf20Sopenharmony_ci * ARP packet. 30128c2ecf20Sopenharmony_ci * 30138c2ecf20Sopenharmony_ci * FIXME: check static buffer is safe here 30148c2ecf20Sopenharmony_ci */ 30158c2ecf20Sopenharmony_cistatic int velocity_set_wol(struct velocity_info *vptr) 30168c2ecf20Sopenharmony_ci{ 30178c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 30188c2ecf20Sopenharmony_ci enum speed_opt spd_dpx = vptr->options.spd_dpx; 30198c2ecf20Sopenharmony_ci static u8 buf[256]; 30208c2ecf20Sopenharmony_ci int i; 30218c2ecf20Sopenharmony_ci 30228c2ecf20Sopenharmony_ci static u32 mask_pattern[2][4] = { 30238c2ecf20Sopenharmony_ci {0x00203000, 0x000003C0, 0x00000000, 0x0000000}, /* ARP */ 30248c2ecf20Sopenharmony_ci {0xfffff000, 0xffffffff, 0xffffffff, 0x000ffff} /* Magic Packet */ 30258c2ecf20Sopenharmony_ci }; 30268c2ecf20Sopenharmony_ci 30278c2ecf20Sopenharmony_ci writew(0xFFFF, ®s->WOLCRClr); 30288c2ecf20Sopenharmony_ci writeb(WOLCFG_SAB | WOLCFG_SAM, ®s->WOLCFGSet); 30298c2ecf20Sopenharmony_ci writew(WOLCR_MAGIC_EN, ®s->WOLCRSet); 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_ci /* 30328c2ecf20Sopenharmony_ci if (vptr->wol_opts & VELOCITY_WOL_PHY) 30338c2ecf20Sopenharmony_ci writew((WOLCR_LINKON_EN|WOLCR_LINKOFF_EN), ®s->WOLCRSet); 30348c2ecf20Sopenharmony_ci */ 30358c2ecf20Sopenharmony_ci 30368c2ecf20Sopenharmony_ci if (vptr->wol_opts & VELOCITY_WOL_UCAST) 30378c2ecf20Sopenharmony_ci writew(WOLCR_UNICAST_EN, ®s->WOLCRSet); 30388c2ecf20Sopenharmony_ci 30398c2ecf20Sopenharmony_ci if (vptr->wol_opts & VELOCITY_WOL_ARP) { 30408c2ecf20Sopenharmony_ci struct arp_packet *arp = (struct arp_packet *) buf; 30418c2ecf20Sopenharmony_ci u16 crc; 30428c2ecf20Sopenharmony_ci memset(buf, 0, sizeof(struct arp_packet) + 7); 30438c2ecf20Sopenharmony_ci 30448c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 30458c2ecf20Sopenharmony_ci writel(mask_pattern[0][i], ®s->ByteMask[0][i]); 30468c2ecf20Sopenharmony_ci 30478c2ecf20Sopenharmony_ci arp->type = htons(ETH_P_ARP); 30488c2ecf20Sopenharmony_ci arp->ar_op = htons(1); 30498c2ecf20Sopenharmony_ci 30508c2ecf20Sopenharmony_ci memcpy(arp->ar_tip, vptr->ip_addr, 4); 30518c2ecf20Sopenharmony_ci 30528c2ecf20Sopenharmony_ci crc = wol_calc_crc((sizeof(struct arp_packet) + 7) / 8, buf, 30538c2ecf20Sopenharmony_ci (u8 *) & mask_pattern[0][0]); 30548c2ecf20Sopenharmony_ci 30558c2ecf20Sopenharmony_ci writew(crc, ®s->PatternCRC[0]); 30568c2ecf20Sopenharmony_ci writew(WOLCR_ARP_EN, ®s->WOLCRSet); 30578c2ecf20Sopenharmony_ci } 30588c2ecf20Sopenharmony_ci 30598c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON(PWCFG_WOLTYPE, ®s->PWCFGSet); 30608c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON(PWCFG_LEGACY_WOLEN, ®s->PWCFGSet); 30618c2ecf20Sopenharmony_ci 30628c2ecf20Sopenharmony_ci writew(0x0FFF, ®s->WOLSRClr); 30638c2ecf20Sopenharmony_ci 30648c2ecf20Sopenharmony_ci if (spd_dpx == SPD_DPX_1000_FULL) 30658c2ecf20Sopenharmony_ci goto mac_done; 30668c2ecf20Sopenharmony_ci 30678c2ecf20Sopenharmony_ci if (spd_dpx != SPD_DPX_AUTO) 30688c2ecf20Sopenharmony_ci goto advertise_done; 30698c2ecf20Sopenharmony_ci 30708c2ecf20Sopenharmony_ci if (vptr->mii_status & VELOCITY_AUTONEG_ENABLE) { 30718c2ecf20Sopenharmony_ci if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201) 30728c2ecf20Sopenharmony_ci MII_REG_BITS_ON(AUXCR_MDPPS, MII_NCONFIG, vptr->mac_regs); 30738c2ecf20Sopenharmony_ci 30748c2ecf20Sopenharmony_ci MII_REG_BITS_OFF(ADVERTISE_1000FULL | ADVERTISE_1000HALF, MII_CTRL1000, vptr->mac_regs); 30758c2ecf20Sopenharmony_ci } 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci if (vptr->mii_status & VELOCITY_SPEED_1000) 30788c2ecf20Sopenharmony_ci MII_REG_BITS_ON(BMCR_ANRESTART, MII_BMCR, vptr->mac_regs); 30798c2ecf20Sopenharmony_ci 30808c2ecf20Sopenharmony_ciadvertise_done: 30818c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON(CHIPGCR_FCMODE, ®s->CHIPGCR); 30828c2ecf20Sopenharmony_ci 30838c2ecf20Sopenharmony_ci { 30848c2ecf20Sopenharmony_ci u8 GCR; 30858c2ecf20Sopenharmony_ci GCR = readb(®s->CHIPGCR); 30868c2ecf20Sopenharmony_ci GCR = (GCR & ~CHIPGCR_FCGMII) | CHIPGCR_FCFDX; 30878c2ecf20Sopenharmony_ci writeb(GCR, ®s->CHIPGCR); 30888c2ecf20Sopenharmony_ci } 30898c2ecf20Sopenharmony_ci 30908c2ecf20Sopenharmony_cimac_done: 30918c2ecf20Sopenharmony_ci BYTE_REG_BITS_OFF(ISR_PWEI, ®s->ISR); 30928c2ecf20Sopenharmony_ci /* Turn on SWPTAG just before entering power mode */ 30938c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON(STICKHW_SWPTAG, ®s->STICKHW); 30948c2ecf20Sopenharmony_ci /* Go to bed ..... */ 30958c2ecf20Sopenharmony_ci BYTE_REG_BITS_ON((STICKHW_DS1 | STICKHW_DS0), ®s->STICKHW); 30968c2ecf20Sopenharmony_ci 30978c2ecf20Sopenharmony_ci return 0; 30988c2ecf20Sopenharmony_ci} 30998c2ecf20Sopenharmony_ci 31008c2ecf20Sopenharmony_ci/** 31018c2ecf20Sopenharmony_ci * velocity_save_context - save registers 31028c2ecf20Sopenharmony_ci * @vptr: velocity 31038c2ecf20Sopenharmony_ci * @context: buffer for stored context 31048c2ecf20Sopenharmony_ci * 31058c2ecf20Sopenharmony_ci * Retrieve the current configuration from the velocity hardware 31068c2ecf20Sopenharmony_ci * and stash it in the context structure, for use by the context 31078c2ecf20Sopenharmony_ci * restore functions. This allows us to save things we need across 31088c2ecf20Sopenharmony_ci * power down states 31098c2ecf20Sopenharmony_ci */ 31108c2ecf20Sopenharmony_cistatic void velocity_save_context(struct velocity_info *vptr, struct velocity_context *context) 31118c2ecf20Sopenharmony_ci{ 31128c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 31138c2ecf20Sopenharmony_ci u16 i; 31148c2ecf20Sopenharmony_ci u8 __iomem *ptr = (u8 __iomem *)regs; 31158c2ecf20Sopenharmony_ci 31168c2ecf20Sopenharmony_ci for (i = MAC_REG_PAR; i < MAC_REG_CR0_CLR; i += 4) 31178c2ecf20Sopenharmony_ci *((u32 *) (context->mac_reg + i)) = readl(ptr + i); 31188c2ecf20Sopenharmony_ci 31198c2ecf20Sopenharmony_ci for (i = MAC_REG_MAR; i < MAC_REG_TDCSR_CLR; i += 4) 31208c2ecf20Sopenharmony_ci *((u32 *) (context->mac_reg + i)) = readl(ptr + i); 31218c2ecf20Sopenharmony_ci 31228c2ecf20Sopenharmony_ci for (i = MAC_REG_RDBASE_LO; i < MAC_REG_FIFO_TEST0; i += 4) 31238c2ecf20Sopenharmony_ci *((u32 *) (context->mac_reg + i)) = readl(ptr + i); 31248c2ecf20Sopenharmony_ci 31258c2ecf20Sopenharmony_ci} 31268c2ecf20Sopenharmony_ci 31278c2ecf20Sopenharmony_cistatic int velocity_suspend(struct device *dev) 31288c2ecf20Sopenharmony_ci{ 31298c2ecf20Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(dev); 31308c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(netdev); 31318c2ecf20Sopenharmony_ci unsigned long flags; 31328c2ecf20Sopenharmony_ci 31338c2ecf20Sopenharmony_ci if (!netif_running(vptr->netdev)) 31348c2ecf20Sopenharmony_ci return 0; 31358c2ecf20Sopenharmony_ci 31368c2ecf20Sopenharmony_ci netif_device_detach(vptr->netdev); 31378c2ecf20Sopenharmony_ci 31388c2ecf20Sopenharmony_ci spin_lock_irqsave(&vptr->lock, flags); 31398c2ecf20Sopenharmony_ci if (vptr->pdev) 31408c2ecf20Sopenharmony_ci pci_save_state(vptr->pdev); 31418c2ecf20Sopenharmony_ci 31428c2ecf20Sopenharmony_ci if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED) { 31438c2ecf20Sopenharmony_ci velocity_get_ip(vptr); 31448c2ecf20Sopenharmony_ci velocity_save_context(vptr, &vptr->context); 31458c2ecf20Sopenharmony_ci velocity_shutdown(vptr); 31468c2ecf20Sopenharmony_ci velocity_set_wol(vptr); 31478c2ecf20Sopenharmony_ci if (vptr->pdev) 31488c2ecf20Sopenharmony_ci pci_enable_wake(vptr->pdev, PCI_D3hot, 1); 31498c2ecf20Sopenharmony_ci velocity_set_power_state(vptr, PCI_D3hot); 31508c2ecf20Sopenharmony_ci } else { 31518c2ecf20Sopenharmony_ci velocity_save_context(vptr, &vptr->context); 31528c2ecf20Sopenharmony_ci velocity_shutdown(vptr); 31538c2ecf20Sopenharmony_ci if (vptr->pdev) 31548c2ecf20Sopenharmony_ci pci_disable_device(vptr->pdev); 31558c2ecf20Sopenharmony_ci velocity_set_power_state(vptr, PCI_D3hot); 31568c2ecf20Sopenharmony_ci } 31578c2ecf20Sopenharmony_ci 31588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vptr->lock, flags); 31598c2ecf20Sopenharmony_ci return 0; 31608c2ecf20Sopenharmony_ci} 31618c2ecf20Sopenharmony_ci 31628c2ecf20Sopenharmony_ci/** 31638c2ecf20Sopenharmony_ci * velocity_restore_context - restore registers 31648c2ecf20Sopenharmony_ci * @vptr: velocity 31658c2ecf20Sopenharmony_ci * @context: buffer for stored context 31668c2ecf20Sopenharmony_ci * 31678c2ecf20Sopenharmony_ci * Reload the register configuration from the velocity context 31688c2ecf20Sopenharmony_ci * created by velocity_save_context. 31698c2ecf20Sopenharmony_ci */ 31708c2ecf20Sopenharmony_cistatic void velocity_restore_context(struct velocity_info *vptr, struct velocity_context *context) 31718c2ecf20Sopenharmony_ci{ 31728c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 31738c2ecf20Sopenharmony_ci int i; 31748c2ecf20Sopenharmony_ci u8 __iomem *ptr = (u8 __iomem *)regs; 31758c2ecf20Sopenharmony_ci 31768c2ecf20Sopenharmony_ci for (i = MAC_REG_PAR; i < MAC_REG_CR0_SET; i += 4) 31778c2ecf20Sopenharmony_ci writel(*((u32 *) (context->mac_reg + i)), ptr + i); 31788c2ecf20Sopenharmony_ci 31798c2ecf20Sopenharmony_ci /* Just skip cr0 */ 31808c2ecf20Sopenharmony_ci for (i = MAC_REG_CR1_SET; i < MAC_REG_CR0_CLR; i++) { 31818c2ecf20Sopenharmony_ci /* Clear */ 31828c2ecf20Sopenharmony_ci writeb(~(*((u8 *) (context->mac_reg + i))), ptr + i + 4); 31838c2ecf20Sopenharmony_ci /* Set */ 31848c2ecf20Sopenharmony_ci writeb(*((u8 *) (context->mac_reg + i)), ptr + i); 31858c2ecf20Sopenharmony_ci } 31868c2ecf20Sopenharmony_ci 31878c2ecf20Sopenharmony_ci for (i = MAC_REG_MAR; i < MAC_REG_IMR; i += 4) 31888c2ecf20Sopenharmony_ci writel(*((u32 *) (context->mac_reg + i)), ptr + i); 31898c2ecf20Sopenharmony_ci 31908c2ecf20Sopenharmony_ci for (i = MAC_REG_RDBASE_LO; i < MAC_REG_FIFO_TEST0; i += 4) 31918c2ecf20Sopenharmony_ci writel(*((u32 *) (context->mac_reg + i)), ptr + i); 31928c2ecf20Sopenharmony_ci 31938c2ecf20Sopenharmony_ci for (i = MAC_REG_TDCSR_SET; i <= MAC_REG_RDCSR_SET; i++) 31948c2ecf20Sopenharmony_ci writeb(*((u8 *) (context->mac_reg + i)), ptr + i); 31958c2ecf20Sopenharmony_ci} 31968c2ecf20Sopenharmony_ci 31978c2ecf20Sopenharmony_cistatic int velocity_resume(struct device *dev) 31988c2ecf20Sopenharmony_ci{ 31998c2ecf20Sopenharmony_ci struct net_device *netdev = dev_get_drvdata(dev); 32008c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(netdev); 32018c2ecf20Sopenharmony_ci unsigned long flags; 32028c2ecf20Sopenharmony_ci int i; 32038c2ecf20Sopenharmony_ci 32048c2ecf20Sopenharmony_ci if (!netif_running(vptr->netdev)) 32058c2ecf20Sopenharmony_ci return 0; 32068c2ecf20Sopenharmony_ci 32078c2ecf20Sopenharmony_ci velocity_set_power_state(vptr, PCI_D0); 32088c2ecf20Sopenharmony_ci 32098c2ecf20Sopenharmony_ci if (vptr->pdev) { 32108c2ecf20Sopenharmony_ci pci_enable_wake(vptr->pdev, PCI_D0, 0); 32118c2ecf20Sopenharmony_ci pci_restore_state(vptr->pdev); 32128c2ecf20Sopenharmony_ci } 32138c2ecf20Sopenharmony_ci 32148c2ecf20Sopenharmony_ci mac_wol_reset(vptr->mac_regs); 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ci spin_lock_irqsave(&vptr->lock, flags); 32178c2ecf20Sopenharmony_ci velocity_restore_context(vptr, &vptr->context); 32188c2ecf20Sopenharmony_ci velocity_init_registers(vptr, VELOCITY_INIT_WOL); 32198c2ecf20Sopenharmony_ci mac_disable_int(vptr->mac_regs); 32208c2ecf20Sopenharmony_ci 32218c2ecf20Sopenharmony_ci velocity_tx_srv(vptr); 32228c2ecf20Sopenharmony_ci 32238c2ecf20Sopenharmony_ci for (i = 0; i < vptr->tx.numq; i++) { 32248c2ecf20Sopenharmony_ci if (vptr->tx.used[i]) 32258c2ecf20Sopenharmony_ci mac_tx_queue_wake(vptr->mac_regs, i); 32268c2ecf20Sopenharmony_ci } 32278c2ecf20Sopenharmony_ci 32288c2ecf20Sopenharmony_ci mac_enable_int(vptr->mac_regs); 32298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vptr->lock, flags); 32308c2ecf20Sopenharmony_ci netif_device_attach(vptr->netdev); 32318c2ecf20Sopenharmony_ci 32328c2ecf20Sopenharmony_ci return 0; 32338c2ecf20Sopenharmony_ci} 32348c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 32358c2ecf20Sopenharmony_ci 32368c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(velocity_pm_ops, velocity_suspend, velocity_resume); 32378c2ecf20Sopenharmony_ci 32388c2ecf20Sopenharmony_ci/* 32398c2ecf20Sopenharmony_ci * Definition for our device driver. The PCI layer interface 32408c2ecf20Sopenharmony_ci * uses this to handle all our card discover and plugging 32418c2ecf20Sopenharmony_ci */ 32428c2ecf20Sopenharmony_cistatic struct pci_driver velocity_pci_driver = { 32438c2ecf20Sopenharmony_ci .name = VELOCITY_NAME, 32448c2ecf20Sopenharmony_ci .id_table = velocity_pci_id_table, 32458c2ecf20Sopenharmony_ci .probe = velocity_pci_probe, 32468c2ecf20Sopenharmony_ci .remove = velocity_pci_remove, 32478c2ecf20Sopenharmony_ci .driver = { 32488c2ecf20Sopenharmony_ci .pm = &velocity_pm_ops, 32498c2ecf20Sopenharmony_ci }, 32508c2ecf20Sopenharmony_ci}; 32518c2ecf20Sopenharmony_ci 32528c2ecf20Sopenharmony_cistatic struct platform_driver velocity_platform_driver = { 32538c2ecf20Sopenharmony_ci .probe = velocity_platform_probe, 32548c2ecf20Sopenharmony_ci .remove = velocity_platform_remove, 32558c2ecf20Sopenharmony_ci .driver = { 32568c2ecf20Sopenharmony_ci .name = "via-velocity", 32578c2ecf20Sopenharmony_ci .of_match_table = velocity_of_ids, 32588c2ecf20Sopenharmony_ci .pm = &velocity_pm_ops, 32598c2ecf20Sopenharmony_ci }, 32608c2ecf20Sopenharmony_ci}; 32618c2ecf20Sopenharmony_ci 32628c2ecf20Sopenharmony_ci/** 32638c2ecf20Sopenharmony_ci * velocity_ethtool_up - pre hook for ethtool 32648c2ecf20Sopenharmony_ci * @dev: network device 32658c2ecf20Sopenharmony_ci * 32668c2ecf20Sopenharmony_ci * Called before an ethtool operation. We need to make sure the 32678c2ecf20Sopenharmony_ci * chip is out of D3 state before we poke at it. In case of ethtool 32688c2ecf20Sopenharmony_ci * ops nesting, only wake the device up in the outermost block. 32698c2ecf20Sopenharmony_ci */ 32708c2ecf20Sopenharmony_cistatic int velocity_ethtool_up(struct net_device *dev) 32718c2ecf20Sopenharmony_ci{ 32728c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 32738c2ecf20Sopenharmony_ci 32748c2ecf20Sopenharmony_ci if (vptr->ethtool_ops_nesting == U32_MAX) 32758c2ecf20Sopenharmony_ci return -EBUSY; 32768c2ecf20Sopenharmony_ci if (!vptr->ethtool_ops_nesting++ && !netif_running(dev)) 32778c2ecf20Sopenharmony_ci velocity_set_power_state(vptr, PCI_D0); 32788c2ecf20Sopenharmony_ci return 0; 32798c2ecf20Sopenharmony_ci} 32808c2ecf20Sopenharmony_ci 32818c2ecf20Sopenharmony_ci/** 32828c2ecf20Sopenharmony_ci * velocity_ethtool_down - post hook for ethtool 32838c2ecf20Sopenharmony_ci * @dev: network device 32848c2ecf20Sopenharmony_ci * 32858c2ecf20Sopenharmony_ci * Called after an ethtool operation. Restore the chip back to D3 32868c2ecf20Sopenharmony_ci * state if it isn't running. In case of ethtool ops nesting, only 32878c2ecf20Sopenharmony_ci * put the device to sleep in the outermost block. 32888c2ecf20Sopenharmony_ci */ 32898c2ecf20Sopenharmony_cistatic void velocity_ethtool_down(struct net_device *dev) 32908c2ecf20Sopenharmony_ci{ 32918c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 32928c2ecf20Sopenharmony_ci 32938c2ecf20Sopenharmony_ci if (!--vptr->ethtool_ops_nesting && !netif_running(dev)) 32948c2ecf20Sopenharmony_ci velocity_set_power_state(vptr, PCI_D3hot); 32958c2ecf20Sopenharmony_ci} 32968c2ecf20Sopenharmony_ci 32978c2ecf20Sopenharmony_cistatic int velocity_get_link_ksettings(struct net_device *dev, 32988c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 32998c2ecf20Sopenharmony_ci{ 33008c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 33018c2ecf20Sopenharmony_ci struct mac_regs __iomem *regs = vptr->mac_regs; 33028c2ecf20Sopenharmony_ci u32 status; 33038c2ecf20Sopenharmony_ci u32 supported, advertising; 33048c2ecf20Sopenharmony_ci 33058c2ecf20Sopenharmony_ci status = check_connection_type(vptr->mac_regs); 33068c2ecf20Sopenharmony_ci 33078c2ecf20Sopenharmony_ci supported = SUPPORTED_TP | 33088c2ecf20Sopenharmony_ci SUPPORTED_Autoneg | 33098c2ecf20Sopenharmony_ci SUPPORTED_10baseT_Half | 33108c2ecf20Sopenharmony_ci SUPPORTED_10baseT_Full | 33118c2ecf20Sopenharmony_ci SUPPORTED_100baseT_Half | 33128c2ecf20Sopenharmony_ci SUPPORTED_100baseT_Full | 33138c2ecf20Sopenharmony_ci SUPPORTED_1000baseT_Half | 33148c2ecf20Sopenharmony_ci SUPPORTED_1000baseT_Full; 33158c2ecf20Sopenharmony_ci 33168c2ecf20Sopenharmony_ci advertising = ADVERTISED_TP | ADVERTISED_Autoneg; 33178c2ecf20Sopenharmony_ci if (vptr->options.spd_dpx == SPD_DPX_AUTO) { 33188c2ecf20Sopenharmony_ci advertising |= 33198c2ecf20Sopenharmony_ci ADVERTISED_10baseT_Half | 33208c2ecf20Sopenharmony_ci ADVERTISED_10baseT_Full | 33218c2ecf20Sopenharmony_ci ADVERTISED_100baseT_Half | 33228c2ecf20Sopenharmony_ci ADVERTISED_100baseT_Full | 33238c2ecf20Sopenharmony_ci ADVERTISED_1000baseT_Half | 33248c2ecf20Sopenharmony_ci ADVERTISED_1000baseT_Full; 33258c2ecf20Sopenharmony_ci } else { 33268c2ecf20Sopenharmony_ci switch (vptr->options.spd_dpx) { 33278c2ecf20Sopenharmony_ci case SPD_DPX_1000_FULL: 33288c2ecf20Sopenharmony_ci advertising |= ADVERTISED_1000baseT_Full; 33298c2ecf20Sopenharmony_ci break; 33308c2ecf20Sopenharmony_ci case SPD_DPX_100_HALF: 33318c2ecf20Sopenharmony_ci advertising |= ADVERTISED_100baseT_Half; 33328c2ecf20Sopenharmony_ci break; 33338c2ecf20Sopenharmony_ci case SPD_DPX_100_FULL: 33348c2ecf20Sopenharmony_ci advertising |= ADVERTISED_100baseT_Full; 33358c2ecf20Sopenharmony_ci break; 33368c2ecf20Sopenharmony_ci case SPD_DPX_10_HALF: 33378c2ecf20Sopenharmony_ci advertising |= ADVERTISED_10baseT_Half; 33388c2ecf20Sopenharmony_ci break; 33398c2ecf20Sopenharmony_ci case SPD_DPX_10_FULL: 33408c2ecf20Sopenharmony_ci advertising |= ADVERTISED_10baseT_Full; 33418c2ecf20Sopenharmony_ci break; 33428c2ecf20Sopenharmony_ci default: 33438c2ecf20Sopenharmony_ci break; 33448c2ecf20Sopenharmony_ci } 33458c2ecf20Sopenharmony_ci } 33468c2ecf20Sopenharmony_ci 33478c2ecf20Sopenharmony_ci if (status & VELOCITY_SPEED_1000) 33488c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_1000; 33498c2ecf20Sopenharmony_ci else if (status & VELOCITY_SPEED_100) 33508c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_100; 33518c2ecf20Sopenharmony_ci else 33528c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_10; 33538c2ecf20Sopenharmony_ci 33548c2ecf20Sopenharmony_ci cmd->base.autoneg = (status & VELOCITY_AUTONEG_ENABLE) ? 33558c2ecf20Sopenharmony_ci AUTONEG_ENABLE : AUTONEG_DISABLE; 33568c2ecf20Sopenharmony_ci cmd->base.port = PORT_TP; 33578c2ecf20Sopenharmony_ci cmd->base.phy_address = readb(®s->MIIADR) & 0x1F; 33588c2ecf20Sopenharmony_ci 33598c2ecf20Sopenharmony_ci if (status & VELOCITY_DUPLEX_FULL) 33608c2ecf20Sopenharmony_ci cmd->base.duplex = DUPLEX_FULL; 33618c2ecf20Sopenharmony_ci else 33628c2ecf20Sopenharmony_ci cmd->base.duplex = DUPLEX_HALF; 33638c2ecf20Sopenharmony_ci 33648c2ecf20Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 33658c2ecf20Sopenharmony_ci supported); 33668c2ecf20Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 33678c2ecf20Sopenharmony_ci advertising); 33688c2ecf20Sopenharmony_ci 33698c2ecf20Sopenharmony_ci return 0; 33708c2ecf20Sopenharmony_ci} 33718c2ecf20Sopenharmony_ci 33728c2ecf20Sopenharmony_cistatic int velocity_set_link_ksettings(struct net_device *dev, 33738c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 33748c2ecf20Sopenharmony_ci{ 33758c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 33768c2ecf20Sopenharmony_ci u32 speed = cmd->base.speed; 33778c2ecf20Sopenharmony_ci u32 curr_status; 33788c2ecf20Sopenharmony_ci u32 new_status = 0; 33798c2ecf20Sopenharmony_ci int ret = 0; 33808c2ecf20Sopenharmony_ci 33818c2ecf20Sopenharmony_ci curr_status = check_connection_type(vptr->mac_regs); 33828c2ecf20Sopenharmony_ci curr_status &= (~VELOCITY_LINK_FAIL); 33838c2ecf20Sopenharmony_ci 33848c2ecf20Sopenharmony_ci new_status |= ((cmd->base.autoneg) ? VELOCITY_AUTONEG_ENABLE : 0); 33858c2ecf20Sopenharmony_ci new_status |= ((speed == SPEED_1000) ? VELOCITY_SPEED_1000 : 0); 33868c2ecf20Sopenharmony_ci new_status |= ((speed == SPEED_100) ? VELOCITY_SPEED_100 : 0); 33878c2ecf20Sopenharmony_ci new_status |= ((speed == SPEED_10) ? VELOCITY_SPEED_10 : 0); 33888c2ecf20Sopenharmony_ci new_status |= ((cmd->base.duplex == DUPLEX_FULL) ? 33898c2ecf20Sopenharmony_ci VELOCITY_DUPLEX_FULL : 0); 33908c2ecf20Sopenharmony_ci 33918c2ecf20Sopenharmony_ci if ((new_status & VELOCITY_AUTONEG_ENABLE) && 33928c2ecf20Sopenharmony_ci (new_status != (curr_status | VELOCITY_AUTONEG_ENABLE))) { 33938c2ecf20Sopenharmony_ci ret = -EINVAL; 33948c2ecf20Sopenharmony_ci } else { 33958c2ecf20Sopenharmony_ci enum speed_opt spd_dpx; 33968c2ecf20Sopenharmony_ci 33978c2ecf20Sopenharmony_ci if (new_status & VELOCITY_AUTONEG_ENABLE) 33988c2ecf20Sopenharmony_ci spd_dpx = SPD_DPX_AUTO; 33998c2ecf20Sopenharmony_ci else if ((new_status & VELOCITY_SPEED_1000) && 34008c2ecf20Sopenharmony_ci (new_status & VELOCITY_DUPLEX_FULL)) { 34018c2ecf20Sopenharmony_ci spd_dpx = SPD_DPX_1000_FULL; 34028c2ecf20Sopenharmony_ci } else if (new_status & VELOCITY_SPEED_100) 34038c2ecf20Sopenharmony_ci spd_dpx = (new_status & VELOCITY_DUPLEX_FULL) ? 34048c2ecf20Sopenharmony_ci SPD_DPX_100_FULL : SPD_DPX_100_HALF; 34058c2ecf20Sopenharmony_ci else if (new_status & VELOCITY_SPEED_10) 34068c2ecf20Sopenharmony_ci spd_dpx = (new_status & VELOCITY_DUPLEX_FULL) ? 34078c2ecf20Sopenharmony_ci SPD_DPX_10_FULL : SPD_DPX_10_HALF; 34088c2ecf20Sopenharmony_ci else 34098c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 34108c2ecf20Sopenharmony_ci 34118c2ecf20Sopenharmony_ci vptr->options.spd_dpx = spd_dpx; 34128c2ecf20Sopenharmony_ci 34138c2ecf20Sopenharmony_ci velocity_set_media_mode(vptr, new_status); 34148c2ecf20Sopenharmony_ci } 34158c2ecf20Sopenharmony_ci 34168c2ecf20Sopenharmony_ci return ret; 34178c2ecf20Sopenharmony_ci} 34188c2ecf20Sopenharmony_ci 34198c2ecf20Sopenharmony_cistatic void velocity_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 34208c2ecf20Sopenharmony_ci{ 34218c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 34228c2ecf20Sopenharmony_ci 34238c2ecf20Sopenharmony_ci strlcpy(info->driver, VELOCITY_NAME, sizeof(info->driver)); 34248c2ecf20Sopenharmony_ci strlcpy(info->version, VELOCITY_VERSION, sizeof(info->version)); 34258c2ecf20Sopenharmony_ci if (vptr->pdev) 34268c2ecf20Sopenharmony_ci strlcpy(info->bus_info, pci_name(vptr->pdev), 34278c2ecf20Sopenharmony_ci sizeof(info->bus_info)); 34288c2ecf20Sopenharmony_ci else 34298c2ecf20Sopenharmony_ci strlcpy(info->bus_info, "platform", sizeof(info->bus_info)); 34308c2ecf20Sopenharmony_ci} 34318c2ecf20Sopenharmony_ci 34328c2ecf20Sopenharmony_cistatic void velocity_ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 34338c2ecf20Sopenharmony_ci{ 34348c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 34358c2ecf20Sopenharmony_ci wol->supported = WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP; 34368c2ecf20Sopenharmony_ci wol->wolopts |= WAKE_MAGIC; 34378c2ecf20Sopenharmony_ci /* 34388c2ecf20Sopenharmony_ci if (vptr->wol_opts & VELOCITY_WOL_PHY) 34398c2ecf20Sopenharmony_ci wol.wolopts|=WAKE_PHY; 34408c2ecf20Sopenharmony_ci */ 34418c2ecf20Sopenharmony_ci if (vptr->wol_opts & VELOCITY_WOL_UCAST) 34428c2ecf20Sopenharmony_ci wol->wolopts |= WAKE_UCAST; 34438c2ecf20Sopenharmony_ci if (vptr->wol_opts & VELOCITY_WOL_ARP) 34448c2ecf20Sopenharmony_ci wol->wolopts |= WAKE_ARP; 34458c2ecf20Sopenharmony_ci memcpy(&wol->sopass, vptr->wol_passwd, 6); 34468c2ecf20Sopenharmony_ci} 34478c2ecf20Sopenharmony_ci 34488c2ecf20Sopenharmony_cistatic int velocity_ethtool_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 34498c2ecf20Sopenharmony_ci{ 34508c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 34518c2ecf20Sopenharmony_ci 34528c2ecf20Sopenharmony_ci if (!(wol->wolopts & (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP))) 34538c2ecf20Sopenharmony_ci return -EFAULT; 34548c2ecf20Sopenharmony_ci vptr->wol_opts = VELOCITY_WOL_MAGIC; 34558c2ecf20Sopenharmony_ci 34568c2ecf20Sopenharmony_ci /* 34578c2ecf20Sopenharmony_ci if (wol.wolopts & WAKE_PHY) { 34588c2ecf20Sopenharmony_ci vptr->wol_opts|=VELOCITY_WOL_PHY; 34598c2ecf20Sopenharmony_ci vptr->flags |=VELOCITY_FLAGS_WOL_ENABLED; 34608c2ecf20Sopenharmony_ci } 34618c2ecf20Sopenharmony_ci */ 34628c2ecf20Sopenharmony_ci 34638c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) { 34648c2ecf20Sopenharmony_ci vptr->wol_opts |= VELOCITY_WOL_MAGIC; 34658c2ecf20Sopenharmony_ci vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; 34668c2ecf20Sopenharmony_ci } 34678c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_UCAST) { 34688c2ecf20Sopenharmony_ci vptr->wol_opts |= VELOCITY_WOL_UCAST; 34698c2ecf20Sopenharmony_ci vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; 34708c2ecf20Sopenharmony_ci } 34718c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_ARP) { 34728c2ecf20Sopenharmony_ci vptr->wol_opts |= VELOCITY_WOL_ARP; 34738c2ecf20Sopenharmony_ci vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; 34748c2ecf20Sopenharmony_ci } 34758c2ecf20Sopenharmony_ci memcpy(vptr->wol_passwd, wol->sopass, 6); 34768c2ecf20Sopenharmony_ci return 0; 34778c2ecf20Sopenharmony_ci} 34788c2ecf20Sopenharmony_ci 34798c2ecf20Sopenharmony_cistatic int get_pending_timer_val(int val) 34808c2ecf20Sopenharmony_ci{ 34818c2ecf20Sopenharmony_ci int mult_bits = val >> 6; 34828c2ecf20Sopenharmony_ci int mult = 1; 34838c2ecf20Sopenharmony_ci 34848c2ecf20Sopenharmony_ci switch (mult_bits) 34858c2ecf20Sopenharmony_ci { 34868c2ecf20Sopenharmony_ci case 1: 34878c2ecf20Sopenharmony_ci mult = 4; break; 34888c2ecf20Sopenharmony_ci case 2: 34898c2ecf20Sopenharmony_ci mult = 16; break; 34908c2ecf20Sopenharmony_ci case 3: 34918c2ecf20Sopenharmony_ci mult = 64; break; 34928c2ecf20Sopenharmony_ci case 0: 34938c2ecf20Sopenharmony_ci default: 34948c2ecf20Sopenharmony_ci break; 34958c2ecf20Sopenharmony_ci } 34968c2ecf20Sopenharmony_ci 34978c2ecf20Sopenharmony_ci return (val & 0x3f) * mult; 34988c2ecf20Sopenharmony_ci} 34998c2ecf20Sopenharmony_ci 35008c2ecf20Sopenharmony_cistatic void set_pending_timer_val(int *val, u32 us) 35018c2ecf20Sopenharmony_ci{ 35028c2ecf20Sopenharmony_ci u8 mult = 0; 35038c2ecf20Sopenharmony_ci u8 shift = 0; 35048c2ecf20Sopenharmony_ci 35058c2ecf20Sopenharmony_ci if (us >= 0x3f) { 35068c2ecf20Sopenharmony_ci mult = 1; /* mult with 4 */ 35078c2ecf20Sopenharmony_ci shift = 2; 35088c2ecf20Sopenharmony_ci } 35098c2ecf20Sopenharmony_ci if (us >= 0x3f * 4) { 35108c2ecf20Sopenharmony_ci mult = 2; /* mult with 16 */ 35118c2ecf20Sopenharmony_ci shift = 4; 35128c2ecf20Sopenharmony_ci } 35138c2ecf20Sopenharmony_ci if (us >= 0x3f * 16) { 35148c2ecf20Sopenharmony_ci mult = 3; /* mult with 64 */ 35158c2ecf20Sopenharmony_ci shift = 6; 35168c2ecf20Sopenharmony_ci } 35178c2ecf20Sopenharmony_ci 35188c2ecf20Sopenharmony_ci *val = (mult << 6) | ((us >> shift) & 0x3f); 35198c2ecf20Sopenharmony_ci} 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_ci 35228c2ecf20Sopenharmony_cistatic int velocity_get_coalesce(struct net_device *dev, 35238c2ecf20Sopenharmony_ci struct ethtool_coalesce *ecmd) 35248c2ecf20Sopenharmony_ci{ 35258c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 35268c2ecf20Sopenharmony_ci 35278c2ecf20Sopenharmony_ci ecmd->tx_max_coalesced_frames = vptr->options.tx_intsup; 35288c2ecf20Sopenharmony_ci ecmd->rx_max_coalesced_frames = vptr->options.rx_intsup; 35298c2ecf20Sopenharmony_ci 35308c2ecf20Sopenharmony_ci ecmd->rx_coalesce_usecs = get_pending_timer_val(vptr->options.rxqueue_timer); 35318c2ecf20Sopenharmony_ci ecmd->tx_coalesce_usecs = get_pending_timer_val(vptr->options.txqueue_timer); 35328c2ecf20Sopenharmony_ci 35338c2ecf20Sopenharmony_ci return 0; 35348c2ecf20Sopenharmony_ci} 35358c2ecf20Sopenharmony_ci 35368c2ecf20Sopenharmony_cistatic int velocity_set_coalesce(struct net_device *dev, 35378c2ecf20Sopenharmony_ci struct ethtool_coalesce *ecmd) 35388c2ecf20Sopenharmony_ci{ 35398c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 35408c2ecf20Sopenharmony_ci int max_us = 0x3f * 64; 35418c2ecf20Sopenharmony_ci unsigned long flags; 35428c2ecf20Sopenharmony_ci 35438c2ecf20Sopenharmony_ci /* 6 bits of */ 35448c2ecf20Sopenharmony_ci if (ecmd->tx_coalesce_usecs > max_us) 35458c2ecf20Sopenharmony_ci return -EINVAL; 35468c2ecf20Sopenharmony_ci if (ecmd->rx_coalesce_usecs > max_us) 35478c2ecf20Sopenharmony_ci return -EINVAL; 35488c2ecf20Sopenharmony_ci 35498c2ecf20Sopenharmony_ci if (ecmd->tx_max_coalesced_frames > 0xff) 35508c2ecf20Sopenharmony_ci return -EINVAL; 35518c2ecf20Sopenharmony_ci if (ecmd->rx_max_coalesced_frames > 0xff) 35528c2ecf20Sopenharmony_ci return -EINVAL; 35538c2ecf20Sopenharmony_ci 35548c2ecf20Sopenharmony_ci vptr->options.rx_intsup = ecmd->rx_max_coalesced_frames; 35558c2ecf20Sopenharmony_ci vptr->options.tx_intsup = ecmd->tx_max_coalesced_frames; 35568c2ecf20Sopenharmony_ci 35578c2ecf20Sopenharmony_ci set_pending_timer_val(&vptr->options.rxqueue_timer, 35588c2ecf20Sopenharmony_ci ecmd->rx_coalesce_usecs); 35598c2ecf20Sopenharmony_ci set_pending_timer_val(&vptr->options.txqueue_timer, 35608c2ecf20Sopenharmony_ci ecmd->tx_coalesce_usecs); 35618c2ecf20Sopenharmony_ci 35628c2ecf20Sopenharmony_ci /* Setup the interrupt suppression and queue timers */ 35638c2ecf20Sopenharmony_ci spin_lock_irqsave(&vptr->lock, flags); 35648c2ecf20Sopenharmony_ci mac_disable_int(vptr->mac_regs); 35658c2ecf20Sopenharmony_ci setup_adaptive_interrupts(vptr); 35668c2ecf20Sopenharmony_ci setup_queue_timers(vptr); 35678c2ecf20Sopenharmony_ci 35688c2ecf20Sopenharmony_ci mac_write_int_mask(vptr->int_mask, vptr->mac_regs); 35698c2ecf20Sopenharmony_ci mac_clear_isr(vptr->mac_regs); 35708c2ecf20Sopenharmony_ci mac_enable_int(vptr->mac_regs); 35718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vptr->lock, flags); 35728c2ecf20Sopenharmony_ci 35738c2ecf20Sopenharmony_ci return 0; 35748c2ecf20Sopenharmony_ci} 35758c2ecf20Sopenharmony_ci 35768c2ecf20Sopenharmony_cistatic const char velocity_gstrings[][ETH_GSTRING_LEN] = { 35778c2ecf20Sopenharmony_ci "rx_all", 35788c2ecf20Sopenharmony_ci "rx_ok", 35798c2ecf20Sopenharmony_ci "tx_ok", 35808c2ecf20Sopenharmony_ci "rx_error", 35818c2ecf20Sopenharmony_ci "rx_runt_ok", 35828c2ecf20Sopenharmony_ci "rx_runt_err", 35838c2ecf20Sopenharmony_ci "rx_64", 35848c2ecf20Sopenharmony_ci "tx_64", 35858c2ecf20Sopenharmony_ci "rx_65_to_127", 35868c2ecf20Sopenharmony_ci "tx_65_to_127", 35878c2ecf20Sopenharmony_ci "rx_128_to_255", 35888c2ecf20Sopenharmony_ci "tx_128_to_255", 35898c2ecf20Sopenharmony_ci "rx_256_to_511", 35908c2ecf20Sopenharmony_ci "tx_256_to_511", 35918c2ecf20Sopenharmony_ci "rx_512_to_1023", 35928c2ecf20Sopenharmony_ci "tx_512_to_1023", 35938c2ecf20Sopenharmony_ci "rx_1024_to_1518", 35948c2ecf20Sopenharmony_ci "tx_1024_to_1518", 35958c2ecf20Sopenharmony_ci "tx_ether_collisions", 35968c2ecf20Sopenharmony_ci "rx_crc_errors", 35978c2ecf20Sopenharmony_ci "rx_jumbo", 35988c2ecf20Sopenharmony_ci "tx_jumbo", 35998c2ecf20Sopenharmony_ci "rx_mac_control_frames", 36008c2ecf20Sopenharmony_ci "tx_mac_control_frames", 36018c2ecf20Sopenharmony_ci "rx_frame_alignment_errors", 36028c2ecf20Sopenharmony_ci "rx_long_ok", 36038c2ecf20Sopenharmony_ci "rx_long_err", 36048c2ecf20Sopenharmony_ci "tx_sqe_errors", 36058c2ecf20Sopenharmony_ci "rx_no_buf", 36068c2ecf20Sopenharmony_ci "rx_symbol_errors", 36078c2ecf20Sopenharmony_ci "in_range_length_errors", 36088c2ecf20Sopenharmony_ci "late_collisions" 36098c2ecf20Sopenharmony_ci}; 36108c2ecf20Sopenharmony_ci 36118c2ecf20Sopenharmony_cistatic void velocity_get_strings(struct net_device *dev, u32 sset, u8 *data) 36128c2ecf20Sopenharmony_ci{ 36138c2ecf20Sopenharmony_ci switch (sset) { 36148c2ecf20Sopenharmony_ci case ETH_SS_STATS: 36158c2ecf20Sopenharmony_ci memcpy(data, *velocity_gstrings, sizeof(velocity_gstrings)); 36168c2ecf20Sopenharmony_ci break; 36178c2ecf20Sopenharmony_ci } 36188c2ecf20Sopenharmony_ci} 36198c2ecf20Sopenharmony_ci 36208c2ecf20Sopenharmony_cistatic int velocity_get_sset_count(struct net_device *dev, int sset) 36218c2ecf20Sopenharmony_ci{ 36228c2ecf20Sopenharmony_ci switch (sset) { 36238c2ecf20Sopenharmony_ci case ETH_SS_STATS: 36248c2ecf20Sopenharmony_ci return ARRAY_SIZE(velocity_gstrings); 36258c2ecf20Sopenharmony_ci default: 36268c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 36278c2ecf20Sopenharmony_ci } 36288c2ecf20Sopenharmony_ci} 36298c2ecf20Sopenharmony_ci 36308c2ecf20Sopenharmony_cistatic void velocity_get_ethtool_stats(struct net_device *dev, 36318c2ecf20Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 36328c2ecf20Sopenharmony_ci{ 36338c2ecf20Sopenharmony_ci if (netif_running(dev)) { 36348c2ecf20Sopenharmony_ci struct velocity_info *vptr = netdev_priv(dev); 36358c2ecf20Sopenharmony_ci u32 *p = vptr->mib_counter; 36368c2ecf20Sopenharmony_ci int i; 36378c2ecf20Sopenharmony_ci 36388c2ecf20Sopenharmony_ci spin_lock_irq(&vptr->lock); 36398c2ecf20Sopenharmony_ci velocity_update_hw_mibs(vptr); 36408c2ecf20Sopenharmony_ci spin_unlock_irq(&vptr->lock); 36418c2ecf20Sopenharmony_ci 36428c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(velocity_gstrings); i++) 36438c2ecf20Sopenharmony_ci *data++ = *p++; 36448c2ecf20Sopenharmony_ci } 36458c2ecf20Sopenharmony_ci} 36468c2ecf20Sopenharmony_ci 36478c2ecf20Sopenharmony_cistatic const struct ethtool_ops velocity_ethtool_ops = { 36488c2ecf20Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 36498c2ecf20Sopenharmony_ci ETHTOOL_COALESCE_MAX_FRAMES, 36508c2ecf20Sopenharmony_ci .get_drvinfo = velocity_get_drvinfo, 36518c2ecf20Sopenharmony_ci .get_wol = velocity_ethtool_get_wol, 36528c2ecf20Sopenharmony_ci .set_wol = velocity_ethtool_set_wol, 36538c2ecf20Sopenharmony_ci .get_link = velocity_get_link, 36548c2ecf20Sopenharmony_ci .get_strings = velocity_get_strings, 36558c2ecf20Sopenharmony_ci .get_sset_count = velocity_get_sset_count, 36568c2ecf20Sopenharmony_ci .get_ethtool_stats = velocity_get_ethtool_stats, 36578c2ecf20Sopenharmony_ci .get_coalesce = velocity_get_coalesce, 36588c2ecf20Sopenharmony_ci .set_coalesce = velocity_set_coalesce, 36598c2ecf20Sopenharmony_ci .begin = velocity_ethtool_up, 36608c2ecf20Sopenharmony_ci .complete = velocity_ethtool_down, 36618c2ecf20Sopenharmony_ci .get_link_ksettings = velocity_get_link_ksettings, 36628c2ecf20Sopenharmony_ci .set_link_ksettings = velocity_set_link_ksettings, 36638c2ecf20Sopenharmony_ci}; 36648c2ecf20Sopenharmony_ci 36658c2ecf20Sopenharmony_ci#if defined(CONFIG_PM) && defined(CONFIG_INET) 36668c2ecf20Sopenharmony_cistatic int velocity_netdev_event(struct notifier_block *nb, unsigned long notification, void *ptr) 36678c2ecf20Sopenharmony_ci{ 36688c2ecf20Sopenharmony_ci struct in_ifaddr *ifa = ptr; 36698c2ecf20Sopenharmony_ci struct net_device *dev = ifa->ifa_dev->dev; 36708c2ecf20Sopenharmony_ci 36718c2ecf20Sopenharmony_ci if (dev_net(dev) == &init_net && 36728c2ecf20Sopenharmony_ci dev->netdev_ops == &velocity_netdev_ops) 36738c2ecf20Sopenharmony_ci velocity_get_ip(netdev_priv(dev)); 36748c2ecf20Sopenharmony_ci 36758c2ecf20Sopenharmony_ci return NOTIFY_DONE; 36768c2ecf20Sopenharmony_ci} 36778c2ecf20Sopenharmony_ci 36788c2ecf20Sopenharmony_cistatic struct notifier_block velocity_inetaddr_notifier = { 36798c2ecf20Sopenharmony_ci .notifier_call = velocity_netdev_event, 36808c2ecf20Sopenharmony_ci}; 36818c2ecf20Sopenharmony_ci 36828c2ecf20Sopenharmony_cistatic void velocity_register_notifier(void) 36838c2ecf20Sopenharmony_ci{ 36848c2ecf20Sopenharmony_ci register_inetaddr_notifier(&velocity_inetaddr_notifier); 36858c2ecf20Sopenharmony_ci} 36868c2ecf20Sopenharmony_ci 36878c2ecf20Sopenharmony_cistatic void velocity_unregister_notifier(void) 36888c2ecf20Sopenharmony_ci{ 36898c2ecf20Sopenharmony_ci unregister_inetaddr_notifier(&velocity_inetaddr_notifier); 36908c2ecf20Sopenharmony_ci} 36918c2ecf20Sopenharmony_ci 36928c2ecf20Sopenharmony_ci#else 36938c2ecf20Sopenharmony_ci 36948c2ecf20Sopenharmony_ci#define velocity_register_notifier() do {} while (0) 36958c2ecf20Sopenharmony_ci#define velocity_unregister_notifier() do {} while (0) 36968c2ecf20Sopenharmony_ci 36978c2ecf20Sopenharmony_ci#endif /* defined(CONFIG_PM) && defined(CONFIG_INET) */ 36988c2ecf20Sopenharmony_ci 36998c2ecf20Sopenharmony_ci/** 37008c2ecf20Sopenharmony_ci * velocity_init_module - load time function 37018c2ecf20Sopenharmony_ci * 37028c2ecf20Sopenharmony_ci * Called when the velocity module is loaded. The PCI driver 37038c2ecf20Sopenharmony_ci * is registered with the PCI layer, and in turn will call 37048c2ecf20Sopenharmony_ci * the probe functions for each velocity adapter installed 37058c2ecf20Sopenharmony_ci * in the system. 37068c2ecf20Sopenharmony_ci */ 37078c2ecf20Sopenharmony_cistatic int __init velocity_init_module(void) 37088c2ecf20Sopenharmony_ci{ 37098c2ecf20Sopenharmony_ci int ret_pci, ret_platform; 37108c2ecf20Sopenharmony_ci 37118c2ecf20Sopenharmony_ci velocity_register_notifier(); 37128c2ecf20Sopenharmony_ci 37138c2ecf20Sopenharmony_ci ret_pci = pci_register_driver(&velocity_pci_driver); 37148c2ecf20Sopenharmony_ci ret_platform = platform_driver_register(&velocity_platform_driver); 37158c2ecf20Sopenharmony_ci 37168c2ecf20Sopenharmony_ci /* if both_registers failed, remove the notifier */ 37178c2ecf20Sopenharmony_ci if ((ret_pci < 0) && (ret_platform < 0)) { 37188c2ecf20Sopenharmony_ci velocity_unregister_notifier(); 37198c2ecf20Sopenharmony_ci return ret_pci; 37208c2ecf20Sopenharmony_ci } 37218c2ecf20Sopenharmony_ci 37228c2ecf20Sopenharmony_ci return 0; 37238c2ecf20Sopenharmony_ci} 37248c2ecf20Sopenharmony_ci 37258c2ecf20Sopenharmony_ci/** 37268c2ecf20Sopenharmony_ci * velocity_cleanup - module unload 37278c2ecf20Sopenharmony_ci * 37288c2ecf20Sopenharmony_ci * When the velocity hardware is unloaded this function is called. 37298c2ecf20Sopenharmony_ci * It will clean up the notifiers and the unregister the PCI 37308c2ecf20Sopenharmony_ci * driver interface for this hardware. This in turn cleans up 37318c2ecf20Sopenharmony_ci * all discovered interfaces before returning from the function 37328c2ecf20Sopenharmony_ci */ 37338c2ecf20Sopenharmony_cistatic void __exit velocity_cleanup_module(void) 37348c2ecf20Sopenharmony_ci{ 37358c2ecf20Sopenharmony_ci velocity_unregister_notifier(); 37368c2ecf20Sopenharmony_ci 37378c2ecf20Sopenharmony_ci pci_unregister_driver(&velocity_pci_driver); 37388c2ecf20Sopenharmony_ci platform_driver_unregister(&velocity_platform_driver); 37398c2ecf20Sopenharmony_ci} 37408c2ecf20Sopenharmony_ci 37418c2ecf20Sopenharmony_cimodule_init(velocity_init_module); 37428c2ecf20Sopenharmony_cimodule_exit(velocity_cleanup_module); 3743