370 lines · 8.7 KB · C
RawDownload
/* * 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/etherdevice.h>#include <linux/netdevice.h>#include <linux/platform_device.h>#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 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,			  IRQF_SHARED, 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);