18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Sealevel Systems 4021 driver. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) Copyright 1999, 2001 Alan Cox 68c2ecf20Sopenharmony_ci * (c) Copyright 2001 Red Hat Inc. 78c2ecf20Sopenharmony_ci * Generic HDLC port Copyright (C) 2008 Krzysztof Halasa <khc@pm.waw.pl> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/mm.h> 158c2ecf20Sopenharmony_ci#include <linux/net.h> 168c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 178c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 188c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci#include <linux/hdlc.h> 218c2ecf20Sopenharmony_ci#include <linux/ioport.h> 228c2ecf20Sopenharmony_ci#include <linux/init.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <net/arp.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <asm/irq.h> 278c2ecf20Sopenharmony_ci#include <asm/io.h> 288c2ecf20Sopenharmony_ci#include <asm/dma.h> 298c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 308c2ecf20Sopenharmony_ci#include "z85230.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct slvl_device 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct z8530_channel *chan; 368c2ecf20Sopenharmony_ci int channel; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct slvl_board 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct slvl_device dev[2]; 438c2ecf20Sopenharmony_ci struct z8530_dev board; 448c2ecf20Sopenharmony_ci int iobase; 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * Network driver support routines 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic inline struct slvl_device* dev_to_chan(struct net_device *dev) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci return (struct slvl_device *)dev_to_hdlc(dev)->priv; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* 578c2ecf20Sopenharmony_ci * Frame receive. Simple for our card as we do HDLC and there 588c2ecf20Sopenharmony_ci * is no funny garbage involved 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic void sealevel_input(struct z8530_channel *c, struct sk_buff *skb) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci /* Drop the CRC - it's not a good idea to try and negotiate it ;) */ 648c2ecf20Sopenharmony_ci skb_trim(skb, skb->len - 2); 658c2ecf20Sopenharmony_ci skb->protocol = hdlc_type_trans(skb, c->netdevice); 668c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 678c2ecf20Sopenharmony_ci skb->dev = c->netdevice; 688c2ecf20Sopenharmony_ci netif_rx(skb); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * We've been placed in the UP state 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int sealevel_open(struct net_device *d) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct slvl_device *slvl = dev_to_chan(d); 788c2ecf20Sopenharmony_ci int err = -1; 798c2ecf20Sopenharmony_ci int unit = slvl->channel; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* 828c2ecf20Sopenharmony_ci * Link layer up. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci switch (unit) { 868c2ecf20Sopenharmony_ci case 0: 878c2ecf20Sopenharmony_ci err = z8530_sync_dma_open(d, slvl->chan); 888c2ecf20Sopenharmony_ci break; 898c2ecf20Sopenharmony_ci case 1: 908c2ecf20Sopenharmony_ci err = z8530_sync_open(d, slvl->chan); 918c2ecf20Sopenharmony_ci break; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (err) 958c2ecf20Sopenharmony_ci return err; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci err = hdlc_open(d); 988c2ecf20Sopenharmony_ci if (err) { 998c2ecf20Sopenharmony_ci switch (unit) { 1008c2ecf20Sopenharmony_ci case 0: 1018c2ecf20Sopenharmony_ci z8530_sync_dma_close(d, slvl->chan); 1028c2ecf20Sopenharmony_ci break; 1038c2ecf20Sopenharmony_ci case 1: 1048c2ecf20Sopenharmony_ci z8530_sync_close(d, slvl->chan); 1058c2ecf20Sopenharmony_ci break; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci return err; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci slvl->chan->rx_function = sealevel_input; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* 1138c2ecf20Sopenharmony_ci * Go go go 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ci netif_start_queue(d); 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int sealevel_close(struct net_device *d) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct slvl_device *slvl = dev_to_chan(d); 1228c2ecf20Sopenharmony_ci int unit = slvl->channel; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* 1258c2ecf20Sopenharmony_ci * Discard new frames 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci slvl->chan->rx_function = z8530_null_rx; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci hdlc_close(d); 1318c2ecf20Sopenharmony_ci netif_stop_queue(d); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci switch (unit) { 1348c2ecf20Sopenharmony_ci case 0: 1358c2ecf20Sopenharmony_ci z8530_sync_dma_close(d, slvl->chan); 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci case 1: 1388c2ecf20Sopenharmony_ci z8530_sync_close(d, slvl->chan); 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int sealevel_ioctl(struct net_device *d, struct ifreq *ifr, int cmd) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci /* struct slvl_device *slvl=dev_to_chan(d); 1478c2ecf20Sopenharmony_ci z8530_ioctl(d,&slvl->sync.chanA,ifr,cmd) */ 1488c2ecf20Sopenharmony_ci return hdlc_ioctl(d, ifr, cmd); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* 1528c2ecf20Sopenharmony_ci * Passed network frames, fire them downwind. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic netdev_tx_t sealevel_queue_xmit(struct sk_buff *skb, 1568c2ecf20Sopenharmony_ci struct net_device *d) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci return z8530_queue_xmit(dev_to_chan(d)->chan, skb); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int sealevel_attach(struct net_device *dev, unsigned short encoding, 1628c2ecf20Sopenharmony_ci unsigned short parity) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci if (encoding == ENCODING_NRZ && parity == PARITY_CRC16_PR1_CCITT) 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci return -EINVAL; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic const struct net_device_ops sealevel_ops = { 1708c2ecf20Sopenharmony_ci .ndo_open = sealevel_open, 1718c2ecf20Sopenharmony_ci .ndo_stop = sealevel_close, 1728c2ecf20Sopenharmony_ci .ndo_start_xmit = hdlc_start_xmit, 1738c2ecf20Sopenharmony_ci .ndo_do_ioctl = sealevel_ioctl, 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int slvl_setup(struct slvl_device *sv, int iobase, int irq) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct net_device *dev = alloc_hdlcdev(sv); 1798c2ecf20Sopenharmony_ci if (!dev) 1808c2ecf20Sopenharmony_ci return -1; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci dev_to_hdlc(dev)->attach = sealevel_attach; 1838c2ecf20Sopenharmony_ci dev_to_hdlc(dev)->xmit = sealevel_queue_xmit; 1848c2ecf20Sopenharmony_ci dev->netdev_ops = &sealevel_ops; 1858c2ecf20Sopenharmony_ci dev->base_addr = iobase; 1868c2ecf20Sopenharmony_ci dev->irq = irq; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (register_hdlc_device(dev)) { 1898c2ecf20Sopenharmony_ci pr_err("unable to register HDLC device\n"); 1908c2ecf20Sopenharmony_ci free_netdev(dev); 1918c2ecf20Sopenharmony_ci return -1; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci sv->chan->netdevice = dev; 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* 2008c2ecf20Sopenharmony_ci * Allocate and setup Sealevel board. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic __init struct slvl_board *slvl_init(int iobase, int irq, 2048c2ecf20Sopenharmony_ci int txdma, int rxdma, int slow) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct z8530_dev *dev; 2078c2ecf20Sopenharmony_ci struct slvl_board *b; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* 2108c2ecf20Sopenharmony_ci * Get the needed I/O space 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (!request_region(iobase, 8, "Sealevel 4021")) { 2148c2ecf20Sopenharmony_ci pr_warn("I/O 0x%X already in use\n", iobase); 2158c2ecf20Sopenharmony_ci return NULL; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci b = kzalloc(sizeof(struct slvl_board), GFP_KERNEL); 2198c2ecf20Sopenharmony_ci if (!b) 2208c2ecf20Sopenharmony_ci goto err_kzalloc; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci b->dev[0].chan = &b->board.chanA; 2238c2ecf20Sopenharmony_ci b->dev[0].channel = 0; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci b->dev[1].chan = &b->board.chanB; 2268c2ecf20Sopenharmony_ci b->dev[1].channel = 1; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci dev = &b->board; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* 2318c2ecf20Sopenharmony_ci * Stuff in the I/O addressing 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci dev->active = 0; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci b->iobase = iobase; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* 2398c2ecf20Sopenharmony_ci * Select 8530 delays for the old board 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (slow) 2438c2ecf20Sopenharmony_ci iobase |= Z8530_PORT_SLEEP; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci dev->chanA.ctrlio = iobase + 1; 2468c2ecf20Sopenharmony_ci dev->chanA.dataio = iobase; 2478c2ecf20Sopenharmony_ci dev->chanB.ctrlio = iobase + 3; 2488c2ecf20Sopenharmony_ci dev->chanB.dataio = iobase + 2; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci dev->chanA.irqs = &z8530_nop; 2518c2ecf20Sopenharmony_ci dev->chanB.irqs = &z8530_nop; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* 2548c2ecf20Sopenharmony_ci * Assert DTR enable DMA 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci outb(3 | (1 << 7), b->iobase + 4); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* We want a fast IRQ for this device. Actually we'd like an even faster 2618c2ecf20Sopenharmony_ci IRQ ;) - This is one driver RtLinux is made for */ 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (request_irq(irq, z8530_interrupt, 0, 2648c2ecf20Sopenharmony_ci "SeaLevel", dev) < 0) { 2658c2ecf20Sopenharmony_ci pr_warn("IRQ %d already in use\n", irq); 2668c2ecf20Sopenharmony_ci goto err_request_irq; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci dev->irq = irq; 2708c2ecf20Sopenharmony_ci dev->chanA.private = &b->dev[0]; 2718c2ecf20Sopenharmony_ci dev->chanB.private = &b->dev[1]; 2728c2ecf20Sopenharmony_ci dev->chanA.dev = dev; 2738c2ecf20Sopenharmony_ci dev->chanB.dev = dev; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci dev->chanA.txdma = 3; 2768c2ecf20Sopenharmony_ci dev->chanA.rxdma = 1; 2778c2ecf20Sopenharmony_ci if (request_dma(dev->chanA.txdma, "SeaLevel (TX)")) 2788c2ecf20Sopenharmony_ci goto err_dma_tx; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (request_dma(dev->chanA.rxdma, "SeaLevel (RX)")) 2818c2ecf20Sopenharmony_ci goto err_dma_rx; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci disable_irq(irq); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* 2868c2ecf20Sopenharmony_ci * Begin normal initialise 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (z8530_init(dev) != 0) { 2908c2ecf20Sopenharmony_ci pr_err("Z8530 series device not found\n"); 2918c2ecf20Sopenharmony_ci enable_irq(irq); 2928c2ecf20Sopenharmony_ci goto free_hw; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci if (dev->type == Z85C30) { 2958c2ecf20Sopenharmony_ci z8530_channel_load(&dev->chanA, z8530_hdlc_kilostream); 2968c2ecf20Sopenharmony_ci z8530_channel_load(&dev->chanB, z8530_hdlc_kilostream); 2978c2ecf20Sopenharmony_ci } else { 2988c2ecf20Sopenharmony_ci z8530_channel_load(&dev->chanA, z8530_hdlc_kilostream_85230); 2998c2ecf20Sopenharmony_ci z8530_channel_load(&dev->chanB, z8530_hdlc_kilostream_85230); 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* 3038c2ecf20Sopenharmony_ci * Now we can take the IRQ 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci enable_irq(irq); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (slvl_setup(&b->dev[0], iobase, irq)) 3098c2ecf20Sopenharmony_ci goto free_hw; 3108c2ecf20Sopenharmony_ci if (slvl_setup(&b->dev[1], iobase, irq)) 3118c2ecf20Sopenharmony_ci goto free_netdev0; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci z8530_describe(dev, "I/O", iobase); 3148c2ecf20Sopenharmony_ci dev->active = 1; 3158c2ecf20Sopenharmony_ci return b; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cifree_netdev0: 3188c2ecf20Sopenharmony_ci unregister_hdlc_device(b->dev[0].chan->netdevice); 3198c2ecf20Sopenharmony_ci free_netdev(b->dev[0].chan->netdevice); 3208c2ecf20Sopenharmony_cifree_hw: 3218c2ecf20Sopenharmony_ci free_dma(dev->chanA.rxdma); 3228c2ecf20Sopenharmony_cierr_dma_rx: 3238c2ecf20Sopenharmony_ci free_dma(dev->chanA.txdma); 3248c2ecf20Sopenharmony_cierr_dma_tx: 3258c2ecf20Sopenharmony_ci free_irq(irq, dev); 3268c2ecf20Sopenharmony_cierr_request_irq: 3278c2ecf20Sopenharmony_ci kfree(b); 3288c2ecf20Sopenharmony_cierr_kzalloc: 3298c2ecf20Sopenharmony_ci release_region(iobase, 8); 3308c2ecf20Sopenharmony_ci return NULL; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic void __exit slvl_shutdown(struct slvl_board *b) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci int u; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci z8530_shutdown(&b->board); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci for (u = 0; u < 2; u++) { 3408c2ecf20Sopenharmony_ci struct net_device *d = b->dev[u].chan->netdevice; 3418c2ecf20Sopenharmony_ci unregister_hdlc_device(d); 3428c2ecf20Sopenharmony_ci free_netdev(d); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci free_irq(b->board.irq, &b->board); 3468c2ecf20Sopenharmony_ci free_dma(b->board.chanA.rxdma); 3478c2ecf20Sopenharmony_ci free_dma(b->board.chanA.txdma); 3488c2ecf20Sopenharmony_ci /* DMA off on the card, drop DTR */ 3498c2ecf20Sopenharmony_ci outb(0, b->iobase); 3508c2ecf20Sopenharmony_ci release_region(b->iobase, 8); 3518c2ecf20Sopenharmony_ci kfree(b); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int io=0x238; 3568c2ecf20Sopenharmony_cistatic int txdma=1; 3578c2ecf20Sopenharmony_cistatic int rxdma=3; 3588c2ecf20Sopenharmony_cistatic int irq=5; 3598c2ecf20Sopenharmony_cistatic bool slow=false; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cimodule_param_hw(io, int, ioport, 0); 3628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(io, "The I/O base of the Sealevel card"); 3638c2ecf20Sopenharmony_cimodule_param_hw(txdma, int, dma, 0); 3648c2ecf20Sopenharmony_ciMODULE_PARM_DESC(txdma, "Transmit DMA channel"); 3658c2ecf20Sopenharmony_cimodule_param_hw(rxdma, int, dma, 0); 3668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rxdma, "Receive DMA channel"); 3678c2ecf20Sopenharmony_cimodule_param_hw(irq, int, irq, 0); 3688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "The interrupt line setting for the SeaLevel card"); 3698c2ecf20Sopenharmony_cimodule_param(slow, bool, 0); 3708c2ecf20Sopenharmony_ciMODULE_PARM_DESC(slow, "Set this for an older Sealevel card such as the 4012"); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alan Cox"); 3738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3748c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Modular driver for the SeaLevel 4021"); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic struct slvl_board *slvl_unit; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic int __init slvl_init_module(void) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci slvl_unit = slvl_init(io, irq, txdma, rxdma, slow); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return slvl_unit ? 0 : -ENODEV; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic void __exit slvl_cleanup_module(void) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci if (slvl_unit) 3888c2ecf20Sopenharmony_ci slvl_shutdown(slvl_unit); 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cimodule_init(slvl_init_module); 3928c2ecf20Sopenharmony_cimodule_exit(slvl_cleanup_module); 393