2005-04-17 02:20:36 +04:00
/*******************************************************************************
*
* Linux ThunderLAN Driver
*
* tlan . c
* by James Banks
*
* ( C ) 1997 - 1998 Caldera , Inc .
* ( C ) 1998 James Banks
* ( C ) 1999 - 2001 Torben Mathiasen
* ( C ) 2002 Samuel Chessman
*
* This software may be used and distributed according to the terms
* of the GNU General Public License , incorporated herein by reference .
*
* * Useful ( if not required ) reading :
*
* Texas Instruments , ThunderLAN Programmer ' s Guide ,
* TI Literature Number SPWU013A
* available in PDF format from www . ti . com
* Level One , LXT901 and LXT970 Data Sheets
* available in PDF format from www . level1 . com
* National Semiconductor , DP83840A Data Sheet
* available in PDF format from www . national . com
* Microchip Technology , 24 C01A / 02 A / 04 A Data Sheet
* available in PDF format from www . microchip . com
*
2011-01-21 13:59:30 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-03-01 09:56:33 +03:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2011-06-06 14:43:46 +04:00
# include <linux/hardirq.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/init.h>
2011-06-06 14:43:46 +04:00
# include <linux/interrupt.h>
2005-04-17 02:20:36 +04:00
# include <linux/ioport.h>
# include <linux/eisa.h>
# include <linux/pci.h>
2005-06-27 02:22:14 +04:00
# include <linux/dma-mapping.h>
2005-04-17 02:20:36 +04:00
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/delay.h>
# include <linux/spinlock.h>
# include <linux/workqueue.h>
# include <linux/mii.h>
# include "tlan.h"
/* For removing EISA devices */
2011-01-21 13:59:30 +03:00
static struct net_device * tlan_eisa_devices ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static int tlan_devices_installed ;
2005-04-17 02:20:36 +04:00
/* Set speed, duplex and aui settings */
static int aui [ MAX_TLAN_BOARDS ] ;
static int duplex [ MAX_TLAN_BOARDS ] ;
static int speed [ MAX_TLAN_BOARDS ] ;
static int boards_found ;
2005-05-05 02:33:11 +04:00
module_param_array ( aui , int , NULL , 0 ) ;
module_param_array ( duplex , int , NULL , 0 ) ;
module_param_array ( speed , int , NULL , 0 ) ;
MODULE_PARM_DESC ( aui , " ThunderLAN use AUI port(s) (0-1) " ) ;
2011-01-21 13:59:30 +03:00
MODULE_PARM_DESC ( duplex ,
" ThunderLAN duplex setting(s) (0-default, 1-half, 2-full) " ) ;
2011-03-01 09:56:33 +03:00
MODULE_PARM_DESC ( speed , " ThunderLAN port speed setting(s) (0,10,100) " ) ;
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Maintainer: Samuel Chessman <chessman@tux.org> " ) ;
MODULE_DESCRIPTION ( " Driver for TI ThunderLAN based ethernet PCI adapters " ) ;
MODULE_LICENSE ( " GPL " ) ;
/* Define this to enable Link beat monitoring */
# undef MONITOR
/* Turn on debugging. See Documentation/networking/tlan.txt for details */
static int debug ;
2005-05-05 02:33:11 +04:00
module_param ( debug , int , 0 ) ;
MODULE_PARM_DESC ( debug , " ThunderLAN debug mask " ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static const char tlan_signature [ ] = " TLAN " ;
2011-01-21 13:59:31 +03:00
static const char tlan_banner [ ] = " ThunderLAN driver v1.17 \n " ;
2005-04-17 02:20:36 +04:00
static int tlan_have_pci ;
static int tlan_have_eisa ;
2011-01-21 13:59:30 +03:00
static const char * const media [ ] = {
" 10BaseT-HD " , " 10BaseT-FD " , " 100baseTx-HD " ,
" 100BaseTx-FD " , " 100BaseT4 " , NULL
2005-04-17 02:20:36 +04:00
} ;
static struct board {
2011-01-21 13:59:30 +03:00
const char * device_label ;
u32 flags ;
u16 addr_ofs ;
2005-04-17 02:20:36 +04:00
} board_info [ ] = {
{ " Compaq Netelligent 10 T PCI UTP " , TLAN_ADAPTER_ACTIVITY_LED , 0x83 } ,
2011-01-21 13:59:30 +03:00
{ " Compaq Netelligent 10/100 TX PCI UTP " ,
TLAN_ADAPTER_ACTIVITY_LED , 0x83 } ,
2005-04-17 02:20:36 +04:00
{ " Compaq Integrated NetFlex-3/P " , TLAN_ADAPTER_NONE , 0x83 } ,
2008-05-30 20:49:58 +04:00
{ " Compaq NetFlex-3/P " ,
TLAN_ADAPTER_UNMANAGED_PHY | TLAN_ADAPTER_BIT_RATE_PHY , 0x83 } ,
2005-04-17 02:20:36 +04:00
{ " Compaq NetFlex-3/P " , TLAN_ADAPTER_NONE , 0x83 } ,
2008-05-30 20:49:58 +04:00
{ " Compaq Netelligent Integrated 10/100 TX UTP " ,
TLAN_ADAPTER_ACTIVITY_LED , 0x83 } ,
2011-01-21 13:59:30 +03:00
{ " Compaq Netelligent Dual 10/100 TX PCI UTP " ,
TLAN_ADAPTER_NONE , 0x83 } ,
{ " Compaq Netelligent 10/100 TX Embedded UTP " ,
TLAN_ADAPTER_NONE , 0x83 } ,
2005-04-17 02:20:36 +04:00
{ " Olicom OC-2183/2185 " , TLAN_ADAPTER_USE_INTERN_10 , 0x83 } ,
2011-01-21 13:59:30 +03:00
{ " Olicom OC-2325 " , TLAN_ADAPTER_UNMANAGED_PHY , 0xf8 } ,
{ " Olicom OC-2326 " , TLAN_ADAPTER_USE_INTERN_10 , 0xf8 } ,
2005-04-17 02:20:36 +04:00
{ " Compaq Netelligent 10/100 TX UTP " , TLAN_ADAPTER_ACTIVITY_LED , 0x83 } ,
2011-01-21 13:59:30 +03:00
{ " Compaq Netelligent 10 T/2 PCI UTP/coax " , TLAN_ADAPTER_NONE , 0x83 } ,
2008-05-30 20:49:58 +04:00
{ " Compaq NetFlex-3/E " ,
2011-01-21 13:59:30 +03:00
TLAN_ADAPTER_ACTIVITY_LED | /* EISA card */
2008-05-30 20:49:58 +04:00
TLAN_ADAPTER_UNMANAGED_PHY | TLAN_ADAPTER_BIT_RATE_PHY , 0x83 } ,
2011-01-21 13:59:30 +03:00
{ " Compaq NetFlex-3/E " ,
TLAN_ADAPTER_ACTIVITY_LED , 0x83 } , /* EISA card */
2005-04-17 02:20:36 +04:00
} ;
2010-01-07 14:58:11 +03:00
static DEFINE_PCI_DEVICE_TABLE ( tlan_pci_tbl ) = {
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_COMPAQ , PCI_DEVICE_ID_COMPAQ_NETEL10 ,
2011-01-21 13:59:30 +03:00
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_COMPAQ , PCI_DEVICE_ID_COMPAQ_NETEL100 ,
2011-01-21 13:59:30 +03:00
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 1 } ,
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_COMPAQ , PCI_DEVICE_ID_COMPAQ_NETFLEX3I ,
2011-01-21 13:59:30 +03:00
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 2 } ,
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_COMPAQ , PCI_DEVICE_ID_COMPAQ_THUNDER ,
2011-01-21 13:59:30 +03:00
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 3 } ,
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_COMPAQ , PCI_DEVICE_ID_COMPAQ_NETFLEX3B ,
2011-01-21 13:59:30 +03:00
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 4 } ,
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_COMPAQ , PCI_DEVICE_ID_COMPAQ_NETEL100PI ,
2011-01-21 13:59:30 +03:00
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 5 } ,
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_COMPAQ , PCI_DEVICE_ID_COMPAQ_NETEL100D ,
2011-01-21 13:59:30 +03:00
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 6 } ,
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_COMPAQ , PCI_DEVICE_ID_COMPAQ_NETEL100I ,
2011-01-21 13:59:30 +03:00
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 7 } ,
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_OLICOM , PCI_DEVICE_ID_OLICOM_OC2183 ,
2011-01-21 13:59:30 +03:00
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 8 } ,
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_OLICOM , PCI_DEVICE_ID_OLICOM_OC2325 ,
2011-01-21 13:59:30 +03:00
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 9 } ,
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_OLICOM , PCI_DEVICE_ID_OLICOM_OC2326 ,
2011-01-21 13:59:30 +03:00
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 10 } ,
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_COMPAQ , PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100 ,
2011-01-21 13:59:30 +03:00
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 11 } ,
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_COMPAQ , PCI_DEVICE_ID_NETELLIGENT_10_T2 ,
2011-01-21 13:59:30 +03:00
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 12 } ,
2005-04-17 02:20:36 +04:00
{ 0 , }
} ;
2006-09-13 21:24:59 +04:00
MODULE_DEVICE_TABLE ( pci , tlan_pci_tbl ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_eisa_probe ( void ) ;
static void tlan_eisa_cleanup ( void ) ;
static int tlan_init ( struct net_device * ) ;
static int tlan_open ( struct net_device * dev ) ;
static netdev_tx_t tlan_start_tx ( struct sk_buff * , struct net_device * ) ;
static irqreturn_t tlan_handle_interrupt ( int , void * ) ;
static int tlan_close ( struct net_device * ) ;
static struct net_device_stats * tlan_get_stats ( struct net_device * ) ;
static void tlan_set_multicast_list ( struct net_device * ) ;
static int tlan_ioctl ( struct net_device * dev , struct ifreq * rq , int cmd ) ;
static int tlan_probe1 ( struct pci_dev * pdev , long ioaddr ,
int irq , int rev , const struct pci_device_id * ent ) ;
static void tlan_tx_timeout ( struct net_device * dev ) ;
static void tlan_tx_timeout_work ( struct work_struct * work ) ;
static int tlan_init_one ( struct pci_dev * pdev ,
const struct pci_device_id * ent ) ;
static u32 tlan_handle_tx_eof ( struct net_device * , u16 ) ;
static u32 tlan_handle_stat_overflow ( struct net_device * , u16 ) ;
static u32 tlan_handle_rx_eof ( struct net_device * , u16 ) ;
static u32 tlan_handle_dummy ( struct net_device * , u16 ) ;
static u32 tlan_handle_tx_eoc ( struct net_device * , u16 ) ;
static u32 tlan_handle_status_check ( struct net_device * , u16 ) ;
static u32 tlan_handle_rx_eoc ( struct net_device * , u16 ) ;
static void tlan_timer ( unsigned long ) ;
static void tlan_reset_lists ( struct net_device * ) ;
static void tlan_free_lists ( struct net_device * ) ;
static void tlan_print_dio ( u16 ) ;
static void tlan_print_list ( struct tlan_list * , char * , int ) ;
static void tlan_read_and_clear_stats ( struct net_device * , int ) ;
static void tlan_reset_adapter ( struct net_device * ) ;
static void tlan_finish_reset ( struct net_device * ) ;
static void tlan_set_mac ( struct net_device * , int areg , char * mac ) ;
static void tlan_phy_print ( struct net_device * ) ;
static void tlan_phy_detect ( struct net_device * ) ;
static void tlan_phy_power_down ( struct net_device * ) ;
static void tlan_phy_power_up ( struct net_device * ) ;
static void tlan_phy_reset ( struct net_device * ) ;
static void tlan_phy_start_link ( struct net_device * ) ;
static void tlan_phy_finish_auto_neg ( struct net_device * ) ;
2005-04-17 02:20:36 +04:00
# ifdef MONITOR
2011-01-21 13:59:30 +03:00
static void tlan_phy_monitor ( struct net_device * ) ;
2005-04-17 02:20:36 +04:00
# endif
/*
2011-01-21 13:59:30 +03:00
static int tlan_phy_nop ( struct net_device * ) ;
static int tlan_phy_internal_check ( struct net_device * ) ;
static int tlan_phy_internal_service ( struct net_device * ) ;
static int tlan_phy_dp83840a_check ( struct net_device * ) ;
2005-04-17 02:20:36 +04:00
*/
2011-01-21 13:59:30 +03:00
static bool tlan_mii_read_reg ( struct net_device * , u16 , u16 , u16 * ) ;
static void tlan_mii_send_data ( u16 , u32 , unsigned ) ;
static void tlan_mii_sync ( u16 ) ;
static void tlan_mii_write_reg ( struct net_device * , u16 , u16 , u16 ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_ee_send_start ( u16 ) ;
static int tlan_ee_send_byte ( u16 , u8 , int ) ;
static void tlan_ee_receive_byte ( u16 , u8 * , int ) ;
static int tlan_ee_read_byte ( struct net_device * , u8 , u8 * ) ;
2005-04-17 02:20:36 +04:00
2008-05-30 20:49:55 +04:00
static inline void
2011-01-21 13:59:30 +03:00
tlan_store_skb ( struct tlan_list * tag , struct sk_buff * skb )
2005-04-17 02:20:36 +04:00
{
unsigned long addr = ( unsigned long ) skb ;
2008-05-30 20:49:55 +04:00
tag - > buffer [ 9 ] . address = addr ;
tag - > buffer [ 8 ] . address = upper_32_bits ( addr ) ;
2005-04-17 02:20:36 +04:00
}
2008-05-30 20:49:55 +04:00
static inline struct sk_buff *
2011-01-21 13:59:30 +03:00
tlan_get_skb ( const struct tlan_list * tag )
2005-04-17 02:20:36 +04:00
{
2008-05-30 20:49:55 +04:00
unsigned long addr ;
2008-08-09 20:54:02 +04:00
addr = tag - > buffer [ 9 ] . address ;
addr | = ( tag - > buffer [ 8 ] . address < < 16 ) < < 16 ;
2005-04-17 02:20:36 +04:00
return ( struct sk_buff * ) addr ;
}
2011-01-21 13:59:30 +03:00
static u32
( * tlan_int_vector [ TLAN_INT_NUMBER_OF_INTS ] ) ( struct net_device * , u16 ) = {
2008-05-30 20:49:57 +04:00
NULL ,
2011-01-21 13:59:30 +03:00
tlan_handle_tx_eof ,
tlan_handle_stat_overflow ,
tlan_handle_rx_eof ,
tlan_handle_dummy ,
tlan_handle_tx_eoc ,
tlan_handle_status_check ,
tlan_handle_rx_eoc
2005-04-17 02:20:36 +04:00
} ;
static inline void
2011-01-21 13:59:30 +03:00
tlan_set_timer ( struct net_device * dev , u32 ticks , u32 type )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags = 0 ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
if ( ! in_irq ( ) )
spin_lock_irqsave ( & priv - > lock , flags ) ;
2011-01-21 13:59:30 +03:00
if ( priv - > timer . function ! = NULL & &
priv - > timer_type ! = TLAN_TIMER_ACTIVITY ) {
2005-04-17 02:20:36 +04:00
if ( ! in_irq ( ) )
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
return ;
}
2011-01-21 13:59:30 +03:00
priv - > timer . function = tlan_timer ;
2005-04-17 02:20:36 +04:00
if ( ! in_irq ( ) )
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
priv - > timer . data = ( unsigned long ) dev ;
2011-01-21 13:59:30 +03:00
priv - > timer_set_at = jiffies ;
priv - > timer_type = type ;
2005-04-17 02:20:36 +04:00
mod_timer ( & priv - > timer , jiffies + ticks ) ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
/*****************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2011-01-21 13:59:30 +03:00
ThunderLAN driver primary functions
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
these functions are more or less common to all linux network drivers .
2005-04-17 02:20:36 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_remove_one
*
* Returns :
* Nothing
* Parms :
* None
*
* Goes through the TLanDevices list and frees the device
* structs and memory associated with each device ( lists
* and buffers ) . It also ureserves the IO port regions
* associated with this device .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void __devexit tlan_remove_one ( struct pci_dev * pdev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct net_device * dev = pci_get_drvdata ( pdev ) ;
struct tlan_priv * priv = netdev_priv ( dev ) ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
unregister_netdev ( dev ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
if ( priv - > dma_storage ) {
pci_free_consistent ( priv - > pci_dev ,
priv - > dma_size , priv - > dma_storage ,
priv - > dma_storage_dma ) ;
2005-04-17 02:20:36 +04:00
}
# ifdef CONFIG_PCI
pci_release_regions ( pdev ) ;
# endif
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
free_netdev ( dev ) ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
pci_set_drvdata ( pdev , NULL ) ;
2006-09-13 21:24:59 +04:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:31 +03:00
static void tlan_start ( struct net_device * dev )
{
tlan_reset_lists ( dev ) ;
/* NOTE: It might not be necessary to read the stats before a
reset if you don ' t care what the values are .
*/
tlan_read_and_clear_stats ( dev , TLAN_IGNORE ) ;
tlan_reset_adapter ( dev ) ;
netif_wake_queue ( dev ) ;
}
static void tlan_stop ( struct net_device * dev )
{
struct tlan_priv * priv = netdev_priv ( dev ) ;
tlan_read_and_clear_stats ( dev , TLAN_RECORD ) ;
outl ( TLAN_HC_AD_RST , dev - > base_addr + TLAN_HOST_CMD ) ;
/* Reset and power down phy */
tlan_reset_adapter ( dev ) ;
if ( priv - > timer . function ! = NULL ) {
del_timer_sync ( & priv - > timer ) ;
priv - > timer . function = NULL ;
}
}
# ifdef CONFIG_PM
static int tlan_suspend ( struct pci_dev * pdev , pm_message_t state )
{
struct net_device * dev = pci_get_drvdata ( pdev ) ;
if ( netif_running ( dev ) )
tlan_stop ( dev ) ;
netif_device_detach ( dev ) ;
pci_save_state ( pdev ) ;
pci_disable_device ( pdev ) ;
pci_wake_from_d3 ( pdev , false ) ;
pci_set_power_state ( pdev , PCI_D3hot ) ;
return 0 ;
}
static int tlan_resume ( struct pci_dev * pdev )
{
struct net_device * dev = pci_get_drvdata ( pdev ) ;
pci_set_power_state ( pdev , PCI_D0 ) ;
pci_restore_state ( pdev ) ;
pci_enable_wake ( pdev , 0 , 0 ) ;
netif_device_attach ( dev ) ;
if ( netif_running ( dev ) )
tlan_start ( dev ) ;
return 0 ;
}
# else /* CONFIG_PM */
# define tlan_suspend NULL
# define tlan_resume NULL
# endif /* CONFIG_PM */
2005-04-17 02:20:36 +04:00
static struct pci_driver tlan_driver = {
. name = " tlan " ,
. id_table = tlan_pci_tbl ,
. probe = tlan_init_one ,
2006-09-13 21:24:59 +04:00
. remove = __devexit_p ( tlan_remove_one ) ,
2011-01-21 13:59:31 +03:00
. suspend = tlan_suspend ,
. resume = tlan_resume ,
2005-04-17 02:20:36 +04:00
} ;
static int __init tlan_probe ( void )
{
2008-02-05 10:47:16 +03:00
int rc = - ENODEV ;
2006-09-13 21:24:59 +04:00
2011-03-01 09:56:33 +03:00
pr_info ( " %s " , tlan_banner ) ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
TLAN_DBG ( TLAN_DEBUG_PROBE , " Starting PCI Probe.... \n " ) ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
/* Use new style PCI probing. Now the kernel will
do most of this for us */
2008-02-05 10:47:16 +03:00
rc = pci_register_driver ( & tlan_driver ) ;
if ( rc ! = 0 ) {
2011-03-01 09:56:33 +03:00
pr_err ( " Could not register pci driver \n " ) ;
2008-02-05 10:47:16 +03:00
goto err_out_pci_free ;
}
2005-04-17 02:20:36 +04:00
TLAN_DBG ( TLAN_DEBUG_PROBE , " Starting EISA Probe.... \n " ) ;
2011-01-21 13:59:30 +03:00
tlan_eisa_probe ( ) ;
2006-09-13 21:24:59 +04:00
2011-03-01 09:56:33 +03:00
pr_info ( " %d device%s installed, PCI: %d EISA: %d \n " ,
tlan_devices_installed , tlan_devices_installed = = 1 ? " " : " s " ,
tlan_have_pci , tlan_have_eisa ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
if ( tlan_devices_installed = = 0 ) {
2008-02-05 10:47:16 +03:00
rc = - ENODEV ;
goto err_out_pci_unreg ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
2008-02-05 10:47:16 +03:00
err_out_pci_unreg :
pci_unregister_driver ( & tlan_driver ) ;
err_out_pci_free :
return rc ;
2005-04-17 02:20:36 +04:00
}
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static int __devinit tlan_init_one ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
return tlan_probe1 ( pdev , - 1 , - 1 , 0 , ent ) ;
2005-04-17 02:20:36 +04:00
}
/*
2011-01-21 13:59:30 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* tlan_probe1
*
* Returns :
* 0 on success , error code on error
* Parms :
* none
*
* The name is lower case to fit in with all the rest of
* the netcard_probe names . This function looks for
* another TLan based adapter , setting it up with the
* allocated device struct if one is found .
* tlan_probe has been ported to the new net API and
* now allocates its own device structure . This function
* is also used by modules .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int __devinit tlan_probe1 ( struct pci_dev * pdev ,
2008-05-30 20:49:58 +04:00
long ioaddr , int irq , int rev ,
2011-01-21 13:59:30 +03:00
const struct pci_device_id * ent )
2005-04-17 02:20:36 +04:00
{
struct net_device * dev ;
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv ;
2005-04-17 02:20:36 +04:00
u16 device_id ;
int reg , rc = - ENODEV ;
2006-02-05 02:37:47 +03:00
# ifdef CONFIG_PCI
2005-04-17 02:20:36 +04:00
if ( pdev ) {
rc = pci_enable_device ( pdev ) ;
if ( rc )
return rc ;
2011-01-21 13:59:30 +03:00
rc = pci_request_regions ( pdev , tlan_signature ) ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
2011-03-01 09:56:33 +03:00
pr_err ( " Could not reserve IO regions \n " ) ;
2005-04-17 02:20:36 +04:00
goto err_out ;
}
}
2006-02-05 02:37:47 +03:00
# endif /* CONFIG_PCI */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
dev = alloc_etherdev ( sizeof ( struct tlan_priv ) ) ;
2005-04-17 02:20:36 +04:00
if ( dev = = NULL ) {
2011-03-01 09:56:33 +03:00
pr_err ( " Could not allocate memory for device \n " ) ;
2005-04-17 02:20:36 +04:00
rc = - ENOMEM ;
goto err_out_regions ;
}
SET_NETDEV_DEV ( dev , & pdev - > dev ) ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
priv = netdev_priv ( dev ) ;
2011-01-21 13:59:30 +03:00
priv - > pci_dev = pdev ;
2006-11-22 17:57:56 +03:00
priv - > dev = dev ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
/* Is this a PCI device? */
if ( pdev ) {
2011-01-21 13:59:30 +03:00
u32 pci_io_base = 0 ;
2005-04-17 02:20:36 +04:00
priv - > adapter = & board_info [ ent - > driver_data ] ;
2009-04-07 06:01:15 +04:00
rc = pci_set_dma_mask ( pdev , DMA_BIT_MASK ( 32 ) ) ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
2011-03-01 09:56:33 +03:00
pr_err ( " No suitable PCI mapping available \n " ) ;
2005-04-17 02:20:36 +04:00
goto err_out_free_dev ;
}
2011-01-21 13:59:30 +03:00
for ( reg = 0 ; reg < = 5 ; reg + + ) {
2005-04-17 02:20:36 +04:00
if ( pci_resource_flags ( pdev , reg ) & IORESOURCE_IO ) {
pci_io_base = pci_resource_start ( pdev , reg ) ;
2011-01-21 13:59:30 +03:00
TLAN_DBG ( TLAN_DEBUG_GNRL ,
" IO mapping is available at %x. \n " ,
pci_io_base ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
if ( ! pci_io_base ) {
2011-03-01 09:56:33 +03:00
pr_err ( " No IO mappings available \n " ) ;
2005-04-17 02:20:36 +04:00
rc = - EIO ;
goto err_out_free_dev ;
}
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
dev - > base_addr = pci_io_base ;
dev - > irq = pdev - > irq ;
2011-01-21 13:59:30 +03:00
priv - > adapter_rev = pdev - > revision ;
2005-04-17 02:20:36 +04:00
pci_set_master ( pdev ) ;
pci_set_drvdata ( pdev , dev ) ;
} else { /* EISA card */
/* This is a hack. We need to know which board structure
* is suited for this adapter */
device_id = inw ( ioaddr + EISA_ID2 ) ;
priv - > is_eisa = 1 ;
if ( device_id = = 0x20F1 ) {
2011-01-21 13:59:30 +03:00
priv - > adapter = & board_info [ 13 ] ; /* NetFlex-3/E */
priv - > adapter_rev = 23 ; /* TLAN 2.3 */
2005-04-17 02:20:36 +04:00
} else {
priv - > adapter = & board_info [ 14 ] ;
2011-01-21 13:59:30 +03:00
priv - > adapter_rev = 10 ; /* TLAN 1.0 */
2005-04-17 02:20:36 +04:00
}
dev - > base_addr = ioaddr ;
dev - > irq = irq ;
}
/* Kernel parameters */
if ( dev - > mem_start ) {
priv - > aui = dev - > mem_start & 0x01 ;
2008-05-30 20:49:58 +04:00
priv - > duplex = ( ( dev - > mem_start & 0x06 ) = = 0x06 ) ? 0
: ( dev - > mem_start & 0x06 ) > > 1 ;
priv - > speed = ( ( dev - > mem_start & 0x18 ) = = 0x18 ) ? 0
: ( dev - > mem_start & 0x18 ) > > 3 ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
if ( priv - > speed = = 0x1 )
2005-04-17 02:20:36 +04:00
priv - > speed = TLAN_SPEED_10 ;
2011-01-21 13:59:30 +03:00
else if ( priv - > speed = = 0x2 )
2005-04-17 02:20:36 +04:00
priv - > speed = TLAN_SPEED_100 ;
2011-01-21 13:59:30 +03:00
2005-04-17 02:20:36 +04:00
debug = priv - > debug = dev - > mem_end ;
} else {
priv - > aui = aui [ boards_found ] ;
priv - > speed = speed [ boards_found ] ;
priv - > duplex = duplex [ boards_found ] ;
priv - > debug = debug ;
}
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
/* This will be used when we get an adapter error from
* within our irq handler */
2011-01-21 13:59:30 +03:00
INIT_WORK ( & priv - > tlan_tqueue , tlan_tx_timeout_work ) ;
2005-04-17 02:20:36 +04:00
spin_lock_init ( & priv - > lock ) ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
rc = tlan_init ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
2011-03-01 09:56:33 +03:00
pr_err ( " Could not set up device \n " ) ;
2005-04-17 02:20:36 +04:00
goto err_out_free_dev ;
}
rc = register_netdev ( dev ) ;
if ( rc ) {
2011-03-01 09:56:33 +03:00
pr_err ( " Could not register device \n " ) ;
2005-04-17 02:20:36 +04:00
goto err_out_uninit ;
}
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
tlan_devices_installed + + ;
2005-04-17 02:20:36 +04:00
boards_found + + ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
/* pdev is NULL if this is an EISA device */
if ( pdev )
tlan_have_pci + + ;
else {
2011-01-21 13:59:30 +03:00
priv - > next_device = tlan_eisa_devices ;
tlan_eisa_devices = dev ;
2005-04-17 02:20:36 +04:00
tlan_have_eisa + + ;
}
2006-09-13 21:24:59 +04:00
2011-03-01 09:56:33 +03:00
netdev_info ( dev , " irq=%2d, io=%04x, %s, Rev. %d \n " ,
( int ) dev - > irq ,
( int ) dev - > base_addr ,
priv - > adapter - > device_label ,
priv - > adapter_rev ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
err_out_uninit :
2011-01-21 13:59:30 +03:00
pci_free_consistent ( priv - > pci_dev , priv - > dma_size , priv - > dma_storage ,
priv - > dma_storage_dma ) ;
2005-04-17 02:20:36 +04:00
err_out_free_dev :
free_netdev ( dev ) ;
err_out_regions :
# ifdef CONFIG_PCI
if ( pdev )
pci_release_regions ( pdev ) ;
# endif
err_out :
if ( pdev )
pci_disable_device ( pdev ) ;
return rc ;
}
2011-01-21 13:59:30 +03:00
static void tlan_eisa_cleanup ( void )
2005-04-17 02:20:36 +04:00
{
struct net_device * dev ;
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
while ( tlan_have_eisa ) {
dev = tlan_eisa_devices ;
2005-04-17 02:20:36 +04:00
priv = netdev_priv ( dev ) ;
2011-01-21 13:59:30 +03:00
if ( priv - > dma_storage ) {
pci_free_consistent ( priv - > pci_dev , priv - > dma_size ,
priv - > dma_storage ,
priv - > dma_storage_dma ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
release_region ( dev - > base_addr , 0x10 ) ;
unregister_netdev ( dev ) ;
tlan_eisa_devices = priv - > next_device ;
free_netdev ( dev ) ;
2005-04-17 02:20:36 +04:00
tlan_have_eisa - - ;
}
}
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
static void __exit tlan_exit ( void )
{
pci_unregister_driver ( & tlan_driver ) ;
if ( tlan_have_eisa )
2011-01-21 13:59:30 +03:00
tlan_eisa_cleanup ( ) ;
2005-04-17 02:20:36 +04:00
}
/* Module loading/unloading */
module_init ( tlan_probe ) ;
module_exit ( tlan_exit ) ;
2011-01-21 13:59:30 +03:00
/**************************************************************
* tlan_eisa_probe
*
* Returns : 0 on success , 1 otherwise
*
* Parms : None
*
*
* This functions probes for EISA devices and calls
* TLan_probe1 when one is found .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void __init tlan_eisa_probe ( void )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
long ioaddr ;
int rc = - ENODEV ;
int irq ;
2005-04-17 02:20:36 +04:00
u16 device_id ;
2006-09-13 21:24:59 +04:00
if ( ! EISA_bus ) {
2005-04-17 02:20:36 +04:00
TLAN_DBG ( TLAN_DEBUG_PROBE , " No EISA bus present \n " ) ;
return ;
}
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
/* Loop through all slots of the EISA bus */
for ( ioaddr = 0x1000 ; ioaddr < 0x9000 ; ioaddr + = 0x1000 ) {
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
TLAN_DBG ( TLAN_DEBUG_PROBE , " EISA_ID 0x%4x: 0x%4x \n " ,
( int ) ioaddr + 0xc80 , inw ( ioaddr + EISA_ID ) ) ;
TLAN_DBG ( TLAN_DEBUG_PROBE , " EISA_ID 0x%4x: 0x%4x \n " ,
( int ) ioaddr + 0xc82 , inw ( ioaddr + EISA_ID2 ) ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
TLAN_DBG ( TLAN_DEBUG_PROBE ,
" Probing for EISA adapter at IO: 0x%4x : " ,
( int ) ioaddr ) ;
if ( request_region ( ioaddr , 0x10 , tlan_signature ) = = NULL )
2005-04-17 02:20:36 +04:00
goto out ;
2006-09-13 21:24:59 +04:00
if ( inw ( ioaddr + EISA_ID ) ! = 0x110E ) {
2005-04-17 02:20:36 +04:00
release_region ( ioaddr , 0x10 ) ;
goto out ;
}
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
device_id = inw ( ioaddr + EISA_ID2 ) ;
2006-09-13 21:24:59 +04:00
if ( device_id ! = 0x20F1 & & device_id ! = 0x40F1 ) {
2011-01-21 13:59:30 +03:00
release_region ( ioaddr , 0x10 ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
/* check if adapter is enabled */
if ( inb ( ioaddr + EISA_CR ) ! = 0x1 ) {
release_region ( ioaddr , 0x10 ) ;
2005-04-17 02:20:36 +04:00
goto out2 ;
}
2006-09-13 21:24:59 +04:00
if ( debug = = 0x10 )
2011-03-01 09:56:33 +03:00
pr_info ( " Found one \n " ) ;
2005-04-17 02:20:36 +04:00
/* Get irq from board */
2011-01-21 13:59:30 +03:00
switch ( inb ( ioaddr + 0xcc0 ) ) {
case ( 0x10 ) :
irq = 5 ;
break ;
case ( 0x20 ) :
irq = 9 ;
break ;
case ( 0x40 ) :
irq = 10 ;
break ;
case ( 0x80 ) :
irq = 11 ;
break ;
default :
goto out ;
2006-09-13 21:24:59 +04:00
}
2005-04-17 02:20:36 +04:00
/* Setup the newly found eisa adapter */
2011-01-21 13:59:30 +03:00
rc = tlan_probe1 ( NULL , ioaddr , irq ,
12 , NULL ) ;
2005-04-17 02:20:36 +04:00
continue ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
out :
if ( debug = = 0x10 )
2011-03-01 09:56:33 +03:00
pr_info ( " None found \n " ) ;
2011-01-21 13:59:30 +03:00
continue ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
out2 :
if ( debug = = 0x10 )
2011-03-01 09:56:33 +03:00
pr_info ( " Card found but it is not enabled, skipping \n " ) ;
2011-01-21 13:59:30 +03:00
continue ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_NET_POLL_CONTROLLER
2011-01-21 13:59:30 +03:00
static void tlan_poll ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
disable_irq ( dev - > irq ) ;
2011-01-21 13:59:30 +03:00
tlan_handle_interrupt ( dev - > irq , dev ) ;
2005-04-17 02:20:36 +04:00
enable_irq ( dev - > irq ) ;
}
# endif
2011-01-21 13:59:30 +03:00
static const struct net_device_ops tlan_netdev_ops = {
. ndo_open = tlan_open ,
. ndo_stop = tlan_close ,
. ndo_start_xmit = tlan_start_tx ,
. ndo_tx_timeout = tlan_tx_timeout ,
. ndo_get_stats = tlan_get_stats ,
. ndo_set_multicast_list = tlan_set_multicast_list ,
. ndo_do_ioctl = tlan_ioctl ,
2009-01-08 04:27:15 +03:00
. ndo_change_mtu = eth_change_mtu ,
2011-01-21 13:59:30 +03:00
. ndo_set_mac_address = eth_mac_addr ,
2009-01-08 04:27:15 +03:00
. ndo_validate_addr = eth_validate_addr ,
# ifdef CONFIG_NET_POLL_CONTROLLER
2011-01-21 13:59:30 +03:00
. ndo_poll_controller = tlan_poll ,
2009-01-08 04:27:15 +03:00
# endif
} ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_init
*
* Returns :
* 0 on success , error code otherwise .
* Parms :
* dev The structure of the device to be
* init ' ed .
*
* This function completes the initialization of the
* device structure and driver . It reserves the IO
* addresses , allocates memory for the lists and bounce
* buffers , retrieves the MAC address from the eeprom
* and assignes the device ' s methods .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int tlan_init ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
int dma_size ;
2011-01-21 13:59:30 +03:00
int err ;
2005-04-17 02:20:36 +04:00
int i ;
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv ;
2005-04-17 02:20:36 +04:00
priv = netdev_priv ( dev ) ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
* ( sizeof ( struct tlan_list ) ) ;
priv - > dma_storage = pci_alloc_consistent ( priv - > pci_dev ,
dma_size ,
& priv - > dma_storage_dma ) ;
priv - > dma_size = dma_size ;
if ( priv - > dma_storage = = NULL ) {
2011-03-01 09:56:33 +03:00
pr_err ( " Could not allocate lists and buffers for %s \n " ,
2011-01-21 13:59:30 +03:00
dev - > name ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
2011-01-21 13:59:30 +03:00
memset ( priv - > dma_storage , 0 , dma_size ) ;
priv - > rx_list = ( struct tlan_list * )
ALIGN ( ( unsigned long ) priv - > dma_storage , 8 ) ;
priv - > rx_list_dma = ALIGN ( priv - > dma_storage_dma , 8 ) ;
priv - > tx_list = priv - > rx_list + TLAN_NUM_RX_LISTS ;
priv - > tx_list_dma =
priv - > rx_list_dma + sizeof ( struct tlan_list ) * TLAN_NUM_RX_LISTS ;
2008-05-30 20:49:55 +04:00
2005-04-17 02:20:36 +04:00
err = 0 ;
2011-01-21 13:59:30 +03:00
for ( i = 0 ; i < 6 ; i + + )
err | = tlan_ee_read_byte ( dev ,
( u8 ) priv - > adapter - > addr_ofs + i ,
( u8 * ) & dev - > dev_addr [ i ] ) ;
if ( err ) {
2011-03-01 09:56:33 +03:00
pr_err ( " %s: Error reading MAC from eeprom: %d \n " ,
dev - > name , err ) ;
2005-04-17 02:20:36 +04:00
}
dev - > addr_len = 6 ;
netif_carrier_off ( dev ) ;
/* Device methods */
2011-01-21 13:59:30 +03:00
dev - > netdev_ops = & tlan_netdev_ops ;
2005-04-17 02:20:36 +04:00
dev - > watchdog_timeo = TX_TIMEOUT ;
return 0 ;
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_open
*
* Returns :
* 0 on success , error code otherwise .
* Parms :
* dev Structure of device to be opened .
*
* This routine puts the driver and TLAN adapter in a
* state where it is ready to send and receive packets .
* It allocates the IRQ , resets and brings the adapter
* out of reset , and allows interrupts . It also delays
* the startup for autonegotiation or sends a Rx GO
* command to the adapter , as appropriate .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static int tlan_open ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
int err ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
priv - > tlan_rev = tlan_dio_read8 ( dev - > base_addr , TLAN_DEF_REVISION ) ;
err = request_irq ( dev - > irq , tlan_handle_interrupt , IRQF_SHARED ,
dev - > name , dev ) ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
if ( err ) {
2011-03-01 09:56:33 +03:00
netdev_err ( dev , " Cannot open because IRQ %d is already in use \n " ,
dev - > irq ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
init_timer ( & priv - > timer ) ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:31 +03:00
tlan_start ( dev ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
TLAN_DBG ( TLAN_DEBUG_GNRL , " %s: Opened. TLAN Chip Rev: %x \n " ,
dev - > name , priv - > tlan_rev ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/**************************************************************
* tlan_ioctl
*
* Returns :
* 0 on success , error code otherwise
* Params :
* dev structure of device to receive ioctl .
*
* rq ifreq structure to hold userspace data .
*
* cmd ioctl command .
*
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static int tlan_ioctl ( struct net_device * dev , struct ifreq * rq , int cmd )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
struct mii_ioctl_data * data = if_mii ( rq ) ;
2011-01-21 13:59:30 +03:00
u32 phy = priv - > phy [ priv - > phy_num ] ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
if ( ! priv - > phy_online )
2005-04-17 02:20:36 +04:00
return - EAGAIN ;
2011-01-21 13:59:30 +03:00
switch ( cmd ) {
case SIOCGMIIPHY : /* get address of MII PHY in use. */
data - > phy_id = phy ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
case SIOCGMIIREG : /* read MII PHY register. */
tlan_mii_read_reg ( dev , data - > phy_id & 0x1f ,
data - > reg_num & 0x1f , & data - > val_out ) ;
return 0 ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
case SIOCSMIIREG : /* write MII PHY register. */
tlan_mii_write_reg ( dev , data - > phy_id & 0x1f ,
data - > reg_num & 0x1f , data - > val_in ) ;
return 0 ;
default :
return - EOPNOTSUPP ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_tx_timeout
*
* Returns : nothing
*
* Params :
* dev structure of device which timed out
* during transmit .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_tx_timeout ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
TLAN_DBG ( TLAN_DEBUG_GNRL , " %s: Transmit timed out. \n " , dev - > name ) ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
/* Ok so we timed out, lets see what we can do about it...*/
2011-01-21 13:59:30 +03:00
tlan_free_lists ( dev ) ;
tlan_reset_lists ( dev ) ;
tlan_read_and_clear_stats ( dev , TLAN_IGNORE ) ;
tlan_reset_adapter ( dev ) ;
2010-05-10 16:01:31 +04:00
dev - > trans_start = jiffies ; /* prevent tx timeout */
2011-01-21 13:59:30 +03:00
netif_wake_queue ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_tx_timeout_work
*
* Returns : nothing
*
* Params :
* work work item of device which timed out
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-11-22 17:57:56 +03:00
2011-01-21 13:59:30 +03:00
static void tlan_tx_timeout_work ( struct work_struct * work )
2006-11-22 17:57:56 +03:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv =
container_of ( work , struct tlan_priv , tlan_tqueue ) ;
2006-11-22 17:57:56 +03:00
2011-01-21 13:59:30 +03:00
tlan_tx_timeout ( priv - > dev ) ;
2006-11-22 17:57:56 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_start_tx
*
* Returns :
* 0 on success , non - zero on failure .
* Parms :
* skb A pointer to the sk_buff containing the
* frame to be sent .
* dev The device to send the data on .
*
* This function adds a frame to the Tx list to be sent
* ASAP . First it verifies that the adapter is ready and
* there is room in the queue . Then it sets up the next
* available list , copies the frame to the corresponding
* buffer . If the adapter Tx channel is idle , it gives
* the adapter a Tx Go command on the list , otherwise it
* sets the forward address of the previous list to point
* to this one . Then it frees the sk_buff .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static netdev_tx_t tlan_start_tx ( struct sk_buff * skb , struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
dma_addr_t tail_list_phys ;
2011-01-21 13:59:30 +03:00
struct tlan_list * tail_list ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2008-12-16 12:44:05 +03:00
unsigned int txlen ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
if ( ! priv - > phy_online ) {
TLAN_DBG ( TLAN_DEBUG_TX , " TRANSMIT: %s PHY is not ready \n " ,
dev - > name ) ;
2005-04-17 02:20:36 +04:00
dev_kfree_skb_any ( skb ) ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
2008-05-30 20:49:52 +04:00
if ( skb_padto ( skb , TLAN_MIN_FRAME_SIZE ) )
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2008-12-16 12:44:05 +03:00
txlen = max ( skb - > len , ( unsigned int ) TLAN_MIN_FRAME_SIZE ) ;
2008-05-30 20:49:52 +04:00
2011-01-21 13:59:30 +03:00
tail_list = priv - > tx_list + priv - > tx_tail ;
tail_list_phys =
priv - > tx_list_dma + sizeof ( struct tlan_list ) * priv - > tx_tail ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
if ( tail_list - > c_stat ! = TLAN_CSTAT_UNUSED ) {
TLAN_DBG ( TLAN_DEBUG_TX ,
" TRANSMIT: %s is busy (Head=%d Tail=%d) \n " ,
dev - > name , priv - > tx_head , priv - > tx_tail ) ;
2005-04-17 02:20:36 +04:00
netif_stop_queue ( dev ) ;
2011-01-21 13:59:30 +03:00
priv - > tx_busy_count + + ;
2009-06-12 10:22:29 +04:00
return NETDEV_TX_BUSY ;
2005-04-17 02:20:36 +04:00
}
tail_list - > forward = 0 ;
2011-01-21 13:59:30 +03:00
tail_list - > buffer [ 0 ] . address = pci_map_single ( priv - > pci_dev ,
2008-12-17 02:24:05 +03:00
skb - > data , txlen ,
PCI_DMA_TODEVICE ) ;
2011-01-21 13:59:30 +03:00
tlan_store_skb ( tail_list , skb ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
tail_list - > frame_size = ( u16 ) txlen ;
2008-12-16 12:44:05 +03:00
tail_list - > buffer [ 0 ] . count = TLAN_LAST_BUFFER | ( u32 ) txlen ;
2008-05-30 20:49:52 +04:00
tail_list - > buffer [ 1 ] . count = 0 ;
tail_list - > buffer [ 1 ] . address = 0 ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2011-01-21 13:59:30 +03:00
tail_list - > c_stat = TLAN_CSTAT_READY ;
if ( ! priv - > tx_in_progress ) {
priv - > tx_in_progress = 1 ;
TLAN_DBG ( TLAN_DEBUG_TX ,
" TRANSMIT: Starting TX on buffer %d \n " ,
priv - > tx_tail ) ;
outl ( tail_list_phys , dev - > base_addr + TLAN_CH_PARM ) ;
outl ( TLAN_HC_GO , dev - > base_addr + TLAN_HOST_CMD ) ;
2005-04-17 02:20:36 +04:00
} else {
2011-01-21 13:59:30 +03:00
TLAN_DBG ( TLAN_DEBUG_TX ,
" TRANSMIT: Adding buffer %d to TX channel \n " ,
priv - > tx_tail ) ;
if ( priv - > tx_tail = = 0 ) {
( priv - > tx_list + ( TLAN_NUM_TX_LISTS - 1 ) ) - > forward
2008-05-30 20:49:58 +04:00
= tail_list_phys ;
2005-04-17 02:20:36 +04:00
} else {
2011-01-21 13:59:30 +03:00
( priv - > tx_list + ( priv - > tx_tail - 1 ) ) - > forward
2008-05-30 20:49:58 +04:00
= tail_list_phys ;
2005-04-17 02:20:36 +04:00
}
}
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2011-01-21 13:59:30 +03:00
CIRC_INC ( priv - > tx_tail , TLAN_NUM_TX_LISTS ) ;
2005-04-17 02:20:36 +04:00
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_handle_interrupt
*
* Returns :
* Nothing
* Parms :
* irq The line on which the interrupt
* occurred .
* dev_id A pointer to the device assigned to
* this irq line .
*
* This function handles an interrupt generated by its
* assigned TLAN adapter . The function deactivates
* interrupts on its adapter , records the type of
* interrupt , executes the appropriate subhandler , and
* acknowdges the interrupt to the adapter ( thus
* re - enabling adapter interrupts .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static irqreturn_t tlan_handle_interrupt ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2008-05-30 20:49:57 +04:00
struct net_device * dev = dev_id ;
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
u16 host_int ;
2008-05-30 20:49:57 +04:00
u16 type ;
2005-04-17 02:20:36 +04:00
spin_lock ( & priv - > lock ) ;
2011-01-21 13:59:30 +03:00
host_int = inw ( dev - > base_addr + TLAN_HOST_INT ) ;
type = ( host_int & TLAN_HI_IT_MASK ) > > 2 ;
if ( type ) {
2008-05-30 20:49:57 +04:00
u32 ack ;
u32 host_cmd ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
outw ( host_int , dev - > base_addr + TLAN_HOST_INT ) ;
ack = tlan_int_vector [ type ] ( dev , host_int ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
if ( ack ) {
host_cmd = TLAN_HC_ACK | ack | ( type < < 18 ) ;
outl ( host_cmd , dev - > base_addr + TLAN_HOST_CMD ) ;
2008-05-30 20:49:57 +04:00
}
2005-04-17 02:20:36 +04:00
}
spin_unlock ( & priv - > lock ) ;
2008-05-30 20:49:57 +04:00
return IRQ_RETVAL ( type ) ;
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_close
*
* Returns :
* An error code .
* Parms :
* dev The device structure of the device to
* close .
*
* This function shuts down the adapter . It records any
* stats , puts the adapter into reset state , deactivates
* its time as needed , and frees the irq it is using .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static int tlan_close ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
priv - > neg_be_verbose = 0 ;
2011-01-21 13:59:31 +03:00
tlan_stop ( dev ) ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
free_irq ( dev - > irq , dev ) ;
tlan_free_lists ( dev ) ;
TLAN_DBG ( TLAN_DEBUG_GNRL , " Device %s closed. \n " , dev - > name ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_get_stats
*
* Returns :
* A pointer to the device ' s statistics structure .
* Parms :
* dev The device structure to return the
* stats for .
*
* This function updates the devices statistics by reading
* the TLAN chip ' s onboard registers . Then it returns the
* address of the statistics structure .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static struct net_device_stats * tlan_get_stats ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
int i ;
/* Should only read stats if open ? */
2011-01-21 13:59:30 +03:00
tlan_read_and_clear_stats ( dev , TLAN_RECORD ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
TLAN_DBG ( TLAN_DEBUG_RX , " RECEIVE: %s EOC count = %d \n " , dev - > name ,
priv - > rx_eoc_count ) ;
TLAN_DBG ( TLAN_DEBUG_TX , " TRANSMIT: %s Busy count = %d \n " , dev - > name ,
priv - > tx_busy_count ) ;
if ( debug & TLAN_DEBUG_GNRL ) {
tlan_print_dio ( dev - > base_addr ) ;
tlan_phy_print ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
if ( debug & TLAN_DEBUG_LIST ) {
for ( i = 0 ; i < TLAN_NUM_RX_LISTS ; i + + )
tlan_print_list ( priv - > rx_list + i , " RX " , i ) ;
for ( i = 0 ; i < TLAN_NUM_TX_LISTS ; i + + )
tlan_print_list ( priv - > tx_list + i , " TX " , i ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-13 21:24:59 +04:00
2008-05-30 20:49:53 +04:00
return & dev - > stats ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_set_multicast_list
*
* Returns :
* Nothing
* Parms :
* dev The device structure to set the
* multicast list for .
*
* This function sets the TLAN adaptor to various receive
* modes . If the IFF_PROMISC flag is set , promiscuous
* mode is acitviated . Otherwise , promiscuous mode is
* turned off . If the IFF_ALLMULTI flag is set , then
* the hash table is set to receive all group addresses .
* Otherwise , the first three multicast addresses are
* stored in AREG_1 - 3 , and the rest are selected via the
* hash table , as necessary .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_set_multicast_list ( struct net_device * dev )
2006-09-13 21:24:59 +04:00
{
2010-04-02 01:22:57 +04:00
struct netdev_hw_addr * ha ;
2005-04-17 02:20:36 +04:00
u32 hash1 = 0 ;
u32 hash2 = 0 ;
int i ;
u32 offset ;
u8 tmp ;
2011-01-21 13:59:30 +03:00
if ( dev - > flags & IFF_PROMISC ) {
tmp = tlan_dio_read8 ( dev - > base_addr , TLAN_NET_CMD ) ;
tlan_dio_write8 ( dev - > base_addr ,
TLAN_NET_CMD , tmp | TLAN_NET_CMD_CAF ) ;
2005-04-17 02:20:36 +04:00
} else {
2011-01-21 13:59:30 +03:00
tmp = tlan_dio_read8 ( dev - > base_addr , TLAN_NET_CMD ) ;
tlan_dio_write8 ( dev - > base_addr ,
TLAN_NET_CMD , tmp & ~ TLAN_NET_CMD_CAF ) ;
if ( dev - > flags & IFF_ALLMULTI ) {
for ( i = 0 ; i < 3 ; i + + )
tlan_set_mac ( dev , i + 1 , NULL ) ;
tlan_dio_write32 ( dev - > base_addr , TLAN_HASH_1 ,
0xffffffff ) ;
tlan_dio_write32 ( dev - > base_addr , TLAN_HASH_2 ,
0xffffffff ) ;
2005-04-17 02:20:36 +04:00
} else {
2010-02-24 02:17:07 +03:00
i = 0 ;
2010-04-02 01:22:57 +04:00
netdev_for_each_mc_addr ( ha , dev ) {
2011-01-21 13:59:30 +03:00
if ( i < 3 ) {
tlan_set_mac ( dev , i + 1 ,
2010-04-02 01:22:57 +04:00
( char * ) & ha - > addr ) ;
2005-04-17 02:20:36 +04:00
} else {
2011-01-21 13:59:30 +03:00
offset =
tlan_hash_func ( ( u8 * ) & ha - > addr ) ;
if ( offset < 32 )
hash1 | = ( 1 < < offset ) ;
2005-04-17 02:20:36 +04:00
else
2011-01-21 13:59:30 +03:00
hash2 | = ( 1 < < ( offset - 32 ) ) ;
2005-04-17 02:20:36 +04:00
}
2010-02-24 02:17:07 +03:00
i + + ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
for ( ; i < 3 ; i + + )
tlan_set_mac ( dev , i + 1 , NULL ) ;
tlan_dio_write32 ( dev - > base_addr , TLAN_HASH_1 , hash1 ) ;
tlan_dio_write32 ( dev - > base_addr , TLAN_HASH_2 , hash2 ) ;
2005-04-17 02:20:36 +04:00
}
}
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
/*****************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2011-01-21 13:59:30 +03:00
ThunderLAN driver interrupt vectors and table
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
please see chap . 4 , " Interrupt Handling " of the " ThunderLAN
Programmer ' s Guide " for more informations on handling interrupts
generated by TLAN based adapters .
2005-04-17 02:20:36 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_handle_tx_eof
*
* Returns :
* 1
* Parms :
* dev Device assigned the IRQ that was
* raised .
* host_int The contents of the HOST_INT
* port .
*
* This function handles Tx EOF interrupts which are raised
* by the adapter when it has completed sending the
* contents of a buffer . If detemines which list / buffer
* was completed and resets it . If the buffer was the last
* in the channel ( EOC ) , then the function checks to see if
* another buffer is ready to send , and if so , sends a Tx
* Go command . Finally , the driver activates / continues the
* activity LED .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static u32 tlan_handle_tx_eof ( struct net_device * dev , u16 host_int )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
int eoc = 0 ;
2011-01-21 13:59:30 +03:00
struct tlan_list * head_list ;
2005-04-17 02:20:36 +04:00
dma_addr_t head_list_phys ;
u32 ack = 0 ;
2011-01-21 13:59:30 +03:00
u16 tmp_c_stat ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
TLAN_DBG ( TLAN_DEBUG_TX ,
" TRANSMIT: Handling TX EOF (Head=%d Tail=%d) \n " ,
priv - > tx_head , priv - > tx_tail ) ;
head_list = priv - > tx_list + priv - > tx_head ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
while ( ( ( tmp_c_stat = head_list - > c_stat ) & TLAN_CSTAT_FRM_CMP )
& & ( ack < 255 ) ) {
struct sk_buff * skb = tlan_get_skb ( head_list ) ;
2008-12-17 02:24:05 +03:00
2005-04-17 02:20:36 +04:00
ack + + ;
2011-01-21 13:59:30 +03:00
pci_unmap_single ( priv - > pci_dev , head_list - > buffer [ 0 ] . address ,
2008-12-17 02:24:05 +03:00
max ( skb - > len ,
( unsigned int ) TLAN_MIN_FRAME_SIZE ) ,
PCI_DMA_TODEVICE ) ;
dev_kfree_skb_any ( skb ) ;
head_list - > buffer [ 8 ] . address = 0 ;
head_list - > buffer [ 9 ] . address = 0 ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
if ( tmp_c_stat & TLAN_CSTAT_EOC )
2005-04-17 02:20:36 +04:00
eoc = 1 ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
dev - > stats . tx_bytes + = head_list - > frame_size ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
head_list - > c_stat = TLAN_CSTAT_UNUSED ;
2006-09-13 21:24:59 +04:00
netif_start_queue ( dev ) ;
2011-01-21 13:59:30 +03:00
CIRC_INC ( priv - > tx_head , TLAN_NUM_TX_LISTS ) ;
head_list = priv - > tx_list + priv - > tx_head ;
2005-04-17 02:20:36 +04:00
}
if ( ! ack )
2011-03-01 09:56:33 +03:00
netdev_info ( dev ,
" Received interrupt for uncompleted TX frame \n " ) ;
2011-01-21 13:59:30 +03:00
if ( eoc ) {
TLAN_DBG ( TLAN_DEBUG_TX ,
" TRANSMIT: handling TX EOC (Head=%d Tail=%d) \n " ,
priv - > tx_head , priv - > tx_tail ) ;
head_list = priv - > tx_list + priv - > tx_head ;
head_list_phys = priv - > tx_list_dma
+ sizeof ( struct tlan_list ) * priv - > tx_head ;
2011-02-09 13:25:06 +03:00
if ( ( head_list - > c_stat & TLAN_CSTAT_READY )
= = TLAN_CSTAT_READY ) {
2011-01-21 13:59:30 +03:00
outl ( head_list_phys , dev - > base_addr + TLAN_CH_PARM ) ;
2005-04-17 02:20:36 +04:00
ack | = TLAN_HC_GO ;
} else {
2011-01-21 13:59:30 +03:00
priv - > tx_in_progress = 0 ;
2005-04-17 02:20:36 +04:00
}
}
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
if ( priv - > adapter - > flags & TLAN_ADAPTER_ACTIVITY_LED ) {
tlan_dio_write8 ( dev - > base_addr ,
TLAN_LED_REG , TLAN_LED_LINK | TLAN_LED_ACT ) ;
if ( priv - > timer . function = = NULL ) {
priv - > timer . function = tlan_timer ;
priv - > timer . data = ( unsigned long ) dev ;
priv - > timer . expires = jiffies + TLAN_TIMER_ACT_DELAY ;
priv - > timer_set_at = jiffies ;
priv - > timer_type = TLAN_TIMER_ACTIVITY ;
add_timer ( & priv - > timer ) ;
} else if ( priv - > timer_type = = TLAN_TIMER_ACTIVITY ) {
priv - > timer_set_at = jiffies ;
2005-04-17 02:20:36 +04:00
}
}
return ack ;
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* TLan_HandleStatOverflow
*
* Returns :
* 1
* Parms :
* dev Device assigned the IRQ that was
* raised .
* host_int The contents of the HOST_INT
* port .
*
* This function handles the Statistics Overflow interrupt
* which means that one or more of the TLAN statistics
* registers has reached 1 / 2 capacity and needs to be read .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static u32 tlan_handle_stat_overflow ( struct net_device * dev , u16 host_int )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
tlan_read_and_clear_stats ( dev , TLAN_RECORD ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
2011-01-21 13:59:30 +03:00
}
/***************************************************************
* TLan_HandleRxEOF
*
* Returns :
* 1
* Parms :
* dev Device assigned the IRQ that was
* raised .
* host_int The contents of the HOST_INT
* port .
*
* This function handles the Rx EOF interrupt which
* indicates a frame has been received by the adapter from
* the net and the frame has been transferred to memory .
* The function determines the bounce buffer the frame has
* been loaded into , creates a new sk_buff big enough to
* hold the frame , and sends it to protocol stack . It
* then resets the used buffer and appends it to the end
* of the list . If the frame was the last in the Rx
* channel ( EOC ) , the function restarts the receive channel
* by sending an Rx Go command to the adapter . Then it
* activates / continues the activity LED .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static u32 tlan_handle_rx_eof ( struct net_device * dev , u16 host_int )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
u32 ack = 0 ;
int eoc = 0 ;
2011-01-21 13:59:30 +03:00
struct tlan_list * head_list ;
2005-04-17 02:20:36 +04:00
struct sk_buff * skb ;
2011-01-21 13:59:30 +03:00
struct tlan_list * tail_list ;
u16 tmp_c_stat ;
2005-04-17 02:20:36 +04:00
dma_addr_t head_list_phys ;
2011-01-21 13:59:30 +03:00
TLAN_DBG ( TLAN_DEBUG_RX , " RECEIVE: handling RX EOF (Head=%d Tail=%d) \n " ,
priv - > rx_head , priv - > rx_tail ) ;
head_list = priv - > rx_list + priv - > rx_head ;
head_list_phys =
priv - > rx_list_dma + sizeof ( struct tlan_list ) * priv - > rx_head ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
while ( ( ( tmp_c_stat = head_list - > c_stat ) & TLAN_CSTAT_FRM_CMP )
& & ( ack < 255 ) ) {
dma_addr_t frame_dma = head_list - > buffer [ 0 ] . address ;
u32 frame_size = head_list - > frame_size ;
2008-12-17 02:24:05 +03:00
struct sk_buff * new_skb ;
2005-04-17 02:20:36 +04:00
ack + + ;
2011-01-21 13:59:30 +03:00
if ( tmp_c_stat & TLAN_CSTAT_EOC )
2005-04-17 02:20:36 +04:00
eoc = 1 ;
2006-09-13 21:24:59 +04:00
2009-10-13 09:34:20 +04:00
new_skb = netdev_alloc_skb_ip_align ( dev ,
TLAN_MAX_FRAME_SIZE + 5 ) ;
2011-01-21 13:59:30 +03:00
if ( ! new_skb )
2008-12-17 02:24:05 +03:00
goto drop_and_reuse ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
skb = tlan_get_skb ( head_list ) ;
pci_unmap_single ( priv - > pci_dev , frame_dma ,
2008-12-17 02:24:05 +03:00
TLAN_MAX_FRAME_SIZE , PCI_DMA_FROMDEVICE ) ;
2011-01-21 13:59:30 +03:00
skb_put ( skb , frame_size ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
dev - > stats . rx_bytes + = frame_size ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
skb - > protocol = eth_type_trans ( skb , dev ) ;
netif_rx ( skb ) ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
head_list - > buffer [ 0 ] . address =
pci_map_single ( priv - > pci_dev , new_skb - > data ,
TLAN_MAX_FRAME_SIZE , PCI_DMA_FROMDEVICE ) ;
2008-05-30 20:49:55 +04:00
2011-01-21 13:59:30 +03:00
tlan_store_skb ( head_list , new_skb ) ;
2008-05-30 20:49:58 +04:00
drop_and_reuse :
2005-04-17 02:20:36 +04:00
head_list - > forward = 0 ;
2011-01-21 13:59:30 +03:00
head_list - > c_stat = 0 ;
tail_list = priv - > rx_list + priv - > rx_tail ;
2005-04-17 02:20:36 +04:00
tail_list - > forward = head_list_phys ;
2011-01-21 13:59:30 +03:00
CIRC_INC ( priv - > rx_head , TLAN_NUM_RX_LISTS ) ;
CIRC_INC ( priv - > rx_tail , TLAN_NUM_RX_LISTS ) ;
head_list = priv - > rx_list + priv - > rx_head ;
head_list_phys = priv - > rx_list_dma
+ sizeof ( struct tlan_list ) * priv - > rx_head ;
2005-04-17 02:20:36 +04:00
}
if ( ! ack )
2011-03-01 09:56:33 +03:00
netdev_info ( dev ,
" Received interrupt for uncompleted RX frame \n " ) ;
2011-01-21 13:59:30 +03:00
if ( eoc ) {
TLAN_DBG ( TLAN_DEBUG_RX ,
" RECEIVE: handling RX EOC (Head=%d Tail=%d) \n " ,
priv - > rx_head , priv - > rx_tail ) ;
head_list = priv - > rx_list + priv - > rx_head ;
head_list_phys = priv - > rx_list_dma
+ sizeof ( struct tlan_list ) * priv - > rx_head ;
outl ( head_list_phys , dev - > base_addr + TLAN_CH_PARM ) ;
2005-04-17 02:20:36 +04:00
ack | = TLAN_HC_GO | TLAN_HC_RT ;
2011-01-21 13:59:30 +03:00
priv - > rx_eoc_count + + ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
if ( priv - > adapter - > flags & TLAN_ADAPTER_ACTIVITY_LED ) {
tlan_dio_write8 ( dev - > base_addr ,
TLAN_LED_REG , TLAN_LED_LINK | TLAN_LED_ACT ) ;
if ( priv - > timer . function = = NULL ) {
priv - > timer . function = tlan_timer ;
2005-04-17 02:20:36 +04:00
priv - > timer . data = ( unsigned long ) dev ;
priv - > timer . expires = jiffies + TLAN_TIMER_ACT_DELAY ;
2011-01-21 13:59:30 +03:00
priv - > timer_set_at = jiffies ;
priv - > timer_type = TLAN_TIMER_ACTIVITY ;
2005-04-17 02:20:36 +04:00
add_timer ( & priv - > timer ) ;
2011-01-21 13:59:30 +03:00
} else if ( priv - > timer_type = = TLAN_TIMER_ACTIVITY ) {
priv - > timer_set_at = jiffies ;
2005-04-17 02:20:36 +04:00
}
}
return ack ;
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_handle_dummy
*
* Returns :
* 1
* Parms :
* dev Device assigned the IRQ that was
* raised .
* host_int The contents of the HOST_INT
* port .
*
* This function handles the Dummy interrupt , which is
* raised whenever a test interrupt is generated by setting
* the Req_Int bit of HOST_CMD to 1.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static u32 tlan_handle_dummy ( struct net_device * dev , u16 host_int )
2005-04-17 02:20:36 +04:00
{
2011-03-01 09:56:33 +03:00
netdev_info ( dev , " Test interrupt \n " ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_handle_tx_eoc
*
* Returns :
* 1
* Parms :
* dev Device assigned the IRQ that was
* raised .
* host_int The contents of the HOST_INT
* port .
*
* This driver is structured to determine EOC occurrences by
* reading the CSTAT member of the list structure . Tx EOC
* interrupts are disabled via the DIO INTDIS register .
* However , TLAN chips before revision 3.0 didn ' t have this
* functionality , so process EOC events if this is the
* case .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static u32 tlan_handle_tx_eoc ( struct net_device * dev , u16 host_int )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
struct tlan_list * head_list ;
2005-04-17 02:20:36 +04:00
dma_addr_t head_list_phys ;
u32 ack = 1 ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
host_int = 0 ;
2011-01-21 13:59:30 +03:00
if ( priv - > tlan_rev < 0x30 ) {
TLAN_DBG ( TLAN_DEBUG_TX ,
" TRANSMIT: handling TX EOC (Head=%d Tail=%d) -- IRQ \n " ,
priv - > tx_head , priv - > tx_tail ) ;
head_list = priv - > tx_list + priv - > tx_head ;
head_list_phys = priv - > tx_list_dma
+ sizeof ( struct tlan_list ) * priv - > tx_head ;
2011-02-09 13:25:06 +03:00
if ( ( head_list - > c_stat & TLAN_CSTAT_READY )
= = TLAN_CSTAT_READY ) {
2005-04-17 02:20:36 +04:00
netif_stop_queue ( dev ) ;
2011-01-21 13:59:30 +03:00
outl ( head_list_phys , dev - > base_addr + TLAN_CH_PARM ) ;
2005-04-17 02:20:36 +04:00
ack | = TLAN_HC_GO ;
} else {
2011-01-21 13:59:30 +03:00
priv - > tx_in_progress = 0 ;
2005-04-17 02:20:36 +04:00
}
}
return ack ;
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_handle_status_check
*
* Returns :
* 0 if Adapter check , 1 if Network Status check .
* Parms :
* dev Device assigned the IRQ that was
* raised .
* host_int The contents of the HOST_INT
* port .
*
* This function handles Adapter Check / Network Status
* interrupts generated by the adapter . It checks the
* vector in the HOST_INT register to determine if it is
* an Adapter Check interrupt . If so , it resets the
* adapter . Otherwise it clears the status registers
* and services the PHY .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static u32 tlan_handle_status_check ( struct net_device * dev , u16 host_int )
2006-09-13 21:24:59 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
u32 ack ;
u32 error ;
u8 net_sts ;
u32 phy ;
u16 tlphy_ctl ;
u16 tlphy_sts ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
ack = 1 ;
2011-01-21 13:59:30 +03:00
if ( host_int & TLAN_HI_IV_MASK ) {
netif_stop_queue ( dev ) ;
error = inl ( dev - > base_addr + TLAN_CH_PARM ) ;
2011-03-01 09:56:33 +03:00
netdev_info ( dev , " Adaptor Error = 0x%x \n " , error ) ;
2011-01-21 13:59:30 +03:00
tlan_read_and_clear_stats ( dev , TLAN_RECORD ) ;
outl ( TLAN_HC_AD_RST , dev - > base_addr + TLAN_HOST_CMD ) ;
2005-04-17 02:20:36 +04:00
schedule_work ( & priv - > tlan_tqueue ) ;
netif_wake_queue ( dev ) ;
ack = 0 ;
} else {
2011-01-21 13:59:30 +03:00
TLAN_DBG ( TLAN_DEBUG_GNRL , " %s: Status Check \n " , dev - > name ) ;
phy = priv - > phy [ priv - > phy_num ] ;
net_sts = tlan_dio_read8 ( dev - > base_addr , TLAN_NET_STS ) ;
if ( net_sts ) {
tlan_dio_write8 ( dev - > base_addr , TLAN_NET_STS , net_sts ) ;
TLAN_DBG ( TLAN_DEBUG_GNRL , " %s: Net_Sts = %x \n " ,
dev - > name , ( unsigned ) net_sts ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
if ( ( net_sts & TLAN_NET_STS_MIRQ ) & & ( priv - > phy_num = = 0 ) ) {
tlan_mii_read_reg ( dev , phy , TLAN_TLPHY_STS , & tlphy_sts ) ;
tlan_mii_read_reg ( dev , phy , TLAN_TLPHY_CTL , & tlphy_ctl ) ;
if ( ! ( tlphy_sts & TLAN_TS_POLOK ) & &
! ( tlphy_ctl & TLAN_TC_SWAPOL ) ) {
tlphy_ctl | = TLAN_TC_SWAPOL ;
tlan_mii_write_reg ( dev , phy , TLAN_TLPHY_CTL ,
tlphy_ctl ) ;
} else if ( ( tlphy_sts & TLAN_TS_POLOK ) & &
( tlphy_ctl & TLAN_TC_SWAPOL ) ) {
tlphy_ctl & = ~ TLAN_TC_SWAPOL ;
tlan_mii_write_reg ( dev , phy , TLAN_TLPHY_CTL ,
tlphy_ctl ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
if ( debug )
tlan_phy_print ( dev ) ;
2005-04-17 02:20:36 +04:00
}
}
return ack ;
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_handle_rx_eoc
*
* Returns :
* 1
* Parms :
* dev Device assigned the IRQ that was
* raised .
* host_int The contents of the HOST_INT
* port .
*
* This driver is structured to determine EOC occurrences by
* reading the CSTAT member of the list structure . Rx EOC
* interrupts are disabled via the DIO INTDIS register .
* However , TLAN chips before revision 3.0 didn ' t have this
* CSTAT member or a INTDIS register , so if this chip is
* pre - 3.0 , process EOC interrupts normally .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static u32 tlan_handle_rx_eoc ( struct net_device * dev , u16 host_int )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
dma_addr_t head_list_phys ;
u32 ack = 1 ;
2011-01-21 13:59:30 +03:00
if ( priv - > tlan_rev < 0x30 ) {
TLAN_DBG ( TLAN_DEBUG_RX ,
" RECEIVE: Handling RX EOC (head=%d tail=%d) -- IRQ \n " ,
priv - > rx_head , priv - > rx_tail ) ;
head_list_phys = priv - > rx_list_dma
+ sizeof ( struct tlan_list ) * priv - > rx_head ;
outl ( head_list_phys , dev - > base_addr + TLAN_CH_PARM ) ;
2005-04-17 02:20:36 +04:00
ack | = TLAN_HC_GO | TLAN_HC_RT ;
2011-01-21 13:59:30 +03:00
priv - > rx_eoc_count + + ;
2005-04-17 02:20:36 +04:00
}
return ack ;
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
/*****************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2011-01-21 13:59:30 +03:00
ThunderLAN driver timer function
2005-04-17 02:20:36 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_timer
*
* Returns :
* Nothing
* Parms :
* data A value given to add timer when
* add_timer was called .
*
* This function handles timed functionality for the
* TLAN driver . The two current timer uses are for
* delaying for autonegotionation and driving the ACT LED .
* - Autonegotiation requires being allowed about
* 2 1 / 2 seconds before attempting to transmit a
* packet . It would be a very bad thing to hang
* the kernel this long , so the driver doesn ' t
* allow transmission ' til after this time , for
* certain PHYs . It would be much nicer if all
* PHYs were interrupt - capable like the internal
* PHY .
* - The ACT LED , which shows adapter activity , is
* driven by the driver , and so must be left on
* for a short period to power up the LED so it
* can be seen . This delay can be changed by
* changing the TLAN_TIMER_ACT_DELAY in tlan . h ,
* if desired . 100 ms produces a slightly
* sluggish response .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void tlan_timer ( unsigned long data )
2005-04-17 02:20:36 +04:00
{
struct net_device * dev = ( struct net_device * ) data ;
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
u32 elapsed ;
unsigned long flags = 0 ;
priv - > timer . function = NULL ;
2011-01-21 13:59:30 +03:00
switch ( priv - > timer_type ) {
2006-09-13 21:24:59 +04:00
# ifdef MONITOR
2011-01-21 13:59:30 +03:00
case TLAN_TIMER_LINK_BEAT :
tlan_phy_monitor ( dev ) ;
break ;
2005-04-17 02:20:36 +04:00
# endif
2011-01-21 13:59:30 +03:00
case TLAN_TIMER_PHY_PDOWN :
tlan_phy_power_down ( dev ) ;
break ;
case TLAN_TIMER_PHY_PUP :
tlan_phy_power_up ( dev ) ;
break ;
case TLAN_TIMER_PHY_RESET :
tlan_phy_reset ( dev ) ;
break ;
case TLAN_TIMER_PHY_START_LINK :
tlan_phy_start_link ( dev ) ;
break ;
case TLAN_TIMER_PHY_FINISH_AN :
tlan_phy_finish_auto_neg ( dev ) ;
break ;
case TLAN_TIMER_FINISH_RESET :
tlan_finish_reset ( dev ) ;
break ;
case TLAN_TIMER_ACTIVITY :
spin_lock_irqsave ( & priv - > lock , flags ) ;
if ( priv - > timer . function = = NULL ) {
elapsed = jiffies - priv - > timer_set_at ;
if ( elapsed > = TLAN_TIMER_ACT_DELAY ) {
tlan_dio_write8 ( dev - > base_addr ,
TLAN_LED_REG , TLAN_LED_LINK ) ;
} else {
priv - > timer . function = tlan_timer ;
priv - > timer . expires = priv - > timer_set_at
+ TLAN_TIMER_ACT_DELAY ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
add_timer ( & priv - > timer ) ;
break ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
}
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
break ;
default :
break ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
/*****************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2011-01-21 13:59:30 +03:00
ThunderLAN driver adapter related routines
2005-04-17 02:20:36 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_reset_lists
*
* Returns :
* Nothing
* Parms :
* dev The device structure with the list
* stuctures to be reset .
*
* This routine sets the variables associated with managing
* the TLAN lists to their initial values .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void tlan_reset_lists ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
int i ;
2011-01-21 13:59:30 +03:00
struct tlan_list * list ;
2005-04-17 02:20:36 +04:00
dma_addr_t list_phys ;
struct sk_buff * skb ;
2011-01-21 13:59:30 +03:00
priv - > tx_head = 0 ;
priv - > tx_tail = 0 ;
for ( i = 0 ; i < TLAN_NUM_TX_LISTS ; i + + ) {
list = priv - > tx_list + i ;
list - > c_stat = TLAN_CSTAT_UNUSED ;
2008-12-17 02:24:05 +03:00
list - > buffer [ 0 ] . address = 0 ;
2005-04-17 02:20:36 +04:00
list - > buffer [ 2 ] . count = 0 ;
list - > buffer [ 2 ] . address = 0 ;
list - > buffer [ 8 ] . address = 0 ;
list - > buffer [ 9 ] . address = 0 ;
}
2011-01-21 13:59:30 +03:00
priv - > rx_head = 0 ;
priv - > rx_tail = TLAN_NUM_RX_LISTS - 1 ;
for ( i = 0 ; i < TLAN_NUM_RX_LISTS ; i + + ) {
list = priv - > rx_list + i ;
list_phys = priv - > rx_list_dma + sizeof ( struct tlan_list ) * i ;
list - > c_stat = TLAN_CSTAT_READY ;
list - > frame_size = TLAN_MAX_FRAME_SIZE ;
2005-04-17 02:20:36 +04:00
list - > buffer [ 0 ] . count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER ;
2009-10-13 09:34:20 +04:00
skb = netdev_alloc_skb_ip_align ( dev , TLAN_MAX_FRAME_SIZE + 5 ) ;
2011-01-21 13:59:30 +03:00
if ( ! skb ) {
2011-03-01 09:56:33 +03:00
netdev_err ( dev , " Out of memory for received data \n " ) ;
2008-12-17 02:24:05 +03:00
break ;
2005-04-17 02:20:36 +04:00
}
2008-12-17 02:24:05 +03:00
2011-01-21 13:59:30 +03:00
list - > buffer [ 0 ] . address = pci_map_single ( priv - > pci_dev ,
2008-12-17 02:24:05 +03:00
skb - > data ,
TLAN_MAX_FRAME_SIZE ,
PCI_DMA_FROMDEVICE ) ;
2011-01-21 13:59:30 +03:00
tlan_store_skb ( list , skb ) ;
2005-04-17 02:20:36 +04:00
list - > buffer [ 1 ] . count = 0 ;
list - > buffer [ 1 ] . address = 0 ;
2011-01-21 13:59:30 +03:00
list - > forward = list_phys + sizeof ( struct tlan_list ) ;
2008-05-30 20:49:56 +04:00
}
/* in case ran out of memory early, clear bits */
while ( i < TLAN_NUM_RX_LISTS ) {
2011-01-21 13:59:30 +03:00
tlan_store_skb ( priv - > rx_list + i , NULL ) ;
2008-05-30 20:49:56 +04:00
+ + i ;
2005-04-17 02:20:36 +04:00
}
2008-05-30 20:49:56 +04:00
list - > forward = 0 ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_free_lists ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
int i ;
2011-01-21 13:59:30 +03:00
struct tlan_list * list ;
2005-04-17 02:20:36 +04:00
struct sk_buff * skb ;
2011-01-21 13:59:30 +03:00
for ( i = 0 ; i < TLAN_NUM_TX_LISTS ; i + + ) {
list = priv - > tx_list + i ;
skb = tlan_get_skb ( list ) ;
if ( skb ) {
2008-12-17 02:24:05 +03:00
pci_unmap_single (
2011-01-21 13:59:30 +03:00
priv - > pci_dev ,
2008-12-17 02:24:05 +03:00
list - > buffer [ 0 ] . address ,
max ( skb - > len ,
( unsigned int ) TLAN_MIN_FRAME_SIZE ) ,
PCI_DMA_TODEVICE ) ;
2011-01-21 13:59:30 +03:00
dev_kfree_skb_any ( skb ) ;
2008-12-17 02:24:05 +03:00
list - > buffer [ 8 ] . address = 0 ;
list - > buffer [ 9 ] . address = 0 ;
2005-04-17 02:20:36 +04:00
}
2008-12-17 02:24:05 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
for ( i = 0 ; i < TLAN_NUM_RX_LISTS ; i + + ) {
list = priv - > rx_list + i ;
skb = tlan_get_skb ( list ) ;
if ( skb ) {
pci_unmap_single ( priv - > pci_dev ,
2008-12-17 02:24:05 +03:00
list - > buffer [ 0 ] . address ,
TLAN_MAX_FRAME_SIZE ,
PCI_DMA_FROMDEVICE ) ;
2011-01-21 13:59:30 +03:00
dev_kfree_skb_any ( skb ) ;
2008-12-17 02:24:05 +03:00
list - > buffer [ 8 ] . address = 0 ;
list - > buffer [ 9 ] . address = 0 ;
2005-04-17 02:20:36 +04:00
}
}
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_print_dio
*
* Returns :
* Nothing
* Parms :
* io_base Base IO port of the device of
* which to print DIO registers .
*
* This function prints out all the internal ( DIO )
* registers of a TLAN chip .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_print_dio ( u16 io_base )
2005-04-17 02:20:36 +04:00
{
u32 data0 , data1 ;
int i ;
2011-03-01 09:56:33 +03:00
pr_info ( " Contents of internal registers for io base 0x%04hx \n " ,
io_base ) ;
pr_info ( " Off. +0 +4 \n " ) ;
2011-01-21 13:59:30 +03:00
for ( i = 0 ; i < 0x4C ; i + = 8 ) {
data0 = tlan_dio_read32 ( io_base , i ) ;
data1 = tlan_dio_read32 ( io_base , i + 0x4 ) ;
2011-03-01 09:56:33 +03:00
pr_info ( " 0x%02x 0x%08x 0x%08x \n " , i , data0 , data1 ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* TLan_PrintList
*
* Returns :
* Nothing
* Parms :
* list A pointer to the struct tlan_list structure to
* be printed .
* type A string to designate type of list ,
* " Rx " or " Tx " .
* num The index of the list .
*
* This function prints out the contents of the list
* pointed to by the list parameter .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_print_list ( struct tlan_list * list , char * type , int num )
2005-04-17 02:20:36 +04:00
{
int i ;
2011-03-01 09:56:33 +03:00
pr_info ( " %s List %d at %p \n " , type , num , list ) ;
pr_info ( " Forward = 0x%08x \n " , list - > forward ) ;
pr_info ( " CSTAT = 0x%04hx \n " , list - > c_stat ) ;
pr_info ( " Frame Size = 0x%04hx \n " , list - > frame_size ) ;
2011-01-21 13:59:30 +03:00
/* for (i = 0; i < 10; i++) { */
for ( i = 0 ; i < 2 ; i + + ) {
2011-03-01 09:56:33 +03:00
pr_info ( " Buffer[%d].count, addr = 0x%08x, 0x%08x \n " ,
i , list - > buffer [ i ] . count , list - > buffer [ i ] . address ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_read_and_clear_stats
*
* Returns :
* Nothing
* Parms :
* dev Pointer to device structure of adapter
* to which to read stats .
* record Flag indicating whether to add
*
* This functions reads all the internal status registers
* of the TLAN chip , which clears them as a side effect .
* It then either adds the values to the device ' s status
* struct , or discards them , depending on whether record
* is TLAN_RECORD ( ! = 0 ) or TLAN_IGNORE ( = = 0 ) .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_read_and_clear_stats ( struct net_device * dev , int record )
2005-04-17 02:20:36 +04:00
{
u32 tx_good , tx_under ;
u32 rx_good , rx_over ;
u32 def_tx , crc , code ;
u32 multi_col , single_col ;
u32 excess_col , late_col , loss ;
2011-01-21 13:59:30 +03:00
outw ( TLAN_GOOD_TX_FRMS , dev - > base_addr + TLAN_DIO_ADR ) ;
tx_good = inb ( dev - > base_addr + TLAN_DIO_DATA ) ;
tx_good + = inb ( dev - > base_addr + TLAN_DIO_DATA + 1 ) < < 8 ;
tx_good + = inb ( dev - > base_addr + TLAN_DIO_DATA + 2 ) < < 16 ;
tx_under = inb ( dev - > base_addr + TLAN_DIO_DATA + 3 ) ;
outw ( TLAN_GOOD_RX_FRMS , dev - > base_addr + TLAN_DIO_ADR ) ;
rx_good = inb ( dev - > base_addr + TLAN_DIO_DATA ) ;
rx_good + = inb ( dev - > base_addr + TLAN_DIO_DATA + 1 ) < < 8 ;
rx_good + = inb ( dev - > base_addr + TLAN_DIO_DATA + 2 ) < < 16 ;
rx_over = inb ( dev - > base_addr + TLAN_DIO_DATA + 3 ) ;
outw ( TLAN_DEFERRED_TX , dev - > base_addr + TLAN_DIO_ADR ) ;
def_tx = inb ( dev - > base_addr + TLAN_DIO_DATA ) ;
def_tx + = inb ( dev - > base_addr + TLAN_DIO_DATA + 1 ) < < 8 ;
crc = inb ( dev - > base_addr + TLAN_DIO_DATA + 2 ) ;
code = inb ( dev - > base_addr + TLAN_DIO_DATA + 3 ) ;
outw ( TLAN_MULTICOL_FRMS , dev - > base_addr + TLAN_DIO_ADR ) ;
multi_col = inb ( dev - > base_addr + TLAN_DIO_DATA ) ;
multi_col + = inb ( dev - > base_addr + TLAN_DIO_DATA + 1 ) < < 8 ;
single_col = inb ( dev - > base_addr + TLAN_DIO_DATA + 2 ) ;
single_col + = inb ( dev - > base_addr + TLAN_DIO_DATA + 3 ) < < 8 ;
outw ( TLAN_EXCESSCOL_FRMS , dev - > base_addr + TLAN_DIO_ADR ) ;
excess_col = inb ( dev - > base_addr + TLAN_DIO_DATA ) ;
late_col = inb ( dev - > base_addr + TLAN_DIO_DATA + 1 ) ;
loss = inb ( dev - > base_addr + TLAN_DIO_DATA + 2 ) ;
if ( record ) {
2008-05-30 20:49:53 +04:00
dev - > stats . rx_packets + = rx_good ;
dev - > stats . rx_errors + = rx_over + crc + code ;
dev - > stats . tx_packets + = tx_good ;
dev - > stats . tx_errors + = tx_under + loss ;
2011-01-21 13:59:30 +03:00
dev - > stats . collisions + = multi_col
+ single_col + excess_col + late_col ;
2008-05-30 20:49:53 +04:00
dev - > stats . rx_over_errors + = rx_over ;
dev - > stats . rx_crc_errors + = crc ;
dev - > stats . rx_frame_errors + = code ;
dev - > stats . tx_aborted_errors + = tx_under ;
dev - > stats . tx_carrier_errors + = loss ;
2005-04-17 02:20:36 +04:00
}
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* TLan_Reset
*
* Returns :
* 0
* Parms :
* dev Pointer to device structure of adapter
* to be reset .
*
* This function resets the adapter and it ' s physical
* device . See Chap . 3 , pp . 9 - 10 of the " ThunderLAN
* Programmer ' s Guide " for details. The routine tries to
* implement what is detailed there , though adjustments
* have been made .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2008-02-18 21:04:38 +03:00
static void
2011-01-21 13:59:30 +03:00
tlan_reset_adapter ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
int i ;
u32 addr ;
u32 data ;
u8 data8 ;
2011-01-21 13:59:30 +03:00
priv - > tlan_full_duplex = false ;
priv - > phy_online = 0 ;
2005-04-17 02:20:36 +04:00
netif_carrier_off ( dev ) ;
/* 1. Assert reset bit. */
data = inl ( dev - > base_addr + TLAN_HOST_CMD ) ;
data | = TLAN_HC_AD_RST ;
outl ( data , dev - > base_addr + TLAN_HOST_CMD ) ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
udelay ( 1000 ) ;
2011-01-21 13:59:30 +03:00
/* 2. Turn off interrupts. (Probably isn't necessary) */
2005-04-17 02:20:36 +04:00
data = inl ( dev - > base_addr + TLAN_HOST_CMD ) ;
data | = TLAN_HC_INT_OFF ;
outl ( data , dev - > base_addr + TLAN_HOST_CMD ) ;
/* 3. Clear AREGs and HASHs. */
2011-01-21 13:59:30 +03:00
for ( i = TLAN_AREG_0 ; i < = TLAN_HASH_2 ; i + = 4 )
tlan_dio_write32 ( dev - > base_addr , ( u16 ) i , 0 ) ;
2005-04-17 02:20:36 +04:00
/* 4. Setup NetConfig register. */
data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN ;
2011-01-21 13:59:30 +03:00
tlan_dio_write16 ( dev - > base_addr , TLAN_NET_CONFIG , ( u16 ) data ) ;
2005-04-17 02:20:36 +04:00
/* 5. Load Ld_Tmr and Ld_Thr in HOST_CMD. */
2011-01-21 13:59:30 +03:00
outl ( TLAN_HC_LD_TMR | 0x3f , dev - > base_addr + TLAN_HOST_CMD ) ;
outl ( TLAN_HC_LD_THR | 0x9 , dev - > base_addr + TLAN_HOST_CMD ) ;
2005-04-17 02:20:36 +04:00
/* 6. Unreset the MII by setting NMRST (in NetSio) to 1. */
2011-01-21 13:59:30 +03:00
outw ( TLAN_NET_SIO , dev - > base_addr + TLAN_DIO_ADR ) ;
2005-04-17 02:20:36 +04:00
addr = dev - > base_addr + TLAN_DIO_DATA + TLAN_NET_SIO ;
2011-01-21 13:59:30 +03:00
tlan_set_bit ( TLAN_NET_SIO_NMRST , addr ) ;
2005-04-17 02:20:36 +04:00
/* 7. Setup the remaining registers. */
2011-01-21 13:59:30 +03:00
if ( priv - > tlan_rev > = 0x30 ) {
2005-04-17 02:20:36 +04:00
data8 = TLAN_ID_TX_EOC | TLAN_ID_RX_EOC ;
2011-01-21 13:59:30 +03:00
tlan_dio_write8 ( dev - > base_addr , TLAN_INT_DIS , data8 ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
tlan_phy_detect ( dev ) ;
2005-04-17 02:20:36 +04:00
data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
if ( priv - > adapter - > flags & TLAN_ADAPTER_BIT_RATE_PHY ) {
2005-04-17 02:20:36 +04:00
data | = TLAN_NET_CFG_BIT ;
2011-01-21 13:59:30 +03:00
if ( priv - > aui = = 1 ) {
tlan_dio_write8 ( dev - > base_addr , TLAN_ACOMMIT , 0x0a ) ;
} else if ( priv - > duplex = = TLAN_DUPLEX_FULL ) {
tlan_dio_write8 ( dev - > base_addr , TLAN_ACOMMIT , 0x00 ) ;
priv - > tlan_full_duplex = true ;
2005-04-17 02:20:36 +04:00
} else {
2011-01-21 13:59:30 +03:00
tlan_dio_write8 ( dev - > base_addr , TLAN_ACOMMIT , 0x08 ) ;
2005-04-17 02:20:36 +04:00
}
}
2011-01-21 13:59:30 +03:00
if ( priv - > phy_num = = 0 )
2005-04-17 02:20:36 +04:00
data | = TLAN_NET_CFG_PHY_EN ;
2011-01-21 13:59:30 +03:00
tlan_dio_write16 ( dev - > base_addr , TLAN_NET_CONFIG , ( u16 ) data ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
if ( priv - > adapter - > flags & TLAN_ADAPTER_UNMANAGED_PHY )
tlan_finish_reset ( dev ) ;
else
tlan_phy_power_down ( dev ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2008-02-18 21:04:38 +03:00
static void
2011-01-21 13:59:30 +03:00
tlan_finish_reset ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
u8 data ;
u32 phy ;
u8 sio ;
u16 status ;
u16 partner ;
u16 tlphy_ctl ;
2011-01-21 13:59:30 +03:00
u16 tlphy_par ;
2005-04-17 02:20:36 +04:00
u16 tlphy_id1 , tlphy_id2 ;
2011-01-21 13:59:30 +03:00
int i ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
phy = priv - > phy [ priv - > phy_num ] ;
2005-04-17 02:20:36 +04:00
data = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP ;
2011-01-21 13:59:30 +03:00
if ( priv - > tlan_full_duplex )
2005-04-17 02:20:36 +04:00
data | = TLAN_NET_CMD_DUPLEX ;
2011-01-21 13:59:30 +03:00
tlan_dio_write8 ( dev - > base_addr , TLAN_NET_CMD , data ) ;
2006-09-13 21:24:59 +04:00
data = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5 ;
2011-01-21 13:59:30 +03:00
if ( priv - > phy_num = = 0 )
2006-09-13 21:24:59 +04:00
data | = TLAN_NET_MASK_MASK7 ;
2011-01-21 13:59:30 +03:00
tlan_dio_write8 ( dev - > base_addr , TLAN_NET_MASK , data ) ;
tlan_dio_write16 ( dev - > base_addr , TLAN_MAX_RX , ( ( 1536 ) + 7 ) & ~ 7 ) ;
tlan_mii_read_reg ( dev , phy , MII_GEN_ID_HI , & tlphy_id1 ) ;
tlan_mii_read_reg ( dev , phy , MII_GEN_ID_LO , & tlphy_id2 ) ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
if ( ( priv - > adapter - > flags & TLAN_ADAPTER_UNMANAGED_PHY ) | |
( priv - > aui ) ) {
2005-04-17 02:20:36 +04:00
status = MII_GS_LINK ;
2011-03-01 09:56:33 +03:00
netdev_info ( dev , " Link forced \n " ) ;
2005-04-17 02:20:36 +04:00
} else {
2011-01-21 13:59:30 +03:00
tlan_mii_read_reg ( dev , phy , MII_GEN_STS , & status ) ;
udelay ( 1000 ) ;
tlan_mii_read_reg ( dev , phy , MII_GEN_STS , & status ) ;
if ( ( status & MII_GS_LINK ) & &
/* We only support link info on Nat.Sem. PHY's */
( tlphy_id1 = = NAT_SEM_ID1 ) & &
( tlphy_id2 = = NAT_SEM_ID2 ) ) {
tlan_mii_read_reg ( dev , phy , MII_AN_LPA , & partner ) ;
tlan_mii_read_reg ( dev , phy , TLAN_TLPHY_PAR , & tlphy_par ) ;
2011-03-01 09:56:33 +03:00
netdev_info ( dev ,
" Link active with %s %uMbps %s-Duplex \n " ,
! ( tlphy_par & TLAN_PHY_AN_EN_STAT )
? " forced " : " Autonegotiation enabled, " ,
tlphy_par & TLAN_PHY_SPEED_100
? 100 : 10 ,
tlphy_par & TLAN_PHY_DUPLEX_FULL
? " Full " : " Half " ) ;
if ( tlphy_par & TLAN_PHY_AN_EN_STAT ) {
netdev_info ( dev , " Partner capability: " ) ;
for ( i = 5 ; i < 10 ; i + + )
if ( partner & ( 1 < < i ) )
pr_cont ( " %s " , media [ i - 5 ] ) ;
pr_cont ( " \n " ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
tlan_dio_write8 ( dev - > base_addr , TLAN_LED_REG ,
TLAN_LED_LINK ) ;
2006-09-13 21:24:59 +04:00
# ifdef MONITOR
2005-04-17 02:20:36 +04:00
/* We have link beat..for now anyway */
2011-01-21 13:59:30 +03:00
priv - > link = 1 ;
/*Enabling link beat monitoring */
tlan_set_timer ( dev , ( 10 * HZ ) , TLAN_TIMER_LINK_BEAT ) ;
2006-09-13 21:24:59 +04:00
# endif
2005-04-17 02:20:36 +04:00
} else if ( status & MII_GS_LINK ) {
2011-03-01 09:56:33 +03:00
netdev_info ( dev , " Link active \n " ) ;
2011-01-21 13:59:30 +03:00
tlan_dio_write8 ( dev - > base_addr , TLAN_LED_REG ,
TLAN_LED_LINK ) ;
2005-04-17 02:20:36 +04:00
}
}
2011-01-21 13:59:30 +03:00
if ( priv - > phy_num = = 0 ) {
tlan_mii_read_reg ( dev , phy , TLAN_TLPHY_CTL , & tlphy_ctl ) ;
tlphy_ctl | = TLAN_TC_INTEN ;
tlan_mii_write_reg ( dev , phy , TLAN_TLPHY_CTL , tlphy_ctl ) ;
sio = tlan_dio_read8 ( dev - > base_addr , TLAN_NET_SIO ) ;
sio | = TLAN_NET_SIO_MINTEN ;
tlan_dio_write8 ( dev - > base_addr , TLAN_NET_SIO , sio ) ;
}
if ( status & MII_GS_LINK ) {
tlan_set_mac ( dev , 0 , dev - > dev_addr ) ;
priv - > phy_online = 1 ;
outb ( ( TLAN_HC_INT_ON > > 8 ) , dev - > base_addr + TLAN_HOST_CMD + 1 ) ;
if ( debug > = 1 & & debug ! = TLAN_DEBUG_PROBE )
outb ( ( TLAN_HC_REQ_INT > > 8 ) ,
dev - > base_addr + TLAN_HOST_CMD + 1 ) ;
outl ( priv - > rx_list_dma , dev - > base_addr + TLAN_CH_PARM ) ;
outl ( TLAN_HC_GO | TLAN_HC_RT , dev - > base_addr + TLAN_HOST_CMD ) ;
2005-04-17 02:20:36 +04:00
netif_carrier_on ( dev ) ;
} else {
2011-03-01 09:56:33 +03:00
netdev_info ( dev , " Link inactive, will retry in 10 secs... \n " ) ;
2011-01-21 13:59:30 +03:00
tlan_set_timer ( dev , ( 10 * HZ ) , TLAN_TIMER_FINISH_RESET ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2011-01-21 13:59:30 +03:00
tlan_set_multicast_list ( dev ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_set_mac
*
* Returns :
* Nothing
* Parms :
* dev Pointer to device structure of adapter
* on which to change the AREG .
* areg The AREG to set the address in ( 0 - 3 ) .
* mac A pointer to an array of chars . Each
* element stores one byte of the address .
* IE , it isn ' t in ascii .
*
* This function transfers a MAC address to one of the
* TLAN AREGs ( address registers ) . The TLAN chip locks
* the register on writing to offset 0 and unlocks the
* register after writing to offset 5. If NULL is passed
* in mac , then the AREG is filled with 0 ' s .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_set_mac ( struct net_device * dev , int areg , char * mac )
2005-04-17 02:20:36 +04:00
{
int i ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
areg * = 6 ;
2011-01-21 13:59:30 +03:00
if ( mac ! = NULL ) {
for ( i = 0 ; i < 6 ; i + + )
tlan_dio_write8 ( dev - > base_addr ,
TLAN_AREG_0 + areg + i , mac [ i ] ) ;
2005-04-17 02:20:36 +04:00
} else {
2011-01-21 13:59:30 +03:00
for ( i = 0 ; i < 6 ; i + + )
tlan_dio_write8 ( dev - > base_addr ,
TLAN_AREG_0 + areg + i , 0 ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
/*****************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2011-01-21 13:59:30 +03:00
ThunderLAN driver PHY layer routines
2005-04-17 02:20:36 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-01-21 13:59:30 +03:00
/*********************************************************************
* tlan_phy_print
*
* Returns :
* Nothing
* Parms :
* dev A pointer to the device structure of the
* TLAN device having the PHYs to be detailed .
*
* This function prints the registers a PHY ( aka transceiver ) .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_phy_print ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
u16 i , data0 , data1 , data2 , data3 , phy ;
2011-01-21 13:59:30 +03:00
phy = priv - > phy [ priv - > phy_num ] ;
if ( priv - > adapter - > flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
2011-03-01 09:56:33 +03:00
netdev_info ( dev , " Unmanaged PHY \n " ) ;
2011-01-21 13:59:30 +03:00
} else if ( phy < = TLAN_PHY_MAX_ADDR ) {
2011-03-01 09:56:33 +03:00
netdev_info ( dev , " PHY 0x%02x \n " , phy ) ;
pr_info ( " Off. +0 +1 +2 +3 \n " ) ;
2011-01-21 13:59:30 +03:00
for ( i = 0 ; i < 0x20 ; i + = 4 ) {
tlan_mii_read_reg ( dev , phy , i , & data0 ) ;
tlan_mii_read_reg ( dev , phy , i + 1 , & data1 ) ;
tlan_mii_read_reg ( dev , phy , i + 2 , & data2 ) ;
tlan_mii_read_reg ( dev , phy , i + 3 , & data3 ) ;
2011-03-01 09:56:33 +03:00
pr_info ( " 0x%02x 0x%04hx 0x%04hx 0x%04hx 0x%04hx \n " ,
i , data0 , data1 , data2 , data3 ) ;
2005-04-17 02:20:36 +04:00
}
} else {
2011-03-01 09:56:33 +03:00
netdev_info ( dev , " Invalid PHY \n " ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/*********************************************************************
* tlan_phy_detect
*
* Returns :
* Nothing
* Parms :
* dev A pointer to the device structure of the adapter
* for which the PHY needs determined .
*
* So far I ' ve found that adapters which have external PHYs
* may also use the internal PHY for part of the functionality .
* ( eg , AUI / Thinnet ) . This function finds out if this TLAN
* chip has an internal PHY , and then finds the first external
* PHY ( starting from address 0 ) if it exists ) .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_phy_detect ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
u16 control ;
u16 hi ;
u16 lo ;
u32 phy ;
2011-01-21 13:59:30 +03:00
if ( priv - > adapter - > flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
priv - > phy_num = 0xffff ;
2005-04-17 02:20:36 +04:00
return ;
}
2011-01-21 13:59:30 +03:00
tlan_mii_read_reg ( dev , TLAN_PHY_MAX_ADDR , MII_GEN_ID_HI , & hi ) ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
if ( hi ! = 0xffff )
2005-04-17 02:20:36 +04:00
priv - > phy [ 0 ] = TLAN_PHY_MAX_ADDR ;
2011-01-21 13:59:30 +03:00
else
2005-04-17 02:20:36 +04:00
priv - > phy [ 0 ] = TLAN_PHY_NONE ;
priv - > phy [ 1 ] = TLAN_PHY_NONE ;
2011-01-21 13:59:30 +03:00
for ( phy = 0 ; phy < = TLAN_PHY_MAX_ADDR ; phy + + ) {
tlan_mii_read_reg ( dev , phy , MII_GEN_CTL , & control ) ;
tlan_mii_read_reg ( dev , phy , MII_GEN_ID_HI , & hi ) ;
tlan_mii_read_reg ( dev , phy , MII_GEN_ID_LO , & lo ) ;
if ( ( control ! = 0xffff ) | |
( hi ! = 0xffff ) | | ( lo ! = 0xffff ) ) {
TLAN_DBG ( TLAN_DEBUG_GNRL ,
" PHY found at %02x %04x %04x %04x \n " ,
phy , control , hi , lo ) ;
if ( ( priv - > phy [ 1 ] = = TLAN_PHY_NONE ) & &
( phy ! = TLAN_PHY_MAX_ADDR ) ) {
2005-04-17 02:20:36 +04:00
priv - > phy [ 1 ] = phy ;
}
}
}
2011-01-21 13:59:30 +03:00
if ( priv - > phy [ 1 ] ! = TLAN_PHY_NONE )
priv - > phy_num = 1 ;
else if ( priv - > phy [ 0 ] ! = TLAN_PHY_NONE )
priv - > phy_num = 0 ;
else
2011-03-01 09:56:33 +03:00
netdev_info ( dev , " Cannot initialize device, no PHY was found! \n " ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_phy_power_down ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
u16 value ;
2011-01-21 13:59:30 +03:00
TLAN_DBG ( TLAN_DEBUG_GNRL , " %s: Powering down PHY(s). \n " , dev - > name ) ;
2005-04-17 02:20:36 +04:00
value = MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE ;
2011-01-21 13:59:30 +03:00
tlan_mii_sync ( dev - > base_addr ) ;
tlan_mii_write_reg ( dev , priv - > phy [ priv - > phy_num ] , MII_GEN_CTL , value ) ;
if ( ( priv - > phy_num = = 0 ) & &
( priv - > phy [ 1 ] ! = TLAN_PHY_NONE ) & &
( ! ( priv - > adapter - > flags & TLAN_ADAPTER_USE_INTERN_10 ) ) ) {
tlan_mii_sync ( dev - > base_addr ) ;
tlan_mii_write_reg ( dev , priv - > phy [ 1 ] , MII_GEN_CTL , value ) ;
2005-04-17 02:20:36 +04:00
}
/* Wait for 50 ms and powerup
* This is abitrary . It is intended to make sure the
* transceiver settles .
*/
2011-01-21 13:59:30 +03:00
tlan_set_timer ( dev , ( HZ / 20 ) , TLAN_TIMER_PHY_PUP ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_phy_power_up ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
u16 value ;
2011-01-21 13:59:30 +03:00
TLAN_DBG ( TLAN_DEBUG_GNRL , " %s: Powering up PHY. \n " , dev - > name ) ;
tlan_mii_sync ( dev - > base_addr ) ;
2005-04-17 02:20:36 +04:00
value = MII_GC_LOOPBK ;
2011-01-21 13:59:30 +03:00
tlan_mii_write_reg ( dev , priv - > phy [ priv - > phy_num ] , MII_GEN_CTL , value ) ;
tlan_mii_sync ( dev - > base_addr ) ;
2005-04-17 02:20:36 +04:00
/* Wait for 500 ms and reset the
* transceiver . The TLAN docs say both 50 ms and
* 500 ms , so do the longer , just in case .
*/
2011-01-21 13:59:30 +03:00
tlan_set_timer ( dev , ( HZ / 20 ) , TLAN_TIMER_PHY_RESET ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_phy_reset ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
u16 phy ;
u16 value ;
2011-01-21 13:59:30 +03:00
phy = priv - > phy [ priv - > phy_num ] ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
TLAN_DBG ( TLAN_DEBUG_GNRL , " %s: Reseting PHY. \n " , dev - > name ) ;
tlan_mii_sync ( dev - > base_addr ) ;
2005-04-17 02:20:36 +04:00
value = MII_GC_LOOPBK | MII_GC_RESET ;
2011-01-21 13:59:30 +03:00
tlan_mii_write_reg ( dev , phy , MII_GEN_CTL , value ) ;
tlan_mii_read_reg ( dev , phy , MII_GEN_CTL , & value ) ;
while ( value & MII_GC_RESET )
tlan_mii_read_reg ( dev , phy , MII_GEN_CTL , & value ) ;
2005-04-17 02:20:36 +04:00
/* Wait for 500 ms and initialize.
* I don ' t remember why I wait this long .
* I ' ve changed this to 50 ms , as it seems long enough .
*/
2011-01-21 13:59:30 +03:00
tlan_set_timer ( dev , ( HZ / 20 ) , TLAN_TIMER_PHY_START_LINK ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_phy_start_link ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
u16 ability ;
u16 control ;
u16 data ;
u16 phy ;
u16 status ;
u16 tctl ;
2011-01-21 13:59:30 +03:00
phy = priv - > phy [ priv - > phy_num ] ;
TLAN_DBG ( TLAN_DEBUG_GNRL , " %s: Trying to activate link. \n " , dev - > name ) ;
tlan_mii_read_reg ( dev , phy , MII_GEN_STS , & status ) ;
tlan_mii_read_reg ( dev , phy , MII_GEN_STS , & ability ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
if ( ( status & MII_GS_AUTONEG ) & &
( ! priv - > aui ) ) {
2005-04-17 02:20:36 +04:00
ability = status > > 11 ;
2011-01-21 13:59:30 +03:00
if ( priv - > speed = = TLAN_SPEED_10 & &
priv - > duplex = = TLAN_DUPLEX_HALF ) {
tlan_mii_write_reg ( dev , phy , MII_GEN_CTL , 0x0000 ) ;
} else if ( priv - > speed = = TLAN_SPEED_10 & &
priv - > duplex = = TLAN_DUPLEX_FULL ) {
priv - > tlan_full_duplex = true ;
tlan_mii_write_reg ( dev , phy , MII_GEN_CTL , 0x0100 ) ;
} else if ( priv - > speed = = TLAN_SPEED_100 & &
priv - > duplex = = TLAN_DUPLEX_HALF ) {
tlan_mii_write_reg ( dev , phy , MII_GEN_CTL , 0x2000 ) ;
} else if ( priv - > speed = = TLAN_SPEED_100 & &
priv - > duplex = = TLAN_DUPLEX_FULL ) {
priv - > tlan_full_duplex = true ;
tlan_mii_write_reg ( dev , phy , MII_GEN_CTL , 0x2100 ) ;
2005-04-17 02:20:36 +04:00
} else {
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
/* Set Auto-Neg advertisement */
2011-01-21 13:59:30 +03:00
tlan_mii_write_reg ( dev , phy , MII_AN_ADV ,
( ability < < 5 ) | 1 ) ;
2005-04-17 02:20:36 +04:00
/* Enablee Auto-Neg */
2011-01-21 13:59:30 +03:00
tlan_mii_write_reg ( dev , phy , MII_GEN_CTL , 0x1000 ) ;
2005-04-17 02:20:36 +04:00
/* Restart Auto-Neg */
2011-01-21 13:59:30 +03:00
tlan_mii_write_reg ( dev , phy , MII_GEN_CTL , 0x1200 ) ;
2005-04-17 02:20:36 +04:00
/* Wait for 4 sec for autonegotiation
2011-01-21 13:59:30 +03:00
* to complete . The max spec time is less than this
* but the card need additional time to start AN .
* .5 sec should be plenty extra .
*/
2011-03-01 09:56:33 +03:00
netdev_info ( dev , " Starting autonegotiation \n " ) ;
2011-01-21 13:59:30 +03:00
tlan_set_timer ( dev , ( 2 * HZ ) , TLAN_TIMER_PHY_FINISH_AN ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2006-09-13 21:24:59 +04:00
}
2011-01-21 13:59:30 +03:00
if ( ( priv - > aui ) & & ( priv - > phy_num ! = 0 ) ) {
priv - > phy_num = 0 ;
data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN
| TLAN_NET_CFG_PHY_EN ;
tlan_dio_write16 ( dev - > base_addr , TLAN_NET_CONFIG , data ) ;
tlan_set_timer ( dev , ( 40 * HZ / 1000 ) , TLAN_TIMER_PHY_PDOWN ) ;
2005-04-17 02:20:36 +04:00
return ;
2011-01-21 13:59:30 +03:00
} else if ( priv - > phy_num = = 0 ) {
2005-04-17 02:20:36 +04:00
control = 0 ;
2011-01-21 13:59:30 +03:00
tlan_mii_read_reg ( dev , phy , TLAN_TLPHY_CTL , & tctl ) ;
if ( priv - > aui ) {
tctl | = TLAN_TC_AUISEL ;
2006-09-13 21:24:59 +04:00
} else {
2011-01-21 13:59:30 +03:00
tctl & = ~ TLAN_TC_AUISEL ;
if ( priv - > duplex = = TLAN_DUPLEX_FULL ) {
2005-04-17 02:20:36 +04:00
control | = MII_GC_DUPLEX ;
2011-01-21 13:59:30 +03:00
priv - > tlan_full_duplex = true ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
if ( priv - > speed = = TLAN_SPEED_100 )
2005-04-17 02:20:36 +04:00
control | = MII_GC_SPEEDSEL ;
}
2011-01-21 13:59:30 +03:00
tlan_mii_write_reg ( dev , phy , MII_GEN_CTL , control ) ;
tlan_mii_write_reg ( dev , phy , TLAN_TLPHY_CTL , tctl ) ;
2005-04-17 02:20:36 +04:00
}
/* Wait for 2 sec to give the transceiver time
* to establish link .
*/
2011-01-21 13:59:30 +03:00
tlan_set_timer ( dev , ( 4 * HZ ) , TLAN_TIMER_FINISH_RESET ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_phy_finish_auto_neg ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
u16 an_adv ;
u16 an_lpa ;
u16 data ;
u16 mode ;
u16 phy ;
u16 status ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
phy = priv - > phy [ priv - > phy_num ] ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
tlan_mii_read_reg ( dev , phy , MII_GEN_STS , & status ) ;
udelay ( 1000 ) ;
tlan_mii_read_reg ( dev , phy , MII_GEN_STS , & status ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
if ( ! ( status & MII_GS_AUTOCMPLT ) ) {
2005-04-17 02:20:36 +04:00
/* Wait for 8 sec to give the process
* more time . Perhaps we should fail after a while .
*/
2011-01-21 13:59:30 +03:00
if ( ! priv - > neg_be_verbose + + ) {
2011-03-01 09:56:33 +03:00
pr_info ( " Giving autonegotiation more time. \n " ) ;
pr_info ( " Please check that your adapter has \n " ) ;
pr_info ( " been properly connected to a HUB or Switch. \n " ) ;
pr_info ( " Trying to establish link in the background... \n " ) ;
2011-01-21 13:59:30 +03:00
}
tlan_set_timer ( dev , ( 8 * HZ ) , TLAN_TIMER_PHY_FINISH_AN ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2011-03-01 09:56:33 +03:00
netdev_info ( dev , " Autonegotiation complete \n " ) ;
2011-01-21 13:59:30 +03:00
tlan_mii_read_reg ( dev , phy , MII_AN_ADV , & an_adv ) ;
tlan_mii_read_reg ( dev , phy , MII_AN_LPA , & an_lpa ) ;
2005-04-17 02:20:36 +04:00
mode = an_adv & an_lpa & 0x03E0 ;
2011-01-21 13:59:30 +03:00
if ( mode & 0x0100 )
priv - > tlan_full_duplex = true ;
else if ( ! ( mode & 0x0080 ) & & ( mode & 0x0040 ) )
priv - > tlan_full_duplex = true ;
if ( ( ! ( mode & 0x0180 ) ) & &
( priv - > adapter - > flags & TLAN_ADAPTER_USE_INTERN_10 ) & &
( priv - > phy_num ! = 0 ) ) {
priv - > phy_num = 0 ;
data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN
| TLAN_NET_CFG_PHY_EN ;
tlan_dio_write16 ( dev - > base_addr , TLAN_NET_CONFIG , data ) ;
tlan_set_timer ( dev , ( 400 * HZ / 1000 ) , TLAN_TIMER_PHY_PDOWN ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2011-01-21 13:59:30 +03:00
if ( priv - > phy_num = = 0 ) {
if ( ( priv - > duplex = = TLAN_DUPLEX_FULL ) | |
( an_adv & an_lpa & 0x0040 ) ) {
tlan_mii_write_reg ( dev , phy , MII_GEN_CTL ,
MII_GC_AUTOENB | MII_GC_DUPLEX ) ;
2011-03-01 09:56:33 +03:00
netdev_info ( dev , " Starting internal PHY with FULL-DUPLEX \n " ) ;
2005-04-17 02:20:36 +04:00
} else {
2011-01-21 13:59:30 +03:00
tlan_mii_write_reg ( dev , phy , MII_GEN_CTL ,
MII_GC_AUTOENB ) ;
2011-03-01 09:56:33 +03:00
netdev_info ( dev , " Starting internal PHY with HALF-DUPLEX \n " ) ;
2005-04-17 02:20:36 +04:00
}
}
/* Wait for 100 ms. No reason in partiticular.
*/
2011-01-21 13:59:30 +03:00
tlan_set_timer ( dev , ( HZ / 10 ) , TLAN_TIMER_FINISH_RESET ) ;
2006-09-13 21:24:59 +04:00
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
# ifdef MONITOR
2011-01-21 13:59:30 +03:00
/*********************************************************************
*
* tlan_phy_monitor
*
* Returns :
* None
*
* Params :
* dev The device structure of this device .
*
*
* This function monitors PHY condition by reading the status
* register via the MII bus . This can be used to give info
* about link changes ( up / down ) , and possible switch to alternate
* media .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void tlan_phy_monitor ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
u16 phy ;
u16 phy_status ;
2011-01-21 13:59:30 +03:00
phy = priv - > phy [ priv - > phy_num ] ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/* Get PHY status register */
tlan_mii_read_reg ( dev , phy , MII_GEN_STS , & phy_status ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/* Check if link has been lost */
if ( ! ( phy_status & MII_GS_LINK ) ) {
if ( priv - > link ) {
priv - > link = 0 ;
printk ( KERN_DEBUG " TLAN: %s has lost link \n " ,
dev - > name ) ;
netif_carrier_off ( dev ) ;
tlan_set_timer ( dev , ( 2 * HZ ) , TLAN_TIMER_LINK_BEAT ) ;
return ;
2005-04-17 02:20:36 +04:00
}
}
2011-01-21 13:59:30 +03:00
/* Link restablished? */
if ( ( phy_status & MII_GS_LINK ) & & ! priv - > link ) {
priv - > link = 1 ;
printk ( KERN_DEBUG " TLAN: %s has reestablished link \n " ,
dev - > name ) ;
2005-05-13 03:45:25 +04:00
netif_carrier_on ( dev ) ;
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
/* Setup a new monitor */
2011-01-21 13:59:30 +03:00
tlan_set_timer ( dev , ( 2 * HZ ) , TLAN_TIMER_LINK_BEAT ) ;
2006-09-13 21:24:59 +04:00
}
2005-04-17 02:20:36 +04:00
# endif /* MONITOR */
/*****************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2011-01-21 13:59:30 +03:00
ThunderLAN driver MII routines
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
these routines are based on the information in chap . 2 of the
" ThunderLAN Programmer's Guide " , pp . 15 - 24.
2005-04-17 02:20:36 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_mii_read_reg
*
* Returns :
* false if ack received ok
* true if no ack received or other error
*
* Parms :
* dev The device structure containing
* The io address and interrupt count
* for this device .
* phy The address of the PHY to be queried .
* reg The register whose contents are to be
* retrieved .
* val A pointer to a variable to store the
* retrieved value .
*
* This function uses the TLAN ' s MII bus to retrieve the contents
* of a given register on a PHY . It sends the appropriate info
* and then reads the 16 - bit register value from the MII bus via
* the TLAN SIO register .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static bool
tlan_mii_read_reg ( struct net_device * dev , u16 phy , u16 reg , u16 * val )
2005-04-17 02:20:36 +04:00
{
u8 nack ;
u16 sio , tmp ;
2011-01-21 13:59:30 +03:00
u32 i ;
2010-01-12 23:59:13 +03:00
bool err ;
2005-04-17 02:20:36 +04:00
int minten ;
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags = 0 ;
2010-01-12 23:59:13 +03:00
err = false ;
2005-04-17 02:20:36 +04:00
outw ( TLAN_NET_SIO , dev - > base_addr + TLAN_DIO_ADR ) ;
sio = dev - > base_addr + TLAN_DIO_DATA + TLAN_NET_SIO ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
if ( ! in_irq ( ) )
spin_lock_irqsave ( & priv - > lock , flags ) ;
2011-01-21 13:59:30 +03:00
tlan_mii_sync ( dev - > base_addr ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
minten = tlan_get_bit ( TLAN_NET_SIO_MINTEN , sio ) ;
if ( minten )
tlan_clear_bit ( TLAN_NET_SIO_MINTEN , sio ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
tlan_mii_send_data ( dev - > base_addr , 0x1 , 2 ) ; /* start (01b) */
tlan_mii_send_data ( dev - > base_addr , 0x2 , 2 ) ; /* read (10b) */
tlan_mii_send_data ( dev - > base_addr , phy , 5 ) ; /* device # */
tlan_mii_send_data ( dev - > base_addr , reg , 5 ) ; /* register # */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
tlan_clear_bit ( TLAN_NET_SIO_MTXEN , sio ) ; /* change direction */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
tlan_clear_bit ( TLAN_NET_SIO_MCLK , sio ) ; /* clock idle bit */
tlan_set_bit ( TLAN_NET_SIO_MCLK , sio ) ;
tlan_clear_bit ( TLAN_NET_SIO_MCLK , sio ) ; /* wait 300ns */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
nack = tlan_get_bit ( TLAN_NET_SIO_MDATA , sio ) ; /* check for ACK */
tlan_set_bit ( TLAN_NET_SIO_MCLK , sio ) ; /* finish ACK */
if ( nack ) { /* no ACK, so fake it */
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 16 ; i + + ) {
2011-01-21 13:59:30 +03:00
tlan_clear_bit ( TLAN_NET_SIO_MCLK , sio ) ;
tlan_set_bit ( TLAN_NET_SIO_MCLK , sio ) ;
2005-04-17 02:20:36 +04:00
}
tmp = 0xffff ;
2010-01-12 23:59:13 +03:00
err = true ;
2005-04-17 02:20:36 +04:00
} else { /* ACK, so read data */
for ( tmp = 0 , i = 0x8000 ; i ; i > > = 1 ) {
2011-01-21 13:59:30 +03:00
tlan_clear_bit ( TLAN_NET_SIO_MCLK , sio ) ;
if ( tlan_get_bit ( TLAN_NET_SIO_MDATA , sio ) )
2005-04-17 02:20:36 +04:00
tmp | = i ;
2011-01-21 13:59:30 +03:00
tlan_set_bit ( TLAN_NET_SIO_MCLK , sio ) ;
2005-04-17 02:20:36 +04:00
}
}
2011-01-21 13:59:30 +03:00
tlan_clear_bit ( TLAN_NET_SIO_MCLK , sio ) ; /* idle cycle */
tlan_set_bit ( TLAN_NET_SIO_MCLK , sio ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
if ( minten )
tlan_set_bit ( TLAN_NET_SIO_MINTEN , sio ) ;
2005-04-17 02:20:36 +04:00
* val = tmp ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
if ( ! in_irq ( ) )
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
return err ;
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_mii_send_data
*
* Returns :
* Nothing
* Parms :
* base_port The base IO port of the adapter in
* question .
* dev The address of the PHY to be queried .
* data The value to be placed on the MII bus .
* num_bits The number of bits in data that are to
* be placed on the MII bus .
*
* This function sends on sequence of bits on the MII
* configuration bus .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_mii_send_data ( u16 base_port , u32 data , unsigned num_bits )
2005-04-17 02:20:36 +04:00
{
u16 sio ;
u32 i ;
2011-01-21 13:59:30 +03:00
if ( num_bits = = 0 )
2005-04-17 02:20:36 +04:00
return ;
2011-01-21 13:59:30 +03:00
outw ( TLAN_NET_SIO , base_port + TLAN_DIO_ADR ) ;
2005-04-17 02:20:36 +04:00
sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO ;
2011-01-21 13:59:30 +03:00
tlan_set_bit ( TLAN_NET_SIO_MTXEN , sio ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
for ( i = ( 0x1 < < ( num_bits - 1 ) ) ; i ; i > > = 1 ) {
tlan_clear_bit ( TLAN_NET_SIO_MCLK , sio ) ;
( void ) tlan_get_bit ( TLAN_NET_SIO_MCLK , sio ) ;
if ( data & i )
tlan_set_bit ( TLAN_NET_SIO_MDATA , sio ) ;
2005-04-17 02:20:36 +04:00
else
2011-01-21 13:59:30 +03:00
tlan_clear_bit ( TLAN_NET_SIO_MDATA , sio ) ;
tlan_set_bit ( TLAN_NET_SIO_MCLK , sio ) ;
( void ) tlan_get_bit ( TLAN_NET_SIO_MCLK , sio ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* TLan_MiiSync
*
* Returns :
* Nothing
* Parms :
* base_port The base IO port of the adapter in
* question .
*
* This functions syncs all PHYs in terms of the MII configuration
* bus .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void tlan_mii_sync ( u16 base_port )
2005-04-17 02:20:36 +04:00
{
int i ;
u16 sio ;
2011-01-21 13:59:30 +03:00
outw ( TLAN_NET_SIO , base_port + TLAN_DIO_ADR ) ;
2005-04-17 02:20:36 +04:00
sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO ;
2011-01-21 13:59:30 +03:00
tlan_clear_bit ( TLAN_NET_SIO_MTXEN , sio ) ;
for ( i = 0 ; i < 32 ; i + + ) {
tlan_clear_bit ( TLAN_NET_SIO_MCLK , sio ) ;
tlan_set_bit ( TLAN_NET_SIO_MCLK , sio ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_mii_write_reg
*
* Returns :
* Nothing
* Parms :
* dev The device structure for the device
* to write to .
* phy The address of the PHY to be written to .
* reg The register whose contents are to be
* written .
* val The value to be written to the register .
*
* This function uses the TLAN ' s MII bus to write the contents of a
* given register on a PHY . It sends the appropriate info and then
* writes the 16 - bit register value from the MII configuration bus
* via the TLAN SIO register .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
static void
tlan_mii_write_reg ( struct net_device * dev , u16 phy , u16 reg , u16 val )
2005-04-17 02:20:36 +04:00
{
u16 sio ;
int minten ;
unsigned long flags = 0 ;
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
outw ( TLAN_NET_SIO , dev - > base_addr + TLAN_DIO_ADR ) ;
sio = dev - > base_addr + TLAN_DIO_DATA + TLAN_NET_SIO ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
if ( ! in_irq ( ) )
spin_lock_irqsave ( & priv - > lock , flags ) ;
2011-01-21 13:59:30 +03:00
tlan_mii_sync ( dev - > base_addr ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
minten = tlan_get_bit ( TLAN_NET_SIO_MINTEN , sio ) ;
if ( minten )
tlan_clear_bit ( TLAN_NET_SIO_MINTEN , sio ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
tlan_mii_send_data ( dev - > base_addr , 0x1 , 2 ) ; /* start (01b) */
tlan_mii_send_data ( dev - > base_addr , 0x1 , 2 ) ; /* write (01b) */
tlan_mii_send_data ( dev - > base_addr , phy , 5 ) ; /* device # */
tlan_mii_send_data ( dev - > base_addr , reg , 5 ) ; /* register # */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
tlan_mii_send_data ( dev - > base_addr , 0x2 , 2 ) ; /* send ACK */
tlan_mii_send_data ( dev - > base_addr , val , 16 ) ; /* send data */
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
tlan_clear_bit ( TLAN_NET_SIO_MCLK , sio ) ; /* idle cycle */
tlan_set_bit ( TLAN_NET_SIO_MCLK , sio ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
if ( minten )
tlan_set_bit ( TLAN_NET_SIO_MINTEN , sio ) ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
if ( ! in_irq ( ) )
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00
/*****************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2011-01-21 13:59:30 +03:00
ThunderLAN driver eeprom routines
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
the Compaq netelligent 10 and 10 / 100 cards use a microchip 24 C02A
EEPROM . these functions are based on information in microchip ' s
data sheet . I don ' t know how well this functions will work with
other Eeproms .
2005-04-17 02:20:36 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-01-21 13:59:30 +03:00
/***************************************************************
* tlan_ee_send_start
*
* Returns :
* Nothing
* Parms :
* io_base The IO port base address for the
* TLAN device with the EEPROM to
* use .
*
* This function sends a start cycle to an EEPROM attached
* to a TLAN chip .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void tlan_ee_send_start ( u16 io_base )
2005-04-17 02:20:36 +04:00
{
u16 sio ;
2011-01-21 13:59:30 +03:00
outw ( TLAN_NET_SIO , io_base + TLAN_DIO_ADR ) ;
2005-04-17 02:20:36 +04:00
sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO ;
2011-01-21 13:59:30 +03:00
tlan_set_bit ( TLAN_NET_SIO_ECLOK , sio ) ;
tlan_set_bit ( TLAN_NET_SIO_EDATA , sio ) ;
tlan_set_bit ( TLAN_NET_SIO_ETXEN , sio ) ;
tlan_clear_bit ( TLAN_NET_SIO_EDATA , sio ) ;
tlan_clear_bit ( TLAN_NET_SIO_ECLOK , sio ) ;
}
/***************************************************************
* tlan_ee_send_byte
*
* Returns :
* If the correct ack was received , 0 , otherwise 1
* Parms : io_base The IO port base address for the
* TLAN device with the EEPROM to
* use .
* data The 8 bits of information to
* send to the EEPROM .
* stop If TLAN_EEPROM_STOP is passed , a
* stop cycle is sent after the
* byte is sent after the ack is
* read .
*
* This function sends a byte on the serial EEPROM line ,
* driving the clock to send each bit . The function then
* reverses transmission direction and reads an acknowledge
* bit .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int tlan_ee_send_byte ( u16 io_base , u8 data , int stop )
2005-04-17 02:20:36 +04:00
{
int err ;
u8 place ;
u16 sio ;
2011-01-21 13:59:30 +03:00
outw ( TLAN_NET_SIO , io_base + TLAN_DIO_ADR ) ;
2005-04-17 02:20:36 +04:00
sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO ;
/* Assume clock is low, tx is enabled; */
2011-01-21 13:59:30 +03:00
for ( place = 0x80 ; place ! = 0 ; place > > = 1 ) {
if ( place & data )
tlan_set_bit ( TLAN_NET_SIO_EDATA , sio ) ;
2005-04-17 02:20:36 +04:00
else
2011-01-21 13:59:30 +03:00
tlan_clear_bit ( TLAN_NET_SIO_EDATA , sio ) ;
tlan_set_bit ( TLAN_NET_SIO_ECLOK , sio ) ;
tlan_clear_bit ( TLAN_NET_SIO_ECLOK , sio ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
tlan_clear_bit ( TLAN_NET_SIO_ETXEN , sio ) ;
tlan_set_bit ( TLAN_NET_SIO_ECLOK , sio ) ;
err = tlan_get_bit ( TLAN_NET_SIO_EDATA , sio ) ;
tlan_clear_bit ( TLAN_NET_SIO_ECLOK , sio ) ;
tlan_set_bit ( TLAN_NET_SIO_ETXEN , sio ) ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
if ( ( ! err ) & & stop ) {
2008-05-30 20:49:58 +04:00
/* STOP, raise data while clock is high */
2011-01-21 13:59:30 +03:00
tlan_clear_bit ( TLAN_NET_SIO_EDATA , sio ) ;
tlan_set_bit ( TLAN_NET_SIO_ECLOK , sio ) ;
tlan_set_bit ( TLAN_NET_SIO_EDATA , sio ) ;
2005-04-17 02:20:36 +04:00
}
2010-09-23 09:40:09 +04:00
return err ;
2005-04-17 02:20:36 +04:00
2011-01-21 13:59:30 +03:00
}
/***************************************************************
* tlan_ee_receive_byte
*
* Returns :
* Nothing
* Parms :
* io_base The IO port base address for the
* TLAN device with the EEPROM to
* use .
* data An address to a char to hold the
* data sent from the EEPROM .
* stop If TLAN_EEPROM_STOP is passed , a
* stop cycle is sent after the
* byte is received , and no ack is
* sent .
*
* This function receives 8 bits of data from the EEPROM
* over the serial link . It then sends and ack bit , or no
* ack and a stop bit . This function is used to retrieve
* data after the address of a byte in the EEPROM has been
* sent .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void tlan_ee_receive_byte ( u16 io_base , u8 * data , int stop )
2005-04-17 02:20:36 +04:00
{
u8 place ;
u16 sio ;
2011-01-21 13:59:30 +03:00
outw ( TLAN_NET_SIO , io_base + TLAN_DIO_ADR ) ;
2005-04-17 02:20:36 +04:00
sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO ;
* data = 0 ;
/* Assume clock is low, tx is enabled; */
2011-01-21 13:59:30 +03:00
tlan_clear_bit ( TLAN_NET_SIO_ETXEN , sio ) ;
for ( place = 0x80 ; place ; place > > = 1 ) {
tlan_set_bit ( TLAN_NET_SIO_ECLOK , sio ) ;
if ( tlan_get_bit ( TLAN_NET_SIO_EDATA , sio ) )
2005-04-17 02:20:36 +04:00
* data | = place ;
2011-01-21 13:59:30 +03:00
tlan_clear_bit ( TLAN_NET_SIO_ECLOK , sio ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-21 13:59:30 +03:00
tlan_set_bit ( TLAN_NET_SIO_ETXEN , sio ) ;
if ( ! stop ) {
tlan_clear_bit ( TLAN_NET_SIO_EDATA , sio ) ; /* ack = 0 */
tlan_set_bit ( TLAN_NET_SIO_ECLOK , sio ) ;
tlan_clear_bit ( TLAN_NET_SIO_ECLOK , sio ) ;
2005-04-17 02:20:36 +04:00
} else {
2011-01-21 13:59:30 +03:00
tlan_set_bit ( TLAN_NET_SIO_EDATA , sio ) ; /* no ack = 1 (?) */
tlan_set_bit ( TLAN_NET_SIO_ECLOK , sio ) ;
tlan_clear_bit ( TLAN_NET_SIO_ECLOK , sio ) ;
2008-05-30 20:49:58 +04:00
/* STOP, raise data while clock is high */
2011-01-21 13:59:30 +03:00
tlan_clear_bit ( TLAN_NET_SIO_EDATA , sio ) ;
tlan_set_bit ( TLAN_NET_SIO_ECLOK , sio ) ;
tlan_set_bit ( TLAN_NET_SIO_EDATA , sio ) ;
}
}
/***************************************************************
* tlan_ee_read_byte
*
* Returns :
* No error = 0 , else , the stage at which the error
* occurred .
* Parms :
* io_base The IO port base address for the
* TLAN device with the EEPROM to
* use .
* ee_addr The address of the byte in the
* EEPROM whose contents are to be
* retrieved .
* data An address to a char to hold the
* data obtained from the EEPROM .
*
* This function reads a byte of information from an byte
* cell in the EEPROM .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int tlan_ee_read_byte ( struct net_device * dev , u8 ee_addr , u8 * data )
2005-04-17 02:20:36 +04:00
{
int err ;
2011-01-21 13:59:30 +03:00
struct tlan_priv * priv = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags = 0 ;
2011-01-21 13:59:30 +03:00
int ret = 0 ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
2011-01-21 13:59:30 +03:00
tlan_ee_send_start ( dev - > base_addr ) ;
err = tlan_ee_send_byte ( dev - > base_addr , 0xa0 , TLAN_EEPROM_ACK ) ;
if ( err ) {
ret = 1 ;
2005-04-17 02:20:36 +04:00
goto fail ;
}
2011-01-21 13:59:30 +03:00
err = tlan_ee_send_byte ( dev - > base_addr , ee_addr , TLAN_EEPROM_ACK ) ;
if ( err ) {
ret = 2 ;
2005-04-17 02:20:36 +04:00
goto fail ;
}
2011-01-21 13:59:30 +03:00
tlan_ee_send_start ( dev - > base_addr ) ;
err = tlan_ee_send_byte ( dev - > base_addr , 0xa1 , TLAN_EEPROM_ACK ) ;
if ( err ) {
ret = 3 ;
2005-04-17 02:20:36 +04:00
goto fail ;
}
2011-01-21 13:59:30 +03:00
tlan_ee_receive_byte ( dev - > base_addr , data , TLAN_EEPROM_STOP ) ;
2005-04-17 02:20:36 +04:00
fail :
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
return ret ;
2011-01-21 13:59:30 +03:00
}
2005-04-17 02:20:36 +04:00