2005-10-10 17:51:27 +04:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
# define DEBUG
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/netdevice.h>
# include <linux/sched.h>
# include <linux/etherdevice.h>
# include <linux/netdevice.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2005-10-10 17:51:27 +04:00
# include <asm/io.h>
# include <asm/mips-boards/simint.h>
# include "mipsnet.h" /* actual device IO mapping */
# define MIPSNET_VERSION "2005-06-20"
# define mipsnet_reg_address(dev, field) (dev->base_addr + field_offset(field))
struct mipsnet_priv {
struct net_device_stats stats ;
} ;
static struct platform_device * mips_plat_dev ;
static char mipsnet_string [ ] = " mipsnet " ;
/*
* Copy data from the MIPSNET rx data port
*/
static int ioiocpy_frommipsnet ( struct net_device * dev , unsigned char * kdata ,
int len )
{
uint32_t available_len = inl ( mipsnet_reg_address ( dev , rxDataCount ) ) ;
if ( available_len < len )
return - EFAULT ;
for ( ; len > 0 ; len - - , kdata + + ) {
* kdata = inb ( mipsnet_reg_address ( dev , rxDataBuffer ) ) ;
}
return inl ( mipsnet_reg_address ( dev , rxDataCount ) ) ;
}
static inline ssize_t mipsnet_put_todevice ( struct net_device * dev ,
struct sk_buff * skb )
{
int count_to_go = skb - > len ;
char * buf_ptr = skb - > data ;
struct mipsnet_priv * mp = netdev_priv ( dev ) ;
pr_debug ( " %s: %s(): telling MIPSNET txDataCount(%d) \n " ,
dev - > name , __FUNCTION__ , skb - > len ) ;
outl ( skb - > len , mipsnet_reg_address ( dev , txDataCount ) ) ;
pr_debug ( " %s: %s(): sending data to MIPSNET txDataBuffer(%d) \n " ,
dev - > name , __FUNCTION__ , skb - > len ) ;
for ( ; count_to_go ; buf_ptr + + , count_to_go - - ) {
outb ( * buf_ptr , mipsnet_reg_address ( dev , txDataBuffer ) ) ;
}
mp - > stats . tx_packets + + ;
mp - > stats . tx_bytes + = skb - > len ;
return skb - > len ;
}
static int mipsnet_xmit ( struct sk_buff * skb , struct net_device * dev )
{
pr_debug ( " %s:%s(): transmitting %d bytes \n " ,
dev - > name , __FUNCTION__ , skb - > len ) ;
/* Only one packet at a time. Once TXDONE interrupt is serviced, the
* queue will be restarted .
*/
netif_stop_queue ( dev ) ;
mipsnet_put_todevice ( dev , skb ) ;
return 0 ;
}
static inline ssize_t mipsnet_get_fromdev ( struct net_device * dev , size_t count )
{
struct sk_buff * skb ;
size_t len = count ;
struct mipsnet_priv * mp = netdev_priv ( dev ) ;
if ( ! ( skb = alloc_skb ( len + 2 , GFP_KERNEL ) ) ) {
mp - > stats . rx_dropped + + ;
return - ENOMEM ;
}
skb_reserve ( skb , 2 ) ;
if ( ioiocpy_frommipsnet ( dev , skb_put ( skb , len ) , len ) )
return - EFAULT ;
skb - > dev = dev ;
skb - > protocol = eth_type_trans ( skb , dev ) ;
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
pr_debug ( " %s:%s(): pushing RXed data to kernel \n " ,
dev - > name , __FUNCTION__ ) ;
netif_rx ( skb ) ;
mp - > stats . rx_packets + + ;
mp - > stats . rx_bytes + = len ;
return count ;
}
static irqreturn_t
mipsnet_interrupt ( int irq , void * dev_id , struct pt_regs * regs )
{
struct net_device * dev = dev_id ;
irqreturn_t retval = IRQ_NONE ;
uint64_t interruptFlags ;
if ( irq = = dev - > irq ) {
pr_debug ( " %s:%s(): irq %d for device \n " ,
dev - > name , __FUNCTION__ , irq ) ;
retval = IRQ_HANDLED ;
interruptFlags =
inl ( mipsnet_reg_address ( dev , interruptControl ) ) ;
pr_debug ( " %s:%s(): intCtl=0x%016llx \n " , dev - > name ,
__FUNCTION__ , interruptFlags ) ;
if ( interruptFlags & MIPSNET_INTCTL_TXDONE ) {
pr_debug ( " %s:%s(): got TXDone \n " ,
dev - > name , __FUNCTION__ ) ;
outl ( MIPSNET_INTCTL_TXDONE ,
mipsnet_reg_address ( dev , interruptControl ) ) ;
// only one packet at a time, we are done.
netif_wake_queue ( dev ) ;
} else if ( interruptFlags & MIPSNET_INTCTL_RXDONE ) {
pr_debug ( " %s:%s(): got RX data \n " ,
dev - > name , __FUNCTION__ ) ;
mipsnet_get_fromdev ( dev ,
inl ( mipsnet_reg_address ( dev , rxDataCount ) ) ) ;
pr_debug ( " %s:%s(): clearing RX int \n " ,
dev - > name , __FUNCTION__ ) ;
outl ( MIPSNET_INTCTL_RXDONE ,
mipsnet_reg_address ( dev , interruptControl ) ) ;
} else if ( interruptFlags & MIPSNET_INTCTL_TESTBIT ) {
pr_debug ( " %s:%s(): got test interrupt \n " ,
dev - > name , __FUNCTION__ ) ;
// TESTBIT is cleared on read.
// And takes effect after a write with 0
outl ( 0 , mipsnet_reg_address ( dev , interruptControl ) ) ;
} else {
pr_debug ( " %s:%s(): no valid fags 0x%016llx \n " ,
dev - > name , __FUNCTION__ , interruptFlags ) ;
// Maybe shared IRQ, just ignore, no clearing.
retval = IRQ_NONE ;
}
} else {
printk ( KERN_INFO " %s: %s(): irq %d for unknown device \n " ,
dev - > name , __FUNCTION__ , irq ) ;
retval = IRQ_NONE ;
}
return retval ;
} //mipsnet_interrupt()
static int mipsnet_open ( struct net_device * dev )
{
int err ;
pr_debug ( " %s: mipsnet_open \n " , dev - > name ) ;
err = request_irq ( dev - > irq , & mipsnet_interrupt ,
SA_SHIRQ , dev - > name , ( void * ) dev ) ;
if ( err ) {
pr_debug ( " %s: %s(): can't get irq %d \n " ,
dev - > name , __FUNCTION__ , dev - > irq ) ;
release_region ( dev - > base_addr , MIPSNET_IO_EXTENT ) ;
return err ;
}
pr_debug ( " %s: %s(): got IO region at 0x%04lx and irq %d for dev. \n " ,
dev - > name , __FUNCTION__ , dev - > base_addr , dev - > irq ) ;
netif_start_queue ( dev ) ;
// test interrupt handler
outl ( MIPSNET_INTCTL_TESTBIT ,
mipsnet_reg_address ( dev , interruptControl ) ) ;
return 0 ;
}
static int mipsnet_close ( struct net_device * dev )
{
pr_debug ( " %s: %s() \n " , dev - > name , __FUNCTION__ ) ;
netif_stop_queue ( dev ) ;
return 0 ;
}
static struct net_device_stats * mipsnet_get_stats ( struct net_device * dev )
{
struct mipsnet_priv * mp = netdev_priv ( dev ) ;
return & mp - > stats ;
}
static void mipsnet_set_mclist ( struct net_device * dev )
{
// we don't do anything
return ;
}
static int __init mipsnet_probe ( struct device * dev )
{
struct net_device * netdev ;
int err ;
netdev = alloc_etherdev ( sizeof ( struct mipsnet_priv ) ) ;
if ( ! netdev ) {
err = - ENOMEM ;
goto out ;
}
dev_set_drvdata ( dev , netdev ) ;
netdev - > open = mipsnet_open ;
netdev - > stop = mipsnet_close ;
netdev - > hard_start_xmit = mipsnet_xmit ;
netdev - > get_stats = mipsnet_get_stats ;
netdev - > set_multicast_list = mipsnet_set_mclist ;
/*
* TODO : probe for these or load them from PARAM
*/
netdev - > base_addr = 0x4200 ;
netdev - > irq = MIPSCPU_INT_BASE + MIPSCPU_INT_MB0 +
inl ( mipsnet_reg_address ( netdev , interruptInfo ) ) ;
// Get the io region now, get irq on open()
if ( ! request_region ( netdev - > base_addr , MIPSNET_IO_EXTENT , " mipsnet " ) ) {
pr_debug ( " %s: %s(): IO region {start: 0x%04lux, len: %d} "
" for dev is not availble. \n " , netdev - > name ,
__FUNCTION__ , netdev - > base_addr , MIPSNET_IO_EXTENT ) ;
err = - EBUSY ;
goto out_free_netdev ;
}
/*
* Lacking any better mechanism to allocate a MAC address we use a
* random one . . .
*/
random_ether_addr ( netdev - > dev_addr ) ;
err = register_netdev ( netdev ) ;
if ( err ) {
printk ( KERN_ERR " MIPSNet: failed to register netdev. \n " ) ;
goto out_free_region ;
}
return 0 ;
out_free_region :
release_region ( netdev - > base_addr , MIPSNET_IO_EXTENT ) ;
out_free_netdev :
free_netdev ( netdev ) ;
out :
return err ;
}
static int __devexit mipsnet_device_remove ( struct device * device )
{
struct net_device * dev = dev_get_drvdata ( device ) ;
unregister_netdev ( dev ) ;
release_region ( dev - > base_addr , MIPSNET_IO_EXTENT ) ;
free_netdev ( dev ) ;
dev_set_drvdata ( device , NULL ) ;
return 0 ;
}
static struct device_driver mipsnet_driver = {
. name = mipsnet_string ,
. bus = & platform_bus_type ,
. probe = mipsnet_probe ,
. remove = __devexit_p ( mipsnet_device_remove ) ,
} ;
static void mipsnet_platform_release ( struct device * device )
{
struct platform_device * pldev ;
/* free device */
pldev = to_platform_device ( device ) ;
kfree ( pldev ) ;
}
static int __init mipsnet_init_module ( void )
{
struct platform_device * pldev ;
int err ;
printk ( KERN_INFO " MIPSNet Ethernet driver. Version: %s. "
" (c)2005 MIPS Technologies, Inc. \n " , MIPSNET_VERSION ) ;
if ( driver_register ( & mipsnet_driver ) ) {
printk ( KERN_ERR " Driver registration failed \n " ) ;
err = - ENODEV ;
goto out ;
}
if ( ! ( pldev = kmalloc ( sizeof ( * pldev ) , GFP_KERNEL ) ) ) {
err = - ENOMEM ;
goto out_unregister_driver ;
}
memset ( pldev , 0 , sizeof ( * pldev ) ) ;
pldev - > name = mipsnet_string ;
pldev - > id = 0 ;
pldev - > dev . release = mipsnet_platform_release ;
if ( platform_device_register ( pldev ) ) {
err = - ENODEV ;
goto out_free_pldev ;
}
if ( ! pldev - > dev . driver ) {
/*
* The driver was not bound to this device , there was
* no hardware at this address . Unregister it , as the
* release fuction will take care of freeing the
* allocated structure
*/
platform_device_unregister ( pldev ) ;
}
mips_plat_dev = pldev ;
return 0 ;
out_free_pldev :
kfree ( pldev ) ;
out_unregister_driver :
driver_unregister ( & mipsnet_driver ) ;
out :
return err ;
}
static void __exit mipsnet_exit_module ( void )
{
pr_debug ( " MIPSNet Ethernet driver exiting \n " ) ;
driver_unregister ( & mipsnet_driver ) ;
}
module_init ( mipsnet_init_module ) ;
module_exit ( mipsnet_exit_module ) ;