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