xref: /openbmc/linux/arch/m68k/emu/nfeth.c (revision 4e9b9de6)
19cd7b148SMilan Jurik /*
29cd7b148SMilan Jurik  * atari_nfeth.c - ARAnyM ethernet card driver for GNU/Linux
39cd7b148SMilan Jurik  *
49cd7b148SMilan Jurik  * Copyright (c) 2005 Milan Jurik, Petr Stehlik of ARAnyM dev team
59cd7b148SMilan Jurik  *
69cd7b148SMilan Jurik  * Based on ARAnyM driver for FreeMiNT written by Standa Opichal
79cd7b148SMilan Jurik  *
89cd7b148SMilan Jurik  * This software may be used and distributed according to the terms of
99cd7b148SMilan Jurik  * the GNU General Public License (GPL), incorporated herein by reference.
109cd7b148SMilan Jurik  */
119cd7b148SMilan Jurik 
129cd7b148SMilan Jurik #define DRV_VERSION	"0.3"
139cd7b148SMilan Jurik #define DRV_RELDATE	"10/12/2005"
149cd7b148SMilan Jurik 
159cd7b148SMilan Jurik #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
169cd7b148SMilan Jurik 
179cd7b148SMilan Jurik #include <linux/netdevice.h>
189cd7b148SMilan Jurik #include <linux/etherdevice.h>
19ac752277SGeert Uytterhoeven #include <linux/interrupt.h>
209cd7b148SMilan Jurik #include <linux/module.h>
219cd7b148SMilan Jurik #include <asm/natfeat.h>
229cd7b148SMilan Jurik #include <asm/virtconvert.h>
239cd7b148SMilan Jurik 
249cd7b148SMilan Jurik enum {
259cd7b148SMilan Jurik 	GET_VERSION = 0,/* no parameters, return NFAPI_VERSION in d0 */
269cd7b148SMilan Jurik 	XIF_INTLEVEL,	/* no parameters, return Interrupt Level in d0 */
279cd7b148SMilan Jurik 	XIF_IRQ,	/* acknowledge interrupt from host */
289cd7b148SMilan Jurik 	XIF_START,	/* (ethX), called on 'ifup', start receiver thread */
299cd7b148SMilan Jurik 	XIF_STOP,	/* (ethX), called on 'ifdown', stop the thread */
309cd7b148SMilan Jurik 	XIF_READLENGTH,	/* (ethX), return size of network data block to read */
319cd7b148SMilan Jurik 	XIF_READBLOCK,	/* (ethX, buffer, size), read block of network data */
329cd7b148SMilan Jurik 	XIF_WRITEBLOCK,	/* (ethX, buffer, size), write block of network data */
339cd7b148SMilan Jurik 	XIF_GET_MAC,	/* (ethX, buffer, size), return MAC HW addr in buffer */
349cd7b148SMilan Jurik 	XIF_GET_IPHOST,	/* (ethX, buffer, size), return IP address of host */
359cd7b148SMilan Jurik 	XIF_GET_IPATARI,/* (ethX, buffer, size), return IP address of atari */
369cd7b148SMilan Jurik 	XIF_GET_NETMASK	/* (ethX, buffer, size), return IP netmask */
379cd7b148SMilan Jurik };
389cd7b148SMilan Jurik 
399cd7b148SMilan Jurik #define MAX_UNIT	8
409cd7b148SMilan Jurik 
419cd7b148SMilan Jurik /* These identify the driver base version and may not be removed. */
42b881bc46SGreg Kroah-Hartman static const char version[] =
439cd7b148SMilan Jurik 	KERN_INFO KBUILD_MODNAME ".c:v" DRV_VERSION " " DRV_RELDATE
449cd7b148SMilan Jurik 	" S.Opichal, M.Jurik, P.Stehlik\n"
459cd7b148SMilan Jurik 	KERN_INFO " http://aranym.org/\n";
469cd7b148SMilan Jurik 
479cd7b148SMilan Jurik MODULE_AUTHOR("Milan Jurik");
489cd7b148SMilan Jurik MODULE_DESCRIPTION("Atari NFeth driver");
499cd7b148SMilan Jurik MODULE_LICENSE("GPL");
509cd7b148SMilan Jurik 
519cd7b148SMilan Jurik 
529cd7b148SMilan Jurik static long nfEtherID;
539cd7b148SMilan Jurik static int nfEtherIRQ;
549cd7b148SMilan Jurik 
559cd7b148SMilan Jurik struct nfeth_private {
569cd7b148SMilan Jurik 	int ethX;
579cd7b148SMilan Jurik };
589cd7b148SMilan Jurik 
599cd7b148SMilan Jurik static struct net_device *nfeth_dev[MAX_UNIT];
609cd7b148SMilan Jurik 
nfeth_open(struct net_device * dev)619cd7b148SMilan Jurik static int nfeth_open(struct net_device *dev)
629cd7b148SMilan Jurik {
639cd7b148SMilan Jurik 	struct nfeth_private *priv = netdev_priv(dev);
649cd7b148SMilan Jurik 	int res;
659cd7b148SMilan Jurik 
669cd7b148SMilan Jurik 	res = nf_call(nfEtherID + XIF_START, priv->ethX);
679cd7b148SMilan Jurik 	netdev_dbg(dev, "%s: %d\n", __func__, res);
689cd7b148SMilan Jurik 
699cd7b148SMilan Jurik 	/* Ready for data */
709cd7b148SMilan Jurik 	netif_start_queue(dev);
719cd7b148SMilan Jurik 
729cd7b148SMilan Jurik 	return 0;
739cd7b148SMilan Jurik }
749cd7b148SMilan Jurik 
nfeth_stop(struct net_device * dev)759cd7b148SMilan Jurik static int nfeth_stop(struct net_device *dev)
769cd7b148SMilan Jurik {
779cd7b148SMilan Jurik 	struct nfeth_private *priv = netdev_priv(dev);
789cd7b148SMilan Jurik 
799cd7b148SMilan Jurik 	/* No more data */
809cd7b148SMilan Jurik 	netif_stop_queue(dev);
819cd7b148SMilan Jurik 
829cd7b148SMilan Jurik 	nf_call(nfEtherID + XIF_STOP, priv->ethX);
839cd7b148SMilan Jurik 
849cd7b148SMilan Jurik 	return 0;
859cd7b148SMilan Jurik }
869cd7b148SMilan Jurik 
879cd7b148SMilan Jurik /*
889cd7b148SMilan Jurik  * Read a packet out of the adapter and pass it to the upper layers
899cd7b148SMilan Jurik  */
recv_packet(struct net_device * dev)909cd7b148SMilan Jurik static inline void recv_packet(struct net_device *dev)
919cd7b148SMilan Jurik {
929cd7b148SMilan Jurik 	struct nfeth_private *priv = netdev_priv(dev);
939cd7b148SMilan Jurik 	unsigned short pktlen;
949cd7b148SMilan Jurik 	struct sk_buff *skb;
959cd7b148SMilan Jurik 
969cd7b148SMilan Jurik 	/* read packet length (excluding 32 bit crc) */
979cd7b148SMilan Jurik 	pktlen = nf_call(nfEtherID + XIF_READLENGTH, priv->ethX);
989cd7b148SMilan Jurik 
999cd7b148SMilan Jurik 	netdev_dbg(dev, "%s: %u\n", __func__, pktlen);
1009cd7b148SMilan Jurik 
1019cd7b148SMilan Jurik 	if (!pktlen) {
1029cd7b148SMilan Jurik 		netdev_dbg(dev, "%s: pktlen == 0\n", __func__);
1039cd7b148SMilan Jurik 		dev->stats.rx_errors++;
1049cd7b148SMilan Jurik 		return;
1059cd7b148SMilan Jurik 	}
1069cd7b148SMilan Jurik 
1079cd7b148SMilan Jurik 	skb = dev_alloc_skb(pktlen + 2);
1089cd7b148SMilan Jurik 	if (!skb) {
1099cd7b148SMilan Jurik 		netdev_dbg(dev, "%s: out of mem (buf_alloc failed)\n",
1109cd7b148SMilan Jurik 			   __func__);
1119cd7b148SMilan Jurik 		dev->stats.rx_dropped++;
1129cd7b148SMilan Jurik 		return;
1139cd7b148SMilan Jurik 	}
1149cd7b148SMilan Jurik 
1159cd7b148SMilan Jurik 	skb->dev = dev;
1169cd7b148SMilan Jurik 	skb_reserve(skb, 2);		/* 16 Byte align  */
1179cd7b148SMilan Jurik 	skb_put(skb, pktlen);		/* make room */
1189cd7b148SMilan Jurik 	nf_call(nfEtherID + XIF_READBLOCK, priv->ethX, virt_to_phys(skb->data),
1199cd7b148SMilan Jurik 		pktlen);
1209cd7b148SMilan Jurik 
1219cd7b148SMilan Jurik 	skb->protocol = eth_type_trans(skb, dev);
1229cd7b148SMilan Jurik 	netif_rx(skb);
1239cd7b148SMilan Jurik 	dev->stats.rx_packets++;
1249cd7b148SMilan Jurik 	dev->stats.rx_bytes += pktlen;
1259cd7b148SMilan Jurik 
1269cd7b148SMilan Jurik 	/* and enqueue packet */
1279cd7b148SMilan Jurik 	return;
1289cd7b148SMilan Jurik }
1299cd7b148SMilan Jurik 
nfeth_interrupt(int irq,void * dev_id)1309cd7b148SMilan Jurik static irqreturn_t nfeth_interrupt(int irq, void *dev_id)
1319cd7b148SMilan Jurik {
1329cd7b148SMilan Jurik 	int i, m, mask;
1339cd7b148SMilan Jurik 
1349cd7b148SMilan Jurik 	mask = nf_call(nfEtherID + XIF_IRQ, 0);
1359cd7b148SMilan Jurik 	for (i = 0, m = 1; i < MAX_UNIT; m <<= 1, i++) {
1369cd7b148SMilan Jurik 		if (mask & m && nfeth_dev[i]) {
1379cd7b148SMilan Jurik 			recv_packet(nfeth_dev[i]);
1389cd7b148SMilan Jurik 			nf_call(nfEtherID + XIF_IRQ, m);
1399cd7b148SMilan Jurik 		}
1409cd7b148SMilan Jurik 	}
1419cd7b148SMilan Jurik 	return IRQ_HANDLED;
1429cd7b148SMilan Jurik }
1439cd7b148SMilan Jurik 
nfeth_xmit(struct sk_buff * skb,struct net_device * dev)1449cd7b148SMilan Jurik static int nfeth_xmit(struct sk_buff *skb, struct net_device *dev)
1459cd7b148SMilan Jurik {
1469cd7b148SMilan Jurik 	unsigned int len;
1479cd7b148SMilan Jurik 	char *data, shortpkt[ETH_ZLEN];
1489cd7b148SMilan Jurik 	struct nfeth_private *priv = netdev_priv(dev);
1499cd7b148SMilan Jurik 
1509cd7b148SMilan Jurik 	data = skb->data;
1519cd7b148SMilan Jurik 	len = skb->len;
1529cd7b148SMilan Jurik 	if (len < ETH_ZLEN) {
1539cd7b148SMilan Jurik 		memset(shortpkt, 0, ETH_ZLEN);
1549cd7b148SMilan Jurik 		memcpy(shortpkt, data, len);
1559cd7b148SMilan Jurik 		data = shortpkt;
1569cd7b148SMilan Jurik 		len = ETH_ZLEN;
1579cd7b148SMilan Jurik 	}
1589cd7b148SMilan Jurik 
1599cd7b148SMilan Jurik 	netdev_dbg(dev, "%s: send %u bytes\n", __func__, len);
1609cd7b148SMilan Jurik 	nf_call(nfEtherID + XIF_WRITEBLOCK, priv->ethX, virt_to_phys(data),
1619cd7b148SMilan Jurik 		len);
1629cd7b148SMilan Jurik 
1639cd7b148SMilan Jurik 	dev->stats.tx_packets++;
1649cd7b148SMilan Jurik 	dev->stats.tx_bytes += len;
1659cd7b148SMilan Jurik 
1669cd7b148SMilan Jurik 	dev_kfree_skb(skb);
1679cd7b148SMilan Jurik 	return 0;
1689cd7b148SMilan Jurik }
1699cd7b148SMilan Jurik 
nfeth_tx_timeout(struct net_device * dev,unsigned int txqueue)1700290bd29SMichael S. Tsirkin static void nfeth_tx_timeout(struct net_device *dev, unsigned int txqueue)
1719cd7b148SMilan Jurik {
1729cd7b148SMilan Jurik 	dev->stats.tx_errors++;
1739cd7b148SMilan Jurik 	netif_wake_queue(dev);
1749cd7b148SMilan Jurik }
1759cd7b148SMilan Jurik 
1769cd7b148SMilan Jurik static const struct net_device_ops nfeth_netdev_ops = {
1779cd7b148SMilan Jurik 	.ndo_open		= nfeth_open,
1789cd7b148SMilan Jurik 	.ndo_stop		= nfeth_stop,
1799cd7b148SMilan Jurik 	.ndo_start_xmit		= nfeth_xmit,
1809cd7b148SMilan Jurik 	.ndo_tx_timeout		= nfeth_tx_timeout,
1819cd7b148SMilan Jurik 	.ndo_validate_addr	= eth_validate_addr,
1829cd7b148SMilan Jurik 	.ndo_set_mac_address	= eth_mac_addr,
1839cd7b148SMilan Jurik };
1849cd7b148SMilan Jurik 
nfeth_probe(int unit)1859cd7b148SMilan Jurik static struct net_device * __init nfeth_probe(int unit)
1869cd7b148SMilan Jurik {
1879cd7b148SMilan Jurik 	struct net_device *dev;
1889cd7b148SMilan Jurik 	struct nfeth_private *priv;
1899cd7b148SMilan Jurik 	char mac[ETH_ALEN], host_ip[32], local_ip[32];
1909cd7b148SMilan Jurik 	int err;
1919cd7b148SMilan Jurik 
19255490050SGeert Uytterhoeven 	if (!nf_call(nfEtherID + XIF_GET_MAC, unit, virt_to_phys(mac),
19355490050SGeert Uytterhoeven 		     ETH_ALEN))
1949cd7b148SMilan Jurik 		return NULL;
1959cd7b148SMilan Jurik 
1969cd7b148SMilan Jurik 	dev = alloc_etherdev(sizeof(struct nfeth_private));
1979cd7b148SMilan Jurik 	if (!dev)
1989cd7b148SMilan Jurik 		return NULL;
1999cd7b148SMilan Jurik 
2009cd7b148SMilan Jurik 	dev->irq = nfEtherIRQ;
2019cd7b148SMilan Jurik 	dev->netdev_ops = &nfeth_netdev_ops;
2029cd7b148SMilan Jurik 
203*4e9b9de6SJakub Kicinski 	eth_hw_addr_set(dev, mac);
2049cd7b148SMilan Jurik 
2059cd7b148SMilan Jurik 	priv = netdev_priv(dev);
2069cd7b148SMilan Jurik 	priv->ethX = unit;
2079cd7b148SMilan Jurik 
2089cd7b148SMilan Jurik 	err = register_netdev(dev);
2099cd7b148SMilan Jurik 	if (err) {
2109cd7b148SMilan Jurik 		free_netdev(dev);
2119cd7b148SMilan Jurik 		return NULL;
2129cd7b148SMilan Jurik 	}
2139cd7b148SMilan Jurik 
2149cd7b148SMilan Jurik 	nf_call(nfEtherID + XIF_GET_IPHOST, unit,
21555490050SGeert Uytterhoeven 		virt_to_phys(host_ip), sizeof(host_ip));
2169cd7b148SMilan Jurik 	nf_call(nfEtherID + XIF_GET_IPATARI, unit,
21755490050SGeert Uytterhoeven 		virt_to_phys(local_ip), sizeof(local_ip));
2189cd7b148SMilan Jurik 
2199cd7b148SMilan Jurik 	netdev_info(dev, KBUILD_MODNAME " addr:%s (%s) HWaddr:%pM\n", host_ip,
2209cd7b148SMilan Jurik 		    local_ip, mac);
2219cd7b148SMilan Jurik 
2229cd7b148SMilan Jurik 	return dev;
2239cd7b148SMilan Jurik }
2249cd7b148SMilan Jurik 
nfeth_init(void)2259cd7b148SMilan Jurik static int __init nfeth_init(void)
2269cd7b148SMilan Jurik {
2279cd7b148SMilan Jurik 	long ver;
2289cd7b148SMilan Jurik 	int error, i;
2299cd7b148SMilan Jurik 
2309cd7b148SMilan Jurik 	nfEtherID = nf_get_id("ETHERNET");
2319cd7b148SMilan Jurik 	if (!nfEtherID)
2329cd7b148SMilan Jurik 		return -ENODEV;
2339cd7b148SMilan Jurik 
2349cd7b148SMilan Jurik 	ver = nf_call(nfEtherID + GET_VERSION);
2359cd7b148SMilan Jurik 	pr_info("API %lu\n", ver);
2369cd7b148SMilan Jurik 
2379cd7b148SMilan Jurik 	nfEtherIRQ = nf_call(nfEtherID + XIF_INTLEVEL);
2389cd7b148SMilan Jurik 	error = request_irq(nfEtherIRQ, nfeth_interrupt, IRQF_SHARED,
2399cd7b148SMilan Jurik 			    "eth emu", nfeth_interrupt);
2409cd7b148SMilan Jurik 	if (error) {
2419cd7b148SMilan Jurik 		pr_err("request for irq %d failed %d", nfEtherIRQ, error);
2429cd7b148SMilan Jurik 		return error;
2439cd7b148SMilan Jurik 	}
2449cd7b148SMilan Jurik 
2459cd7b148SMilan Jurik 	for (i = 0; i < MAX_UNIT; i++)
2469cd7b148SMilan Jurik 		nfeth_dev[i] = nfeth_probe(i);
2479cd7b148SMilan Jurik 
2489cd7b148SMilan Jurik 	return 0;
2499cd7b148SMilan Jurik }
2509cd7b148SMilan Jurik 
nfeth_cleanup(void)2519cd7b148SMilan Jurik static void __exit nfeth_cleanup(void)
2529cd7b148SMilan Jurik {
2539cd7b148SMilan Jurik 	int i;
2549cd7b148SMilan Jurik 
2559cd7b148SMilan Jurik 	for (i = 0; i < MAX_UNIT; i++) {
2569cd7b148SMilan Jurik 		if (nfeth_dev[i]) {
257761608f5SPavel Skripkin 			unregister_netdev(nfeth_dev[i]);
258761608f5SPavel Skripkin 			free_netdev(nfeth_dev[i]);
2599cd7b148SMilan Jurik 		}
2609cd7b148SMilan Jurik 	}
2619cd7b148SMilan Jurik 	free_irq(nfEtherIRQ, nfeth_interrupt);
2629cd7b148SMilan Jurik }
2639cd7b148SMilan Jurik 
2649cd7b148SMilan Jurik module_init(nfeth_init);
2659cd7b148SMilan Jurik module_exit(nfeth_cleanup);
266