2005-09-09 23:10:10 +04:00
/*
* rionet - Ethernet driver over RapidIO messaging services
*
* Copyright 2005 MontaVista Software , Inc .
* Matt Porter < mporter @ kernel . crashing . org >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/dma-mapping.h>
# include <linux/delay.h>
# include <linux/rio.h>
# include <linux/rio_drv.h>
# include <linux/rio_ids.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/skbuff.h>
# include <linux/crc32.h>
# include <linux/ethtool.h>
# define DRV_NAME "rionet"
# define DRV_VERSION "0.2"
# define DRV_AUTHOR "Matt Porter <mporter@kernel.crashing.org>"
# define DRV_DESC "Ethernet over RapidIO"
MODULE_AUTHOR ( DRV_AUTHOR ) ;
MODULE_DESCRIPTION ( DRV_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
# define RIONET_DEFAULT_MSGLEVEL \
( NETIF_MSG_DRV | \
NETIF_MSG_LINK | \
NETIF_MSG_RX_ERR | \
NETIF_MSG_TX_ERR )
# define RIONET_DOORBELL_JOIN 0x1000
# define RIONET_DOORBELL_LEAVE 0x1001
# define RIONET_MAILBOX 0
# define RIONET_TX_RING_SIZE CONFIG_RIONET_TX_SIZE
# define RIONET_RX_RING_SIZE CONFIG_RIONET_RX_SIZE
static LIST_HEAD ( rionet_peers ) ;
struct rionet_private {
struct rio_mport * mport ;
struct sk_buff * rx_skb [ RIONET_RX_RING_SIZE ] ;
struct sk_buff * tx_skb [ RIONET_TX_RING_SIZE ] ;
int rx_slot ;
int tx_slot ;
int tx_cnt ;
int ack_slot ;
spinlock_t lock ;
spinlock_t tx_lock ;
u32 msg_enable ;
} ;
struct rionet_peer {
struct list_head node ;
struct rio_dev * rdev ;
struct resource * res ;
} ;
static int rionet_check = 0 ;
static int rionet_capable = 1 ;
/*
2009-07-07 13:54:23 +04:00
* This is a fast lookup table for translating TX
2005-09-09 23:10:10 +04:00
* Ethernet packets into a destination RIO device . It
* could be made into a hash table to save memory depending
* on system trade - offs .
*/
2008-04-19 00:33:42 +04:00
static struct rio_dev * * rionet_active ;
2005-09-09 23:10:10 +04:00
# define is_rionet_capable(pef, src_ops, dst_ops) \
( ( pef & RIO_PEF_INB_MBOX ) & & \
( pef & RIO_PEF_INB_DOORBELL ) & & \
( src_ops & RIO_SRC_OPS_DOORBELL ) & & \
( dst_ops & RIO_DST_OPS_DOORBELL ) )
# define dev_rionet_capable(dev) \
is_rionet_capable ( dev - > pef , dev - > src_ops , dev - > dst_ops )
# define RIONET_MAC_MATCH(x) (*(u32 *)x == 0x00010001)
# define RIONET_GET_DESTID(x) (*(u16 *)(x + 4))
static int rionet_rx_clean ( struct net_device * ndev )
{
int i ;
int error = 0 ;
2008-11-13 10:38:14 +03:00
struct rionet_private * rnet = netdev_priv ( ndev ) ;
2005-09-09 23:10:10 +04:00
void * data ;
i = rnet - > rx_slot ;
do {
if ( ! rnet - > rx_skb [ i ] )
continue ;
if ( ! ( data = rio_get_inb_message ( rnet - > mport , RIONET_MAILBOX ) ) )
break ;
rnet - > rx_skb [ i ] - > data = data ;
skb_put ( rnet - > rx_skb [ i ] , RIO_MAX_MSG_SIZE ) ;
rnet - > rx_skb [ i ] - > protocol =
eth_type_trans ( rnet - > rx_skb [ i ] , ndev ) ;
error = netif_rx ( rnet - > rx_skb [ i ] ) ;
if ( error = = NET_RX_DROP ) {
2007-10-04 04:41:50 +04:00
ndev - > stats . rx_dropped + + ;
2005-09-09 23:10:10 +04:00
} else {
2007-10-04 04:41:50 +04:00
ndev - > stats . rx_packets + + ;
ndev - > stats . rx_bytes + = RIO_MAX_MSG_SIZE ;
2005-09-09 23:10:10 +04:00
}
} while ( ( i = ( i + 1 ) % RIONET_RX_RING_SIZE ) ! = rnet - > rx_slot ) ;
return i ;
}
static void rionet_rx_fill ( struct net_device * ndev , int end )
{
int i ;
2008-11-13 10:38:14 +03:00
struct rionet_private * rnet = netdev_priv ( ndev ) ;
2005-09-09 23:10:10 +04:00
i = rnet - > rx_slot ;
do {
rnet - > rx_skb [ i ] = dev_alloc_skb ( RIO_MAX_MSG_SIZE ) ;
if ( ! rnet - > rx_skb [ i ] )
break ;
rio_add_inb_buffer ( rnet - > mport , RIONET_MAILBOX ,
rnet - > rx_skb [ i ] - > data ) ;
} while ( ( i = ( i + 1 ) % RIONET_RX_RING_SIZE ) ! = end ) ;
rnet - > rx_slot = i ;
}
static int rionet_queue_tx_msg ( struct sk_buff * skb , struct net_device * ndev ,
struct rio_dev * rdev )
{
2008-11-13 10:38:14 +03:00
struct rionet_private * rnet = netdev_priv ( ndev ) ;
2005-09-09 23:10:10 +04:00
rio_add_outb_message ( rnet - > mport , rdev , 0 , skb - > data , skb - > len ) ;
rnet - > tx_skb [ rnet - > tx_slot ] = skb ;
2007-10-04 04:41:50 +04:00
ndev - > stats . tx_packets + + ;
ndev - > stats . tx_bytes + = skb - > len ;
2005-09-09 23:10:10 +04:00
if ( + + rnet - > tx_cnt = = RIONET_TX_RING_SIZE )
netif_stop_queue ( ndev ) ;
+ + rnet - > tx_slot ;
rnet - > tx_slot & = ( RIONET_TX_RING_SIZE - 1 ) ;
if ( netif_msg_tx_queued ( rnet ) )
printk ( KERN_INFO " %s: queued skb %8.8x len %8.8x \n " , DRV_NAME ,
( u32 ) skb , skb - > len ) ;
return 0 ;
}
static int rionet_start_xmit ( struct sk_buff * skb , struct net_device * ndev )
{
int i ;
2008-11-13 10:38:14 +03:00
struct rionet_private * rnet = netdev_priv ( ndev ) ;
2005-09-09 23:10:10 +04:00
struct ethhdr * eth = ( struct ethhdr * ) skb - > data ;
u16 destid ;
unsigned long flags ;
local_irq_save ( flags ) ;
if ( ! spin_trylock ( & rnet - > tx_lock ) ) {
local_irq_restore ( flags ) ;
return NETDEV_TX_LOCKED ;
}
if ( ( rnet - > tx_cnt + 1 ) > RIONET_TX_RING_SIZE ) {
netif_stop_queue ( ndev ) ;
spin_unlock_irqrestore ( & rnet - > tx_lock , flags ) ;
printk ( KERN_ERR " %s: BUG! Tx Ring full when queue awake! \n " ,
ndev - > name ) ;
return NETDEV_TX_BUSY ;
}
if ( eth - > h_dest [ 0 ] & 0x01 ) {
2008-04-19 00:33:42 +04:00
for ( i = 0 ; i < RIO_MAX_ROUTE_ENTRIES ( rnet - > mport - > sys_size ) ;
i + + )
2005-09-09 23:10:10 +04:00
if ( rionet_active [ i ] )
rionet_queue_tx_msg ( skb , ndev ,
rionet_active [ i ] ) ;
} else if ( RIONET_MAC_MATCH ( eth - > h_dest ) ) {
destid = RIONET_GET_DESTID ( eth - > h_dest ) ;
if ( rionet_active [ destid ] )
rionet_queue_tx_msg ( skb , ndev , rionet_active [ destid ] ) ;
}
spin_unlock_irqrestore ( & rnet - > tx_lock , flags ) ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2005-09-09 23:10:10 +04:00
}
static void rionet_dbell_event ( struct rio_mport * mport , void * dev_id , u16 sid , u16 tid ,
u16 info )
{
struct net_device * ndev = dev_id ;
2008-11-13 10:38:14 +03:00
struct rionet_private * rnet = netdev_priv ( ndev ) ;
2005-09-09 23:10:10 +04:00
struct rionet_peer * peer ;
if ( netif_msg_intr ( rnet ) )
printk ( KERN_INFO " %s: doorbell sid %4.4x tid %4.4x info %4.4x " ,
DRV_NAME , sid , tid , info ) ;
if ( info = = RIONET_DOORBELL_JOIN ) {
if ( ! rionet_active [ sid ] ) {
list_for_each_entry ( peer , & rionet_peers , node ) {
if ( peer - > rdev - > destid = = sid )
rionet_active [ sid ] = peer - > rdev ;
}
rio_mport_send_doorbell ( mport , sid ,
RIONET_DOORBELL_JOIN ) ;
}
} else if ( info = = RIONET_DOORBELL_LEAVE ) {
rionet_active [ sid ] = NULL ;
} else {
if ( netif_msg_intr ( rnet ) )
printk ( KERN_WARNING " %s: unhandled doorbell \n " ,
DRV_NAME ) ;
}
}
static void rionet_inb_msg_event ( struct rio_mport * mport , void * dev_id , int mbox , int slot )
{
int n ;
struct net_device * ndev = dev_id ;
2008-11-13 10:38:14 +03:00
struct rionet_private * rnet = netdev_priv ( ndev ) ;
2005-09-09 23:10:10 +04:00
if ( netif_msg_intr ( rnet ) )
printk ( KERN_INFO " %s: inbound message event, mbox %d slot %d \n " ,
DRV_NAME , mbox , slot ) ;
spin_lock ( & rnet - > lock ) ;
if ( ( n = rionet_rx_clean ( ndev ) ) ! = rnet - > rx_slot )
rionet_rx_fill ( ndev , n ) ;
spin_unlock ( & rnet - > lock ) ;
}
static void rionet_outb_msg_event ( struct rio_mport * mport , void * dev_id , int mbox , int slot )
{
struct net_device * ndev = dev_id ;
2008-11-13 10:38:14 +03:00
struct rionet_private * rnet = netdev_priv ( ndev ) ;
2005-09-09 23:10:10 +04:00
spin_lock ( & rnet - > lock ) ;
if ( netif_msg_intr ( rnet ) )
printk ( KERN_INFO
" %s: outbound message event, mbox %d slot %d \n " ,
DRV_NAME , mbox , slot ) ;
while ( rnet - > tx_cnt & & ( rnet - > ack_slot ! = slot ) ) {
/* dma unmap single */
dev_kfree_skb_irq ( rnet - > tx_skb [ rnet - > ack_slot ] ) ;
rnet - > tx_skb [ rnet - > ack_slot ] = NULL ;
+ + rnet - > ack_slot ;
rnet - > ack_slot & = ( RIONET_TX_RING_SIZE - 1 ) ;
rnet - > tx_cnt - - ;
}
if ( rnet - > tx_cnt < RIONET_TX_RING_SIZE )
netif_wake_queue ( ndev ) ;
spin_unlock ( & rnet - > lock ) ;
}
static int rionet_open ( struct net_device * ndev )
{
int i , rc = 0 ;
struct rionet_peer * peer , * tmp ;
u32 pwdcsr ;
2008-11-13 10:38:14 +03:00
struct rionet_private * rnet = netdev_priv ( ndev ) ;
2005-09-09 23:10:10 +04:00
if ( netif_msg_ifup ( rnet ) )
printk ( KERN_INFO " %s: open \n " , DRV_NAME ) ;
if ( ( rc = rio_request_inb_dbell ( rnet - > mport ,
( void * ) ndev ,
RIONET_DOORBELL_JOIN ,
RIONET_DOORBELL_LEAVE ,
rionet_dbell_event ) ) < 0 )
goto out ;
if ( ( rc = rio_request_inb_mbox ( rnet - > mport ,
( void * ) ndev ,
RIONET_MAILBOX ,
RIONET_RX_RING_SIZE ,
rionet_inb_msg_event ) ) < 0 )
goto out ;
if ( ( rc = rio_request_outb_mbox ( rnet - > mport ,
( void * ) ndev ,
RIONET_MAILBOX ,
RIONET_TX_RING_SIZE ,
rionet_outb_msg_event ) ) < 0 )
goto out ;
/* Initialize inbound message ring */
for ( i = 0 ; i < RIONET_RX_RING_SIZE ; i + + )
rnet - > rx_skb [ i ] = NULL ;
rnet - > rx_slot = 0 ;
rionet_rx_fill ( ndev , 0 ) ;
rnet - > tx_slot = 0 ;
rnet - > tx_cnt = 0 ;
rnet - > ack_slot = 0 ;
netif_carrier_on ( ndev ) ;
netif_start_queue ( ndev ) ;
list_for_each_entry_safe ( peer , tmp , & rionet_peers , node ) {
if ( ! ( peer - > res = rio_request_outb_dbell ( peer - > rdev ,
RIONET_DOORBELL_JOIN ,
RIONET_DOORBELL_LEAVE ) ) )
{
printk ( KERN_ERR " %s: error requesting doorbells \n " ,
DRV_NAME ) ;
continue ;
}
/*
* If device has initialized inbound doorbells ,
* send a join message
*/
rio_read_config_32 ( peer - > rdev , RIO_WRITE_PORT_CSR , & pwdcsr ) ;
if ( pwdcsr & RIO_DOORBELL_AVAIL )
rio_send_doorbell ( peer - > rdev , RIONET_DOORBELL_JOIN ) ;
}
out :
return rc ;
}
static int rionet_close ( struct net_device * ndev )
{
2008-11-13 10:38:14 +03:00
struct rionet_private * rnet = netdev_priv ( ndev ) ;
2005-09-09 23:10:10 +04:00
struct rionet_peer * peer , * tmp ;
int i ;
if ( netif_msg_ifup ( rnet ) )
printk ( KERN_INFO " %s: close \n " , DRV_NAME ) ;
netif_stop_queue ( ndev ) ;
netif_carrier_off ( ndev ) ;
for ( i = 0 ; i < RIONET_RX_RING_SIZE ; i + + )
2009-02-25 03:18:12 +03:00
kfree_skb ( rnet - > rx_skb [ i ] ) ;
2005-09-09 23:10:10 +04:00
list_for_each_entry_safe ( peer , tmp , & rionet_peers , node ) {
if ( rionet_active [ peer - > rdev - > destid ] ) {
rio_send_doorbell ( peer - > rdev , RIONET_DOORBELL_LEAVE ) ;
rionet_active [ peer - > rdev - > destid ] = NULL ;
}
rio_release_outb_dbell ( peer - > rdev , peer - > res ) ;
}
rio_release_inb_dbell ( rnet - > mport , RIONET_DOORBELL_JOIN ,
RIONET_DOORBELL_LEAVE ) ;
rio_release_inb_mbox ( rnet - > mport , RIONET_MAILBOX ) ;
rio_release_outb_mbox ( rnet - > mport , RIONET_MAILBOX ) ;
return 0 ;
}
static void rionet_remove ( struct rio_dev * rdev )
{
struct net_device * ndev = NULL ;
struct rionet_peer * peer , * tmp ;
2008-04-19 00:33:42 +04:00
free_pages ( ( unsigned long ) rionet_active , rdev - > net - > hport - > sys_size ?
__ilog2 ( sizeof ( void * ) ) + 4 : 0 ) ;
2005-09-09 23:10:10 +04:00
unregister_netdev ( ndev ) ;
kfree ( ndev ) ;
list_for_each_entry_safe ( peer , tmp , & rionet_peers , node ) {
list_del ( & peer - > node ) ;
kfree ( peer ) ;
}
}
static void rionet_get_drvinfo ( struct net_device * ndev ,
struct ethtool_drvinfo * info )
{
2008-11-13 10:38:14 +03:00
struct rionet_private * rnet = netdev_priv ( ndev ) ;
2005-09-09 23:10:10 +04:00
strcpy ( info - > driver , DRV_NAME ) ;
strcpy ( info - > version , DRV_VERSION ) ;
strcpy ( info - > fw_version , " n/a " ) ;
strcpy ( info - > bus_info , rnet - > mport - > name ) ;
}
static u32 rionet_get_msglevel ( struct net_device * ndev )
{
2008-11-13 10:38:14 +03:00
struct rionet_private * rnet = netdev_priv ( ndev ) ;
2005-09-09 23:10:10 +04:00
return rnet - > msg_enable ;
}
static void rionet_set_msglevel ( struct net_device * ndev , u32 value )
{
2008-11-13 10:38:14 +03:00
struct rionet_private * rnet = netdev_priv ( ndev ) ;
2005-09-09 23:10:10 +04:00
rnet - > msg_enable = value ;
}
2006-09-13 22:30:00 +04:00
static const struct ethtool_ops rionet_ethtool_ops = {
2005-09-09 23:10:10 +04:00
. get_drvinfo = rionet_get_drvinfo ,
. get_msglevel = rionet_get_msglevel ,
. set_msglevel = rionet_set_msglevel ,
. get_link = ethtool_op_get_link ,
} ;
2009-04-15 16:52:56 +04:00
static const struct net_device_ops rionet_netdev_ops = {
. ndo_open = rionet_open ,
. ndo_stop = rionet_close ,
. ndo_start_xmit = rionet_start_xmit ,
. ndo_change_mtu = eth_change_mtu ,
. ndo_validate_addr = eth_validate_addr ,
. ndo_set_mac_address = eth_mac_addr ,
} ;
2005-09-09 23:10:10 +04:00
static int rionet_setup_netdev ( struct rio_mport * mport )
{
int rc = 0 ;
struct net_device * ndev = NULL ;
struct rionet_private * rnet ;
u16 device_id ;
/* Allocate our net_device structure */
ndev = alloc_etherdev ( sizeof ( struct rionet_private ) ) ;
if ( ndev = = NULL ) {
printk ( KERN_INFO " %s: could not allocate ethernet device. \n " ,
DRV_NAME ) ;
rc = - ENOMEM ;
goto out ;
}
2008-04-19 00:33:42 +04:00
rionet_active = ( struct rio_dev * * ) __get_free_pages ( GFP_KERNEL ,
mport - > sys_size ? __ilog2 ( sizeof ( void * ) ) + 4 : 0 ) ;
if ( ! rionet_active ) {
rc = - ENOMEM ;
goto out ;
}
memset ( ( void * ) rionet_active , 0 , sizeof ( void * ) *
RIO_MAX_ROUTE_ENTRIES ( mport - > sys_size ) ) ;
2005-09-09 23:10:10 +04:00
/* Set up private area */
2008-11-13 10:38:14 +03:00
rnet = netdev_priv ( ndev ) ;
2005-09-09 23:10:10 +04:00
rnet - > mport = mport ;
/* Set the default MAC address */
device_id = rio_local_get_device_id ( mport ) ;
ndev - > dev_addr [ 0 ] = 0x00 ;
ndev - > dev_addr [ 1 ] = 0x01 ;
ndev - > dev_addr [ 2 ] = 0x00 ;
ndev - > dev_addr [ 3 ] = 0x01 ;
ndev - > dev_addr [ 4 ] = device_id > > 8 ;
ndev - > dev_addr [ 5 ] = device_id & 0xff ;
2009-04-15 16:52:56 +04:00
ndev - > netdev_ops = & rionet_netdev_ops ;
2005-09-09 23:10:10 +04:00
ndev - > mtu = RIO_MAX_MSG_SIZE - 14 ;
ndev - > features = NETIF_F_LLTX ;
SET_ETHTOOL_OPS ( ndev , & rionet_ethtool_ops ) ;
spin_lock_init ( & rnet - > lock ) ;
spin_lock_init ( & rnet - > tx_lock ) ;
rnet - > msg_enable = RIONET_DEFAULT_MSGLEVEL ;
rc = register_netdev ( ndev ) ;
if ( rc ! = 0 )
goto out ;
2008-10-28 01:59:26 +03:00
printk ( " %s: %s %s Version %s, MAC %pM \n " ,
2005-09-09 23:10:10 +04:00
ndev - > name ,
DRV_NAME ,
DRV_DESC ,
DRV_VERSION ,
2008-10-28 01:59:26 +03:00
ndev - > dev_addr ) ;
2005-09-09 23:10:10 +04:00
out :
return rc ;
}
/*
* XXX Make multi - net safe
*/
static int rionet_probe ( struct rio_dev * rdev , const struct rio_device_id * id )
{
int rc = - ENODEV ;
u32 lpef , lsrc_ops , ldst_ops ;
struct rionet_peer * peer ;
/* If local device is not rionet capable, give up quickly */
if ( ! rionet_capable )
goto out ;
/*
* First time through , make sure local device is rionet
* capable , setup netdev , and set flags so this is skipped
* on later probes
*/
if ( ! rionet_check ) {
rio_local_read_config_32 ( rdev - > net - > hport , RIO_PEF_CAR , & lpef ) ;
rio_local_read_config_32 ( rdev - > net - > hport , RIO_SRC_OPS_CAR ,
& lsrc_ops ) ;
rio_local_read_config_32 ( rdev - > net - > hport , RIO_DST_OPS_CAR ,
& ldst_ops ) ;
if ( ! is_rionet_capable ( lpef , lsrc_ops , ldst_ops ) ) {
printk ( KERN_ERR
" %s: local device is not network capable \n " ,
DRV_NAME ) ;
rionet_check = 1 ;
rionet_capable = 0 ;
goto out ;
}
rc = rionet_setup_netdev ( rdev - > net - > hport ) ;
rionet_check = 1 ;
}
/*
* If the remote device has mailbox / doorbell capabilities ,
* add it to the peer list .
*/
if ( dev_rionet_capable ( rdev ) ) {
if ( ! ( peer = kmalloc ( sizeof ( struct rionet_peer ) , GFP_KERNEL ) ) ) {
rc = - ENOMEM ;
goto out ;
}
peer - > rdev = rdev ;
list_add_tail ( & peer - > node , & rionet_peers ) ;
}
out :
return rc ;
}
static struct rio_device_id rionet_id_table [ ] = {
{ RIO_DEVICE ( RIO_ANY_ID , RIO_ANY_ID ) }
} ;
static struct rio_driver rionet_driver = {
. name = " rionet " ,
. id_table = rionet_id_table ,
. probe = rionet_probe ,
. remove = rionet_remove ,
} ;
static int __init rionet_init ( void )
{
return rio_register_driver ( & rionet_driver ) ;
}
static void __exit rionet_exit ( void )
{
rio_unregister_driver ( & rionet_driver ) ;
}
module_init ( rionet_init ) ;
module_exit ( rionet_exit ) ;