2009-03-12 09:26:02 +03:00
/*
* Dave DNET Ethernet Controller driver
*
* Copyright ( C ) 2008 Dave S . r . l . < www . dave . eu >
* Copyright ( C ) 2009 Ilya Yanok , Emcraft Systems Ltd , < yanok @ emcraft . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
2009-03-24 23:19:50 +03:00
# include <linux/io.h>
2009-03-12 09:26:02 +03:00
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/dma-mapping.h>
# include <linux/platform_device.h>
# include <linux/phy.h>
# include "dnet.h"
# undef DEBUG
/* function for reading internal MAC register */
u16 dnet_readw_mac ( struct dnet * bp , u16 reg )
{
u16 data_read ;
/* issue a read */
dnet_writel ( bp , reg , MACREG_ADDR ) ;
/* since a read/write op to the MAC is very slow,
* we must wait before reading the data */
ndelay ( 500 ) ;
/* read data read from the MAC register */
data_read = dnet_readl ( bp , MACREG_DATA ) ;
/* all done */
return data_read ;
}
/* function for writing internal MAC register */
void dnet_writew_mac ( struct dnet * bp , u16 reg , u16 val )
{
/* load data to write */
dnet_writel ( bp , val , MACREG_DATA ) ;
/* issue a write */
dnet_writel ( bp , reg | DNET_INTERNAL_WRITE , MACREG_ADDR ) ;
/* since a read/write op to the MAC is very slow,
* we must wait before exiting */
ndelay ( 500 ) ;
}
static void __dnet_set_hwaddr ( struct dnet * bp )
{
u16 tmp ;
tmp = cpu_to_be16 ( * ( ( u16 * ) bp - > dev - > dev_addr ) ) ;
dnet_writew_mac ( bp , DNET_INTERNAL_MAC_ADDR_0_REG , tmp ) ;
tmp = cpu_to_be16 ( * ( ( u16 * ) ( bp - > dev - > dev_addr + 2 ) ) ) ;
dnet_writew_mac ( bp , DNET_INTERNAL_MAC_ADDR_1_REG , tmp ) ;
tmp = cpu_to_be16 ( * ( ( u16 * ) ( bp - > dev - > dev_addr + 4 ) ) ) ;
dnet_writew_mac ( bp , DNET_INTERNAL_MAC_ADDR_2_REG , tmp ) ;
}
static void __devinit dnet_get_hwaddr ( struct dnet * bp )
{
u16 tmp ;
u8 addr [ 6 ] ;
/*
* from MAC docs :
* " Note that the MAC address is stored in the registers in Hexadecimal
* form . For example , to set the MAC Address to : AC - DE - 48 - 00 - 00 - 80
* would require writing 0xAC ( octet 0 ) to address 0x0B ( high byte of
* Mac_addr [ 15 : 0 ] ) , 0xDE ( octet 1 ) to address 0x0A ( Low byte of
* Mac_addr [ 15 : 0 ] ) , 0x48 ( octet 2 ) to address 0x0D ( high byte of
* Mac_addr [ 15 : 0 ] ) , 0x00 ( octet 3 ) to address 0x0C ( Low byte of
* Mac_addr [ 15 : 0 ] ) , 0x00 ( octet 4 ) to address 0x0F ( high byte of
* Mac_addr [ 15 : 0 ] ) , and 0x80 ( octet 5 ) to address * 0x0E ( Low byte of
* Mac_addr [ 15 : 0 ] ) .
*/
tmp = dnet_readw_mac ( bp , DNET_INTERNAL_MAC_ADDR_0_REG ) ;
* ( ( u16 * ) addr ) = be16_to_cpu ( tmp ) ;
tmp = dnet_readw_mac ( bp , DNET_INTERNAL_MAC_ADDR_1_REG ) ;
* ( ( u16 * ) ( addr + 2 ) ) = be16_to_cpu ( tmp ) ;
tmp = dnet_readw_mac ( bp , DNET_INTERNAL_MAC_ADDR_2_REG ) ;
* ( ( u16 * ) ( addr + 4 ) ) = be16_to_cpu ( tmp ) ;
if ( is_valid_ether_addr ( addr ) )
memcpy ( bp - > dev - > dev_addr , addr , sizeof ( addr ) ) ;
}
static int dnet_mdio_read ( struct mii_bus * bus , int mii_id , int regnum )
{
struct dnet * bp = bus - > priv ;
u16 value ;
while ( ! ( dnet_readw_mac ( bp , DNET_INTERNAL_GMII_MNG_CTL_REG )
& DNET_INTERNAL_GMII_MNG_CMD_FIN ) )
cpu_relax ( ) ;
/* only 5 bits allowed for phy-addr and reg_offset */
mii_id & = 0x1f ;
regnum & = 0x1f ;
/* prepare reg_value for a read */
value = ( mii_id < < 8 ) ;
value | = regnum ;
/* write control word */
dnet_writew_mac ( bp , DNET_INTERNAL_GMII_MNG_CTL_REG , value ) ;
/* wait for end of transfer */
while ( ! ( dnet_readw_mac ( bp , DNET_INTERNAL_GMII_MNG_CTL_REG )
& DNET_INTERNAL_GMII_MNG_CMD_FIN ) )
cpu_relax ( ) ;
value = dnet_readw_mac ( bp , DNET_INTERNAL_GMII_MNG_DAT_REG ) ;
pr_debug ( " mdio_read %02x:%02x <- %04x \n " , mii_id , regnum , value ) ;
return value ;
}
static int dnet_mdio_write ( struct mii_bus * bus , int mii_id , int regnum ,
u16 value )
{
struct dnet * bp = bus - > priv ;
u16 tmp ;
pr_debug ( " mdio_write %02x:%02x <- %04x \n " , mii_id , regnum , value ) ;
while ( ! ( dnet_readw_mac ( bp , DNET_INTERNAL_GMII_MNG_CTL_REG )
& DNET_INTERNAL_GMII_MNG_CMD_FIN ) )
cpu_relax ( ) ;
/* prepare for a write operation */
tmp = ( 1 < < 13 ) ;
/* only 5 bits allowed for phy-addr and reg_offset */
mii_id & = 0x1f ;
regnum & = 0x1f ;
/* only 16 bits on data */
value & = 0xffff ;
/* prepare reg_value for a write */
tmp | = ( mii_id < < 8 ) ;
tmp | = regnum ;
/* write data to write first */
dnet_writew_mac ( bp , DNET_INTERNAL_GMII_MNG_DAT_REG , value ) ;
/* write control word */
dnet_writew_mac ( bp , DNET_INTERNAL_GMII_MNG_CTL_REG , tmp ) ;
while ( ! ( dnet_readw_mac ( bp , DNET_INTERNAL_GMII_MNG_CTL_REG )
& DNET_INTERNAL_GMII_MNG_CMD_FIN ) )
cpu_relax ( ) ;
return 0 ;
}
static int dnet_mdio_reset ( struct mii_bus * bus )
{
return 0 ;
}
static void dnet_handle_link_change ( struct net_device * dev )
{
struct dnet * bp = netdev_priv ( dev ) ;
struct phy_device * phydev = bp - > phy_dev ;
unsigned long flags ;
u32 mode_reg , ctl_reg ;
int status_change = 0 ;
spin_lock_irqsave ( & bp - > lock , flags ) ;
mode_reg = dnet_readw_mac ( bp , DNET_INTERNAL_MODE_REG ) ;
ctl_reg = dnet_readw_mac ( bp , DNET_INTERNAL_RXTX_CONTROL_REG ) ;
if ( phydev - > link ) {
if ( bp - > duplex ! = phydev - > duplex ) {
if ( phydev - > duplex )
ctl_reg & =
~ ( DNET_INTERNAL_RXTX_CONTROL_ENABLEHALFDUP ) ;
else
ctl_reg | =
DNET_INTERNAL_RXTX_CONTROL_ENABLEHALFDUP ;
bp - > duplex = phydev - > duplex ;
status_change = 1 ;
}
if ( bp - > speed ! = phydev - > speed ) {
status_change = 1 ;
switch ( phydev - > speed ) {
case 1000 :
mode_reg | = DNET_INTERNAL_MODE_GBITEN ;
break ;
case 100 :
case 10 :
mode_reg & = ~ DNET_INTERNAL_MODE_GBITEN ;
break ;
default :
printk ( KERN_WARNING
" %s: Ack! Speed (%d) is not "
" 10/100/1000! \n " , dev - > name ,
phydev - > speed ) ;
break ;
}
bp - > speed = phydev - > speed ;
}
}
if ( phydev - > link ! = bp - > link ) {
if ( phydev - > link ) {
mode_reg | =
( DNET_INTERNAL_MODE_RXEN | DNET_INTERNAL_MODE_TXEN ) ;
} else {
mode_reg & =
~ ( DNET_INTERNAL_MODE_RXEN |
DNET_INTERNAL_MODE_TXEN ) ;
bp - > speed = 0 ;
bp - > duplex = - 1 ;
}
bp - > link = phydev - > link ;
status_change = 1 ;
}
if ( status_change ) {
dnet_writew_mac ( bp , DNET_INTERNAL_RXTX_CONTROL_REG , ctl_reg ) ;
dnet_writew_mac ( bp , DNET_INTERNAL_MODE_REG , mode_reg ) ;
}
spin_unlock_irqrestore ( & bp - > lock , flags ) ;
if ( status_change ) {
if ( phydev - > link )
printk ( KERN_INFO " %s: link up (%d/%s) \n " ,
dev - > name , phydev - > speed ,
DUPLEX_FULL = = phydev - > duplex ? " Full " : " Half " ) ;
else
printk ( KERN_INFO " %s: link down \n " , dev - > name ) ;
}
}
static int dnet_mii_probe ( struct net_device * dev )
{
struct dnet * bp = netdev_priv ( dev ) ;
struct phy_device * phydev = NULL ;
int phy_addr ;
/* find the first phy */
for ( phy_addr = 0 ; phy_addr < PHY_MAX_ADDR ; phy_addr + + ) {
if ( bp - > mii_bus - > phy_map [ phy_addr ] ) {
phydev = bp - > mii_bus - > phy_map [ phy_addr ] ;
break ;
}
}
if ( ! phydev ) {
printk ( KERN_ERR " %s: no PHY found \n " , dev - > name ) ;
return - ENODEV ;
}
/* TODO : add pin_irq */
/* attach the mac to the phy */
if ( bp - > capabilities & DNET_HAS_RMII ) {
2009-03-23 07:22:48 +03:00
phydev = phy_connect ( dev , dev_name ( & phydev - > dev ) ,
2009-03-12 09:26:02 +03:00
& dnet_handle_link_change , 0 ,
PHY_INTERFACE_MODE_RMII ) ;
} else {
2009-03-23 07:22:48 +03:00
phydev = phy_connect ( dev , dev_name ( & phydev - > dev ) ,
2009-03-12 09:26:02 +03:00
& dnet_handle_link_change , 0 ,
PHY_INTERFACE_MODE_MII ) ;
}
if ( IS_ERR ( phydev ) ) {
printk ( KERN_ERR " %s: Could not attach to PHY \n " , dev - > name ) ;
return PTR_ERR ( phydev ) ;
}
/* mask with MAC supported features */
if ( bp - > capabilities & DNET_HAS_GIGABIT )
phydev - > supported & = PHY_GBIT_FEATURES ;
else
phydev - > supported & = PHY_BASIC_FEATURES ;
phydev - > supported | = SUPPORTED_Asym_Pause | SUPPORTED_Pause ;
phydev - > advertising = phydev - > supported ;
bp - > link = 0 ;
bp - > speed = 0 ;
bp - > duplex = - 1 ;
bp - > phy_dev = phydev ;
return 0 ;
}
static int dnet_mii_init ( struct dnet * bp )
{
int err , i ;
bp - > mii_bus = mdiobus_alloc ( ) ;
if ( bp - > mii_bus = = NULL )
return - ENOMEM ;
bp - > mii_bus - > name = " dnet_mii_bus " ;
bp - > mii_bus - > read = & dnet_mdio_read ;
bp - > mii_bus - > write = & dnet_mdio_write ;
bp - > mii_bus - > reset = & dnet_mdio_reset ;
snprintf ( bp - > mii_bus - > id , MII_BUS_ID_SIZE , " %x " , 0 ) ;
bp - > mii_bus - > priv = bp ;
bp - > mii_bus - > irq = kmalloc ( sizeof ( int ) * PHY_MAX_ADDR , GFP_KERNEL ) ;
if ( ! bp - > mii_bus - > irq ) {
err = - ENOMEM ;
goto err_out ;
}
for ( i = 0 ; i < PHY_MAX_ADDR ; i + + )
bp - > mii_bus - > irq [ i ] = PHY_POLL ;
platform_set_drvdata ( bp - > dev , bp - > mii_bus ) ;
if ( mdiobus_register ( bp - > mii_bus ) ) {
err = - ENXIO ;
goto err_out_free_mdio_irq ;
}
if ( dnet_mii_probe ( bp - > dev ) ! = 0 ) {
err = - ENXIO ;
goto err_out_unregister_bus ;
}
return 0 ;
err_out_unregister_bus :
mdiobus_unregister ( bp - > mii_bus ) ;
err_out_free_mdio_irq :
kfree ( bp - > mii_bus - > irq ) ;
err_out :
mdiobus_free ( bp - > mii_bus ) ;
return err ;
}
/* For Neptune board: LINK1000 as Link LED and TX as activity LED */
int dnet_phy_marvell_fixup ( struct phy_device * phydev )
{
return phy_write ( phydev , 0x18 , 0x4148 ) ;
}
static void dnet_update_stats ( struct dnet * bp )
{
u32 __iomem * reg = bp - > regs + DNET_RX_PKT_IGNR_CNT ;
u32 * p = & bp - > hw_stats . rx_pkt_ignr ;
u32 * end = & bp - > hw_stats . rx_byte + 1 ;
WARN_ON ( ( unsigned long ) ( end - p - 1 ) ! =
( DNET_RX_BYTE_CNT - DNET_RX_PKT_IGNR_CNT ) / 4 ) ;
for ( ; p < end ; p + + , reg + + )
* p + = readl ( reg ) ;
reg = bp - > regs + DNET_TX_UNICAST_CNT ;
p = & bp - > hw_stats . tx_unicast ;
end = & bp - > hw_stats . tx_byte + 1 ;
WARN_ON ( ( unsigned long ) ( end - p - 1 ) ! =
( DNET_TX_BYTE_CNT - DNET_TX_UNICAST_CNT ) / 4 ) ;
for ( ; p < end ; p + + , reg + + )
* p + = readl ( reg ) ;
}
static int dnet_poll ( struct napi_struct * napi , int budget )
{
struct dnet * bp = container_of ( napi , struct dnet , napi ) ;
struct net_device * dev = bp - > dev ;
int npackets = 0 ;
unsigned int pkt_len ;
struct sk_buff * skb ;
unsigned int * data_ptr ;
u32 int_enable ;
u32 cmd_word ;
int i ;
while ( npackets < budget ) {
/*
* break out of while loop if there are no more
* packets waiting
*/
if ( ! ( dnet_readl ( bp , RX_FIFO_WCNT ) > > 16 ) ) {
2009-03-13 19:51:46 +03:00
napi_complete ( napi ) ;
2009-03-12 09:26:02 +03:00
int_enable = dnet_readl ( bp , INTR_ENB ) ;
int_enable | = DNET_INTR_SRC_RX_CMDFIFOAF ;
dnet_writel ( bp , int_enable , INTR_ENB ) ;
return 0 ;
}
cmd_word = dnet_readl ( bp , RX_LEN_FIFO ) ;
pkt_len = cmd_word & 0xFFFF ;
if ( cmd_word & 0xDF180000 )
printk ( KERN_ERR " %s packet receive error %x \n " ,
__func__ , cmd_word ) ;
skb = dev_alloc_skb ( pkt_len + 5 ) ;
if ( skb ! = NULL ) {
/* Align IP on 16 byte boundaries */
skb_reserve ( skb , 2 ) ;
/*
* ' skb_put ( ) ' points to the start of sk_buff
* data area .
*/
data_ptr = ( unsigned int * ) skb_put ( skb , pkt_len ) ;
for ( i = 0 ; i < ( pkt_len + 3 ) > > 2 ; i + + )
* data_ptr + + = dnet_readl ( bp , RX_DATA_FIFO ) ;
skb - > protocol = eth_type_trans ( skb , dev ) ;
netif_receive_skb ( skb ) ;
npackets + + ;
} else
printk ( KERN_NOTICE
" %s: No memory to allocate a sk_buff of "
" size %u. \n " , dev - > name , pkt_len ) ;
}
budget - = npackets ;
if ( npackets < budget ) {
/* We processed all packets available. Tell NAPI it can
* stop polling then re - enable rx interrupts */
2009-03-13 19:51:46 +03:00
napi_complete ( napi ) ;
2009-03-12 09:26:02 +03:00
int_enable = dnet_readl ( bp , INTR_ENB ) ;
int_enable | = DNET_INTR_SRC_RX_CMDFIFOAF ;
dnet_writel ( bp , int_enable , INTR_ENB ) ;
return 0 ;
}
/* There are still packets waiting */
return 1 ;
}
static irqreturn_t dnet_interrupt ( int irq , void * dev_id )
{
struct net_device * dev = dev_id ;
struct dnet * bp = netdev_priv ( dev ) ;
u32 int_src , int_enable , int_current ;
unsigned long flags ;
unsigned int handled = 0 ;
spin_lock_irqsave ( & bp - > lock , flags ) ;
/* read and clear the DNET irq (clear on read) */
int_src = dnet_readl ( bp , INTR_SRC ) ;
int_enable = dnet_readl ( bp , INTR_ENB ) ;
int_current = int_src & int_enable ;
/* restart the queue if we had stopped it for TX fifo almost full */
if ( int_current & DNET_INTR_SRC_TX_FIFOAE ) {
int_enable = dnet_readl ( bp , INTR_ENB ) ;
int_enable & = ~ DNET_INTR_ENB_TX_FIFOAE ;
dnet_writel ( bp , int_enable , INTR_ENB ) ;
netif_wake_queue ( dev ) ;
handled = 1 ;
}
/* RX FIFO error checking */
if ( int_current &
( DNET_INTR_SRC_RX_CMDFIFOFF | DNET_INTR_SRC_RX_DATAFIFOFF ) ) {
printk ( KERN_ERR " %s: RX fifo error %x, irq %x \n " , __func__ ,
dnet_readl ( bp , RX_STATUS ) , int_current ) ;
/* we can only flush the RX FIFOs */
dnet_writel ( bp , DNET_SYS_CTL_RXFIFOFLUSH , SYS_CTL ) ;
ndelay ( 500 ) ;
dnet_writel ( bp , 0 , SYS_CTL ) ;
handled = 1 ;
}
/* TX FIFO error checking */
if ( int_current &
( DNET_INTR_SRC_TX_FIFOFULL | DNET_INTR_SRC_TX_DISCFRM ) ) {
printk ( KERN_ERR " %s: TX fifo error %x, irq %x \n " , __func__ ,
dnet_readl ( bp , TX_STATUS ) , int_current ) ;
/* we can only flush the TX FIFOs */
dnet_writel ( bp , DNET_SYS_CTL_TXFIFOFLUSH , SYS_CTL ) ;
ndelay ( 500 ) ;
dnet_writel ( bp , 0 , SYS_CTL ) ;
handled = 1 ;
}
if ( int_current & DNET_INTR_SRC_RX_CMDFIFOAF ) {
2009-03-13 19:51:46 +03:00
if ( napi_schedule_prep ( & bp - > napi ) ) {
2009-03-12 09:26:02 +03:00
/*
* There ' s no point taking any more interrupts
* until we have processed the buffers
*/
/* Disable Rx interrupts and schedule NAPI poll */
int_enable = dnet_readl ( bp , INTR_ENB ) ;
int_enable & = ~ DNET_INTR_SRC_RX_CMDFIFOAF ;
dnet_writel ( bp , int_enable , INTR_ENB ) ;
2009-03-13 19:51:46 +03:00
__napi_schedule ( & bp - > napi ) ;
2009-03-12 09:26:02 +03:00
}
handled = 1 ;
}
if ( ! handled )
pr_debug ( " %s: irq %x remains \n " , __func__ , int_current ) ;
spin_unlock_irqrestore ( & bp - > lock , flags ) ;
return IRQ_RETVAL ( handled ) ;
}
# ifdef DEBUG
static inline void dnet_print_skb ( struct sk_buff * skb )
{
int k ;
printk ( KERN_DEBUG PFX " data: " ) ;
for ( k = 0 ; k < skb - > len ; k + + )
printk ( " %02x " , ( unsigned int ) skb - > data [ k ] ) ;
printk ( " \n " ) ;
}
# else
# define dnet_print_skb(skb) do {} while (0)
# endif
2009-08-31 23:50:58 +04:00
static netdev_tx_t dnet_start_xmit ( struct sk_buff * skb , struct net_device * dev )
2009-03-12 09:26:02 +03:00
{
struct dnet * bp = netdev_priv ( dev ) ;
u32 tx_status , irq_enable ;
unsigned int len , i , tx_cmd , wrsz ;
unsigned long flags ;
unsigned int * bufp ;
tx_status = dnet_readl ( bp , TX_STATUS ) ;
2009-03-12 09:28:57 +03:00
pr_debug ( " start_xmit: len %u head %p data %p \n " ,
skb - > len , skb - > head , skb - > data ) ;
2009-03-12 09:26:02 +03:00
dnet_print_skb ( skb ) ;
/* frame size (words) */
len = ( skb - > len + 3 ) > > 2 ;
spin_lock_irqsave ( & bp - > lock , flags ) ;
tx_status = dnet_readl ( bp , TX_STATUS ) ;
2009-03-12 09:28:57 +03:00
bufp = ( unsigned int * ) ( ( ( unsigned long ) skb - > data ) & ~ 0x3UL ) ;
2009-03-12 09:26:02 +03:00
wrsz = ( u32 ) skb - > len + 3 ;
2009-03-12 09:28:57 +03:00
wrsz + = ( ( unsigned long ) skb - > data ) & 0x3 ;
2009-03-12 09:26:02 +03:00
wrsz > > = 2 ;
2009-03-12 09:28:57 +03:00
tx_cmd = ( ( ( ( unsigned long ) ( skb - > data ) ) & 0x03 ) < < 16 ) | ( u32 ) skb - > len ;
2009-03-12 09:26:02 +03:00
/* check if there is enough room for the current frame */
if ( wrsz < ( DNET_FIFO_SIZE - dnet_readl ( bp , TX_FIFO_WCNT ) ) ) {
for ( i = 0 ; i < wrsz ; i + + )
dnet_writel ( bp , * bufp + + , TX_DATA_FIFO ) ;
/*
* inform MAC that a packet ' s written and ready to be
* shipped out
*/
dnet_writel ( bp , tx_cmd , TX_LEN_FIFO ) ;
}
if ( dnet_readl ( bp , TX_FIFO_WCNT ) > DNET_FIFO_TX_DATA_AF_TH ) {
netif_stop_queue ( dev ) ;
tx_status = dnet_readl ( bp , INTR_SRC ) ;
irq_enable = dnet_readl ( bp , INTR_ENB ) ;
irq_enable | = DNET_INTR_ENB_TX_FIFOAE ;
dnet_writel ( bp , irq_enable , INTR_ENB ) ;
}
/* free the buffer */
dev_kfree_skb ( skb ) ;
spin_unlock_irqrestore ( & bp - > lock , flags ) ;
dev - > trans_start = jiffies ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2009-03-12 09:26:02 +03:00
}
static void dnet_reset_hw ( struct dnet * bp )
{
/* put ts_mac in IDLE state i.e. disable rx/tx */
dnet_writew_mac ( bp , DNET_INTERNAL_MODE_REG , DNET_INTERNAL_MODE_FCEN ) ;
/*
* RX FIFO almost full threshold : only cmd FIFO almost full is
* implemented for RX side
*/
dnet_writel ( bp , DNET_FIFO_RX_CMD_AF_TH , RX_FIFO_TH ) ;
/*
* TX FIFO almost empty threshold : only data FIFO almost empty
* is implemented for TX side
*/
dnet_writel ( bp , DNET_FIFO_TX_DATA_AE_TH , TX_FIFO_TH ) ;
/* flush rx/tx fifos */
dnet_writel ( bp , DNET_SYS_CTL_RXFIFOFLUSH | DNET_SYS_CTL_TXFIFOFLUSH ,
SYS_CTL ) ;
msleep ( 1 ) ;
dnet_writel ( bp , 0 , SYS_CTL ) ;
}
static void dnet_init_hw ( struct dnet * bp )
{
u32 config ;
dnet_reset_hw ( bp ) ;
__dnet_set_hwaddr ( bp ) ;
config = dnet_readw_mac ( bp , DNET_INTERNAL_RXTX_CONTROL_REG ) ;
if ( bp - > dev - > flags & IFF_PROMISC )
/* Copy All Frames */
config | = DNET_INTERNAL_RXTX_CONTROL_ENPROMISC ;
if ( ! ( bp - > dev - > flags & IFF_BROADCAST ) )
/* No BroadCast */
config | = DNET_INTERNAL_RXTX_CONTROL_RXMULTICAST ;
config | = DNET_INTERNAL_RXTX_CONTROL_RXPAUSE |
DNET_INTERNAL_RXTX_CONTROL_RXBROADCAST |
DNET_INTERNAL_RXTX_CONTROL_DROPCONTROL |
DNET_INTERNAL_RXTX_CONTROL_DISCFXFCS ;
dnet_writew_mac ( bp , DNET_INTERNAL_RXTX_CONTROL_REG , config ) ;
/* clear irq before enabling them */
config = dnet_readl ( bp , INTR_SRC ) ;
/* enable RX/TX interrupt, recv packet ready interrupt */
dnet_writel ( bp , DNET_INTR_ENB_GLOBAL_ENABLE | DNET_INTR_ENB_RX_SUMMARY |
DNET_INTR_ENB_TX_SUMMARY | DNET_INTR_ENB_RX_FIFOERR |
DNET_INTR_ENB_RX_ERROR | DNET_INTR_ENB_RX_FIFOFULL |
DNET_INTR_ENB_TX_FIFOFULL | DNET_INTR_ENB_TX_DISCFRM |
DNET_INTR_ENB_RX_PKTRDY , INTR_ENB ) ;
}
static int dnet_open ( struct net_device * dev )
{
struct dnet * bp = netdev_priv ( dev ) ;
/* if the phy is not yet register, retry later */
if ( ! bp - > phy_dev )
return - EAGAIN ;
if ( ! is_valid_ether_addr ( dev - > dev_addr ) )
return - EADDRNOTAVAIL ;
napi_enable ( & bp - > napi ) ;
dnet_init_hw ( bp ) ;
phy_start_aneg ( bp - > phy_dev ) ;
/* schedule a link state check */
phy_start ( bp - > phy_dev ) ;
netif_start_queue ( dev ) ;
return 0 ;
}
static int dnet_close ( struct net_device * dev )
{
struct dnet * bp = netdev_priv ( dev ) ;
netif_stop_queue ( dev ) ;
napi_disable ( & bp - > napi ) ;
if ( bp - > phy_dev )
phy_stop ( bp - > phy_dev ) ;
dnet_reset_hw ( bp ) ;
netif_carrier_off ( dev ) ;
return 0 ;
}
static inline void dnet_print_pretty_hwstats ( struct dnet_stats * hwstat )
{
pr_debug ( " %s \n " , __func__ ) ;
pr_debug ( " ----------------------------- RX statistics "
" ------------------------------- \n " ) ;
pr_debug ( " RX_PKT_IGNR_CNT %-8x \n " , hwstat - > rx_pkt_ignr ) ;
pr_debug ( " RX_LEN_CHK_ERR_CNT %-8x \n " , hwstat - > rx_len_chk_err ) ;
pr_debug ( " RX_LNG_FRM_CNT %-8x \n " , hwstat - > rx_lng_frm ) ;
pr_debug ( " RX_SHRT_FRM_CNT %-8x \n " , hwstat - > rx_shrt_frm ) ;
pr_debug ( " RX_IPG_VIOL_CNT %-8x \n " , hwstat - > rx_ipg_viol ) ;
pr_debug ( " RX_CRC_ERR_CNT %-8x \n " , hwstat - > rx_crc_err ) ;
pr_debug ( " RX_OK_PKT_CNT %-8x \n " , hwstat - > rx_ok_pkt ) ;
pr_debug ( " RX_CTL_FRM_CNT %-8x \n " , hwstat - > rx_ctl_frm ) ;
pr_debug ( " RX_PAUSE_FRM_CNT %-8x \n " , hwstat - > rx_pause_frm ) ;
pr_debug ( " RX_MULTICAST_CNT %-8x \n " , hwstat - > rx_multicast ) ;
pr_debug ( " RX_BROADCAST_CNT %-8x \n " , hwstat - > rx_broadcast ) ;
pr_debug ( " RX_VLAN_TAG_CNT %-8x \n " , hwstat - > rx_vlan_tag ) ;
pr_debug ( " RX_PRE_SHRINK_CNT %-8x \n " , hwstat - > rx_pre_shrink ) ;
pr_debug ( " RX_DRIB_NIB_CNT %-8x \n " , hwstat - > rx_drib_nib ) ;
pr_debug ( " RX_UNSUP_OPCD_CNT %-8x \n " , hwstat - > rx_unsup_opcd ) ;
pr_debug ( " RX_BYTE_CNT %-8x \n " , hwstat - > rx_byte ) ;
pr_debug ( " ----------------------------- TX statistics "
" ------------------------------- \n " ) ;
pr_debug ( " TX_UNICAST_CNT %-8x \n " , hwstat - > tx_unicast ) ;
pr_debug ( " TX_PAUSE_FRM_CNT %-8x \n " , hwstat - > tx_pause_frm ) ;
pr_debug ( " TX_MULTICAST_CNT %-8x \n " , hwstat - > tx_multicast ) ;
pr_debug ( " TX_BRDCAST_CNT %-8x \n " , hwstat - > tx_brdcast ) ;
pr_debug ( " TX_VLAN_TAG_CNT %-8x \n " , hwstat - > tx_vlan_tag ) ;
pr_debug ( " TX_BAD_FCS_CNT %-8x \n " , hwstat - > tx_bad_fcs ) ;
pr_debug ( " TX_JUMBO_CNT %-8x \n " , hwstat - > tx_jumbo ) ;
pr_debug ( " TX_BYTE_CNT %-8x \n " , hwstat - > tx_byte ) ;
}
static struct net_device_stats * dnet_get_stats ( struct net_device * dev )
{
struct dnet * bp = netdev_priv ( dev ) ;
struct net_device_stats * nstat = & dev - > stats ;
struct dnet_stats * hwstat = & bp - > hw_stats ;
/* read stats from hardware */
dnet_update_stats ( bp ) ;
/* Convert HW stats into netdevice stats */
nstat - > rx_errors = ( hwstat - > rx_len_chk_err +
hwstat - > rx_lng_frm + hwstat - > rx_shrt_frm +
/* ignore IGP violation error
hwstat - > rx_ipg_viol + */
hwstat - > rx_crc_err +
hwstat - > rx_pre_shrink +
hwstat - > rx_drib_nib + hwstat - > rx_unsup_opcd ) ;
nstat - > tx_errors = hwstat - > tx_bad_fcs ;
nstat - > rx_length_errors = ( hwstat - > rx_len_chk_err +
hwstat - > rx_lng_frm +
hwstat - > rx_shrt_frm + hwstat - > rx_pre_shrink ) ;
nstat - > rx_crc_errors = hwstat - > rx_crc_err ;
nstat - > rx_frame_errors = hwstat - > rx_pre_shrink + hwstat - > rx_drib_nib ;
nstat - > rx_packets = hwstat - > rx_ok_pkt ;
nstat - > tx_packets = ( hwstat - > tx_unicast +
hwstat - > tx_multicast + hwstat - > tx_brdcast ) ;
nstat - > rx_bytes = hwstat - > rx_byte ;
nstat - > tx_bytes = hwstat - > tx_byte ;
nstat - > multicast = hwstat - > rx_multicast ;
nstat - > rx_missed_errors = hwstat - > rx_pkt_ignr ;
dnet_print_pretty_hwstats ( hwstat ) ;
return nstat ;
}
static int dnet_get_settings ( struct net_device * dev , struct ethtool_cmd * cmd )
{
struct dnet * bp = netdev_priv ( dev ) ;
struct phy_device * phydev = bp - > phy_dev ;
if ( ! phydev )
return - ENODEV ;
return phy_ethtool_gset ( phydev , cmd ) ;
}
static int dnet_set_settings ( struct net_device * dev , struct ethtool_cmd * cmd )
{
struct dnet * bp = netdev_priv ( dev ) ;
struct phy_device * phydev = bp - > phy_dev ;
if ( ! phydev )
return - ENODEV ;
return phy_ethtool_sset ( phydev , cmd ) ;
}
static int dnet_ioctl ( struct net_device * dev , struct ifreq * rq , int cmd )
{
struct dnet * bp = netdev_priv ( dev ) ;
struct phy_device * phydev = bp - > phy_dev ;
if ( ! netif_running ( dev ) )
return - EINVAL ;
if ( ! phydev )
return - ENODEV ;
return phy_mii_ioctl ( phydev , if_mii ( rq ) , cmd ) ;
}
static void dnet_get_drvinfo ( struct net_device * dev ,
struct ethtool_drvinfo * info )
{
strcpy ( info - > driver , DRV_NAME ) ;
strcpy ( info - > version , DRV_VERSION ) ;
strcpy ( info - > bus_info , " 0 " ) ;
}
static const struct ethtool_ops dnet_ethtool_ops = {
. get_settings = dnet_get_settings ,
. set_settings = dnet_set_settings ,
. get_drvinfo = dnet_get_drvinfo ,
. get_link = ethtool_op_get_link ,
} ;
static const struct net_device_ops dnet_netdev_ops = {
. ndo_open = dnet_open ,
. ndo_stop = dnet_close ,
. ndo_get_stats = dnet_get_stats ,
. ndo_start_xmit = dnet_start_xmit ,
. ndo_do_ioctl = dnet_ioctl ,
. ndo_set_mac_address = eth_mac_addr ,
. ndo_validate_addr = eth_validate_addr ,
. ndo_change_mtu = eth_change_mtu ,
} ;
static int __devinit dnet_probe ( struct platform_device * pdev )
{
struct resource * res ;
struct net_device * dev ;
struct dnet * bp ;
struct phy_device * phydev ;
int err = - ENXIO ;
unsigned int mem_base , mem_size , irq ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " no mmio resource defined \n " ) ;
goto err_out ;
}
mem_base = res - > start ;
mem_size = resource_size ( res ) ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( ! request_mem_region ( mem_base , mem_size , DRV_NAME ) ) {
dev_err ( & pdev - > dev , " no memory region available \n " ) ;
err = - EBUSY ;
goto err_out ;
}
err = - ENOMEM ;
dev = alloc_etherdev ( sizeof ( * bp ) ) ;
if ( ! dev ) {
dev_err ( & pdev - > dev , " etherdev alloc failed, aborting. \n " ) ;
goto err_out ;
}
/* TODO: Actually, we have some interesting features... */
dev - > features | = 0 ;
bp = netdev_priv ( dev ) ;
bp - > dev = dev ;
SET_NETDEV_DEV ( dev , & pdev - > dev ) ;
spin_lock_init ( & bp - > lock ) ;
bp - > regs = ioremap ( mem_base , mem_size ) ;
if ( ! bp - > regs ) {
dev_err ( & pdev - > dev , " failed to map registers, aborting. \n " ) ;
err = - ENOMEM ;
goto err_out_free_dev ;
}
dev - > irq = irq ;
err = request_irq ( dev - > irq , dnet_interrupt , 0 , DRV_NAME , dev ) ;
if ( err ) {
dev_err ( & pdev - > dev , " Unable to request IRQ %d (error %d) \n " ,
irq , err ) ;
goto err_out_iounmap ;
}
dev - > netdev_ops = & dnet_netdev_ops ;
netif_napi_add ( dev , & bp - > napi , dnet_poll , 64 ) ;
dev - > ethtool_ops = & dnet_ethtool_ops ;
dev - > base_addr = ( unsigned long ) bp - > regs ;
bp - > capabilities = dnet_readl ( bp , VERCAPS ) & DNET_CAPS_MASK ;
dnet_get_hwaddr ( bp ) ;
if ( ! is_valid_ether_addr ( dev - > dev_addr ) ) {
/* choose a random ethernet address */
random_ether_addr ( dev - > dev_addr ) ;
__dnet_set_hwaddr ( bp ) ;
}
err = register_netdev ( dev ) ;
if ( err ) {
dev_err ( & pdev - > dev , " Cannot register net device, aborting. \n " ) ;
goto err_out_free_irq ;
}
/* register the PHY board fixup (for Marvell 88E1111) */
err = phy_register_fixup_for_uid ( 0x01410cc0 , 0xfffffff0 ,
dnet_phy_marvell_fixup ) ;
/* we can live without it, so just issue a warning */
if ( err )
dev_warn ( & pdev - > dev , " Cannot register PHY board fixup. \n " ) ;
if ( dnet_mii_init ( bp ) ! = 0 )
goto err_out_unregister_netdev ;
dev_info ( & pdev - > dev , " Dave DNET at 0x%p (0x%08x) irq %d %pM \n " ,
bp - > regs , mem_base , dev - > irq , dev - > dev_addr ) ;
dev_info ( & pdev - > dev , " has %smdio, %sirq, %sgigabit, %sdma \n " ,
( bp - > capabilities & DNET_HAS_MDIO ) ? " " : " no " ,
( bp - > capabilities & DNET_HAS_IRQ ) ? " " : " no " ,
( bp - > capabilities & DNET_HAS_GIGABIT ) ? " " : " no " ,
( bp - > capabilities & DNET_HAS_DMA ) ? " " : " no " ) ;
phydev = bp - > phy_dev ;
dev_info ( & pdev - > dev , " attached PHY driver [%s] "
" (mii_bus:phy_addr=%s, irq=%d) \n " ,
2009-03-23 07:22:48 +03:00
phydev - > drv - > name , dev_name ( & phydev - > dev ) , phydev - > irq ) ;
2009-03-12 09:26:02 +03:00
return 0 ;
err_out_unregister_netdev :
unregister_netdev ( dev ) ;
err_out_free_irq :
free_irq ( dev - > irq , dev ) ;
err_out_iounmap :
iounmap ( bp - > regs ) ;
err_out_free_dev :
free_netdev ( dev ) ;
err_out :
return err ;
}
static int __devexit dnet_remove ( struct platform_device * pdev )
{
struct net_device * dev ;
struct dnet * bp ;
dev = platform_get_drvdata ( pdev ) ;
if ( dev ) {
bp = netdev_priv ( dev ) ;
if ( bp - > phy_dev )
phy_disconnect ( bp - > phy_dev ) ;
mdiobus_unregister ( bp - > mii_bus ) ;
kfree ( bp - > mii_bus - > irq ) ;
mdiobus_free ( bp - > mii_bus ) ;
unregister_netdev ( dev ) ;
free_irq ( dev - > irq , dev ) ;
iounmap ( bp - > regs ) ;
free_netdev ( dev ) ;
}
return 0 ;
}
static struct platform_driver dnet_driver = {
. probe = dnet_probe ,
. remove = __devexit_p ( dnet_remove ) ,
. driver = {
. name = " dnet " ,
} ,
} ;
static int __init dnet_init ( void )
{
return platform_driver_register ( & dnet_driver ) ;
}
static void __exit dnet_exit ( void )
{
platform_driver_unregister ( & dnet_driver ) ;
}
module_init ( dnet_init ) ;
module_exit ( dnet_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Dave DNET Ethernet driver " ) ;
MODULE_AUTHOR ( " Ilya Yanok <yanok@emcraft.com>, "
" Matteo Vit <matteo.vit@dave.eu> " ) ;