2011-05-06 02:10:01 +04:00
/*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 , USA .
*
* Copyright ( C ) 2011 John Crispin < blogic @ openwrt . org >
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/interrupt.h>
# include <linux/uaccess.h>
# include <linux/in.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/phy.h>
# include <linux/ip.h>
# include <linux/tcp.h>
# include <linux/skbuff.h>
# include <linux/mm.h>
# include <linux/platform_device.h>
# include <linux/ethtool.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/io.h>
# include <asm/checksum.h>
# include <lantiq_soc.h>
# include <xway_dma.h>
# include <lantiq_platform.h>
# define LTQ_ETOP_MDIO 0x11804
# define MDIO_REQUEST 0x80000000
# define MDIO_READ 0x40000000
# define MDIO_ADDR_MASK 0x1f
# define MDIO_ADDR_OFFSET 0x15
# define MDIO_REG_MASK 0x1f
# define MDIO_REG_OFFSET 0x10
# define MDIO_VAL_MASK 0xffff
# define PPE32_CGEN 0x800
# define LQ_PPE32_ENET_MAC_CFG 0x1840
# define LTQ_ETOP_ENETS0 0x11850
# define LTQ_ETOP_MAC_DA0 0x1186C
# define LTQ_ETOP_MAC_DA1 0x11870
# define LTQ_ETOP_CFG 0x16020
# define LTQ_ETOP_IGPLEN 0x16080
# define MAX_DMA_CHAN 0x8
# define MAX_DMA_CRC_LEN 0x4
# define MAX_DMA_DATA_LEN 0x600
# define ETOP_FTCU BIT(28)
# define ETOP_MII_MASK 0xf
# define ETOP_MII_NORMAL 0xd
# define ETOP_MII_REVERSE 0xe
# define ETOP_PLEN_UNDER 0x40
# define ETOP_CGEN 0x800
/* use 2 static channels for TX/RX */
# define LTQ_ETOP_TX_CHANNEL 1
# define LTQ_ETOP_RX_CHANNEL 6
# define IS_TX(x) (x == LTQ_ETOP_TX_CHANNEL)
# define IS_RX(x) (x == LTQ_ETOP_RX_CHANNEL)
# define ltq_etop_r32(x) ltq_r32(ltq_etop_membase + (x))
# define ltq_etop_w32(x, y) ltq_w32(x, ltq_etop_membase + (y))
# define ltq_etop_w32_mask(x, y, z) \
ltq_w32_mask ( x , y , ltq_etop_membase + ( z ) )
# define DRV_VERSION "1.0"
static void __iomem * ltq_etop_membase ;
struct ltq_etop_chan {
int idx ;
int tx_free ;
struct net_device * netdev ;
struct napi_struct napi ;
struct ltq_dma_channel dma ;
struct sk_buff * skb [ LTQ_DESC_NUM ] ;
} ;
struct ltq_etop_priv {
struct net_device * netdev ;
struct ltq_eth_data * pldata ;
struct resource * res ;
struct mii_bus * mii_bus ;
struct phy_device * phydev ;
struct ltq_etop_chan ch [ MAX_DMA_CHAN ] ;
int tx_free [ MAX_DMA_CHAN > > 1 ] ;
spinlock_t lock ;
} ;
static int
ltq_etop_alloc_skb ( struct ltq_etop_chan * ch )
{
ch - > skb [ ch - > dma . desc ] = dev_alloc_skb ( MAX_DMA_DATA_LEN ) ;
if ( ! ch - > skb [ ch - > dma . desc ] )
return - ENOMEM ;
ch - > dma . desc_base [ ch - > dma . desc ] . addr = dma_map_single ( NULL ,
ch - > skb [ ch - > dma . desc ] - > data , MAX_DMA_DATA_LEN ,
DMA_FROM_DEVICE ) ;
ch - > dma . desc_base [ ch - > dma . desc ] . addr =
CPHYSADDR ( ch - > skb [ ch - > dma . desc ] - > data ) ;
ch - > dma . desc_base [ ch - > dma . desc ] . ctl =
LTQ_DMA_OWN | LTQ_DMA_RX_OFFSET ( NET_IP_ALIGN ) |
MAX_DMA_DATA_LEN ;
skb_reserve ( ch - > skb [ ch - > dma . desc ] , NET_IP_ALIGN ) ;
return 0 ;
}
static void
ltq_etop_hw_receive ( struct ltq_etop_chan * ch )
{
struct ltq_etop_priv * priv = netdev_priv ( ch - > netdev ) ;
struct ltq_dma_desc * desc = & ch - > dma . desc_base [ ch - > dma . desc ] ;
struct sk_buff * skb = ch - > skb [ ch - > dma . desc ] ;
int len = ( desc - > ctl & LTQ_DMA_SIZE_MASK ) - MAX_DMA_CRC_LEN ;
unsigned long flags ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
if ( ltq_etop_alloc_skb ( ch ) ) {
netdev_err ( ch - > netdev ,
" failed to allocate new rx buffer, stopping DMA \n " ) ;
ltq_dma_close ( & ch - > dma ) ;
}
ch - > dma . desc + + ;
ch - > dma . desc % = LTQ_DESC_NUM ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
skb_put ( skb , len ) ;
skb - > dev = ch - > netdev ;
skb - > protocol = eth_type_trans ( skb , ch - > netdev ) ;
netif_receive_skb ( skb ) ;
}
static int
ltq_etop_poll_rx ( struct napi_struct * napi , int budget )
{
struct ltq_etop_chan * ch = container_of ( napi ,
struct ltq_etop_chan , napi ) ;
int rx = 0 ;
int complete = 0 ;
while ( ( rx < budget ) & & ! complete ) {
struct ltq_dma_desc * desc = & ch - > dma . desc_base [ ch - > dma . desc ] ;
if ( ( desc - > ctl & ( LTQ_DMA_OWN | LTQ_DMA_C ) ) = = LTQ_DMA_C ) {
ltq_etop_hw_receive ( ch ) ;
rx + + ;
} else {
complete = 1 ;
}
}
if ( complete | | ! rx ) {
napi_complete ( & ch - > napi ) ;
ltq_dma_ack_irq ( & ch - > dma ) ;
}
return rx ;
}
static int
ltq_etop_poll_tx ( struct napi_struct * napi , int budget )
{
struct ltq_etop_chan * ch =
container_of ( napi , struct ltq_etop_chan , napi ) ;
struct ltq_etop_priv * priv = netdev_priv ( ch - > netdev ) ;
struct netdev_queue * txq =
netdev_get_tx_queue ( ch - > netdev , ch - > idx > > 1 ) ;
unsigned long flags ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
while ( ( ch - > dma . desc_base [ ch - > tx_free ] . ctl &
( LTQ_DMA_OWN | LTQ_DMA_C ) ) = = LTQ_DMA_C ) {
dev_kfree_skb_any ( ch - > skb [ ch - > tx_free ] ) ;
ch - > skb [ ch - > tx_free ] = NULL ;
memset ( & ch - > dma . desc_base [ ch - > tx_free ] , 0 ,
sizeof ( struct ltq_dma_desc ) ) ;
ch - > tx_free + + ;
ch - > tx_free % = LTQ_DESC_NUM ;
}
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
if ( netif_tx_queue_stopped ( txq ) )
netif_tx_start_queue ( txq ) ;
napi_complete ( & ch - > napi ) ;
ltq_dma_ack_irq ( & ch - > dma ) ;
return 1 ;
}
static irqreturn_t
ltq_etop_dma_irq ( int irq , void * _priv )
{
struct ltq_etop_priv * priv = _priv ;
int ch = irq - LTQ_DMA_CH0_INT ;
napi_schedule ( & priv - > ch [ ch ] . napi ) ;
return IRQ_HANDLED ;
}
static void
ltq_etop_free_channel ( struct net_device * dev , struct ltq_etop_chan * ch )
{
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
ltq_dma_free ( & ch - > dma ) ;
if ( ch - > dma . irq )
free_irq ( ch - > dma . irq , priv ) ;
if ( IS_RX ( ch - > idx ) ) {
int desc ;
for ( desc = 0 ; desc < LTQ_DESC_NUM ; desc + + )
dev_kfree_skb_any ( ch - > skb [ ch - > dma . desc ] ) ;
}
}
static void
ltq_etop_hw_exit ( struct net_device * dev )
{
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
int i ;
ltq_pmu_disable ( PMU_PPE ) ;
for ( i = 0 ; i < MAX_DMA_CHAN ; i + + )
if ( IS_TX ( i ) | | IS_RX ( i ) )
ltq_etop_free_channel ( dev , & priv - > ch [ i ] ) ;
}
static int
ltq_etop_hw_init ( struct net_device * dev )
{
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
int i ;
ltq_pmu_enable ( PMU_PPE ) ;
switch ( priv - > pldata - > mii_mode ) {
case PHY_INTERFACE_MODE_RMII :
ltq_etop_w32_mask ( ETOP_MII_MASK ,
ETOP_MII_REVERSE , LTQ_ETOP_CFG ) ;
break ;
case PHY_INTERFACE_MODE_MII :
ltq_etop_w32_mask ( ETOP_MII_MASK ,
ETOP_MII_NORMAL , LTQ_ETOP_CFG ) ;
break ;
default :
netdev_err ( dev , " unknown mii mode %d \n " ,
priv - > pldata - > mii_mode ) ;
return - ENOTSUPP ;
}
/* enable crc generation */
ltq_etop_w32 ( PPE32_CGEN , LQ_PPE32_ENET_MAC_CFG ) ;
ltq_dma_init_port ( DMA_PORT_ETOP ) ;
for ( i = 0 ; i < MAX_DMA_CHAN ; i + + ) {
int irq = LTQ_DMA_CH0_INT + i ;
struct ltq_etop_chan * ch = & priv - > ch [ i ] ;
ch - > idx = ch - > dma . nr = i ;
if ( IS_TX ( i ) ) {
ltq_dma_alloc_tx ( & ch - > dma ) ;
request_irq ( irq , ltq_etop_dma_irq , IRQF_DISABLED ,
" etop_tx " , priv ) ;
} else if ( IS_RX ( i ) ) {
ltq_dma_alloc_rx ( & ch - > dma ) ;
for ( ch - > dma . desc = 0 ; ch - > dma . desc < LTQ_DESC_NUM ;
ch - > dma . desc + + )
if ( ltq_etop_alloc_skb ( ch ) )
return - ENOMEM ;
ch - > dma . desc = 0 ;
request_irq ( irq , ltq_etop_dma_irq , IRQF_DISABLED ,
" etop_rx " , priv ) ;
}
ch - > dma . irq = irq ;
}
return 0 ;
}
static void
ltq_etop_get_drvinfo ( struct net_device * dev , struct ethtool_drvinfo * info )
{
strcpy ( info - > driver , " Lantiq ETOP " ) ;
strcpy ( info - > bus_info , " internal " ) ;
strcpy ( info - > version , DRV_VERSION ) ;
}
static int
ltq_etop_get_settings ( struct net_device * dev , struct ethtool_cmd * cmd )
{
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
return phy_ethtool_gset ( priv - > phydev , cmd ) ;
}
static int
ltq_etop_set_settings ( struct net_device * dev , struct ethtool_cmd * cmd )
{
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
return phy_ethtool_sset ( priv - > phydev , cmd ) ;
}
static int
ltq_etop_nway_reset ( struct net_device * dev )
{
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
return phy_start_aneg ( priv - > phydev ) ;
}
static const struct ethtool_ops ltq_etop_ethtool_ops = {
. get_drvinfo = ltq_etop_get_drvinfo ,
. get_settings = ltq_etop_get_settings ,
. set_settings = ltq_etop_set_settings ,
. nway_reset = ltq_etop_nway_reset ,
} ;
static int
ltq_etop_mdio_wr ( struct mii_bus * bus , int phy_addr , int phy_reg , u16 phy_data )
{
u32 val = MDIO_REQUEST |
( ( phy_addr & MDIO_ADDR_MASK ) < < MDIO_ADDR_OFFSET ) |
( ( phy_reg & MDIO_REG_MASK ) < < MDIO_REG_OFFSET ) |
phy_data ;
while ( ltq_etop_r32 ( LTQ_ETOP_MDIO ) & MDIO_REQUEST )
;
ltq_etop_w32 ( val , LTQ_ETOP_MDIO ) ;
return 0 ;
}
static int
ltq_etop_mdio_rd ( struct mii_bus * bus , int phy_addr , int phy_reg )
{
u32 val = MDIO_REQUEST | MDIO_READ |
( ( phy_addr & MDIO_ADDR_MASK ) < < MDIO_ADDR_OFFSET ) |
( ( phy_reg & MDIO_REG_MASK ) < < MDIO_REG_OFFSET ) ;
while ( ltq_etop_r32 ( LTQ_ETOP_MDIO ) & MDIO_REQUEST )
;
ltq_etop_w32 ( val , LTQ_ETOP_MDIO ) ;
while ( ltq_etop_r32 ( LTQ_ETOP_MDIO ) & MDIO_REQUEST )
;
val = ltq_etop_r32 ( LTQ_ETOP_MDIO ) & MDIO_VAL_MASK ;
return val ;
}
static void
ltq_etop_mdio_link ( struct net_device * dev )
{
/* nothing to do */
}
static int
ltq_etop_mdio_probe ( struct net_device * dev )
{
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
struct phy_device * phydev = NULL ;
int phy_addr ;
for ( phy_addr = 0 ; phy_addr < PHY_MAX_ADDR ; phy_addr + + ) {
if ( priv - > mii_bus - > phy_map [ phy_addr ] ) {
phydev = priv - > mii_bus - > phy_map [ phy_addr ] ;
break ;
}
}
if ( ! phydev ) {
netdev_err ( dev , " no PHY found \n " ) ;
return - ENODEV ;
}
phydev = phy_connect ( dev , dev_name ( & phydev - > dev ) , & ltq_etop_mdio_link ,
0 , priv - > pldata - > mii_mode ) ;
if ( IS_ERR ( phydev ) ) {
netdev_err ( dev , " Could not attach to PHY \n " ) ;
return PTR_ERR ( phydev ) ;
}
phydev - > supported & = ( SUPPORTED_10baseT_Half
| SUPPORTED_10baseT_Full
| SUPPORTED_100baseT_Half
| SUPPORTED_100baseT_Full
| SUPPORTED_Autoneg
| SUPPORTED_MII
| SUPPORTED_TP ) ;
phydev - > advertising = phydev - > supported ;
priv - > phydev = phydev ;
pr_info ( " %s: attached PHY [%s] (phy_addr=%s, irq=%d) \n " ,
dev - > name , phydev - > drv - > name ,
dev_name ( & phydev - > dev ) , phydev - > irq ) ;
return 0 ;
}
static int
ltq_etop_mdio_init ( struct net_device * dev )
{
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
int i ;
int err ;
priv - > mii_bus = mdiobus_alloc ( ) ;
if ( ! priv - > mii_bus ) {
netdev_err ( dev , " failed to allocate mii bus \n " ) ;
err = - ENOMEM ;
goto err_out ;
}
priv - > mii_bus - > priv = dev ;
priv - > mii_bus - > read = ltq_etop_mdio_rd ;
priv - > mii_bus - > write = ltq_etop_mdio_wr ;
priv - > mii_bus - > name = " ltq_mii " ;
snprintf ( priv - > mii_bus - > id , MII_BUS_ID_SIZE , " %x " , 0 ) ;
priv - > mii_bus - > irq = kmalloc ( sizeof ( int ) * PHY_MAX_ADDR , GFP_KERNEL ) ;
if ( ! priv - > mii_bus - > irq ) {
err = - ENOMEM ;
goto err_out_free_mdiobus ;
}
for ( i = 0 ; i < PHY_MAX_ADDR ; + + i )
priv - > mii_bus - > irq [ i ] = PHY_POLL ;
if ( mdiobus_register ( priv - > mii_bus ) ) {
err = - ENXIO ;
goto err_out_free_mdio_irq ;
}
if ( ltq_etop_mdio_probe ( dev ) ) {
err = - ENXIO ;
goto err_out_unregister_bus ;
}
return 0 ;
err_out_unregister_bus :
mdiobus_unregister ( priv - > mii_bus ) ;
err_out_free_mdio_irq :
kfree ( priv - > mii_bus - > irq ) ;
err_out_free_mdiobus :
mdiobus_free ( priv - > mii_bus ) ;
err_out :
return err ;
}
static void
ltq_etop_mdio_cleanup ( struct net_device * dev )
{
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
phy_disconnect ( priv - > phydev ) ;
mdiobus_unregister ( priv - > mii_bus ) ;
kfree ( priv - > mii_bus - > irq ) ;
mdiobus_free ( priv - > mii_bus ) ;
}
static int
ltq_etop_open ( struct net_device * dev )
{
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
int i ;
for ( i = 0 ; i < MAX_DMA_CHAN ; i + + ) {
struct ltq_etop_chan * ch = & priv - > ch [ i ] ;
if ( ! IS_TX ( i ) & & ( ! IS_RX ( i ) ) )
continue ;
ltq_dma_open ( & ch - > dma ) ;
napi_enable ( & ch - > napi ) ;
}
phy_start ( priv - > phydev ) ;
netif_tx_start_all_queues ( dev ) ;
return 0 ;
}
static int
ltq_etop_stop ( struct net_device * dev )
{
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
int i ;
netif_tx_stop_all_queues ( dev ) ;
phy_stop ( priv - > phydev ) ;
for ( i = 0 ; i < MAX_DMA_CHAN ; i + + ) {
struct ltq_etop_chan * ch = & priv - > ch [ i ] ;
if ( ! IS_RX ( i ) & & ! IS_TX ( i ) )
continue ;
napi_disable ( & ch - > napi ) ;
ltq_dma_close ( & ch - > dma ) ;
}
return 0 ;
}
static int
ltq_etop_tx ( struct sk_buff * skb , struct net_device * dev )
{
int queue = skb_get_queue_mapping ( skb ) ;
struct netdev_queue * txq = netdev_get_tx_queue ( dev , queue ) ;
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
struct ltq_etop_chan * ch = & priv - > ch [ ( queue < < 1 ) | 1 ] ;
struct ltq_dma_desc * desc = & ch - > dma . desc_base [ ch - > dma . desc ] ;
int len ;
unsigned long flags ;
u32 byte_offset ;
len = skb - > len < ETH_ZLEN ? ETH_ZLEN : skb - > len ;
if ( ( desc - > ctl & ( LTQ_DMA_OWN | LTQ_DMA_C ) ) | | ch - > skb [ ch - > dma . desc ] ) {
dev_kfree_skb_any ( skb ) ;
netdev_err ( dev , " tx ring full \n " ) ;
netif_tx_stop_queue ( txq ) ;
return NETDEV_TX_BUSY ;
}
/* dma needs to start on a 16 byte aligned address */
byte_offset = CPHYSADDR ( skb - > data ) % 16 ;
ch - > skb [ ch - > dma . desc ] = skb ;
dev - > trans_start = jiffies ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
desc - > addr = ( ( unsigned int ) dma_map_single ( NULL , skb - > data , len ,
DMA_TO_DEVICE ) ) - byte_offset ;
wmb ( ) ;
desc - > ctl = LTQ_DMA_OWN | LTQ_DMA_SOP | LTQ_DMA_EOP |
LTQ_DMA_TX_OFFSET ( byte_offset ) | ( len & LTQ_DMA_SIZE_MASK ) ;
ch - > dma . desc + + ;
ch - > dma . desc % = LTQ_DESC_NUM ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
if ( ch - > dma . desc_base [ ch - > dma . desc ] . ctl & LTQ_DMA_OWN )
netif_tx_stop_queue ( txq ) ;
return NETDEV_TX_OK ;
}
static int
ltq_etop_change_mtu ( struct net_device * dev , int new_mtu )
{
int ret = eth_change_mtu ( dev , new_mtu ) ;
if ( ! ret ) {
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
unsigned long flags ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
ltq_etop_w32 ( ( ETOP_PLEN_UNDER < < 16 ) | new_mtu ,
LTQ_ETOP_IGPLEN ) ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
}
return ret ;
}
static int
ltq_etop_ioctl ( struct net_device * dev , struct ifreq * rq , int cmd )
{
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
/* TODO: mii-toll reports "No MII transceiver present!." ?!*/
return phy_mii_ioctl ( priv - > phydev , rq , cmd ) ;
}
static int
ltq_etop_set_mac_address ( struct net_device * dev , void * p )
{
int ret = eth_mac_addr ( dev , p ) ;
if ( ! ret ) {
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
unsigned long flags ;
/* store the mac for the unicast filter */
spin_lock_irqsave ( & priv - > lock , flags ) ;
ltq_etop_w32 ( * ( ( u32 * ) dev - > dev_addr ) , LTQ_ETOP_MAC_DA0 ) ;
ltq_etop_w32 ( * ( ( u16 * ) & dev - > dev_addr [ 4 ] ) < < 16 ,
LTQ_ETOP_MAC_DA1 ) ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
}
return ret ;
}
static void
ltq_etop_set_multicast_list ( struct net_device * dev )
{
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
unsigned long flags ;
/* ensure that the unicast filter is not enabled in promiscious mode */
spin_lock_irqsave ( & priv - > lock , flags ) ;
if ( ( dev - > flags & IFF_PROMISC ) | | ( dev - > flags & IFF_ALLMULTI ) )
ltq_etop_w32_mask ( ETOP_FTCU , 0 , LTQ_ETOP_ENETS0 ) ;
else
ltq_etop_w32_mask ( 0 , ETOP_FTCU , LTQ_ETOP_ENETS0 ) ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
}
static u16
ltq_etop_select_queue ( struct net_device * dev , struct sk_buff * skb )
{
/* we are currently only using the first queue */
return 0 ;
}
static int
ltq_etop_init ( struct net_device * dev )
{
struct ltq_etop_priv * priv = netdev_priv ( dev ) ;
struct sockaddr mac ;
int err ;
ether_setup ( dev ) ;
dev - > watchdog_timeo = 10 * HZ ;
err = ltq_etop_hw_init ( dev ) ;
if ( err )
goto err_hw ;
ltq_etop_change_mtu ( dev , 1500 ) ;
memcpy ( & mac , & priv - > pldata - > mac , sizeof ( struct sockaddr ) ) ;
if ( ! is_valid_ether_addr ( mac . sa_data ) ) {
pr_warn ( " etop: invalid MAC, using random \n " ) ;
random_ether_addr ( mac . sa_data ) ;
}
err = ltq_etop_set_mac_address ( dev , & mac ) ;
if ( err )
goto err_netdev ;
ltq_etop_set_multicast_list ( dev ) ;
err = ltq_etop_mdio_init ( dev ) ;
if ( err )
goto err_netdev ;
return 0 ;
err_netdev :
unregister_netdev ( dev ) ;
free_netdev ( dev ) ;
err_hw :
ltq_etop_hw_exit ( dev ) ;
return err ;
}
static void
ltq_etop_tx_timeout ( struct net_device * dev )
{
int err ;
ltq_etop_hw_exit ( dev ) ;
err = ltq_etop_hw_init ( dev ) ;
if ( err )
goto err_hw ;
dev - > trans_start = jiffies ;
netif_wake_queue ( dev ) ;
return ;
err_hw :
ltq_etop_hw_exit ( dev ) ;
netdev_err ( dev , " failed to restart etop after TX timeout \n " ) ;
}
static const struct net_device_ops ltq_eth_netdev_ops = {
. ndo_open = ltq_etop_open ,
. ndo_stop = ltq_etop_stop ,
. ndo_start_xmit = ltq_etop_tx ,
. ndo_change_mtu = ltq_etop_change_mtu ,
. ndo_do_ioctl = ltq_etop_ioctl ,
. ndo_set_mac_address = ltq_etop_set_mac_address ,
. ndo_validate_addr = eth_validate_addr ,
2011-08-16 10:29:01 +04:00
. ndo_set_rx_mode = ltq_etop_set_multicast_list ,
2011-05-06 02:10:01 +04:00
. ndo_select_queue = ltq_etop_select_queue ,
. ndo_init = ltq_etop_init ,
. ndo_tx_timeout = ltq_etop_tx_timeout ,
} ;
static int __init
ltq_etop_probe ( struct platform_device * pdev )
{
struct net_device * dev ;
struct ltq_etop_priv * priv ;
struct resource * res ;
int err ;
int i ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " failed to get etop resource \n " ) ;
err = - ENOENT ;
goto err_out ;
}
res = devm_request_mem_region ( & pdev - > dev , res - > start ,
resource_size ( res ) , dev_name ( & pdev - > dev ) ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " failed to request etop resource \n " ) ;
err = - EBUSY ;
goto err_out ;
}
ltq_etop_membase = devm_ioremap_nocache ( & pdev - > dev ,
res - > start , resource_size ( res ) ) ;
if ( ! ltq_etop_membase ) {
dev_err ( & pdev - > dev , " failed to remap etop engine %d \n " ,
pdev - > id ) ;
err = - ENOMEM ;
goto err_out ;
}
dev = alloc_etherdev_mq ( sizeof ( struct ltq_etop_priv ) , 4 ) ;
strcpy ( dev - > name , " eth%d " ) ;
dev - > netdev_ops = & ltq_eth_netdev_ops ;
dev - > ethtool_ops = & ltq_etop_ethtool_ops ;
priv = netdev_priv ( dev ) ;
priv - > res = res ;
priv - > pldata = dev_get_platdata ( & pdev - > dev ) ;
priv - > netdev = dev ;
spin_lock_init ( & priv - > lock ) ;
for ( i = 0 ; i < MAX_DMA_CHAN ; i + + ) {
if ( IS_TX ( i ) )
netif_napi_add ( dev , & priv - > ch [ i ] . napi ,
ltq_etop_poll_tx , 8 ) ;
else if ( IS_RX ( i ) )
netif_napi_add ( dev , & priv - > ch [ i ] . napi ,
ltq_etop_poll_rx , 32 ) ;
priv - > ch [ i ] . netdev = dev ;
}
err = register_netdev ( dev ) ;
if ( err )
goto err_free ;
platform_set_drvdata ( pdev , dev ) ;
return 0 ;
err_free :
kfree ( dev ) ;
err_out :
return err ;
}
static int __devexit
ltq_etop_remove ( struct platform_device * pdev )
{
struct net_device * dev = platform_get_drvdata ( pdev ) ;
if ( dev ) {
netif_tx_stop_all_queues ( dev ) ;
ltq_etop_hw_exit ( dev ) ;
ltq_etop_mdio_cleanup ( dev ) ;
unregister_netdev ( dev ) ;
}
return 0 ;
}
static struct platform_driver ltq_mii_driver = {
. remove = __devexit_p ( ltq_etop_remove ) ,
. driver = {
. name = " ltq_etop " ,
. owner = THIS_MODULE ,
} ,
} ;
int __init
init_ltq_etop ( void )
{
int ret = platform_driver_probe ( & ltq_mii_driver , ltq_etop_probe ) ;
if ( ret )
pr_err ( " ltq_etop: Error registering platfom driver! " ) ;
return ret ;
}
static void __exit
exit_ltq_etop ( void )
{
platform_driver_unregister ( & ltq_mii_driver ) ;
}
module_init ( init_ltq_etop ) ;
module_exit ( exit_ltq_etop ) ;
MODULE_AUTHOR ( " John Crispin <blogic@openwrt.org> " ) ;
MODULE_DESCRIPTION ( " Lantiq SoC ETOP " ) ;
MODULE_LICENSE ( " GPL " ) ;