2005-04-17 02:20:36 +04:00
/*
* This code is derived from the VIA reference driver ( copyright message
* below ) provided to Red Hat by VIA Networking Technologies , Inc . for
* addition to the Linux kernel .
*
* The code has been merged into one source file , cleaned up to follow
* Linux coding style , ported to the Linux 2.6 kernel tree and cleaned
* for 64 bit hardware platforms .
*
* TODO
* rx_copybreak / alignment
* More testing
*
2008-10-14 06:01:08 +04:00
* The changes are ( c ) Copyright 2004 , Red Hat Inc . < alan @ lxorguk . ukuu . org . uk >
2005-04-17 02:20:36 +04:00
* Additional fixes and clean up : Francois Romieu
*
* This source has not been verified for use in safety critical systems .
*
* Please direct queries about the revamped driver to the linux - kernel
* list not VIA .
*
* Original code :
*
* Copyright ( c ) 1996 , 2003 VIA Networking Technologies , Inc .
* All rights reserved .
*
* This software may be redistributed and / or modified under
* the terms of the GNU General Public License as published by the Free
* Software Foundation ; either version 2 of the License , or
* any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* for more details .
*
* Author : Chuang Liang - Shing , AJ Jiang
*
* Date : Jan 24 , 2003
*
* MODULE_LICENSE ( " GPL " ) ;
*
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/init.h>
# include <linux/mm.h>
# include <linux/errno.h>
# include <linux/ioport.h>
# include <linux/pci.h>
# include <linux/kernel.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/skbuff.h>
# include <linux/delay.h>
# include <linux/timer.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/string.h>
# include <linux/wait.h>
2009-07-20 21:35:21 +04:00
# include <linux/io.h>
2005-04-17 02:20:36 +04:00
# include <linux/if.h>
2009-07-20 21:35:21 +04:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
# include <linux/proc_fs.h>
# include <linux/inetdevice.h>
# include <linux/reboot.h>
# include <linux/ethtool.h>
# include <linux/mii.h>
# include <linux/in.h>
# include <linux/if_arp.h>
2007-08-25 00:56:49 +04:00
# include <linux/if_vlan.h>
2005-04-17 02:20:36 +04:00
# include <linux/ip.h>
# include <linux/tcp.h>
# include <linux/udp.h>
# include <linux/crc-ccitt.h>
# include <linux/crc32.h>
# include "via-velocity.h"
2009-07-20 21:35:21 +04:00
static int velocity_nics ;
2005-04-17 02:20:36 +04:00
static int msglevel = MSG_LEVEL_INFO ;
2007-08-25 01:40:45 +04:00
/**
* mac_get_cam_mask - Read a CAM mask
* @ regs : register block for this velocity
* @ mask : buffer to store mask
*
* Fetch the mask bits of the selected CAM and store them into the
* provided mask buffer .
*/
2009-07-20 21:35:21 +04:00
static void mac_get_cam_mask ( struct mac_regs __iomem * regs , u8 * mask )
2007-08-25 01:40:45 +04:00
{
int i ;
/* Select CAM mask */
BYTE_REG_BITS_SET ( CAMCR_PS_CAM_MASK , CAMCR_PS1 | CAMCR_PS0 , & regs - > CAMCR ) ;
writeb ( 0 , & regs - > CAMADDR ) ;
/* read mask */
for ( i = 0 ; i < 8 ; i + + )
* mask + + = readb ( & ( regs - > MARCAM [ i ] ) ) ;
/* disable CAMEN */
writeb ( 0 , & regs - > CAMADDR ) ;
/* Select mar */
BYTE_REG_BITS_SET ( CAMCR_PS_MAR , CAMCR_PS1 | CAMCR_PS0 , & regs - > CAMCR ) ;
}
/**
* mac_set_cam_mask - Set a CAM mask
* @ regs : register block for this velocity
* @ mask : CAM mask to load
*
* Store a new mask into a CAM
*/
2009-07-20 21:35:21 +04:00
static void mac_set_cam_mask ( struct mac_regs __iomem * regs , u8 * mask )
2007-08-25 01:40:45 +04:00
{
int i ;
/* Select CAM mask */
BYTE_REG_BITS_SET ( CAMCR_PS_CAM_MASK , CAMCR_PS1 | CAMCR_PS0 , & regs - > CAMCR ) ;
writeb ( CAMADDR_CAMEN , & regs - > CAMADDR ) ;
2009-07-20 21:35:21 +04:00
for ( i = 0 ; i < 8 ; i + + )
2007-08-25 01:40:45 +04:00
writeb ( * mask + + , & ( regs - > MARCAM [ i ] ) ) ;
2009-07-20 21:35:21 +04:00
2007-08-25 01:40:45 +04:00
/* disable CAMEN */
writeb ( 0 , & regs - > CAMADDR ) ;
/* Select mar */
BYTE_REG_BITS_SET ( CAMCR_PS_MAR , CAMCR_PS1 | CAMCR_PS0 , & regs - > CAMCR ) ;
}
2009-07-20 21:35:21 +04:00
static void mac_set_vlan_cam_mask ( struct mac_regs __iomem * regs , u8 * mask )
2007-08-25 01:40:45 +04:00
{
int i ;
/* Select CAM mask */
BYTE_REG_BITS_SET ( CAMCR_PS_CAM_MASK , CAMCR_PS1 | CAMCR_PS0 , & regs - > CAMCR ) ;
writeb ( CAMADDR_CAMEN | CAMADDR_VCAMSL , & regs - > CAMADDR ) ;
2009-07-20 21:35:21 +04:00
for ( i = 0 ; i < 8 ; i + + )
2007-08-25 01:40:45 +04:00
writeb ( * mask + + , & ( regs - > MARCAM [ i ] ) ) ;
2009-07-20 21:35:21 +04:00
2007-08-25 01:40:45 +04:00
/* disable CAMEN */
writeb ( 0 , & regs - > CAMADDR ) ;
/* Select mar */
BYTE_REG_BITS_SET ( CAMCR_PS_MAR , CAMCR_PS1 | CAMCR_PS0 , & regs - > CAMCR ) ;
}
/**
* mac_set_cam - set CAM data
* @ regs : register block of this velocity
* @ idx : Cam index
* @ addr : 2 or 6 bytes of CAM data
*
* Load an address or vlan tag into a CAM
*/
2009-07-20 21:35:21 +04:00
static void mac_set_cam ( struct mac_regs __iomem * regs , int idx , const u8 * addr )
2007-08-25 01:40:45 +04:00
{
int i ;
/* Select CAM mask */
BYTE_REG_BITS_SET ( CAMCR_PS_CAM_DATA , CAMCR_PS1 | CAMCR_PS0 , & regs - > CAMCR ) ;
idx & = ( 64 - 1 ) ;
writeb ( CAMADDR_CAMEN | idx , & regs - > CAMADDR ) ;
2009-07-20 21:35:21 +04:00
for ( i = 0 ; i < 6 ; i + + )
2007-08-25 01:40:45 +04:00
writeb ( * addr + + , & ( regs - > MARCAM [ i ] ) ) ;
2009-07-20 21:35:21 +04:00
2007-08-25 01:40:45 +04:00
BYTE_REG_BITS_ON ( CAMCR_CAMWR , & regs - > CAMCR ) ;
udelay ( 10 ) ;
writeb ( 0 , & regs - > CAMADDR ) ;
/* Select mar */
BYTE_REG_BITS_SET ( CAMCR_PS_MAR , CAMCR_PS1 | CAMCR_PS0 , & regs - > CAMCR ) ;
}
2009-07-20 21:35:21 +04:00
static void mac_set_vlan_cam ( struct mac_regs __iomem * regs , int idx ,
2007-08-25 01:40:45 +04:00
const u8 * addr )
{
/* Select CAM mask */
BYTE_REG_BITS_SET ( CAMCR_PS_CAM_DATA , CAMCR_PS1 | CAMCR_PS0 , & regs - > CAMCR ) ;
idx & = ( 64 - 1 ) ;
writeb ( CAMADDR_CAMEN | CAMADDR_VCAMSL | idx , & regs - > CAMADDR ) ;
writew ( * ( ( u16 * ) addr ) , & regs - > MARCAM [ 0 ] ) ;
BYTE_REG_BITS_ON ( CAMCR_CAMWR , & regs - > CAMCR ) ;
udelay ( 10 ) ;
writeb ( 0 , & regs - > CAMADDR ) ;
/* Select mar */
BYTE_REG_BITS_SET ( CAMCR_PS_MAR , CAMCR_PS1 | CAMCR_PS0 , & regs - > CAMCR ) ;
}
/**
* mac_wol_reset - reset WOL after exiting low power
* @ regs : register block of this velocity
*
* Called after we drop out of wake on lan mode in order to
* reset the Wake on lan features . This function doesn ' t restore
* the rest of the logic from the result of sleep / wakeup
*/
2009-07-20 21:35:21 +04:00
static void mac_wol_reset ( struct mac_regs __iomem * regs )
2007-08-25 01:40:45 +04:00
{
/* Turn off SWPTAG right after leaving power mode */
BYTE_REG_BITS_OFF ( STICKHW_SWPTAG , & regs - > STICKHW ) ;
/* clear sticky bits */
BYTE_REG_BITS_OFF ( ( STICKHW_DS1 | STICKHW_DS0 ) , & regs - > STICKHW ) ;
BYTE_REG_BITS_OFF ( CHIPGCR_FCGMII , & regs - > CHIPGCR ) ;
BYTE_REG_BITS_OFF ( CHIPGCR_FCMODE , & regs - > CHIPGCR ) ;
/* disable force PME-enable */
writeb ( WOLCFG_PMEOVR , & regs - > WOLCFGClr ) ;
/* disable power-event config bit */
writew ( 0xFFFF , & regs - > WOLCRClr ) ;
/* clear power status */
writew ( 0xFFFF , & regs - > WOLSRClr ) ;
}
2005-04-17 02:20:36 +04:00
2006-09-13 22:30:00 +04:00
static const struct ethtool_ops velocity_ethtool_ops ;
2005-04-17 02:20:36 +04:00
/*
Define module options
*/
MODULE_AUTHOR ( " VIA Networking Technologies, Inc. " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " VIA Networking Velocity Family Gigabit Ethernet Adapter Driver " ) ;
2009-07-20 21:35:21 +04:00
# define VELOCITY_PARAM(N, D) \
static int N [ MAX_UNITS ] = OPTION_DEFAULT ; \
2005-04-17 02:20:36 +04:00
module_param_array ( N , int , NULL , 0 ) ; \
2009-07-20 21:35:21 +04:00
MODULE_PARM_DESC ( N , D ) ;
2005-04-17 02:20:36 +04:00
# define RX_DESC_MIN 64
# define RX_DESC_MAX 255
# define RX_DESC_DEF 64
VELOCITY_PARAM ( RxDescriptors , " Number of receive descriptors " ) ;
# define TX_DESC_MIN 16
# define TX_DESC_MAX 256
# define TX_DESC_DEF 64
VELOCITY_PARAM ( TxDescriptors , " Number of transmit descriptors " ) ;
# define RX_THRESH_MIN 0
# define RX_THRESH_MAX 3
# define RX_THRESH_DEF 0
/* rx_thresh[] is used for controlling the receive fifo threshold.
0 : indicate the rxfifo threshold is 128 bytes .
1 : indicate the rxfifo threshold is 512 bytes .
2 : indicate the rxfifo threshold is 1024 bytes .
3 : indicate the rxfifo threshold is store & forward .
*/
VELOCITY_PARAM ( rx_thresh , " Receive fifo threshold " ) ;
# define DMA_LENGTH_MIN 0
# define DMA_LENGTH_MAX 7
2009-11-26 01:10:34 +03:00
# define DMA_LENGTH_DEF 6
2005-04-17 02:20:36 +04:00
/* DMA_length[] is used for controlling the DMA length
0 : 8 DWORDs
1 : 16 DWORDs
2 : 32 DWORDs
3 : 64 DWORDs
4 : 128 DWORDs
5 : 256 DWORDs
6 : SF ( flush till emply )
7 : SF ( flush till emply )
*/
VELOCITY_PARAM ( DMA_length , " DMA length " ) ;
# define IP_ALIG_DEF 0
/* IP_byte_align[] is used for IP header DWORD byte aligned
0 : indicate the IP header won ' t be DWORD byte aligned . ( Default ) .
1 : indicate the IP header will be DWORD byte aligned .
In some enviroment , the IP header should be DWORD byte aligned ,
or the packet will be droped when we receive it . ( eg : IPVS )
*/
VELOCITY_PARAM ( IP_byte_align , " Enable IP header dword aligned " ) ;
# define FLOW_CNTL_DEF 1
# define FLOW_CNTL_MIN 1
# define FLOW_CNTL_MAX 5
/* flow_control[] is used for setting the flow control ability of NIC.
1 : hardware deafult - AUTO ( default ) . Use Hardware default value in ANAR .
2 : enable TX flow control .
3 : enable RX flow control .
4 : enable RX / TX flow control .
5 : disable
*/
VELOCITY_PARAM ( flow_control , " Enable flow control ability " ) ;
# define MED_LNK_DEF 0
# define MED_LNK_MIN 0
# define MED_LNK_MAX 4
/* speed_duplex[] is used for setting the speed and duplex mode of NIC.
0 : indicate autonegotiation for both speed and duplex mode
1 : indicate 100 Mbps half duplex mode
2 : indicate 100 Mbps full duplex mode
3 : indicate 10 Mbps half duplex mode
4 : indicate 10 Mbps full duplex mode
Note :
2009-07-20 21:35:21 +04:00
if EEPROM have been set to the force mode , this option is ignored
by driver .
2005-04-17 02:20:36 +04:00
*/
VELOCITY_PARAM ( speed_duplex , " Setting the speed and duplex mode " ) ;
# define VAL_PKT_LEN_DEF 0
/* ValPktLen[] is used for setting the checksum offload ability of NIC.
0 : Receive frame with invalid layer 2 length ( Default )
1 : Drop frame with invalid layer 2 length
*/
VELOCITY_PARAM ( ValPktLen , " Receiving or Drop invalid 802.3 frame " ) ;
# define WOL_OPT_DEF 0
# define WOL_OPT_MIN 0
# define WOL_OPT_MAX 7
/* wol_opts[] is used for controlling wake on lan behavior.
0 : Wake up if recevied a magic packet . ( Default )
1 : Wake up if link status is on / off .
2 : Wake up if recevied an arp packet .
4 : Wake up if recevied any unicast packet .
Those value can be sumed up to support more than one option .
*/
VELOCITY_PARAM ( wol_opts , " Wake On Lan options " ) ;
static int rx_copybreak = 200 ;
module_param ( rx_copybreak , int , 0644 ) ;
MODULE_PARM_DESC ( rx_copybreak , " Copy breakpoint for copy-only-tiny-frames " ) ;
/*
* Internal board variants . At the moment we have only one
*/
2008-02-10 10:41:40 +03:00
static struct velocity_info_tbl chip_info_table [ ] = {
2006-06-27 17:25:28 +04:00
{ CHIP_TYPE_VT6110 , " VIA Networking Velocity Family Gigabit Ethernet Adapter " , 1 , 0x00FFFFFFUL } ,
{ }
2005-04-17 02:20:36 +04:00
} ;
/*
* Describe the PCI device identifiers that we support in this
* device driver . Used for hotplug autoloading .
*/
2010-01-07 14:58:11 +03:00
static DEFINE_PCI_DEVICE_TABLE ( velocity_id_table ) = {
2006-06-27 17:20:08 +04:00
{ PCI_DEVICE ( PCI_VENDOR_ID_VIA , PCI_DEVICE_ID_VIA_612X ) } ,
{ }
2005-04-17 02:20:36 +04:00
} ;
MODULE_DEVICE_TABLE ( pci , velocity_id_table ) ;
/**
* get_chip_name - identifier to name
* @ id : chip identifier
*
* Given a chip identifier return a suitable description . Returns
* a pointer a static string valid while the driver is loaded .
*/
2007-08-25 01:40:45 +04:00
static const char __devinit * get_chip_name ( enum chip_type chip_id )
2005-04-17 02:20:36 +04:00
{
int i ;
for ( i = 0 ; chip_info_table [ i ] . name ! = NULL ; i + + )
if ( chip_info_table [ i ] . chip_id = = chip_id )
break ;
return chip_info_table [ i ] . name ;
}
/**
* velocity_remove1 - device unplug
* @ pdev : PCI device being removed
*
* Device unload callback . Called on an unplug or on module
* unload for each active device that is present . Disconnects
* the device from the network layer and frees all the resources
*/
static void __devexit velocity_remove1 ( struct pci_dev * pdev )
{
struct net_device * dev = pci_get_drvdata ( pdev ) ;
2006-06-27 16:56:23 +04:00
struct velocity_info * vptr = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
unregister_netdev ( dev ) ;
iounmap ( vptr - > mac_regs ) ;
pci_release_regions ( pdev ) ;
pci_disable_device ( pdev ) ;
pci_set_drvdata ( pdev , NULL ) ;
free_netdev ( dev ) ;
velocity_nics - - ;
}
/**
* velocity_set_int_opt - parser for integer options
* @ opt : pointer to option value
* @ val : value the user requested ( or - 1 for default )
* @ min : lowest value allowed
* @ max : highest value allowed
* @ def : default value
* @ name : property name
* @ dev : device name
*
* Set an integer property in the module options . This function does
* all the verification and checking as well as reporting so that
* we don ' t duplicate code for each option .
*/
2008-10-23 17:03:44 +04:00
static void __devinit velocity_set_int_opt ( int * opt , int val , int min , int max , int def , char * name , const char * devname )
2005-04-17 02:20:36 +04:00
{
if ( val = = - 1 )
* opt = def ;
else if ( val < min | | val > max ) {
VELOCITY_PRT ( MSG_LEVEL_INFO , KERN_NOTICE " %s: the value of parameter %s is invalid, the valid range is (%d-%d) \n " ,
devname , name , min , max ) ;
* opt = def ;
} else {
VELOCITY_PRT ( MSG_LEVEL_INFO , KERN_INFO " %s: set value of parameter %s to %d \n " ,
devname , name , val ) ;
* opt = val ;
}
}
/**
* velocity_set_bool_opt - parser for boolean options
* @ opt : pointer to option value
* @ val : value the user requested ( or - 1 for default )
* @ def : default value ( yes / no )
* @ flag : numeric value to set for true .
* @ name : property name
* @ dev : device name
*
* Set a boolean property in the module options . This function does
* all the verification and checking as well as reporting so that
* we don ' t duplicate code for each option .
*/
2009-07-20 21:35:21 +04:00
static void __devinit velocity_set_bool_opt ( u32 * opt , int val , int def , u32 flag , char * name , const char * devname )
2005-04-17 02:20:36 +04:00
{
( * opt ) & = ( ~ flag ) ;
if ( val = = - 1 )
* opt | = ( def ? flag : 0 ) ;
else if ( val < 0 | | val > 1 ) {
2006-09-13 21:24:59 +04:00
printk ( KERN_NOTICE " %s: the value of parameter %s is invalid, the valid range is (0-1) \n " ,
2005-04-17 02:20:36 +04:00
devname , name ) ;
* opt | = ( def ? flag : 0 ) ;
} else {
2006-09-13 21:24:59 +04:00
printk ( KERN_INFO " %s: set parameter %s to %s \n " ,
2005-04-17 02:20:36 +04:00
devname , name , val ? " TRUE " : " FALSE " ) ;
* opt | = ( val ? flag : 0 ) ;
}
}
/**
* velocity_get_options - set options on device
* @ opts : option structure for the device
* @ index : index of option to use in module options array
* @ devname : device name
*
* Turn the module and command options into a single structure
* for the current device
*/
2008-10-23 17:03:44 +04:00
static void __devinit velocity_get_options ( struct velocity_opt * opts , int index , const char * devname )
2005-04-17 02:20:36 +04:00
{
velocity_set_int_opt ( & opts - > rx_thresh , rx_thresh [ index ] , RX_THRESH_MIN , RX_THRESH_MAX , RX_THRESH_DEF , " rx_thresh " , devname ) ;
velocity_set_int_opt ( & opts - > DMA_length , DMA_length [ index ] , DMA_LENGTH_MIN , DMA_LENGTH_MAX , DMA_LENGTH_DEF , " DMA_length " , devname ) ;
velocity_set_int_opt ( & opts - > numrx , RxDescriptors [ index ] , RX_DESC_MIN , RX_DESC_MAX , RX_DESC_DEF , " RxDescriptors " , devname ) ;
velocity_set_int_opt ( & opts - > numtx , TxDescriptors [ index ] , TX_DESC_MIN , TX_DESC_MAX , TX_DESC_DEF , " TxDescriptors " , devname ) ;
2007-08-25 00:56:49 +04:00
2005-04-17 02:20:36 +04:00
velocity_set_int_opt ( & opts - > flow_cntl , flow_control [ index ] , FLOW_CNTL_MIN , FLOW_CNTL_MAX , FLOW_CNTL_DEF , " flow_control " , devname ) ;
velocity_set_bool_opt ( & opts - > flags , IP_byte_align [ index ] , IP_ALIG_DEF , VELOCITY_FLAGS_IP_ALIGN , " IP_byte_align " , devname ) ;
velocity_set_bool_opt ( & opts - > flags , ValPktLen [ index ] , VAL_PKT_LEN_DEF , VELOCITY_FLAGS_VAL_PKT_LEN , " ValPktLen " , devname ) ;
velocity_set_int_opt ( ( int * ) & opts - > spd_dpx , speed_duplex [ index ] , MED_LNK_MIN , MED_LNK_MAX , MED_LNK_DEF , " Media link mode " , devname ) ;
velocity_set_int_opt ( ( int * ) & opts - > wol_opts , wol_opts [ index ] , WOL_OPT_MIN , WOL_OPT_MAX , WOL_OPT_DEF , " Wake On Lan options " , devname ) ;
opts - > numrx = ( opts - > numrx & ~ 3 ) ;
}
/**
* velocity_init_cam_filter - initialise CAM
* @ vptr : velocity to program
*
* Initialize the content addressable memory used for filters . Load
* appropriately according to the presence of VLAN
*/
static void velocity_init_cam_filter ( struct velocity_info * vptr )
{
2009-07-20 21:35:21 +04:00
struct mac_regs __iomem * regs = vptr - > mac_regs ;
2005-04-17 02:20:36 +04:00
/* Turn on MCFG_PQEN, turn off MCFG_RTGOPT */
WORD_REG_BITS_SET ( MCFG_PQEN , MCFG_RTGOPT , & regs - > MCFG ) ;
WORD_REG_BITS_ON ( MCFG_VIDFR , & regs - > MCFG ) ;
/* Disable all CAMs */
memset ( vptr - > vCAMmask , 0 , sizeof ( u8 ) * 8 ) ;
memset ( vptr - > mCAMmask , 0 , sizeof ( u8 ) * 8 ) ;
2007-08-25 01:40:45 +04:00
mac_set_vlan_cam_mask ( regs , vptr - > vCAMmask ) ;
mac_set_cam_mask ( regs , vptr - > mCAMmask ) ;
2005-04-17 02:20:36 +04:00
2008-04-25 01:32:33 +04:00
/* Enable VCAMs */
2007-08-25 00:56:49 +04:00
if ( vptr - > vlgrp ) {
2008-04-25 01:32:33 +04:00
unsigned int vid , i = 0 ;
if ( ! vlan_group_get_device ( vptr - > vlgrp , 0 ) )
WORD_REG_BITS_ON ( MCFG_RTGOPT , & regs - > MCFG ) ;
2007-08-25 00:56:49 +04:00
2008-04-25 01:32:33 +04:00
for ( vid = 1 ; ( vid < VLAN_VID_MASK ) ; vid + + ) {
if ( vlan_group_get_device ( vptr - > vlgrp , vid ) ) {
mac_set_vlan_cam ( regs , i , ( u8 * ) & vid ) ;
vptr - > vCAMmask [ i / 8 ] | = 0x1 < < ( i % 8 ) ;
if ( + + i > = VCAM_SIZE )
break ;
2007-08-25 00:56:49 +04:00
}
}
2007-08-25 01:40:45 +04:00
mac_set_vlan_cam_mask ( regs , vptr - > vCAMmask ) ;
2005-04-17 02:20:36 +04:00
}
}
2008-04-25 01:32:33 +04:00
static void velocity_vlan_rx_register ( struct net_device * dev ,
struct vlan_group * grp )
{
struct velocity_info * vptr = netdev_priv ( dev ) ;
vptr - > vlgrp = grp ;
}
2007-08-25 00:56:49 +04:00
static void velocity_vlan_rx_add_vid ( struct net_device * dev , unsigned short vid )
{
struct velocity_info * vptr = netdev_priv ( dev ) ;
2009-07-20 21:35:21 +04:00
spin_lock_irq ( & vptr - > lock ) ;
2007-08-25 00:56:49 +04:00
velocity_init_cam_filter ( vptr ) ;
2009-07-20 21:35:21 +04:00
spin_unlock_irq ( & vptr - > lock ) ;
2007-08-25 00:56:49 +04:00
}
static void velocity_vlan_rx_kill_vid ( struct net_device * dev , unsigned short vid )
{
struct velocity_info * vptr = netdev_priv ( dev ) ;
2009-07-20 21:35:21 +04:00
spin_lock_irq ( & vptr - > lock ) ;
2007-08-25 00:56:49 +04:00
vlan_group_set_device ( vptr - > vlgrp , vid , NULL ) ;
velocity_init_cam_filter ( vptr ) ;
2009-07-20 21:35:21 +04:00
spin_unlock_irq ( & vptr - > lock ) ;
2007-08-25 00:56:49 +04:00
}
2008-08-01 00:51:18 +04:00
static void velocity_init_rx_ring_indexes ( struct velocity_info * vptr )
{
vptr - > rx . dirty = vptr - > rx . filled = vptr - > rx . curr = 0 ;
}
2007-08-25 00:56:49 +04:00
2005-04-17 02:20:36 +04:00
/**
* velocity_rx_reset - handle a receive reset
* @ vptr : velocity we are resetting
*
* Reset the ownership and status for the receive ring side .
* Hand all the receive queue to the NIC .
*/
static void velocity_rx_reset ( struct velocity_info * vptr )
{
2009-07-20 21:35:21 +04:00
struct mac_regs __iomem * regs = vptr - > mac_regs ;
2005-04-17 02:20:36 +04:00
int i ;
2008-08-01 00:51:18 +04:00
velocity_init_rx_ring_indexes ( vptr ) ;
2005-04-17 02:20:36 +04:00
/*
* Init state , all RD entries belong to the NIC
*/
for ( i = 0 ; i < vptr - > options . numrx ; + + i )
2008-08-01 00:10:10 +04:00
vptr - > rx . ring [ i ] . rdesc0 . len | = OWNED_BY_NIC ;
2005-04-17 02:20:36 +04:00
writew ( vptr - > options . numrx , & regs - > RBRDU ) ;
2008-08-01 00:10:10 +04:00
writel ( vptr - > rx . pool_dma , & regs - > RDBaseLo ) ;
2005-04-17 02:20:36 +04:00
writew ( 0 , & regs - > RDIdx ) ;
writew ( vptr - > options . numrx - 1 , & regs - > RDCSize ) ;
}
/**
2009-07-24 05:11:12 +04:00
* velocity_get_opt_media_mode - get media selection
* @ vptr : velocity adapter
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Get the media mode stored in EEPROM or module options and load
* mii_status accordingly . The requested link state information
* is also returned .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static u32 velocity_get_opt_media_mode ( struct velocity_info * vptr )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
u32 status = 0 ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
switch ( vptr - > options . spd_dpx ) {
case SPD_DPX_AUTO :
status = VELOCITY_AUTONEG_ENABLE ;
2005-04-17 02:20:36 +04:00
break ;
2009-07-24 05:11:12 +04:00
case SPD_DPX_100_FULL :
status = VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL ;
break ;
case SPD_DPX_10_FULL :
status = VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL ;
break ;
case SPD_DPX_100_HALF :
status = VELOCITY_SPEED_100 ;
break ;
case SPD_DPX_10_HALF :
status = VELOCITY_SPEED_10 ;
break ;
}
vptr - > mii_status = status ;
return status ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/**
* safe_disable_mii_autopoll - autopoll off
* @ regs : velocity registers
*
* Turn off the autopoll and wait for it to disable on the chip
*/
static void safe_disable_mii_autopoll ( struct mac_regs __iomem * regs )
{
u16 ww ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/* turn off MAUTO */
writeb ( 0 , & regs - > MIICR ) ;
for ( ww = 0 ; ww < W_MAX_TIMEOUT ; ww + + ) {
udelay ( 1 ) ;
if ( BYTE_REG_BITS_IS_ON ( MIISR_MIDLE , & regs - > MIISR ) )
break ;
}
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/**
* enable_mii_autopoll - turn on autopolling
* @ regs : velocity registers
*
* Enable the MII link status autopoll feature on the Velocity
* hardware . Wait for it to enable .
*/
static void enable_mii_autopoll ( struct mac_regs __iomem * regs )
{
int ii ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
writeb ( 0 , & ( regs - > MIICR ) ) ;
writeb ( MIIADR_SWMPL , & regs - > MIIADR ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
for ( ii = 0 ; ii < W_MAX_TIMEOUT ; ii + + ) {
udelay ( 1 ) ;
if ( BYTE_REG_BITS_IS_ON ( MIISR_MIDLE , & regs - > MIISR ) )
break ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
writeb ( MIICR_MAUTO , & regs - > MIICR ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
for ( ii = 0 ; ii < W_MAX_TIMEOUT ; ii + + ) {
udelay ( 1 ) ;
if ( ! BYTE_REG_BITS_IS_ON ( MIISR_MIDLE , & regs - > MIISR ) )
break ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/**
* velocity_mii_read - read MII data
* @ regs : velocity registers
* @ index : MII register index
* @ data : buffer for received data
*
* Perform a single read of an MII 16 bit register . Returns zero
* on success or - ETIMEDOUT if the PHY did not respond .
*/
static int velocity_mii_read ( struct mac_regs __iomem * regs , u8 index , u16 * data )
{
u16 ww ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
* Disable MIICR_MAUTO , so that mii addr can be set normally
*/
safe_disable_mii_autopoll ( regs ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
writeb ( index , & regs - > MIIADR ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
BYTE_REG_BITS_ON ( MIICR_RCMD , & regs - > MIICR ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
for ( ww = 0 ; ww < W_MAX_TIMEOUT ; ww + + ) {
if ( ! ( readb ( & regs - > MIICR ) & MIICR_RCMD ) )
break ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
* data = readw ( & regs - > MIIDATA ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
enable_mii_autopoll ( regs ) ;
if ( ww = = W_MAX_TIMEOUT )
return - ETIMEDOUT ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
2005-04-17 02:20:36 +04:00
/**
2009-07-24 05:11:12 +04:00
* mii_check_media_mode - check media state
* @ regs : velocity registers
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Check the current MII status and determine the link status
* accordingly
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static u32 mii_check_media_mode ( struct mac_regs __iomem * regs )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
u32 status = 0 ;
u16 ANAR ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( ! MII_REG_BITS_IS_ON ( BMSR_LNK , MII_REG_BMSR , regs ) )
status | = VELOCITY_LINK_FAIL ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( MII_REG_BITS_IS_ON ( G1000CR_1000FD , MII_REG_G1000CR , regs ) )
status | = VELOCITY_SPEED_1000 | VELOCITY_DUPLEX_FULL ;
else if ( MII_REG_BITS_IS_ON ( G1000CR_1000 , MII_REG_G1000CR , regs ) )
status | = ( VELOCITY_SPEED_1000 ) ;
else {
velocity_mii_read ( regs , MII_REG_ANAR , & ANAR ) ;
if ( ANAR & ANAR_TXFD )
status | = ( VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL ) ;
else if ( ANAR & ANAR_TX )
status | = VELOCITY_SPEED_100 ;
else if ( ANAR & ANAR_10FD )
status | = ( VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL ) ;
else
status | = ( VELOCITY_SPEED_10 ) ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
if ( MII_REG_BITS_IS_ON ( BMCR_AUTO , MII_REG_BMCR , regs ) ) {
velocity_mii_read ( regs , MII_REG_ANAR , & ANAR ) ;
if ( ( ANAR & ( ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10 ) )
= = ( ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10 ) ) {
if ( MII_REG_BITS_IS_ON ( G1000CR_1000 | G1000CR_1000FD , MII_REG_G1000CR , regs ) )
status | = VELOCITY_AUTONEG_ENABLE ;
}
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
return status ;
}
2008-11-20 09:19:33 +03:00
2005-04-17 02:20:36 +04:00
/**
2009-07-24 05:11:12 +04:00
* velocity_mii_write - write MII data
* @ regs : velocity registers
* @ index : MII register index
* @ data : 16 bit data for the MII register
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Perform a single write to an MII 16 bit register . Returns zero
* on success or - ETIMEDOUT if the PHY did not respond .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static int velocity_mii_write ( struct mac_regs __iomem * regs , u8 mii_addr , u16 data )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
u16 ww ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
* Disable MIICR_MAUTO , so that mii addr can be set normally
2006-06-27 17:20:08 +04:00
*/
2009-07-24 05:11:12 +04:00
safe_disable_mii_autopoll ( regs ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/* MII reg offset */
writeb ( mii_addr , & regs - > MIIADR ) ;
/* set MII data */
writew ( data , & regs - > MIIDATA ) ;
/* turn on MIICR_WCMD */
BYTE_REG_BITS_ON ( MIICR_WCMD , & regs - > MIICR ) ;
/* W_MAX_TIMEOUT is the timeout period */
for ( ww = 0 ; ww < W_MAX_TIMEOUT ; ww + + ) {
udelay ( 5 ) ;
if ( ! ( readb ( & regs - > MIICR ) & MIICR_WCMD ) )
break ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
enable_mii_autopoll ( regs ) ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
if ( ww = = W_MAX_TIMEOUT )
return - ETIMEDOUT ;
return 0 ;
}
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
/**
* set_mii_flow_control - flow control setup
* @ vptr : velocity interface
*
* Set up the flow control on this interface according to
* the supplied user / eeprom options .
*/
static void set_mii_flow_control ( struct velocity_info * vptr )
{
/*Enable or Disable PAUSE in ANAR */
switch ( vptr - > options . flow_cntl ) {
case FLOW_CNTL_TX :
MII_REG_BITS_OFF ( ANAR_PAUSE , MII_REG_ANAR , vptr - > mac_regs ) ;
MII_REG_BITS_ON ( ANAR_ASMDIR , MII_REG_ANAR , vptr - > mac_regs ) ;
break ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
case FLOW_CNTL_RX :
MII_REG_BITS_ON ( ANAR_PAUSE , MII_REG_ANAR , vptr - > mac_regs ) ;
MII_REG_BITS_ON ( ANAR_ASMDIR , MII_REG_ANAR , vptr - > mac_regs ) ;
break ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
case FLOW_CNTL_TX_RX :
MII_REG_BITS_ON ( ANAR_PAUSE , MII_REG_ANAR , vptr - > mac_regs ) ;
MII_REG_BITS_ON ( ANAR_ASMDIR , MII_REG_ANAR , vptr - > mac_regs ) ;
break ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
case FLOW_CNTL_DISABLE :
MII_REG_BITS_OFF ( ANAR_PAUSE , MII_REG_ANAR , vptr - > mac_regs ) ;
MII_REG_BITS_OFF ( ANAR_ASMDIR , MII_REG_ANAR , vptr - > mac_regs ) ;
break ;
default :
break ;
}
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/**
* mii_set_auto_on - autonegotiate on
* @ vptr : velocity
*
* Enable autonegotation on this interface
*/
static void mii_set_auto_on ( struct velocity_info * vptr )
{
if ( MII_REG_BITS_IS_ON ( BMCR_AUTO , MII_REG_BMCR , vptr - > mac_regs ) )
MII_REG_BITS_ON ( BMCR_REAUTO , MII_REG_BMCR , vptr - > mac_regs ) ;
else
MII_REG_BITS_ON ( BMCR_AUTO , MII_REG_BMCR , vptr - > mac_regs ) ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
static u32 check_connection_type ( struct mac_regs __iomem * regs )
{
u32 status = 0 ;
u8 PHYSR0 ;
u16 ANAR ;
PHYSR0 = readb ( & regs - > PHYSR0 ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
if ( ! ( PHYSR0 & PHYSR0_LINKGD ) )
status | = VELOCITY_LINK_FAIL ;
*/
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( PHYSR0 & PHYSR0_FDPX )
status | = VELOCITY_DUPLEX_FULL ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( PHYSR0 & PHYSR0_SPDG )
status | = VELOCITY_SPEED_1000 ;
else if ( PHYSR0 & PHYSR0_SPD10 )
status | = VELOCITY_SPEED_10 ;
else
status | = VELOCITY_SPEED_100 ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( MII_REG_BITS_IS_ON ( BMCR_AUTO , MII_REG_BMCR , regs ) ) {
velocity_mii_read ( regs , MII_REG_ANAR , & ANAR ) ;
if ( ( ANAR & ( ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10 ) )
= = ( ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10 ) ) {
if ( MII_REG_BITS_IS_ON ( G1000CR_1000 | G1000CR_1000FD , MII_REG_G1000CR , regs ) )
status | = VELOCITY_AUTONEG_ENABLE ;
}
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
return status ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/**
* velocity_set_media_mode - set media mode
* @ mii_status : old MII link state
*
* Check the media link state and configure the flow control
* PHY and also velocity hardware setup accordingly . In particular
* we need to set up CD polling and frame bursting .
*/
static int velocity_set_media_mode ( struct velocity_info * vptr , u32 mii_status )
{
u32 curr_status ;
struct mac_regs __iomem * regs = vptr - > mac_regs ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
vptr - > mii_status = mii_check_media_mode ( vptr - > mac_regs ) ;
curr_status = vptr - > mii_status & ( ~ VELOCITY_LINK_FAIL ) ;
2008-10-23 17:03:44 +04:00
2009-07-24 05:11:12 +04:00
/* Set mii link status */
set_mii_flow_control ( vptr ) ;
2005-04-17 02:20:36 +04:00
2006-09-13 21:24:59 +04:00
/*
2009-07-24 05:11:12 +04:00
Check if new status is consisent with current status
2009-12-03 10:58:21 +03:00
if ( ( ( mii_status & curr_status ) & VELOCITY_AUTONEG_ENABLE ) | |
( mii_status = = curr_status ) ) {
2009-07-24 05:11:12 +04:00
vptr - > mii_status = mii_check_media_mode ( vptr - > mac_regs ) ;
vptr - > mii_status = check_connection_type ( vptr - > mac_regs ) ;
VELOCITY_PRT ( MSG_LEVEL_INFO , " Velocity link no change \n " ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
*/
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
if ( PHYID_GET_PHY_ID ( vptr - > phy_id ) = = PHYID_CICADA_CS8201 )
MII_REG_BITS_ON ( AUXCR_MDPPS , MII_REG_AUXCR , vptr - > mac_regs ) ;
2005-04-17 02:20:36 +04:00
/*
2009-07-24 05:11:12 +04:00
* If connection type is AUTO
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
if ( mii_status & VELOCITY_AUTONEG_ENABLE ) {
VELOCITY_PRT ( MSG_LEVEL_INFO , " Velocity is AUTO mode \n " ) ;
/* clear force MAC mode bit */
BYTE_REG_BITS_OFF ( CHIPGCR_FCMODE , & regs - > CHIPGCR ) ;
/* set duplex mode of MAC according to duplex mode of MII */
MII_REG_BITS_ON ( ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10 , MII_REG_ANAR , vptr - > mac_regs ) ;
MII_REG_BITS_ON ( G1000CR_1000FD | G1000CR_1000 , MII_REG_G1000CR , vptr - > mac_regs ) ;
MII_REG_BITS_ON ( BMCR_SPEED1G , MII_REG_BMCR , vptr - > mac_regs ) ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
/* enable AUTO-NEGO mode */
mii_set_auto_on ( vptr ) ;
} else {
u16 ANAR ;
u8 CHIPGCR ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
* 1. if it ' s 3119 , disable frame bursting in halfduplex mode
* and enable it in fullduplex mode
* 2. set correct MII / GMII and half / full duplex mode in CHIPGCR
* 3. only enable CD heart beat counter in 10 HD mode
*/
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/* set force MAC mode bit */
BYTE_REG_BITS_ON ( CHIPGCR_FCMODE , & regs - > CHIPGCR ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
CHIPGCR = readb ( & regs - > CHIPGCR ) ;
CHIPGCR & = ~ CHIPGCR_FCGMII ;
2007-08-25 00:56:49 +04:00
2009-07-24 05:11:12 +04:00
if ( mii_status & VELOCITY_DUPLEX_FULL ) {
CHIPGCR | = CHIPGCR_FCFDX ;
writeb ( CHIPGCR , & regs - > CHIPGCR ) ;
VELOCITY_PRT ( MSG_LEVEL_INFO , " set Velocity to forced full mode \n " ) ;
if ( vptr - > rev_id < REV_ID_VT3216_A0 )
BYTE_REG_BITS_OFF ( TCR_TB2BDIS , & regs - > TCR ) ;
} else {
CHIPGCR & = ~ CHIPGCR_FCFDX ;
VELOCITY_PRT ( MSG_LEVEL_INFO , " set Velocity to forced half mode \n " ) ;
writeb ( CHIPGCR , & regs - > CHIPGCR ) ;
if ( vptr - > rev_id < REV_ID_VT3216_A0 )
BYTE_REG_BITS_ON ( TCR_TB2BDIS , & regs - > TCR ) ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
MII_REG_BITS_OFF ( G1000CR_1000FD | G1000CR_1000 , MII_REG_G1000CR , vptr - > mac_regs ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( ! ( mii_status & VELOCITY_DUPLEX_FULL ) & & ( mii_status & VELOCITY_SPEED_10 ) )
BYTE_REG_BITS_OFF ( TESTCFG_HBDIS , & regs - > TESTCFG ) ;
else
BYTE_REG_BITS_ON ( TESTCFG_HBDIS , & regs - > TESTCFG ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/* MII_REG_BITS_OFF(BMCR_SPEED1G, MII_REG_BMCR, vptr->mac_regs); */
velocity_mii_read ( vptr - > mac_regs , MII_REG_ANAR , & ANAR ) ;
ANAR & = ( ~ ( ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10 ) ) ;
if ( mii_status & VELOCITY_SPEED_100 ) {
if ( mii_status & VELOCITY_DUPLEX_FULL )
ANAR | = ANAR_TXFD ;
else
ANAR | = ANAR_TX ;
} else {
if ( mii_status & VELOCITY_DUPLEX_FULL )
ANAR | = ANAR_10FD ;
else
ANAR | = ANAR_10 ;
}
velocity_mii_write ( vptr - > mac_regs , MII_REG_ANAR , ANAR ) ;
/* enable AUTO-NEGO mode */
mii_set_auto_on ( vptr ) ;
/* MII_REG_BITS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs); */
2009-06-16 15:25:49 +04:00
}
2009-07-24 05:11:12 +04:00
/* vptr->mii_status=mii_check_media_mode(vptr->mac_regs); */
/* vptr->mii_status=check_connection_type(vptr->mac_regs); */
return VELOCITY_LINK_CHANGE ;
}
2006-06-23 02:47:06 +04:00
2009-07-24 05:11:12 +04:00
/**
* velocity_print_link_status - link status reporting
* @ vptr : velocity to report on
*
* Turn the link status of the velocity card into a kernel log
* description of the new link state , detailing speed and duplex
* status
*/
static void velocity_print_link_status ( struct velocity_info * vptr )
{
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
if ( vptr - > mii_status & VELOCITY_LINK_FAIL ) {
VELOCITY_PRT ( MSG_LEVEL_INFO , KERN_NOTICE " %s: failed to detect cable link \n " , vptr - > dev - > name ) ;
} else if ( vptr - > options . spd_dpx = = SPD_DPX_AUTO ) {
VELOCITY_PRT ( MSG_LEVEL_INFO , KERN_NOTICE " %s: Link auto-negotiation " , vptr - > dev - > name ) ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
if ( vptr - > mii_status & VELOCITY_SPEED_1000 )
VELOCITY_PRT ( MSG_LEVEL_INFO , " speed 1000M bps " ) ;
else if ( vptr - > mii_status & VELOCITY_SPEED_100 )
VELOCITY_PRT ( MSG_LEVEL_INFO , " speed 100M bps " ) ;
else
VELOCITY_PRT ( MSG_LEVEL_INFO , " speed 10M bps " ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( vptr - > mii_status & VELOCITY_DUPLEX_FULL )
VELOCITY_PRT ( MSG_LEVEL_INFO , " full duplex \n " ) ;
else
VELOCITY_PRT ( MSG_LEVEL_INFO , " half duplex \n " ) ;
} else {
VELOCITY_PRT ( MSG_LEVEL_INFO , KERN_NOTICE " %s: Link forced " , vptr - > dev - > name ) ;
switch ( vptr - > options . spd_dpx ) {
case SPD_DPX_100_HALF :
VELOCITY_PRT ( MSG_LEVEL_INFO , " speed 100M bps half duplex \n " ) ;
break ;
case SPD_DPX_100_FULL :
VELOCITY_PRT ( MSG_LEVEL_INFO , " speed 100M bps full duplex \n " ) ;
break ;
case SPD_DPX_10_HALF :
VELOCITY_PRT ( MSG_LEVEL_INFO , " speed 10M bps half duplex \n " ) ;
break ;
case SPD_DPX_10_FULL :
VELOCITY_PRT ( MSG_LEVEL_INFO , " speed 10M bps full duplex \n " ) ;
break ;
default :
break ;
}
2005-04-17 02:20:36 +04:00
}
}
/**
2009-07-24 05:11:12 +04:00
* enable_flow_control_ability - flow control
* @ vptr : veloity to configure
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Set up flow control according to the flow control options
* determined by the eeprom / configuration .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static void enable_flow_control_ability ( struct velocity_info * vptr )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct mac_regs __iomem * regs = vptr - > mac_regs ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
switch ( vptr - > options . flow_cntl ) {
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
case FLOW_CNTL_DEFAULT :
if ( BYTE_REG_BITS_IS_ON ( PHYSR0_RXFLC , & regs - > PHYSR0 ) )
writel ( CR0_FDXRFCEN , & regs - > CR0Set ) ;
else
writel ( CR0_FDXRFCEN , & regs - > CR0Clr ) ;
if ( BYTE_REG_BITS_IS_ON ( PHYSR0_TXFLC , & regs - > PHYSR0 ) )
writel ( CR0_FDXTFCEN , & regs - > CR0Set ) ;
else
writel ( CR0_FDXTFCEN , & regs - > CR0Clr ) ;
break ;
case FLOW_CNTL_TX :
writel ( CR0_FDXTFCEN , & regs - > CR0Set ) ;
writel ( CR0_FDXRFCEN , & regs - > CR0Clr ) ;
break ;
case FLOW_CNTL_RX :
writel ( CR0_FDXRFCEN , & regs - > CR0Set ) ;
writel ( CR0_FDXTFCEN , & regs - > CR0Clr ) ;
break ;
case FLOW_CNTL_TX_RX :
writel ( CR0_FDXTFCEN , & regs - > CR0Set ) ;
writel ( CR0_FDXRFCEN , & regs - > CR0Set ) ;
break ;
case FLOW_CNTL_DISABLE :
writel ( CR0_FDXRFCEN , & regs - > CR0Clr ) ;
writel ( CR0_FDXTFCEN , & regs - > CR0Clr ) ;
break ;
default :
break ;
}
2005-04-17 02:20:36 +04:00
}
/**
2009-07-24 05:11:12 +04:00
* velocity_soft_reset - soft reset
* @ vptr : velocity to reset
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Kick off a soft reset of the velocity adapter and then poll
* until the reset sequence has completed before returning .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static int velocity_soft_reset ( struct velocity_info * vptr )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct mac_regs __iomem * regs = vptr - > mac_regs ;
int i = 0 ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
writel ( CR0_SFRST , & regs - > CR0Set ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
for ( i = 0 ; i < W_MAX_TIMEOUT ; i + + ) {
udelay ( 5 ) ;
if ( ! DWORD_REG_BITS_IS_ON ( CR0_SFRST , & regs - > CR0Set ) )
break ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
if ( i = = W_MAX_TIMEOUT ) {
writel ( CR0_FORSRST , & regs - > CR0Set ) ;
/* FIXME: PCI POSTING */
/* delay 2ms */
mdelay ( 2 ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
/**
2009-07-24 05:11:12 +04:00
* velocity_set_multi - filter list change callback
* @ dev : network device
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Called by the network layer when the filter lists need to change
* for a velocity adapter . Reload the CAMs with the new address
* filter ruleset .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static void velocity_set_multi ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct velocity_info * vptr = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
struct mac_regs __iomem * regs = vptr - > mac_regs ;
2009-07-24 05:11:12 +04:00
u8 rx_mode ;
int i ;
struct dev_mc_list * mclist ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( dev - > flags & IFF_PROMISC ) { /* Set promiscuous. */
writel ( 0xffffffff , & regs - > MARCAM [ 0 ] ) ;
writel ( 0xffffffff , & regs - > MARCAM [ 4 ] ) ;
rx_mode = ( RCR_AM | RCR_AB | RCR_PROM ) ;
2010-02-08 07:30:35 +03:00
} else if ( ( netdev_mc_count ( dev ) > vptr - > multicast_limit ) | |
2009-12-03 10:58:21 +03:00
( dev - > flags & IFF_ALLMULTI ) ) {
2009-07-24 05:11:12 +04:00
writel ( 0xffffffff , & regs - > MARCAM [ 0 ] ) ;
writel ( 0xffffffff , & regs - > MARCAM [ 4 ] ) ;
rx_mode = ( RCR_AM | RCR_AB ) ;
} else {
int offset = MCAM_SIZE - vptr - > multicast_limit ;
mac_get_cam_mask ( regs , vptr - > mCAMmask ) ;
2005-04-17 02:20:36 +04:00
2010-02-08 07:30:35 +03:00
for ( i = 0 , mclist = dev - > mc_list ;
mclist & & i < netdev_mc_count ( dev ) ;
i + + , mclist = mclist - > next ) {
2009-07-24 05:11:12 +04:00
mac_set_cam ( regs , i + offset , mclist - > dmi_addr ) ;
vptr - > mCAMmask [ ( offset + i ) / 8 ] | = 1 < < ( ( offset + i ) & 7 ) ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
mac_set_cam_mask ( regs , vptr - > mCAMmask ) ;
rx_mode = RCR_AM | RCR_AB | RCR_AP ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
if ( dev - > mtu > 1500 )
rx_mode | = RCR_AL ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
BYTE_REG_BITS_ON ( rx_mode , & regs - > RCR ) ;
2005-04-17 02:20:36 +04:00
2008-07-11 02:05:57 +04:00
}
2009-07-24 05:11:12 +04:00
/*
* MII access , media link mode setting functions
2005-04-17 02:20:36 +04:00
*/
/**
2009-07-24 05:11:12 +04:00
* mii_init - set up MII
* @ vptr : velocity adapter
* @ mii_status : links tatus
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Set up the PHY for the current link state .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static void mii_init ( struct velocity_info * vptr , u32 mii_status )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
u16 BMCR ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
switch ( PHYID_GET_PHY_ID ( vptr - > phy_id ) ) {
case PHYID_CICADA_CS8201 :
/*
* Reset to hardware default
*/
MII_REG_BITS_OFF ( ( ANAR_ASMDIR | ANAR_PAUSE ) , MII_REG_ANAR , vptr - > mac_regs ) ;
/*
* Turn on ECHODIS bit in NWay - forced full mode and turn it
* off it in NWay - forced half mode for NWay - forced v . s .
* legacy - forced issue .
*/
if ( vptr - > mii_status & VELOCITY_DUPLEX_FULL )
MII_REG_BITS_ON ( TCSR_ECHODIS , MII_REG_TCSR , vptr - > mac_regs ) ;
else
MII_REG_BITS_OFF ( TCSR_ECHODIS , MII_REG_TCSR , vptr - > mac_regs ) ;
/*
* Turn on Link / Activity LED enable bit for CIS8201
*/
MII_REG_BITS_ON ( PLED_LALBE , MII_REG_PLED , vptr - > mac_regs ) ;
break ;
case PHYID_VT3216_32BIT :
case PHYID_VT3216_64BIT :
/*
* Reset to hardware default
*/
MII_REG_BITS_ON ( ( ANAR_ASMDIR | ANAR_PAUSE ) , MII_REG_ANAR , vptr - > mac_regs ) ;
/*
* Turn on ECHODIS bit in NWay - forced full mode and turn it
* off it in NWay - forced half mode for NWay - forced v . s .
* legacy - forced issue
*/
if ( vptr - > mii_status & VELOCITY_DUPLEX_FULL )
MII_REG_BITS_ON ( TCSR_ECHODIS , MII_REG_TCSR , vptr - > mac_regs ) ;
else
MII_REG_BITS_OFF ( TCSR_ECHODIS , MII_REG_TCSR , vptr - > mac_regs ) ;
break ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
case PHYID_MARVELL_1000 :
case PHYID_MARVELL_1000S :
/*
* Assert CRS on Transmit
*/
MII_REG_BITS_ON ( PSCR_ACRSTX , MII_REG_PSCR , vptr - > mac_regs ) ;
/*
* Reset to hardware default
*/
MII_REG_BITS_ON ( ( ANAR_ASMDIR | ANAR_PAUSE ) , MII_REG_ANAR , vptr - > mac_regs ) ;
break ;
default :
;
}
velocity_mii_read ( vptr - > mac_regs , MII_REG_BMCR , & BMCR ) ;
if ( BMCR & BMCR_ISO ) {
BMCR & = ~ BMCR_ISO ;
velocity_mii_write ( vptr - > mac_regs , MII_REG_BMCR , BMCR ) ;
2005-04-17 02:20:36 +04:00
}
}
2009-11-26 01:10:12 +03:00
/**
* setup_queue_timers - Setup interrupt timers
*
* Setup interrupt frequency during suppression ( timeout if the frame
* count isn ' t filled ) .
*/
static void setup_queue_timers ( struct velocity_info * vptr )
{
/* Only for newer revisions */
if ( vptr - > rev_id > = REV_ID_VT3216_A0 ) {
u8 txqueue_timer = 0 ;
u8 rxqueue_timer = 0 ;
if ( vptr - > mii_status & ( VELOCITY_SPEED_1000 |
VELOCITY_SPEED_100 ) ) {
txqueue_timer = vptr - > options . txqueue_timer ;
rxqueue_timer = vptr - > options . rxqueue_timer ;
}
writeb ( txqueue_timer , & vptr - > mac_regs - > TQETMR ) ;
writeb ( rxqueue_timer , & vptr - > mac_regs - > RQETMR ) ;
}
}
/**
* setup_adaptive_interrupts - Setup interrupt suppression
*
* @ vptr velocity adapter
*
* The velocity is able to suppress interrupt during high interrupt load .
* This function turns on that feature .
*/
static void setup_adaptive_interrupts ( struct velocity_info * vptr )
{
struct mac_regs __iomem * regs = vptr - > mac_regs ;
u16 tx_intsup = vptr - > options . tx_intsup ;
u16 rx_intsup = vptr - > options . rx_intsup ;
/* Setup default interrupt mask (will be changed below) */
vptr - > int_mask = INT_MASK_DEF ;
/* Set Tx Interrupt Suppression Threshold */
writeb ( CAMCR_PS0 , & regs - > CAMCR ) ;
if ( tx_intsup ! = 0 ) {
vptr - > int_mask & = ~ ( ISR_PTXI | ISR_PTX0I | ISR_PTX1I |
ISR_PTX2I | ISR_PTX3I ) ;
writew ( tx_intsup , & regs - > ISRCTL ) ;
} else
writew ( ISRCTL_TSUPDIS , & regs - > ISRCTL ) ;
/* Set Rx Interrupt Suppression Threshold */
writeb ( CAMCR_PS1 , & regs - > CAMCR ) ;
if ( rx_intsup ! = 0 ) {
vptr - > int_mask & = ~ ISR_PRXI ;
writew ( rx_intsup , & regs - > ISRCTL ) ;
} else
writew ( ISRCTL_RSUPDIS , & regs - > ISRCTL ) ;
/* Select page to interrupt hold timer */
writeb ( 0 , & regs - > CAMCR ) ;
}
2009-07-24 05:11:12 +04:00
2005-04-17 02:20:36 +04:00
/**
2009-07-24 05:11:12 +04:00
* velocity_init_registers - initialise MAC registers
* @ vptr : velocity to init
* @ type : type of initialisation ( hot or cold )
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Initialise the MAC on a reset or on first set up on the
* hardware .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static void velocity_init_registers ( struct velocity_info * vptr ,
enum velocity_init_type type )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct mac_regs __iomem * regs = vptr - > mac_regs ;
int i , mii_status ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
mac_wol_reset ( regs ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
switch ( type ) {
case VELOCITY_INIT_RESET :
case VELOCITY_INIT_WOL :
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
netif_stop_queue ( vptr - > dev ) ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
/*
* Reset RX to prevent RX pointer not on the 4 X location
*/
velocity_rx_reset ( vptr ) ;
mac_rx_queue_run ( regs ) ;
mac_rx_queue_wake ( regs ) ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
mii_status = velocity_get_opt_media_mode ( vptr ) ;
if ( velocity_set_media_mode ( vptr , mii_status ) ! = VELOCITY_LINK_CHANGE ) {
velocity_print_link_status ( vptr ) ;
if ( ! ( vptr - > mii_status & VELOCITY_LINK_FAIL ) )
netif_wake_queue ( vptr - > dev ) ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
enable_flow_control_ability ( vptr ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
mac_clear_isr ( regs ) ;
writel ( CR0_STOP , & regs - > CR0Clr ) ;
writel ( ( CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT ) ,
& regs - > CR0Set ) ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
break ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
case VELOCITY_INIT_COLD :
default :
/*
* Do reset
*/
velocity_soft_reset ( vptr ) ;
mdelay ( 5 ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
mac_eeprom_reload ( regs ) ;
for ( i = 0 ; i < 6 ; i + + )
writeb ( vptr - > dev - > dev_addr [ i ] , & ( regs - > PAR [ i ] ) ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
* clear Pre_ACPI bit .
*/
BYTE_REG_BITS_OFF ( CFGA_PACPI , & ( regs - > CFGA ) ) ;
mac_set_rx_thresh ( regs , vptr - > options . rx_thresh ) ;
mac_set_dma_length ( regs , vptr - > options . DMA_length ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
writeb ( WOLCFG_SAM | WOLCFG_SAB , & regs - > WOLCFGSet ) ;
/*
* Back off algorithm use original IEEE standard
*/
BYTE_REG_BITS_SET ( CFGB_OFSET , ( CFGB_CRANDOM | CFGB_CAP | CFGB_MBA | CFGB_BAKOPT ) , & regs - > CFGB ) ;
2005-04-17 02:20:36 +04:00
/*
2009-07-24 05:11:12 +04:00
* Init CAM filter
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
velocity_init_cam_filter ( vptr ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
* Set packet filter : Receive directed and broadcast address
*/
velocity_set_multi ( vptr - > dev ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
* Enable MII auto - polling
*/
enable_mii_autopoll ( regs ) ;
2005-04-17 02:20:36 +04:00
2009-11-26 01:10:12 +03:00
setup_adaptive_interrupts ( vptr ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
writel ( vptr - > rx . pool_dma , & regs - > RDBaseLo ) ;
writew ( vptr - > options . numrx - 1 , & regs - > RDCSize ) ;
mac_rx_queue_run ( regs ) ;
mac_rx_queue_wake ( regs ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
writew ( vptr - > options . numtx - 1 , & regs - > TDCSize ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
for ( i = 0 ; i < vptr - > tx . numq ; i + + ) {
writel ( vptr - > tx . pool_dma [ i ] , & regs - > TDBaseLo [ i ] ) ;
mac_tx_queue_run ( regs , i ) ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
init_flow_control_register ( vptr ) ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
writel ( CR0_STOP , & regs - > CR0Clr ) ;
writel ( ( CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT ) , & regs - > CR0Set ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
mii_status = velocity_get_opt_media_mode ( vptr ) ;
netif_stop_queue ( vptr - > dev ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
mii_init ( vptr , mii_status ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( velocity_set_media_mode ( vptr , mii_status ) ! = VELOCITY_LINK_CHANGE ) {
velocity_print_link_status ( vptr ) ;
if ( ! ( vptr - > mii_status & VELOCITY_LINK_FAIL ) )
netif_wake_queue ( vptr - > dev ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
enable_flow_control_ability ( vptr ) ;
mac_hw_mibs_init ( regs ) ;
mac_write_int_mask ( vptr - > int_mask , regs ) ;
mac_clear_isr ( regs ) ;
2005-04-17 02:20:36 +04:00
}
}
2009-07-24 05:11:12 +04:00
static void velocity_give_many_rx_descs ( struct velocity_info * vptr )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct mac_regs __iomem * regs = vptr - > mac_regs ;
int avail , dirty , unusable ;
/*
* RD number must be equal to 4 X per hardware spec
* ( programming guide rev 1.20 , p .13 )
*/
if ( vptr - > rx . filled < 4 )
return ;
wmb ( ) ;
unusable = vptr - > rx . filled & 0x0003 ;
dirty = vptr - > rx . dirty - unusable ;
for ( avail = vptr - > rx . filled & 0xfffc ; avail ; avail - - ) {
dirty = ( dirty > 0 ) ? dirty - 1 : vptr - > options . numrx - 1 ;
vptr - > rx . ring [ dirty ] . rdesc0 . len | = OWNED_BY_NIC ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
writew ( vptr - > rx . filled & 0xfffc , & regs - > RBRDU ) ;
vptr - > rx . filled = unusable ;
2005-04-17 02:20:36 +04:00
}
/**
2009-07-24 05:11:12 +04:00
* velocity_init_dma_rings - set up DMA rings
* @ vptr : Velocity to set up
2006-09-13 21:24:59 +04:00
*
2009-07-24 05:11:12 +04:00
* Allocate PCI mapped DMA rings for the receive and transmit layer
* to use .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static int velocity_init_dma_rings ( struct velocity_info * vptr )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct velocity_opt * opt = & vptr - > options ;
const unsigned int rx_ring_size = opt - > numrx * sizeof ( struct rx_desc ) ;
const unsigned int tx_ring_size = opt - > numtx * sizeof ( struct tx_desc ) ;
struct pci_dev * pdev = vptr - > pdev ;
dma_addr_t pool_dma ;
void * pool ;
unsigned int i ;
2005-04-17 02:20:36 +04:00
/*
2009-07-24 05:11:12 +04:00
* Allocate all RD / TD rings a single pool .
*
* pci_alloc_consistent ( ) fulfills the requirement for 64 bytes
* alignment
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
pool = pci_alloc_consistent ( pdev , tx_ring_size * vptr - > tx . numq +
rx_ring_size , & pool_dma ) ;
if ( ! pool ) {
dev_err ( & pdev - > dev , " %s : DMA memory allocation failed. \n " ,
vptr - > dev - > name ) ;
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
vptr - > rx . ring = pool ;
vptr - > rx . pool_dma = pool_dma ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
pool + = rx_ring_size ;
pool_dma + = rx_ring_size ;
2008-04-25 01:32:33 +04:00
2009-07-24 05:11:12 +04:00
for ( i = 0 ; i < vptr - > tx . numq ; i + + ) {
vptr - > tx . rings [ i ] = pool ;
vptr - > tx . pool_dma [ i ] = pool_dma ;
pool + = tx_ring_size ;
pool_dma + = tx_ring_size ;
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-07-24 05:11:12 +04:00
static void velocity_set_rxbufsize ( struct velocity_info * vptr , int mtu )
{
vptr - > rx . buf_sz = ( mtu < = ETH_DATA_LEN ) ? PKT_BUF_SZ : mtu + 32 ;
}
2005-04-17 02:20:36 +04:00
/**
* velocity_alloc_rx_buf - allocate aligned receive buffer
* @ vptr : velocity
* @ idx : ring index
*
* Allocate a new full sized buffer for the reception of a frame and
* map it into PCI space for the hardware to use . The hardware
* requires * 64 * byte alignment of the buffer which makes life
* less fun than would be ideal .
*/
static int velocity_alloc_rx_buf ( struct velocity_info * vptr , int idx )
{
2008-08-01 00:10:10 +04:00
struct rx_desc * rd = & ( vptr - > rx . ring [ idx ] ) ;
struct velocity_rd_info * rd_info = & ( vptr - > rx . info [ idx ] ) ;
2005-04-17 02:20:36 +04:00
2008-08-01 00:10:10 +04:00
rd_info - > skb = dev_alloc_skb ( vptr - > rx . buf_sz + 64 ) ;
2005-04-17 02:20:36 +04:00
if ( rd_info - > skb = = NULL )
return - ENOMEM ;
/*
* Do the gymnastics to get the buffer head for data at
* 64 byte alignment .
*/
2009-11-26 01:09:53 +03:00
skb_reserve ( rd_info - > skb ,
64 - ( ( unsigned long ) rd_info - > skb - > data & 63 ) ) ;
2008-08-01 00:10:10 +04:00
rd_info - > skb_dma = pci_map_single ( vptr - > pdev , rd_info - > skb - > data ,
vptr - > rx . buf_sz , PCI_DMA_FROMDEVICE ) ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
/*
* Fill in the descriptor to match
2008-08-01 00:10:10 +04:00
*/
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
* ( ( u32 * ) & ( rd - > rdesc0 ) ) = 0 ;
2008-08-01 00:10:10 +04:00
rd - > size = cpu_to_le16 ( vptr - > rx . buf_sz ) | RX_INTEN ;
2005-04-17 02:20:36 +04:00
rd - > pa_low = cpu_to_le32 ( rd_info - > skb_dma ) ;
rd - > pa_high = 0 ;
return 0 ;
}
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
static int velocity_rx_refill ( struct velocity_info * vptr )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
int dirty = vptr - > rx . dirty , done = 0 ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
do {
struct rx_desc * rd = vptr - > rx . ring + dirty ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/* Fine for an all zero Rx desc at init time as well */
if ( rd - > rdesc0 . len & OWNED_BY_NIC )
break ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( ! vptr - > rx . info [ dirty ] . skb ) {
if ( velocity_alloc_rx_buf ( vptr , dirty ) < 0 )
2005-04-17 02:20:36 +04:00
break ;
}
2009-07-24 05:11:12 +04:00
done + + ;
dirty = ( dirty < vptr - > options . numrx - 1 ) ? dirty + 1 : 0 ;
} while ( dirty ! = vptr - > rx . curr ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( done ) {
vptr - > rx . dirty = dirty ;
vptr - > rx . filled + = done ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
return done ;
2005-04-17 02:20:36 +04:00
}
/**
2009-07-24 05:11:12 +04:00
* velocity_free_rd_ring - free receive ring
* @ vptr : velocity to clean up
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Free the receive buffers for each ring slot and any
* attached socket buffers that need to go away .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static void velocity_free_rd_ring ( struct velocity_info * vptr )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
int i ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( vptr - > rx . info = = NULL )
return ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
for ( i = 0 ; i < vptr - > options . numrx ; i + + ) {
struct velocity_rd_info * rd_info = & ( vptr - > rx . info [ i ] ) ;
struct rx_desc * rd = vptr - > rx . ring + i ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
memset ( rd , 0 , sizeof ( * rd ) ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( ! rd_info - > skb )
continue ;
pci_unmap_single ( vptr - > pdev , rd_info - > skb_dma , vptr - > rx . buf_sz ,
PCI_DMA_FROMDEVICE ) ;
rd_info - > skb_dma = 0 ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
dev_kfree_skb ( rd_info - > skb ) ;
rd_info - > skb = NULL ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
kfree ( vptr - > rx . info ) ;
vptr - > rx . info = NULL ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/**
* velocity_init_rd_ring - set up receive ring
* @ vptr : velocity to configure
*
* Allocate and set up the receive buffers for each ring slot and
* assign them to the network adapter .
*/
static int velocity_init_rd_ring ( struct velocity_info * vptr )
{
int ret = - ENOMEM ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
vptr - > rx . info = kcalloc ( vptr - > options . numrx ,
sizeof ( struct velocity_rd_info ) , GFP_KERNEL ) ;
if ( ! vptr - > rx . info )
goto out ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
velocity_init_rx_ring_indexes ( vptr ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( velocity_rx_refill ( vptr ) ! = vptr - > options . numrx ) {
VELOCITY_PRT ( MSG_LEVEL_ERR , KERN_ERR
" %s: failed to allocate RX buffer. \n " , vptr - > dev - > name ) ;
velocity_free_rd_ring ( vptr ) ;
goto out ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
ret = 0 ;
out :
return ret ;
2005-04-17 02:20:36 +04:00
}
/**
2009-07-24 05:11:12 +04:00
* velocity_init_td_ring - set up transmit ring
* @ vptr : velocity
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Set up the transmit ring and chain the ring pointers together .
* Returns zero on success or a negative posix errno code for
* failure .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static int velocity_init_td_ring ( struct velocity_info * vptr )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
int j ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/* Init the TD ring entries */
for ( j = 0 ; j < vptr - > tx . numq ; j + + ) {
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
vptr - > tx . infos [ j ] = kcalloc ( vptr - > options . numtx ,
sizeof ( struct velocity_td_info ) ,
GFP_KERNEL ) ;
if ( ! vptr - > tx . infos [ j ] ) {
while ( - - j > = 0 )
kfree ( vptr - > tx . infos [ j ] ) ;
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
vptr - > tx . tail [ j ] = vptr - > tx . curr [ j ] = vptr - > tx . used [ j ] = 0 ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
return 0 ;
}
/**
* velocity_free_dma_rings - free PCI ring pointers
* @ vptr : Velocity to free from
*
* Clean up the PCI ring buffers allocated to this velocity .
*/
static void velocity_free_dma_rings ( struct velocity_info * vptr )
{
const int size = vptr - > options . numrx * sizeof ( struct rx_desc ) +
vptr - > options . numtx * sizeof ( struct tx_desc ) * vptr - > tx . numq ;
pci_free_consistent ( vptr - > pdev , size , vptr - > rx . ring , vptr - > rx . pool_dma ) ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
2008-08-01 00:51:18 +04:00
static int velocity_init_rings ( struct velocity_info * vptr , int mtu )
{
int ret ;
velocity_set_rxbufsize ( vptr , mtu ) ;
ret = velocity_init_dma_rings ( vptr ) ;
if ( ret < 0 )
goto out ;
ret = velocity_init_rd_ring ( vptr ) ;
if ( ret < 0 )
goto err_free_dma_rings_0 ;
ret = velocity_init_td_ring ( vptr ) ;
if ( ret < 0 )
goto err_free_rd_ring_1 ;
out :
return ret ;
err_free_rd_ring_1 :
velocity_free_rd_ring ( vptr ) ;
err_free_dma_rings_0 :
velocity_free_dma_rings ( vptr ) ;
goto out ;
}
2005-04-17 02:20:36 +04:00
/**
2009-07-24 05:11:12 +04:00
* velocity_free_tx_buf - free transmit buffer
* @ vptr : velocity
* @ tdinfo : buffer
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Release an transmit buffer . If the buffer was preallocated then
* recycle it , if not then unmap the buffer .
2005-04-17 02:20:36 +04:00
*/
2009-11-26 01:10:43 +03:00
static void velocity_free_tx_buf ( struct velocity_info * vptr ,
struct velocity_td_info * tdinfo , struct tx_desc * td )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct sk_buff * skb = tdinfo - > skb ;
2008-07-11 02:05:17 +04:00
2009-07-24 05:11:12 +04:00
/*
* Don ' t unmap the pre - allocated tx_bufs
*/
if ( tdinfo - > skb_dma ) {
2009-11-26 01:10:43 +03:00
int i ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
for ( i = 0 ; i < tdinfo - > nskb_dma ; i + + ) {
2009-11-26 01:10:43 +03:00
size_t pktlen = max_t ( size_t , skb - > len , ETH_ZLEN ) ;
/* For scatter-gather */
if ( skb_shinfo ( skb ) - > nr_frags > 0 )
pktlen = max_t ( size_t , pktlen ,
td - > td_buf [ i ] . size & ~ TD_QUEUE ) ;
pci_unmap_single ( vptr - > pdev , tdinfo - > skb_dma [ i ] ,
le16_to_cpu ( pktlen ) , PCI_DMA_TODEVICE ) ;
2009-07-24 05:11:12 +04:00
}
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
dev_kfree_skb_irq ( skb ) ;
tdinfo - > skb = NULL ;
2005-04-17 02:20:36 +04:00
}
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
/*
* FIXME : could we merge this with velocity_free_tx_buf ?
*/
static void velocity_free_td_ring_entry ( struct velocity_info * vptr ,
int q , int n )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct velocity_td_info * td_info = & ( vptr - > tx . infos [ q ] [ n ] ) ;
int i ;
2007-11-15 06:47:27 +03:00
2009-07-24 05:11:12 +04:00
if ( td_info = = NULL )
return ;
2008-08-01 00:51:18 +04:00
2009-07-24 05:11:12 +04:00
if ( td_info - > skb ) {
for ( i = 0 ; i < td_info - > nskb_dma ; i + + ) {
if ( td_info - > skb_dma [ i ] ) {
pci_unmap_single ( vptr - > pdev , td_info - > skb_dma [ i ] ,
td_info - > skb - > len , PCI_DMA_TODEVICE ) ;
td_info - > skb_dma [ i ] = 0 ;
}
2008-08-01 00:51:18 +04:00
}
2009-07-24 05:11:12 +04:00
dev_kfree_skb ( td_info - > skb ) ;
td_info - > skb = NULL ;
2008-08-01 00:51:18 +04:00
}
2005-04-17 02:20:36 +04:00
}
/**
2009-07-24 05:11:12 +04:00
* velocity_free_td_ring - free td ring
* @ vptr : velocity
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Free up the transmit ring for this particular velocity adapter .
* We free the ring contents but not the ring itself .
*/
static void velocity_free_td_ring ( struct velocity_info * vptr )
{
int i , j ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
for ( j = 0 ; j < vptr - > tx . numq ; j + + ) {
if ( vptr - > tx . infos [ j ] = = NULL )
continue ;
for ( i = 0 ; i < vptr - > options . numtx ; i + + )
velocity_free_td_ring_entry ( vptr , j , i ) ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
kfree ( vptr - > tx . infos [ j ] ) ;
vptr - > tx . infos [ j ] = NULL ;
}
}
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
static void velocity_free_rings ( struct velocity_info * vptr )
{
velocity_free_td_ring ( vptr ) ;
velocity_free_rd_ring ( vptr ) ;
velocity_free_dma_rings ( vptr ) ;
2005-04-17 02:20:36 +04:00
}
/**
2009-07-24 05:11:12 +04:00
* velocity_error - handle error from controller
* @ vptr : velocity
* @ status : card status
*
* Process an error report from the hardware and attempt to recover
* the card itself . At the moment we cannot recover from some
* theoretically impossible errors but this could be fixed using
* the pci_device_failed logic to bounce the hardware
2005-04-17 02:20:36 +04:00
*
*/
2009-07-24 05:11:12 +04:00
static void velocity_error ( struct velocity_info * vptr , int status )
2005-04-17 02:20:36 +04:00
{
2008-07-11 02:03:44 +04:00
2009-07-24 05:11:12 +04:00
if ( status & ISR_TXSTLI ) {
struct mac_regs __iomem * regs = vptr - > mac_regs ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
printk ( KERN_ERR " TD structure error TDindex=%hx \n " , readw ( & regs - > TDIdx [ 0 ] ) ) ;
BYTE_REG_BITS_ON ( TXESR_TDSTR , & regs - > TXESR ) ;
writew ( TRDCSR_RUN , & regs - > TDCSRClr ) ;
netif_stop_queue ( vptr - > dev ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/* FIXME: port over the pci_device_failed code and use it
here */
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( status & ISR_SRCI ) {
struct mac_regs __iomem * regs = vptr - > mac_regs ;
int linked ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( vptr - > options . spd_dpx = = SPD_DPX_AUTO ) {
vptr - > mii_status = check_connection_type ( regs ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
* If it is a 3119 , disable frame bursting in
* halfduplex mode and enable it in fullduplex
* mode
*/
if ( vptr - > rev_id < REV_ID_VT3216_A0 ) {
2009-08-14 06:09:56 +04:00
if ( vptr - > mii_status & VELOCITY_DUPLEX_FULL )
2009-07-24 05:11:12 +04:00
BYTE_REG_BITS_ON ( TCR_TB2BDIS , & regs - > TCR ) ;
else
BYTE_REG_BITS_OFF ( TCR_TB2BDIS , & regs - > TCR ) ;
}
/*
* Only enable CD heart beat counter in 10 HD mode
*/
if ( ! ( vptr - > mii_status & VELOCITY_DUPLEX_FULL ) & & ( vptr - > mii_status & VELOCITY_SPEED_10 ) )
BYTE_REG_BITS_OFF ( TESTCFG_HBDIS , & regs - > TESTCFG ) ;
else
BYTE_REG_BITS_ON ( TESTCFG_HBDIS , & regs - > TESTCFG ) ;
2009-11-26 01:10:12 +03:00
setup_queue_timers ( vptr ) ;
2009-07-24 05:11:12 +04:00
}
/*
* Get link status from PHYSR0
*/
linked = readb ( & regs - > PHYSR0 ) & PHYSR0_LINKGD ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( linked ) {
vptr - > mii_status & = ~ VELOCITY_LINK_FAIL ;
netif_carrier_on ( vptr - > dev ) ;
} else {
vptr - > mii_status | = VELOCITY_LINK_FAIL ;
netif_carrier_off ( vptr - > dev ) ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
velocity_print_link_status ( vptr ) ;
enable_flow_control_ability ( vptr ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
* Re - enable auto - polling because SRCI will disable
* auto - polling
*/
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
enable_mii_autopoll ( regs ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( vptr - > mii_status & VELOCITY_LINK_FAIL )
netif_stop_queue ( vptr - > dev ) ;
else
netif_wake_queue ( vptr - > dev ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
} ;
if ( status & ISR_MIBFI )
velocity_update_hw_mibs ( vptr ) ;
if ( status & ISR_LSTEI )
mac_rx_queue_wake ( vptr - > mac_regs ) ;
2005-04-17 02:20:36 +04:00
}
/**
2009-07-24 05:11:12 +04:00
* tx_srv - transmit interrupt service
* @ vptr ; Velocity
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Scan the queues looking for transmitted packets that
* we can complete and clean up . Update any statistics as
* necessary /
2005-04-17 02:20:36 +04:00
*/
2010-02-10 02:37:54 +03:00
static int velocity_tx_srv ( struct velocity_info * vptr )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct tx_desc * td ;
int qnum ;
int full = 0 ;
int idx ;
int works = 0 ;
struct velocity_td_info * tdinfo ;
struct net_device_stats * stats = & vptr - > dev - > stats ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
for ( qnum = 0 ; qnum < vptr - > tx . numq ; qnum + + ) {
for ( idx = vptr - > tx . tail [ qnum ] ; vptr - > tx . used [ qnum ] > 0 ;
idx = ( idx + 1 ) % vptr - > options . numtx ) {
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
* Get Tx Descriptor
*/
td = & ( vptr - > tx . rings [ qnum ] [ idx ] ) ;
tdinfo = & ( vptr - > tx . infos [ qnum ] [ idx ] ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( td - > tdesc0 . len & OWNED_BY_NIC )
break ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( ( works + + > 15 ) )
break ;
if ( td - > tdesc0 . TSR & TSR0_TERR ) {
stats - > tx_errors + + ;
stats - > tx_dropped + + ;
if ( td - > tdesc0 . TSR & TSR0_CDH )
stats - > tx_heartbeat_errors + + ;
if ( td - > tdesc0 . TSR & TSR0_CRS )
stats - > tx_carrier_errors + + ;
if ( td - > tdesc0 . TSR & TSR0_ABT )
stats - > tx_aborted_errors + + ;
if ( td - > tdesc0 . TSR & TSR0_OWC )
stats - > tx_window_errors + + ;
} else {
stats - > tx_packets + + ;
stats - > tx_bytes + = tdinfo - > skb - > len ;
}
2009-11-26 01:10:43 +03:00
velocity_free_tx_buf ( vptr , tdinfo , td ) ;
2009-07-24 05:11:12 +04:00
vptr - > tx . used [ qnum ] - - ;
}
vptr - > tx . tail [ qnum ] = idx ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( AVAIL_TD ( vptr , qnum ) < 1 )
full = 1 ;
}
2005-04-17 02:20:36 +04:00
/*
2009-07-24 05:11:12 +04:00
* Look to see if we should kick the transmit network
* layer for more work .
2005-04-17 02:20:36 +04:00
*/
2009-12-03 10:58:21 +03:00
if ( netif_queue_stopped ( vptr - > dev ) & & ( full = = 0 ) & &
( ! ( vptr - > mii_status & VELOCITY_LINK_FAIL ) ) ) {
2009-07-24 05:11:12 +04:00
netif_wake_queue ( vptr - > dev ) ;
}
return works ;
}
/**
* velocity_rx_csum - checksum process
* @ rd : receive packet descriptor
* @ skb : network layer packet buffer
*
* Process the status bits for the received packet and determine
* if the checksum was computed and verified by the hardware
*/
static inline void velocity_rx_csum ( struct rx_desc * rd , struct sk_buff * skb )
{
skb - > ip_summed = CHECKSUM_NONE ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
if ( rd - > rdesc1 . CSM & CSM_IPKT ) {
if ( rd - > rdesc1 . CSM & CSM_IPOK ) {
if ( ( rd - > rdesc1 . CSM & CSM_TCPKT ) | |
( rd - > rdesc1 . CSM & CSM_UDPKT ) ) {
if ( ! ( rd - > rdesc1 . CSM & CSM_TUPOK ) )
return ;
}
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
2005-04-17 02:20:36 +04:00
}
}
}
/**
2009-07-24 05:11:12 +04:00
* velocity_rx_copy - in place Rx copy for small packets
* @ rx_skb : network layer packet buffer candidate
* @ pkt_size : received data size
* @ rd : receive packet descriptor
2005-04-17 02:20:36 +04:00
* @ dev : network device
*
2009-07-24 05:11:12 +04:00
* Replace the current skb that is scheduled for Rx processing by a
* shorter , immediatly allocated skb , if the received packet is small
* enough . This function returns a negative value if the received
* packet is too big or if memory is exhausted .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static int velocity_rx_copy ( struct sk_buff * * rx_skb , int pkt_size ,
struct velocity_info * vptr )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
int ret = - 1 ;
if ( pkt_size < rx_copybreak ) {
struct sk_buff * new_skb ;
2005-04-17 02:20:36 +04:00
2009-10-13 09:34:20 +04:00
new_skb = netdev_alloc_skb_ip_align ( vptr - > dev , pkt_size ) ;
2009-07-24 05:11:12 +04:00
if ( new_skb ) {
new_skb - > ip_summed = rx_skb [ 0 ] - > ip_summed ;
skb_copy_from_linear_data ( * rx_skb , new_skb - > data , pkt_size ) ;
* rx_skb = new_skb ;
ret = 0 ;
2005-04-17 02:20:36 +04:00
}
}
2009-07-24 05:11:12 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
/**
2009-07-24 05:11:12 +04:00
* velocity_iph_realign - IP header alignment
* @ vptr : velocity we are handling
* @ skb : network layer packet buffer
* @ pkt_size : received data size
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Align IP header on a 2 bytes boundary . This behavior can be
* configured by the user .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static inline void velocity_iph_realign ( struct velocity_info * vptr ,
struct sk_buff * skb , int pkt_size )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
if ( vptr - > flags & VELOCITY_FLAGS_IP_ALIGN ) {
memmove ( skb - > data + 2 , skb - > data , pkt_size ) ;
skb_reserve ( skb , 2 ) ;
}
}
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/**
* velocity_receive_frame - received packet processor
* @ vptr : velocity we are handling
* @ idx : ring index
*
* A packet has arrived . We process the packet and if appropriate
* pass the frame up the network stack
*/
static int velocity_receive_frame ( struct velocity_info * vptr , int idx )
{
void ( * pci_action ) ( struct pci_dev * , dma_addr_t , size_t , int ) ;
struct net_device_stats * stats = & vptr - > dev - > stats ;
struct velocity_rd_info * rd_info = & ( vptr - > rx . info [ idx ] ) ;
struct rx_desc * rd = & ( vptr - > rx . ring [ idx ] ) ;
int pkt_len = le16_to_cpu ( rd - > rdesc0 . len ) & 0x3fff ;
struct sk_buff * skb ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( rd - > rdesc0 . RSR & ( RSR_STP | RSR_EDP ) ) {
VELOCITY_PRT ( MSG_LEVEL_VERBOSE , KERN_ERR " %s : the received frame span multple RDs. \n " , vptr - > dev - > name ) ;
stats - > rx_length_errors + + ;
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( rd - > rdesc0 . RSR & RSR_MAR )
stats - > multicast + + ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
skb = rd_info - > skb ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
pci_dma_sync_single_for_cpu ( vptr - > pdev , rd_info - > skb_dma ,
vptr - > rx . buf_sz , PCI_DMA_FROMDEVICE ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
* Drop frame not meeting IEEE 802.3
*/
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( vptr - > flags & VELOCITY_FLAGS_VAL_PKT_LEN ) {
if ( rd - > rdesc0 . RSR & RSR_RL ) {
stats - > rx_length_errors + + ;
return - EINVAL ;
}
}
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
pci_action = pci_dma_sync_single_for_device ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
velocity_rx_csum ( rd , skb ) ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
if ( velocity_rx_copy ( & skb , pkt_len , vptr ) < 0 ) {
velocity_iph_realign ( vptr , skb , pkt_len ) ;
pci_action = pci_unmap_single ;
rd_info - > skb = NULL ;
}
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
pci_action ( vptr - > pdev , rd_info - > skb_dma , vptr - > rx . buf_sz ,
PCI_DMA_FROMDEVICE ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
skb_put ( skb , pkt_len - 4 ) ;
skb - > protocol = eth_type_trans ( skb , vptr - > dev ) ;
if ( vptr - > vlgrp & & ( rd - > rdesc0 . RSR & RSR_DETAG ) ) {
vlan_hwaccel_rx ( skb , vptr - > vlgrp ,
swab16 ( le16_to_cpu ( rd - > rdesc1 . PQTAG ) ) ) ;
} else
netif_rx ( skb ) ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
stats - > rx_bytes + = pkt_len ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/**
2009-07-24 05:11:12 +04:00
* velocity_rx_srv - service RX interrupt
* @ vptr : velocity
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Walk the receive ring of the velocity adapter and remove
* any received packets from the receive queue . Hand the ring
* slots back to the adapter for reuse .
2005-04-17 02:20:36 +04:00
*/
2010-02-10 02:37:54 +03:00
static int velocity_rx_srv ( struct velocity_info * vptr , int budget_left )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct net_device_stats * stats = & vptr - > dev - > stats ;
int rd_curr = vptr - > rx . curr ;
int works = 0 ;
2009-11-26 01:10:26 +03:00
while ( works < budget_left ) {
2009-07-24 05:11:12 +04:00
struct rx_desc * rd = vptr - > rx . ring + rd_curr ;
if ( ! vptr - > rx . info [ rd_curr ] . skb )
break ;
if ( rd - > rdesc0 . len & OWNED_BY_NIC )
break ;
rmb ( ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
* Don ' t drop CE or RL error frame although RXOK is off
*/
if ( rd - > rdesc0 . RSR & ( RSR_RXOK | RSR_CE | RSR_RL ) ) {
if ( velocity_receive_frame ( vptr , rd_curr ) < 0 )
stats - > rx_dropped + + ;
} else {
if ( rd - > rdesc0 . RSR & RSR_CRC )
stats - > rx_crc_errors + + ;
if ( rd - > rdesc0 . RSR & RSR_FAE )
stats - > rx_frame_errors + + ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
stats - > rx_dropped + + ;
}
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
rd - > size | = RX_INTEN ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
rd_curr + + ;
if ( rd_curr > = vptr - > options . numrx )
rd_curr = 0 ;
2009-11-26 01:10:26 +03:00
works + + ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
vptr - > rx . curr = rd_curr ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( ( works > 0 ) & & ( velocity_rx_refill ( vptr ) > 0 ) )
velocity_give_many_rx_descs ( vptr ) ;
VAR_USED ( stats ) ;
return works ;
}
2006-09-13 21:24:59 +04:00
2009-11-26 01:10:26 +03:00
static int velocity_poll ( struct napi_struct * napi , int budget )
{
struct velocity_info * vptr = container_of ( napi ,
struct velocity_info , napi ) ;
unsigned int rx_done ;
2010-02-10 02:38:25 +03:00
unsigned long flags ;
2009-11-26 01:10:26 +03:00
2010-02-10 02:38:25 +03:00
spin_lock_irqsave ( & vptr - > lock , flags ) ;
2009-11-26 01:10:26 +03:00
/*
* Do rx and tx twice for performance ( taken from the VIA
* out - of - tree driver ) .
*/
2010-02-10 02:37:54 +03:00
rx_done = velocity_rx_srv ( vptr , budget / 2 ) ;
velocity_tx_srv ( vptr ) ;
rx_done + = velocity_rx_srv ( vptr , budget - rx_done ) ;
velocity_tx_srv ( vptr ) ;
2009-11-26 01:10:26 +03:00
/* If budget not fully consumed, exit the polling mode */
if ( rx_done < budget ) {
napi_complete ( napi ) ;
mac_enable_int ( vptr - > mac_regs ) ;
}
2010-02-10 02:38:25 +03:00
spin_unlock_irqrestore ( & vptr - > lock , flags ) ;
2009-11-26 01:10:26 +03:00
return rx_done ;
}
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
/**
2009-07-24 05:11:12 +04:00
* velocity_intr - interrupt callback
* @ irq : interrupt number
* @ dev_instance : interrupting device
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Called whenever an interrupt is generated by the velocity
* adapter IRQ line . We may not be the source of the interrupt
* and need to identify initially if we are , and if not exit as
* efficiently as possible .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static irqreturn_t velocity_intr ( int irq , void * dev_instance )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct net_device * dev = dev_instance ;
struct velocity_info * vptr = netdev_priv ( dev ) ;
u32 isr_status ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
spin_lock ( & vptr - > lock ) ;
isr_status = mac_read_isr ( vptr - > mac_regs ) ;
/* Not us ? */
if ( isr_status = = 0 ) {
spin_unlock ( & vptr - > lock ) ;
return IRQ_NONE ;
2005-04-17 02:20:36 +04:00
}
2010-02-10 02:38:25 +03:00
/* Ack the interrupt */
mac_write_isr ( vptr - > mac_regs , isr_status ) ;
2009-11-26 01:10:26 +03:00
if ( likely ( napi_schedule_prep ( & vptr - > napi ) ) ) {
mac_disable_int ( vptr - > mac_regs ) ;
__napi_schedule ( & vptr - > napi ) ;
2005-04-17 02:20:36 +04:00
}
2010-02-10 02:38:25 +03:00
if ( isr_status & ( ~ ( ISR_PRXI | ISR_PPRXI | ISR_PTXI | ISR_PPTXI ) ) )
velocity_error ( vptr , isr_status ) ;
2009-07-24 05:11:12 +04:00
spin_unlock ( & vptr - > lock ) ;
2009-11-26 01:10:26 +03:00
return IRQ_HANDLED ;
2005-04-17 02:20:36 +04:00
}
/**
2009-07-24 05:11:12 +04:00
* velocity_open - interface activation callback
* @ dev : network layer device to open
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Called when the network layer brings the interface up . Returns
* a negative posix error code on failure , or zero on success .
*
* All the ring allocation and set up is done on open for this
* adapter to minimise memory usage when inactive
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static int velocity_open ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct velocity_info * vptr = netdev_priv ( dev ) ;
int ret ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
ret = velocity_init_rings ( vptr , dev - > mtu ) ;
if ( ret < 0 )
goto out ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/* Ensure chip is running */
pci_set_power_state ( vptr - > pdev , PCI_D0 ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
velocity_init_registers ( vptr , VELOCITY_INIT_COLD ) ;
2009-11-18 11:24:13 +03:00
ret = request_irq ( vptr - > pdev - > irq , velocity_intr , IRQF_SHARED ,
2009-07-24 05:11:12 +04:00
dev - > name , dev ) ;
if ( ret < 0 ) {
/* Power down the chip */
pci_set_power_state ( vptr - > pdev , PCI_D3hot ) ;
velocity_free_rings ( vptr ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2009-12-14 19:05:09 +03:00
velocity_give_many_rx_descs ( vptr ) ;
2009-07-24 05:11:12 +04:00
mac_enable_int ( vptr - > mac_regs ) ;
netif_start_queue ( dev ) ;
2009-11-26 01:10:26 +03:00
napi_enable ( & vptr - > napi ) ;
2009-07-24 05:11:12 +04:00
vptr - > flags | = VELOCITY_FLAGS_OPENED ;
out :
return ret ;
2005-04-17 02:20:36 +04:00
}
/**
2009-07-24 05:11:12 +04:00
* velocity_shutdown - shut down the chip
* @ vptr : velocity to deactivate
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Shuts down the internal operations of the velocity and
* disables interrupts , autopolling , transmit and receive
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static void velocity_shutdown ( struct velocity_info * vptr )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct mac_regs __iomem * regs = vptr - > mac_regs ;
mac_disable_int ( regs ) ;
writel ( CR0_STOP , & regs - > CR0Set ) ;
writew ( 0xFFFF , & regs - > TDCSRClr ) ;
writeb ( 0xFF , & regs - > RDCSRClr ) ;
2005-04-17 02:20:36 +04:00
safe_disable_mii_autopoll ( regs ) ;
2009-07-24 05:11:12 +04:00
mac_clear_isr ( regs ) ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/**
* velocity_change_mtu - MTU change callback
* @ dev : network device
* @ new_mtu : desired MTU
*
* Handle requests from the networking layer for MTU change on
* this interface . It gets called on a change by the network layer .
* Return zero for success or negative posix error code .
*/
static int velocity_change_mtu ( struct net_device * dev , int new_mtu )
{
struct velocity_info * vptr = netdev_priv ( dev ) ;
int ret = 0 ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( ( new_mtu < VELOCITY_MIN_MTU ) | | new_mtu > ( VELOCITY_MAX_MTU ) ) {
VELOCITY_PRT ( MSG_LEVEL_ERR , KERN_NOTICE " %s: Invalid MTU. \n " ,
vptr - > dev - > name ) ;
ret = - EINVAL ;
goto out_0 ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( ! netif_running ( dev ) ) {
dev - > mtu = new_mtu ;
goto out_0 ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
if ( dev - > mtu ! = new_mtu ) {
struct velocity_info * tmp_vptr ;
unsigned long flags ;
struct rx_info rx ;
struct tx_info tx ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
tmp_vptr = kzalloc ( sizeof ( * tmp_vptr ) , GFP_KERNEL ) ;
if ( ! tmp_vptr ) {
ret = - ENOMEM ;
goto out_0 ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
tmp_vptr - > dev = dev ;
tmp_vptr - > pdev = vptr - > pdev ;
tmp_vptr - > options = vptr - > options ;
tmp_vptr - > tx . numq = vptr - > tx . numq ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
ret = velocity_init_rings ( tmp_vptr , new_mtu ) ;
if ( ret < 0 )
goto out_free_tmp_vptr_1 ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
spin_lock_irqsave ( & vptr - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
netif_stop_queue ( dev ) ;
velocity_shutdown ( vptr ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
rx = vptr - > rx ;
tx = vptr - > tx ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
vptr - > rx = tmp_vptr - > rx ;
vptr - > tx = tmp_vptr - > tx ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
tmp_vptr - > rx = rx ;
tmp_vptr - > tx = tx ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
dev - > mtu = new_mtu ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
velocity_init_registers ( vptr , VELOCITY_INIT_COLD ) ;
2005-04-17 02:20:36 +04:00
2009-12-14 19:05:09 +03:00
velocity_give_many_rx_descs ( vptr ) ;
2009-07-24 05:11:12 +04:00
mac_enable_int ( vptr - > mac_regs ) ;
netif_start_queue ( dev ) ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
spin_unlock_irqrestore ( & vptr - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
velocity_free_rings ( tmp_vptr ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
out_free_tmp_vptr_1 :
kfree ( tmp_vptr ) ;
}
out_0 :
return ret ;
2005-04-17 02:20:36 +04:00
}
/**
2009-07-24 05:11:12 +04:00
* velocity_mii_ioctl - MII ioctl handler
* @ dev : network device
* @ ifr : the ifreq block for the ioctl
* @ cmd : the command
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Process MII requests made via ioctl from the network layer . These
* are used by tools like kudzu to interrogate the link state of the
* hardware
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static int velocity_mii_ioctl ( struct net_device * dev , struct ifreq * ifr , int cmd )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct velocity_info * vptr = netdev_priv ( dev ) ;
struct mac_regs __iomem * regs = vptr - > mac_regs ;
unsigned long flags ;
struct mii_ioctl_data * miidata = if_mii ( ifr ) ;
int err ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
switch ( cmd ) {
case SIOCGMIIPHY :
miidata - > phy_id = readb ( & regs - > MIIADR ) & 0x1f ;
2005-04-17 02:20:36 +04:00
break ;
2009-07-24 05:11:12 +04:00
case SIOCGMIIREG :
if ( velocity_mii_read ( vptr - > mac_regs , miidata - > reg_num & 0x1f , & ( miidata - > val_out ) ) < 0 )
return - ETIMEDOUT ;
2005-04-17 02:20:36 +04:00
break ;
2009-07-24 05:11:12 +04:00
case SIOCSMIIREG :
spin_lock_irqsave ( & vptr - > lock , flags ) ;
err = velocity_mii_write ( vptr - > mac_regs , miidata - > reg_num & 0x1f , miidata - > val_in ) ;
spin_unlock_irqrestore ( & vptr - > lock , flags ) ;
check_connection_type ( vptr - > mac_regs ) ;
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
break ;
default :
2009-07-24 05:11:12 +04:00
return - EOPNOTSUPP ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
2005-04-17 02:20:36 +04:00
/**
2009-07-24 05:11:12 +04:00
* velocity_ioctl - ioctl entry point
* @ dev : network device
* @ rq : interface request ioctl
* @ cmd : command code
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Called when the user issues an ioctl request to the network
* device in question . The velocity interface supports MII .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static int velocity_ioctl ( struct net_device * dev , struct ifreq * rq , int cmd )
{
struct velocity_info * vptr = netdev_priv ( dev ) ;
int ret ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
/* If we are asked for information and the device is power
saving then we need to bring the device back up to talk to it */
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( ! netif_running ( dev ) )
pci_set_power_state ( vptr - > pdev , PCI_D0 ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
switch ( cmd ) {
case SIOCGMIIPHY : /* Get address of MII PHY in use. */
case SIOCGMIIREG : /* Read MII PHY register. */
case SIOCSMIIREG : /* Write to MII PHY register. */
ret = velocity_mii_ioctl ( dev , rq , cmd ) ;
break ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
default :
ret = - EOPNOTSUPP ;
}
if ( ! netif_running ( dev ) )
pci_set_power_state ( vptr - > pdev , PCI_D3hot ) ;
2005-04-17 02:20:36 +04:00
2009-07-20 21:35:21 +04:00
2009-07-24 05:11:12 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
/**
2009-07-24 05:11:12 +04:00
* velocity_get_status - statistics callback
* @ dev : network device
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Callback from the network layer to allow driver statistics
* to be resynchronized with hardware collected state . In the
* case of the velocity we need to pull the MIB counters from
* the hardware into the counters before letting the network
* layer display them .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static struct net_device_stats * velocity_get_stats ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct velocity_info * vptr = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/* If the hardware is down, don't touch MII */
if ( ! netif_running ( dev ) )
return & dev - > stats ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
spin_lock_irq ( & vptr - > lock ) ;
velocity_update_hw_mibs ( vptr ) ;
spin_unlock_irq ( & vptr - > lock ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
dev - > stats . rx_packets = vptr - > mib_counter [ HW_MIB_ifRxAllPkts ] ;
dev - > stats . rx_errors = vptr - > mib_counter [ HW_MIB_ifRxErrorPkts ] ;
dev - > stats . rx_length_errors = vptr - > mib_counter [ HW_MIB_ifInRangeLengthErrors ] ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
// unsigned long rx_dropped; /* no space in linux buffers */
dev - > stats . collisions = vptr - > mib_counter [ HW_MIB_ifTxEtherCollisions ] ;
/* detailed rx_errors: */
// unsigned long rx_length_errors;
// unsigned long rx_over_errors; /* receiver ring buff overflow */
dev - > stats . rx_crc_errors = vptr - > mib_counter [ HW_MIB_ifRxPktCRCE ] ;
// unsigned long rx_frame_errors; /* recv'd frame alignment error */
// unsigned long rx_fifo_errors; /* recv'r fifo overrun */
// unsigned long rx_missed_errors; /* receiver missed packet */
/* detailed tx_errors */
// unsigned long tx_fifo_errors;
return & dev - > stats ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
/**
* velocity_close - close adapter callback
* @ dev : network device
*
* Callback from the network layer when the velocity is being
* deactivated by the network layer
*/
static int velocity_close ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct velocity_info * vptr = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
2009-11-26 01:10:26 +03:00
napi_disable ( & vptr - > napi ) ;
2009-07-24 05:11:12 +04:00
netif_stop_queue ( dev ) ;
velocity_shutdown ( vptr ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( vptr - > flags & VELOCITY_FLAGS_WOL_ENABLED )
velocity_get_ip ( vptr ) ;
if ( dev - > irq ! = 0 )
free_irq ( dev - > irq , dev ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/* Power down the chip */
pci_set_power_state ( vptr - > pdev , PCI_D3hot ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
velocity_free_rings ( vptr ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
vptr - > flags & = ( ~ VELOCITY_FLAGS_OPENED ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
/**
2009-07-24 05:11:12 +04:00
* velocity_xmit - transmit packet callback
* @ skb : buffer to transmit
* @ dev : network device
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Called by the networ layer to request a packet is queued to
* the velocity . Returns zero on success .
2005-04-17 02:20:36 +04:00
*/
2009-08-31 23:50:58 +04:00
static netdev_tx_t velocity_xmit ( struct sk_buff * skb ,
struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct velocity_info * vptr = netdev_priv ( dev ) ;
int qnum = 0 ;
struct tx_desc * td_ptr ;
struct velocity_td_info * tdinfo ;
unsigned long flags ;
int pktlen ;
2009-11-26 01:10:43 +03:00
int index , prev ;
int i = 0 ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( skb_padto ( skb , ETH_ZLEN ) )
goto out ;
2005-04-17 02:20:36 +04:00
2009-11-26 01:10:43 +03:00
/* The hardware can handle at most 7 memory segments, so merge
* the skb if there are more */
if ( skb_shinfo ( skb ) - > nr_frags > 6 & & __skb_linearize ( skb ) ) {
kfree_skb ( skb ) ;
return NETDEV_TX_OK ;
}
pktlen = skb_shinfo ( skb ) - > nr_frags = = 0 ?
max_t ( unsigned int , skb - > len , ETH_ZLEN ) :
skb_headlen ( skb ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
spin_lock_irqsave ( & vptr - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
index = vptr - > tx . curr [ qnum ] ;
td_ptr = & ( vptr - > tx . rings [ qnum ] [ index ] ) ;
tdinfo = & ( vptr - > tx . infos [ qnum ] [ index ] ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
td_ptr - > tdesc1 . TCR = TCR0_TIC ;
td_ptr - > td_buf [ 0 ] . size & = ~ TD_QUEUE ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
* Map the linear network buffer into PCI space and
* add it to the transmit ring .
*/
tdinfo - > skb = skb ;
tdinfo - > skb_dma [ 0 ] = pci_map_single ( vptr - > pdev , skb - > data , pktlen , PCI_DMA_TODEVICE ) ;
2009-11-26 01:10:43 +03:00
td_ptr - > tdesc0 . len = cpu_to_le16 ( pktlen ) ;
2009-07-24 05:11:12 +04:00
td_ptr - > td_buf [ 0 ] . pa_low = cpu_to_le32 ( tdinfo - > skb_dma [ 0 ] ) ;
td_ptr - > td_buf [ 0 ] . pa_high = 0 ;
2009-11-26 01:10:43 +03:00
td_ptr - > td_buf [ 0 ] . size = cpu_to_le16 ( pktlen ) ;
/* Handle fragments */
for ( i = 0 ; i < skb_shinfo ( skb ) - > nr_frags ; i + + ) {
skb_frag_t * frag = & skb_shinfo ( skb ) - > frags [ i ] ;
tdinfo - > skb_dma [ i + 1 ] = pci_map_page ( vptr - > pdev , frag - > page ,
frag - > page_offset , frag - > size ,
PCI_DMA_TODEVICE ) ;
td_ptr - > td_buf [ i + 1 ] . pa_low = cpu_to_le32 ( tdinfo - > skb_dma [ i + 1 ] ) ;
td_ptr - > td_buf [ i + 1 ] . pa_high = 0 ;
td_ptr - > td_buf [ i + 1 ] . size = cpu_to_le16 ( frag - > size ) ;
}
tdinfo - > nskb_dma = i + 1 ;
2009-07-24 05:11:12 +04:00
td_ptr - > tdesc1 . cmd = TCPLS_NORMAL + ( tdinfo - > nskb_dma + 1 ) * 16 ;
if ( vptr - > vlgrp & & vlan_tx_tag_present ( skb ) ) {
td_ptr - > tdesc1 . vlan = cpu_to_le16 ( vlan_tx_tag_get ( skb ) ) ;
td_ptr - > tdesc1 . TCR | = TCR0_VETAG ;
}
/*
* Handle hardware checksum
*/
2009-12-03 10:58:21 +03:00
if ( ( dev - > features & NETIF_F_IP_CSUM ) & &
( skb - > ip_summed = = CHECKSUM_PARTIAL ) ) {
2009-07-24 05:11:12 +04:00
const struct iphdr * ip = ip_hdr ( skb ) ;
if ( ip - > protocol = = IPPROTO_TCP )
td_ptr - > tdesc1 . TCR | = TCR0_TCPCK ;
else if ( ip - > protocol = = IPPROTO_UDP )
td_ptr - > tdesc1 . TCR | = ( TCR0_UDPCK ) ;
td_ptr - > tdesc1 . TCR | = TCR0_IPCK ;
}
2005-04-17 02:20:36 +04:00
2009-11-26 01:10:43 +03:00
prev = index - 1 ;
if ( prev < 0 )
prev = vptr - > options . numtx - 1 ;
td_ptr - > tdesc0 . len | = OWNED_BY_NIC ;
vptr - > tx . used [ qnum ] + + ;
vptr - > tx . curr [ qnum ] = ( index + 1 ) % vptr - > options . numtx ;
2005-04-17 02:20:36 +04:00
2009-11-26 01:10:43 +03:00
if ( AVAIL_TD ( vptr , qnum ) < 1 )
netif_stop_queue ( dev ) ;
2005-04-17 02:20:36 +04:00
2009-11-26 01:10:43 +03:00
td_ptr = & ( vptr - > tx . rings [ qnum ] [ prev ] ) ;
td_ptr - > td_buf [ 0 ] . size | = TD_QUEUE ;
mac_tx_queue_wake ( vptr - > mac_regs , qnum ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
dev - > trans_start = jiffies ;
spin_unlock_irqrestore ( & vptr - > lock , flags ) ;
out :
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
static const struct net_device_ops velocity_netdev_ops = {
. ndo_open = velocity_open ,
. ndo_stop = velocity_close ,
. ndo_start_xmit = velocity_xmit ,
. ndo_get_stats = velocity_get_stats ,
. ndo_validate_addr = eth_validate_addr ,
. ndo_set_mac_address = eth_mac_addr ,
. ndo_set_multicast_list = velocity_set_multi ,
. ndo_change_mtu = velocity_change_mtu ,
. ndo_do_ioctl = velocity_ioctl ,
. ndo_vlan_rx_add_vid = velocity_vlan_rx_add_vid ,
. ndo_vlan_rx_kill_vid = velocity_vlan_rx_kill_vid ,
. ndo_vlan_rx_register = velocity_vlan_rx_register ,
} ;
2005-04-17 02:20:36 +04:00
/**
2009-07-24 05:11:12 +04:00
* velocity_init_info - init private data
* @ pdev : PCI device
* @ vptr : Velocity info
* @ info : Board type
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Set up the initial velocity_info struct for the device that has been
* discovered .
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static void __devinit velocity_init_info ( struct pci_dev * pdev ,
struct velocity_info * vptr ,
const struct velocity_info_tbl * info )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
memset ( vptr , 0 , sizeof ( struct velocity_info ) ) ;
vptr - > pdev = pdev ;
vptr - > chip_id = info - > chip_id ;
vptr - > tx . numq = info - > txqueue ;
vptr - > multicast_limit = MCAM_SIZE ;
spin_lock_init ( & vptr - > lock ) ;
2006-09-13 21:24:59 +04:00
}
2005-04-17 02:20:36 +04:00
/**
2009-07-24 05:11:12 +04:00
* velocity_get_pci_info - retrieve PCI info for device
* @ vptr : velocity device
* @ pdev : PCI device it matches
2005-04-17 02:20:36 +04:00
*
2009-07-24 05:11:12 +04:00
* Retrieve the PCI configuration space data that interests us from
* the kernel PCI layer
2005-04-17 02:20:36 +04:00
*/
2009-07-24 05:11:12 +04:00
static int __devinit velocity_get_pci_info ( struct velocity_info * vptr , struct pci_dev * pdev )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
vptr - > rev_id = pdev - > revision ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
pci_set_master ( pdev ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
vptr - > ioaddr = pci_resource_start ( pdev , 0 ) ;
vptr - > memaddr = pci_resource_start ( pdev , 1 ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
if ( ! ( pci_resource_flags ( pdev , 0 ) & IORESOURCE_IO ) ) {
dev_err ( & pdev - > dev ,
" region #0 is not an I/O resource, aborting. \n " ) ;
return - EINVAL ;
}
if ( ( pci_resource_flags ( pdev , 1 ) & IORESOURCE_IO ) ) {
dev_err ( & pdev - > dev ,
" region #1 is an I/O resource, aborting. \n " ) ;
return - EINVAL ;
}
if ( pci_resource_len ( pdev , 1 ) < VELOCITY_IO_SIZE ) {
dev_err ( & pdev - > dev , " region #1 is too small. \n " ) ;
return - EINVAL ;
}
vptr - > pdev = pdev ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-07-24 05:11:12 +04:00
/**
* velocity_print_info - per driver data
* @ vptr : velocity
*
* Print per driver data as the kernel driver finds Velocity
* hardware
*/
static void __devinit velocity_print_info ( struct velocity_info * vptr )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
struct net_device * dev = vptr - > dev ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
printk ( KERN_INFO " %s: %s \n " , dev - > name , get_chip_name ( vptr - > chip_id ) ) ;
2009-12-30 07:04:14 +03:00
printk ( KERN_INFO " %s: Ethernet Address: %pM \n " ,
dev - > name , dev - > dev_addr ) ;
2005-04-17 02:20:36 +04:00
}
static u32 velocity_get_link ( struct net_device * dev )
{
2006-06-27 16:56:23 +04:00
struct velocity_info * vptr = netdev_priv ( dev ) ;
2009-07-20 21:35:21 +04:00
struct mac_regs __iomem * regs = vptr - > mac_regs ;
2006-07-21 01:23:57 +04:00
return BYTE_REG_BITS_IS_ON ( PHYSR0_LINKGD , & regs - > PHYSR0 ) ? 1 : 0 ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
/**
* velocity_found1 - set up discovered velocity card
* @ pdev : PCI device
* @ ent : PCI device table entry that matched
*
* Configure a discovered adapter from scratch . Return a negative
* errno error code on failure paths .
*/
static int __devinit velocity_found1 ( struct pci_dev * pdev , const struct pci_device_id * ent )
2005-04-17 02:20:36 +04:00
{
2009-07-24 05:11:12 +04:00
static int first = 1 ;
struct net_device * dev ;
int i ;
const char * drv_string ;
const struct velocity_info_tbl * info = & chip_info_table [ ent - > driver_data ] ;
struct velocity_info * vptr ;
struct mac_regs __iomem * regs ;
int ret = - ENOMEM ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/* FIXME: this driver, like almost all other ethernet drivers,
* can support more than MAX_UNITS .
*/
if ( velocity_nics > = MAX_UNITS ) {
dev_notice ( & pdev - > dev , " already found %d NICs. \n " ,
velocity_nics ) ;
return - ENODEV ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
dev = alloc_etherdev ( sizeof ( struct velocity_info ) ) ;
if ( ! dev ) {
dev_err ( & pdev - > dev , " allocate net device failed. \n " ) ;
goto out ;
}
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/* Chain it all together */
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
SET_NETDEV_DEV ( dev , & pdev - > dev ) ;
vptr = netdev_priv ( dev ) ;
if ( first ) {
printk ( KERN_INFO " %s Ver. %s \n " ,
VELOCITY_FULL_DRV_NAM , VELOCITY_VERSION ) ;
printk ( KERN_INFO " Copyright (c) 2002, 2003 VIA Networking Technologies, Inc. \n " ) ;
printk ( KERN_INFO " Copyright (c) 2004 Red Hat Inc. \n " ) ;
first = 0 ;
}
velocity_init_info ( pdev , vptr , info ) ;
vptr - > dev = dev ;
dev - > irq = pdev - > irq ;
ret = pci_enable_device ( pdev ) ;
if ( ret < 0 )
goto err_free_dev ;
ret = velocity_get_pci_info ( vptr , pdev ) ;
if ( ret < 0 ) {
/* error message already printed */
goto err_disable ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
ret = pci_request_regions ( pdev , VELOCITY_NAME ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " No PCI resources. \n " ) ;
goto err_disable ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
regs = ioremap ( vptr - > memaddr , VELOCITY_IO_SIZE ) ;
if ( regs = = NULL ) {
ret = - EIO ;
goto err_release_res ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
vptr - > mac_regs = regs ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
mac_wol_reset ( regs ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
dev - > base_addr = vptr - > ioaddr ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
for ( i = 0 ; i < 6 ; i + + )
dev - > dev_addr [ i ] = readb ( & regs - > PAR [ i ] ) ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
drv_string = dev_driver_string ( & pdev - > dev ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
velocity_get_options ( & vptr - > options , velocity_nics , drv_string ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
* Mask out the options cannot be set to the chip
*/
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
vptr - > options . flags & = info - > flags ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
* Enable the chip specified capbilities
*/
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
vptr - > flags = vptr - > options . flags | ( info - > flags & 0xFF000000UL ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
vptr - > wol_opts = vptr - > options . wol_opts ;
vptr - > flags | = VELOCITY_FLAGS_WOL_ENABLED ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
vptr - > phy_id = MII_GET_PHY_ID ( vptr - > mac_regs ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
dev - > irq = pdev - > irq ;
dev - > netdev_ops = & velocity_netdev_ops ;
dev - > ethtool_ops = & velocity_ethtool_ops ;
2009-11-26 01:10:26 +03:00
netif_napi_add ( dev , & vptr - > napi , velocity_poll , VELOCITY_NAPI_WEIGHT ) ;
2006-09-13 21:24:59 +04:00
2009-07-24 05:11:12 +04:00
dev - > features | = NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER |
2010-02-11 08:39:55 +03:00
NETIF_F_HW_VLAN_RX | NETIF_F_IP_CSUM | NETIF_F_SG ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
ret = register_netdev ( dev ) ;
if ( ret < 0 )
goto err_iounmap ;
if ( ! velocity_get_link ( dev ) ) {
netif_carrier_off ( dev ) ;
vptr - > mii_status | = VELOCITY_LINK_FAIL ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
velocity_print_info ( vptr ) ;
pci_set_drvdata ( pdev , dev ) ;
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/* and leave the chip powered down */
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
pci_set_power_state ( pdev , PCI_D3hot ) ;
velocity_nics + + ;
out :
return ret ;
err_iounmap :
iounmap ( regs ) ;
err_release_res :
pci_release_regions ( pdev ) ;
err_disable :
pci_disable_device ( pdev ) ;
err_free_dev :
free_netdev ( dev ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2009-07-24 05:11:12 +04:00
# ifdef CONFIG_PM
2005-04-17 02:20:36 +04:00
/**
* wol_calc_crc - WOL CRC
* @ pattern : data pattern
* @ mask_pattern : mask
*
* Compute the wake on lan crc hashes for the packet header
* we are interested in .
*/
2009-07-20 21:35:21 +04:00
static u16 wol_calc_crc ( int size , u8 * pattern , u8 * mask_pattern )
2005-04-17 02:20:36 +04:00
{
u16 crc = 0xFFFF ;
u8 mask ;
int i , j ;
for ( i = 0 ; i < size ; i + + ) {
mask = mask_pattern [ i ] ;
/* Skip this loop if the mask equals to zero */
if ( mask = = 0x00 )
continue ;
for ( j = 0 ; j < 8 ; j + + ) {
if ( ( mask & 0x01 ) = = 0 ) {
mask > > = 1 ;
continue ;
}
mask > > = 1 ;
crc = crc_ccitt ( crc , & ( pattern [ i * 8 + j ] ) , 1 ) ;
}
}
/* Finally, invert the result once to get the correct data */
crc = ~ crc ;
2006-12-08 13:36:25 +03:00
return bitrev32 ( crc ) > > 16 ;
2005-04-17 02:20:36 +04:00
}
/**
* velocity_set_wol - set up for wake on lan
* @ vptr : velocity to set WOL status on
*
* Set a card up for wake on lan either by unicast or by
* ARP packet .
*
* FIXME : check static buffer is safe here
*/
static int velocity_set_wol ( struct velocity_info * vptr )
{
2009-07-20 21:35:21 +04:00
struct mac_regs __iomem * regs = vptr - > mac_regs ;
2005-04-17 02:20:36 +04:00
static u8 buf [ 256 ] ;
int i ;
static u32 mask_pattern [ 2 ] [ 4 ] = {
{ 0x00203000 , 0x000003C0 , 0x00000000 , 0x0000000 } , /* ARP */
{ 0xfffff000 , 0xffffffff , 0xffffffff , 0x000ffff } /* Magic Packet */
} ;
writew ( 0xFFFF , & regs - > WOLCRClr ) ;
writeb ( WOLCFG_SAB | WOLCFG_SAM , & regs - > WOLCFGSet ) ;
writew ( WOLCR_MAGIC_EN , & regs - > WOLCRSet ) ;
/*
if ( vptr - > wol_opts & VELOCITY_WOL_PHY )
writew ( ( WOLCR_LINKON_EN | WOLCR_LINKOFF_EN ) , & regs - > WOLCRSet ) ;
*/
2009-07-20 21:35:21 +04:00
if ( vptr - > wol_opts & VELOCITY_WOL_UCAST )
2005-04-17 02:20:36 +04:00
writew ( WOLCR_UNICAST_EN , & regs - > WOLCRSet ) ;
if ( vptr - > wol_opts & VELOCITY_WOL_ARP ) {
struct arp_packet * arp = ( struct arp_packet * ) buf ;
u16 crc ;
memset ( buf , 0 , sizeof ( struct arp_packet ) + 7 ) ;
for ( i = 0 ; i < 4 ; i + + )
writel ( mask_pattern [ 0 ] [ i ] , & regs - > ByteMask [ 0 ] [ i ] ) ;
arp - > type = htons ( ETH_P_ARP ) ;
arp - > ar_op = htons ( 1 ) ;
memcpy ( arp - > ar_tip , vptr - > ip_addr , 4 ) ;
crc = wol_calc_crc ( ( sizeof ( struct arp_packet ) + 7 ) / 8 , buf ,
( u8 * ) & mask_pattern [ 0 ] [ 0 ] ) ;
writew ( crc , & regs - > PatternCRC [ 0 ] ) ;
writew ( WOLCR_ARP_EN , & regs - > WOLCRSet ) ;
}
BYTE_REG_BITS_ON ( PWCFG_WOLTYPE , & regs - > PWCFGSet ) ;
BYTE_REG_BITS_ON ( PWCFG_LEGACY_WOLEN , & regs - > PWCFGSet ) ;
writew ( 0x0FFF , & regs - > WOLSRClr ) ;
if ( vptr - > mii_status & VELOCITY_AUTONEG_ENABLE ) {
if ( PHYID_GET_PHY_ID ( vptr - > phy_id ) = = PHYID_CICADA_CS8201 )
MII_REG_BITS_ON ( AUXCR_MDPPS , MII_REG_AUXCR , vptr - > mac_regs ) ;
MII_REG_BITS_OFF ( G1000CR_1000FD | G1000CR_1000 , MII_REG_G1000CR , vptr - > mac_regs ) ;
}
if ( vptr - > mii_status & VELOCITY_SPEED_1000 )
MII_REG_BITS_ON ( BMCR_REAUTO , MII_REG_BMCR , vptr - > mac_regs ) ;
BYTE_REG_BITS_ON ( CHIPGCR_FCMODE , & regs - > CHIPGCR ) ;
{
u8 GCR ;
GCR = readb ( & regs - > CHIPGCR ) ;
GCR = ( GCR & ~ CHIPGCR_FCGMII ) | CHIPGCR_FCFDX ;
writeb ( GCR , & regs - > CHIPGCR ) ;
}
BYTE_REG_BITS_OFF ( ISR_PWEI , & regs - > ISR ) ;
/* Turn on SWPTAG just before entering power mode */
BYTE_REG_BITS_ON ( STICKHW_SWPTAG , & regs - > STICKHW ) ;
/* Go to bed ..... */
BYTE_REG_BITS_ON ( ( STICKHW_DS1 | STICKHW_DS0 ) , & regs - > STICKHW ) ;
return 0 ;
}
2009-07-24 05:11:12 +04:00
/**
* velocity_save_context - save registers
* @ vptr : velocity
* @ context : buffer for stored context
*
* Retrieve the current configuration from the velocity hardware
* and stash it in the context structure , for use by the context
* restore functions . This allows us to save things we need across
* power down states
*/
static void velocity_save_context ( struct velocity_info * vptr , struct velocity_context * context )
{
struct mac_regs __iomem * regs = vptr - > mac_regs ;
u16 i ;
u8 __iomem * ptr = ( u8 __iomem * ) regs ;
for ( i = MAC_REG_PAR ; i < MAC_REG_CR0_CLR ; i + = 4 )
* ( ( u32 * ) ( context - > mac_reg + i ) ) = readl ( ptr + i ) ;
for ( i = MAC_REG_MAR ; i < MAC_REG_TDCSR_CLR ; i + = 4 )
* ( ( u32 * ) ( context - > mac_reg + i ) ) = readl ( ptr + i ) ;
for ( i = MAC_REG_RDBASE_LO ; i < MAC_REG_FIFO_TEST0 ; i + = 4 )
* ( ( u32 * ) ( context - > mac_reg + i ) ) = readl ( ptr + i ) ;
}
2005-04-17 02:20:36 +04:00
static int velocity_suspend ( struct pci_dev * pdev , pm_message_t state )
{
struct net_device * dev = pci_get_drvdata ( pdev ) ;
struct velocity_info * vptr = netdev_priv ( dev ) ;
unsigned long flags ;
2009-07-20 21:35:21 +04:00
if ( ! netif_running ( vptr - > dev ) )
2005-04-17 02:20:36 +04:00
return 0 ;
netif_device_detach ( vptr - > dev ) ;
spin_lock_irqsave ( & vptr - > lock , flags ) ;
pci_save_state ( pdev ) ;
# ifdef ETHTOOL_GWOL
if ( vptr - > flags & VELOCITY_FLAGS_WOL_ENABLED ) {
velocity_get_ip ( vptr ) ;
velocity_save_context ( vptr , & vptr - > context ) ;
velocity_shutdown ( vptr ) ;
velocity_set_wol ( vptr ) ;
2008-01-30 22:10:13 +03:00
pci_enable_wake ( pdev , PCI_D3hot , 1 ) ;
2005-04-17 02:20:36 +04:00
pci_set_power_state ( pdev , PCI_D3hot ) ;
} else {
velocity_save_context ( vptr , & vptr - > context ) ;
velocity_shutdown ( vptr ) ;
pci_disable_device ( pdev ) ;
pci_set_power_state ( pdev , pci_choose_state ( pdev , state ) ) ;
}
2009-07-24 05:11:12 +04:00
# else
pci_set_power_state ( pdev , pci_choose_state ( pdev , state ) ) ;
# endif
spin_unlock_irqrestore ( & vptr - > lock , flags ) ;
return 0 ;
}
/**
* velocity_restore_context - restore registers
* @ vptr : velocity
* @ context : buffer for stored context
*
* Reload the register configuration from the velocity context
* created by velocity_save_context .
*/
static void velocity_restore_context ( struct velocity_info * vptr , struct velocity_context * context )
{
struct mac_regs __iomem * regs = vptr - > mac_regs ;
int i ;
u8 __iomem * ptr = ( u8 __iomem * ) regs ;
for ( i = MAC_REG_PAR ; i < MAC_REG_CR0_SET ; i + = 4 )
writel ( * ( ( u32 * ) ( context - > mac_reg + i ) ) , ptr + i ) ;
/* Just skip cr0 */
for ( i = MAC_REG_CR1_SET ; i < MAC_REG_CR0_CLR ; i + + ) {
/* Clear */
writeb ( ~ ( * ( ( u8 * ) ( context - > mac_reg + i ) ) ) , ptr + i + 4 ) ;
/* Set */
writeb ( * ( ( u8 * ) ( context - > mac_reg + i ) ) , ptr + i ) ;
}
for ( i = MAC_REG_MAR ; i < MAC_REG_IMR ; i + = 4 )
writel ( * ( ( u32 * ) ( context - > mac_reg + i ) ) , ptr + i ) ;
for ( i = MAC_REG_RDBASE_LO ; i < MAC_REG_FIFO_TEST0 ; i + = 4 )
writel ( * ( ( u32 * ) ( context - > mac_reg + i ) ) , ptr + i ) ;
for ( i = MAC_REG_TDCSR_SET ; i < = MAC_REG_RDCSR_SET ; i + + )
writeb ( * ( ( u8 * ) ( context - > mac_reg + i ) ) , ptr + i ) ;
2005-04-17 02:20:36 +04:00
}
static int velocity_resume ( struct pci_dev * pdev )
{
struct net_device * dev = pci_get_drvdata ( pdev ) ;
struct velocity_info * vptr = netdev_priv ( dev ) ;
unsigned long flags ;
int i ;
2009-07-20 21:35:21 +04:00
if ( ! netif_running ( vptr - > dev ) )
2005-04-17 02:20:36 +04:00
return 0 ;
pci_set_power_state ( pdev , PCI_D0 ) ;
pci_enable_wake ( pdev , 0 , 0 ) ;
pci_restore_state ( pdev ) ;
mac_wol_reset ( vptr - > mac_regs ) ;
spin_lock_irqsave ( & vptr - > lock , flags ) ;
velocity_restore_context ( vptr , & vptr - > context ) ;
velocity_init_registers ( vptr , VELOCITY_INIT_WOL ) ;
mac_disable_int ( vptr - > mac_regs ) ;
2010-02-10 02:37:54 +03:00
velocity_tx_srv ( vptr ) ;
2005-04-17 02:20:36 +04:00
2008-08-01 00:10:10 +04:00
for ( i = 0 ; i < vptr - > tx . numq ; i + + ) {
2009-07-20 21:35:21 +04:00
if ( vptr - > tx . used [ i ] )
2005-04-17 02:20:36 +04:00
mac_tx_queue_wake ( vptr - > mac_regs , i ) ;
}
mac_enable_int ( vptr - > mac_regs ) ;
spin_unlock_irqrestore ( & vptr - > lock , flags ) ;
netif_device_attach ( vptr - > dev ) ;
return 0 ;
}
2009-07-24 05:11:12 +04:00
# endif
2005-04-17 02:20:36 +04:00
2009-07-24 05:11:12 +04:00
/*
* Definition for our device driver . The PCI layer interface
* uses this to handle all our card discover and plugging
*/
static struct pci_driver velocity_driver = {
. name = VELOCITY_NAME ,
. id_table = velocity_id_table ,
. probe = velocity_found1 ,
. remove = __devexit_p ( velocity_remove1 ) ,
# ifdef CONFIG_PM
. suspend = velocity_suspend ,
. resume = velocity_resume ,
# endif
} ;
/**
* velocity_ethtool_up - pre hook for ethtool
* @ dev : network device
*
* Called before an ethtool operation . We need to make sure the
* chip is out of D3 state before we poke at it .
*/
static int velocity_ethtool_up ( struct net_device * dev )
{
struct velocity_info * vptr = netdev_priv ( dev ) ;
if ( ! netif_running ( dev ) )
pci_set_power_state ( vptr - > pdev , PCI_D0 ) ;
return 0 ;
}
/**
* velocity_ethtool_down - post hook for ethtool
* @ dev : network device
*
* Called after an ethtool operation . Restore the chip back to D3
* state if it isn ' t running .
*/
static void velocity_ethtool_down ( struct net_device * dev )
{
struct velocity_info * vptr = netdev_priv ( dev ) ;
if ( ! netif_running ( dev ) )
pci_set_power_state ( vptr - > pdev , PCI_D3hot ) ;
}
static int velocity_get_settings ( struct net_device * dev , struct ethtool_cmd * cmd )
{
struct velocity_info * vptr = netdev_priv ( dev ) ;
struct mac_regs __iomem * regs = vptr - > mac_regs ;
u32 status ;
status = check_connection_type ( vptr - > mac_regs ) ;
cmd - > supported = SUPPORTED_TP |
SUPPORTED_Autoneg |
SUPPORTED_10baseT_Half |
SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Half |
SUPPORTED_100baseT_Full |
SUPPORTED_1000baseT_Half |
SUPPORTED_1000baseT_Full ;
if ( status & VELOCITY_SPEED_1000 )
cmd - > speed = SPEED_1000 ;
else if ( status & VELOCITY_SPEED_100 )
cmd - > speed = SPEED_100 ;
else
cmd - > speed = SPEED_10 ;
cmd - > autoneg = ( status & VELOCITY_AUTONEG_ENABLE ) ? AUTONEG_ENABLE : AUTONEG_DISABLE ;
cmd - > port = PORT_TP ;
cmd - > transceiver = XCVR_INTERNAL ;
cmd - > phy_address = readb ( & regs - > MIIADR ) & 0x1F ;
if ( status & VELOCITY_DUPLEX_FULL )
cmd - > duplex = DUPLEX_FULL ;
else
cmd - > duplex = DUPLEX_HALF ;
return 0 ;
}
static int velocity_set_settings ( struct net_device * dev , struct ethtool_cmd * cmd )
{
struct velocity_info * vptr = netdev_priv ( dev ) ;
u32 curr_status ;
u32 new_status = 0 ;
int ret = 0 ;
curr_status = check_connection_type ( vptr - > mac_regs ) ;
curr_status & = ( ~ VELOCITY_LINK_FAIL ) ;
new_status | = ( ( cmd - > autoneg ) ? VELOCITY_AUTONEG_ENABLE : 0 ) ;
new_status | = ( ( cmd - > speed = = SPEED_100 ) ? VELOCITY_SPEED_100 : 0 ) ;
new_status | = ( ( cmd - > speed = = SPEED_10 ) ? VELOCITY_SPEED_10 : 0 ) ;
new_status | = ( ( cmd - > duplex = = DUPLEX_FULL ) ? VELOCITY_DUPLEX_FULL : 0 ) ;
if ( ( new_status & VELOCITY_AUTONEG_ENABLE ) & & ( new_status ! = ( curr_status | VELOCITY_AUTONEG_ENABLE ) ) )
ret = - EINVAL ;
else
velocity_set_media_mode ( vptr , new_status ) ;
return ret ;
}
static void velocity_get_drvinfo ( struct net_device * dev , struct ethtool_drvinfo * info )
{
struct velocity_info * vptr = netdev_priv ( dev ) ;
strcpy ( info - > driver , VELOCITY_NAME ) ;
strcpy ( info - > version , VELOCITY_VERSION ) ;
strcpy ( info - > bus_info , pci_name ( vptr - > pdev ) ) ;
}
static void velocity_ethtool_get_wol ( struct net_device * dev , struct ethtool_wolinfo * wol )
{
struct velocity_info * vptr = netdev_priv ( dev ) ;
wol - > supported = WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP ;
wol - > wolopts | = WAKE_MAGIC ;
/*
if ( vptr - > wol_opts & VELOCITY_WOL_PHY )
wol . wolopts | = WAKE_PHY ;
*/
if ( vptr - > wol_opts & VELOCITY_WOL_UCAST )
wol - > wolopts | = WAKE_UCAST ;
if ( vptr - > wol_opts & VELOCITY_WOL_ARP )
wol - > wolopts | = WAKE_ARP ;
memcpy ( & wol - > sopass , vptr - > wol_passwd , 6 ) ;
}
static int velocity_ethtool_set_wol ( struct net_device * dev , struct ethtool_wolinfo * wol )
{
struct velocity_info * vptr = netdev_priv ( dev ) ;
if ( ! ( wol - > wolopts & ( WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP ) ) )
return - EFAULT ;
vptr - > wol_opts = VELOCITY_WOL_MAGIC ;
/*
if ( wol . wolopts & WAKE_PHY ) {
vptr - > wol_opts | = VELOCITY_WOL_PHY ;
vptr - > flags | = VELOCITY_FLAGS_WOL_ENABLED ;
}
*/
if ( wol - > wolopts & WAKE_MAGIC ) {
vptr - > wol_opts | = VELOCITY_WOL_MAGIC ;
vptr - > flags | = VELOCITY_FLAGS_WOL_ENABLED ;
}
if ( wol - > wolopts & WAKE_UCAST ) {
vptr - > wol_opts | = VELOCITY_WOL_UCAST ;
vptr - > flags | = VELOCITY_FLAGS_WOL_ENABLED ;
}
if ( wol - > wolopts & WAKE_ARP ) {
vptr - > wol_opts | = VELOCITY_WOL_ARP ;
vptr - > flags | = VELOCITY_FLAGS_WOL_ENABLED ;
}
memcpy ( vptr - > wol_passwd , wol - > sopass , 6 ) ;
return 0 ;
}
static u32 velocity_get_msglevel ( struct net_device * dev )
{
return msglevel ;
}
static void velocity_set_msglevel ( struct net_device * dev , u32 value )
{
msglevel = value ;
}
2009-11-26 01:10:12 +03:00
static int get_pending_timer_val ( int val )
{
int mult_bits = val > > 6 ;
int mult = 1 ;
switch ( mult_bits )
{
case 1 :
mult = 4 ; break ;
case 2 :
mult = 16 ; break ;
case 3 :
mult = 64 ; break ;
case 0 :
default :
break ;
}
return ( val & 0x3f ) * mult ;
}
static void set_pending_timer_val ( int * val , u32 us )
{
u8 mult = 0 ;
u8 shift = 0 ;
if ( us > = 0x3f ) {
mult = 1 ; /* mult with 4 */
shift = 2 ;
}
if ( us > = 0x3f * 4 ) {
mult = 2 ; /* mult with 16 */
shift = 4 ;
}
if ( us > = 0x3f * 16 ) {
mult = 3 ; /* mult with 64 */
shift = 6 ;
}
* val = ( mult < < 6 ) | ( ( us > > shift ) & 0x3f ) ;
}
static int velocity_get_coalesce ( struct net_device * dev ,
struct ethtool_coalesce * ecmd )
{
struct velocity_info * vptr = netdev_priv ( dev ) ;
ecmd - > tx_max_coalesced_frames = vptr - > options . tx_intsup ;
ecmd - > rx_max_coalesced_frames = vptr - > options . rx_intsup ;
ecmd - > rx_coalesce_usecs = get_pending_timer_val ( vptr - > options . rxqueue_timer ) ;
ecmd - > tx_coalesce_usecs = get_pending_timer_val ( vptr - > options . txqueue_timer ) ;
return 0 ;
}
static int velocity_set_coalesce ( struct net_device * dev ,
struct ethtool_coalesce * ecmd )
{
struct velocity_info * vptr = netdev_priv ( dev ) ;
int max_us = 0x3f * 64 ;
2010-02-10 02:38:07 +03:00
unsigned long flags ;
2009-11-26 01:10:12 +03:00
/* 6 bits of */
if ( ecmd - > tx_coalesce_usecs > max_us )
return - EINVAL ;
if ( ecmd - > rx_coalesce_usecs > max_us )
return - EINVAL ;
if ( ecmd - > tx_max_coalesced_frames > 0xff )
return - EINVAL ;
if ( ecmd - > rx_max_coalesced_frames > 0xff )
return - EINVAL ;
vptr - > options . rx_intsup = ecmd - > rx_max_coalesced_frames ;
vptr - > options . tx_intsup = ecmd - > tx_max_coalesced_frames ;
set_pending_timer_val ( & vptr - > options . rxqueue_timer ,
ecmd - > rx_coalesce_usecs ) ;
set_pending_timer_val ( & vptr - > options . txqueue_timer ,
ecmd - > tx_coalesce_usecs ) ;
/* Setup the interrupt suppression and queue timers */
2010-02-10 02:38:07 +03:00
spin_lock_irqsave ( & vptr - > lock , flags ) ;
2009-11-26 01:10:12 +03:00
mac_disable_int ( vptr - > mac_regs ) ;
setup_adaptive_interrupts ( vptr ) ;
setup_queue_timers ( vptr ) ;
mac_write_int_mask ( vptr - > int_mask , vptr - > mac_regs ) ;
mac_clear_isr ( vptr - > mac_regs ) ;
mac_enable_int ( vptr - > mac_regs ) ;
2010-02-10 02:38:07 +03:00
spin_unlock_irqrestore ( & vptr - > lock , flags ) ;
2009-11-26 01:10:12 +03:00
return 0 ;
}
2009-07-24 05:11:12 +04:00
static const struct ethtool_ops velocity_ethtool_ops = {
. get_settings = velocity_get_settings ,
. set_settings = velocity_set_settings ,
. get_drvinfo = velocity_get_drvinfo ,
2009-11-26 01:10:52 +03:00
. set_tx_csum = ethtool_op_set_tx_csum ,
. get_tx_csum = ethtool_op_get_tx_csum ,
2009-07-24 05:11:12 +04:00
. get_wol = velocity_ethtool_get_wol ,
. set_wol = velocity_ethtool_set_wol ,
. get_msglevel = velocity_get_msglevel ,
. set_msglevel = velocity_set_msglevel ,
2009-11-26 01:10:43 +03:00
. set_sg = ethtool_op_set_sg ,
2009-07-24 05:11:12 +04:00
. get_link = velocity_get_link ,
2009-11-26 01:10:12 +03:00
. get_coalesce = velocity_get_coalesce ,
. set_coalesce = velocity_set_coalesce ,
2009-07-24 05:11:12 +04:00
. begin = velocity_ethtool_up ,
. complete = velocity_ethtool_down
} ;
2006-12-19 08:21:10 +03:00
2009-07-24 05:11:12 +04:00
# ifdef CONFIG_PM
# ifdef CONFIG_INET
2005-04-17 02:20:36 +04:00
static int velocity_netdev_event ( struct notifier_block * nb , unsigned long notification , void * ptr )
{
struct in_ifaddr * ifa = ( struct in_ifaddr * ) ptr ;
2008-02-29 07:44:27 +03:00
struct net_device * dev = ifa - > ifa_dev - > dev ;
2005-04-17 02:20:36 +04:00
2009-10-28 14:01:46 +03:00
if ( dev_net ( dev ) = = & init_net & &
dev - > netdev_ops = = & velocity_netdev_ops )
velocity_get_ip ( netdev_priv ( dev ) ) ;
2008-02-29 07:44:27 +03:00
2005-04-17 02:20:36 +04:00
return NOTIFY_DONE ;
}
2009-07-24 05:11:12 +04:00
# endif /* CONFIG_INET */
# endif /* CONFIG_PM */
2006-12-19 08:21:10 +03:00
2009-07-24 05:11:12 +04:00
# if defined(CONFIG_PM) && defined(CONFIG_INET)
static struct notifier_block velocity_inetaddr_notifier = {
. notifier_call = velocity_netdev_event ,
} ;
static void velocity_register_notifier ( void )
{
register_inetaddr_notifier ( & velocity_inetaddr_notifier ) ;
}
static void velocity_unregister_notifier ( void )
{
unregister_inetaddr_notifier ( & velocity_inetaddr_notifier ) ;
}
# else
# define velocity_register_notifier() do {} while (0)
# define velocity_unregister_notifier() do {} while (0)
# endif /* defined(CONFIG_PM) && defined(CONFIG_INET) */
/**
* velocity_init_module - load time function
*
* Called when the velocity module is loaded . The PCI driver
* is registered with the PCI layer , and in turn will call
* the probe functions for each velocity adapter installed
* in the system .
*/
static int __init velocity_init_module ( void )
{
int ret ;
velocity_register_notifier ( ) ;
ret = pci_register_driver ( & velocity_driver ) ;
if ( ret < 0 )
velocity_unregister_notifier ( ) ;
return ret ;
}
/**
* velocity_cleanup - module unload
*
* When the velocity hardware is unloaded this function is called .
* It will clean up the notifiers and the unregister the PCI
* driver interface for this hardware . This in turn cleans up
* all discovered interfaces before returning from the function
*/
static void __exit velocity_cleanup_module ( void )
{
velocity_unregister_notifier ( ) ;
pci_unregister_driver ( & velocity_driver ) ;
}
module_init ( velocity_init_module ) ;
module_exit ( velocity_cleanup_module ) ;