xref: /openbmc/linux/drivers/net/hamradio/scc.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
11da177e4SLinus Torvalds #define RCS_ID "$Id: scc.c,v 1.75 1998/11/04 15:15:01 jreuter Exp jreuter $"
21da177e4SLinus Torvalds 
31da177e4SLinus Torvalds #define VERSION "3.0"
41da177e4SLinus Torvalds 
51da177e4SLinus Torvalds /*
61da177e4SLinus Torvalds  * Please use z8530drv-utils-3.0 with this version.
71da177e4SLinus Torvalds  *            ------------------
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * You can find a subset of the documentation in
10d4dd87f8SMauro Carvalho Chehab  * Documentation/networking/device_drivers/hamradio/z8530drv.rst.
111da177e4SLinus Torvalds  */
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds /*
141da177e4SLinus Torvalds    ********************************************************************
151da177e4SLinus Torvalds    *   SCC.C - Linux driver for Z8530 based HDLC cards for AX.25      *
161da177e4SLinus Torvalds    ********************************************************************
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds    ********************************************************************
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds 	Copyright (c) 1993, 2000 Joerg Reuter DL1BKE
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds 	portions (c) 1993 Guido ten Dolle PE1NNZ
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds    ********************************************************************
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds    The driver and the programs in the archive are UNDER CONSTRUCTION.
281da177e4SLinus Torvalds    The code is likely to fail, and so your kernel could --- even
291da177e4SLinus Torvalds    a whole network.
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds    This driver is intended for Amateur Radio use. If you are running it
321da177e4SLinus Torvalds    for commercial purposes, please drop me a note. I am nosy...
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds    ...BUT:
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds    ! You  m u s t  recognize the appropriate legislations of your country !
371da177e4SLinus Torvalds    ! before you connect a radio to the SCC board and start to transmit or !
381da177e4SLinus Torvalds    ! receive. The GPL allows you to use the  d r i v e r,  NOT the RADIO! !
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds    For non-Amateur-Radio use please note that you might need a special
411da177e4SLinus Torvalds    allowance/licence from the designer of the SCC Board and/or the
421da177e4SLinus Torvalds    MODEM.
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds    This program is free software; you can redistribute it and/or modify
451da177e4SLinus Torvalds    it under the terms of the (modified) GNU General Public License
461da177e4SLinus Torvalds    delivered with the Linux kernel source.
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds    This program is distributed in the hope that it will be useful,
491da177e4SLinus Torvalds    but WITHOUT ANY WARRANTY; without even the implied warranty of
501da177e4SLinus Torvalds    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
511da177e4SLinus Torvalds    GNU General Public License for more details.
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds    You should find a copy of the GNU General Public License in
541da177e4SLinus Torvalds    /usr/src/linux/COPYING;
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds    ********************************************************************
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds 
591da177e4SLinus Torvalds    Incomplete history of z8530drv:
601da177e4SLinus Torvalds    -------------------------------
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds    1994-09-13	started to write the driver, rescued most of my own
631da177e4SLinus Torvalds 		code (and Hans Alblas' memory buffer pool concept) from
641da177e4SLinus Torvalds 		an earlier project "sccdrv" which was initiated by
651da177e4SLinus Torvalds 		Guido ten Dolle. Not much of the old driver survived,
661da177e4SLinus Torvalds 		though. The first version I put my hands on was sccdrv1.3
671da177e4SLinus Torvalds 		from August 1993. The memory buffer pool concept
681da177e4SLinus Torvalds 		appeared in an unauthorized sccdrv version (1.5) from
691da177e4SLinus Torvalds 		August 1994.
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds    1995-01-31	changed copyright notice to GPL without limitations.
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds      .
741da177e4SLinus Torvalds      .	<SNIP>
751da177e4SLinus Torvalds      .
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds    1996-10-05	New semester, new driver...
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds    		  * KISS TNC emulator removed (TTY driver)
801da177e4SLinus Torvalds    		  * Source moved to drivers/net/
811da177e4SLinus Torvalds    		  * Includes Z8530 defines from drivers/net/z8530.h
821da177e4SLinus Torvalds    		  * Uses sk_buffer memory management
831da177e4SLinus Torvalds    		  * Reduced overhead of /proc/net/z8530drv output
841da177e4SLinus Torvalds    		  * Streamlined quite a lot things
851da177e4SLinus Torvalds    		  * Invents brand new bugs... ;-)
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds    		  The move to version number 3.0 reflects theses changes.
881da177e4SLinus Torvalds    		  You can use 'kissbridge' if you need a KISS TNC emulator.
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds    1996-12-13	Fixed for Linux networking changes. (G4KLX)
911da177e4SLinus Torvalds    1997-01-08	Fixed the remaining problems.
921da177e4SLinus Torvalds    1997-04-02	Hopefully fixed the problems with the new *_timer()
931da177e4SLinus Torvalds    		routines, added calibration code.
941da177e4SLinus Torvalds    1997-10-12	Made SCC_DELAY a CONFIG option, added CONFIG_SCC_TRXECHO
951da177e4SLinus Torvalds    1998-01-29	Small fix to avoid lock-up on initialization
961da177e4SLinus Torvalds    1998-09-29	Fixed the "grouping" bugs, tx_inhibit works again,
971da177e4SLinus Torvalds    		using dev->tx_queue_len now instead of MAXQUEUE now.
981da177e4SLinus Torvalds    1998-10-21	Postponed the spinlock changes, would need a lot of
991da177e4SLinus Torvalds    		testing I currently don't have the time to. Softdcd doesn't
1001da177e4SLinus Torvalds    		work.
1011da177e4SLinus Torvalds    1998-11-04	Softdcd does not work correctly in DPLL mode, in fact it
1021da177e4SLinus Torvalds    		never did. The DPLL locks on noise, the SYNC unit sees
1031da177e4SLinus Torvalds    		flags that aren't... Restarting the DPLL does not help
1041da177e4SLinus Torvalds    		either, it resynchronizes too slow and the first received
1051da177e4SLinus Torvalds    		frame gets lost.
1061da177e4SLinus Torvalds    2000-02-13	Fixed for new network driver interface changes, still
1071da177e4SLinus Torvalds    		does TX timeouts itself since it uses its own queue
1081da177e4SLinus Torvalds    		scheme.
1091da177e4SLinus Torvalds 
1101da177e4SLinus Torvalds    Thanks to all who contributed to this driver with ideas and bug
1111da177e4SLinus Torvalds    reports!
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds    NB -- if you find errors, change something, please let me know
1141da177e4SLinus Torvalds       	 first before you distribute it... And please don't touch
1151da177e4SLinus Torvalds    	 the version number. Just replace my callsign in
1161da177e4SLinus Torvalds    	 "v3.0.dl1bke" with your own. Just to avoid confusion...
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds    If you want to add your modification to the linux distribution
1191da177e4SLinus Torvalds    please (!) contact me first.
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds    New versions of the driver will be announced on the linux-hams
1221da177e4SLinus Torvalds    mailing list on vger.kernel.org. To subscribe send an e-mail
1231da177e4SLinus Torvalds    to majordomo@vger.kernel.org with the following line in
1241da177e4SLinus Torvalds    the body of the mail:
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 	   subscribe linux-hams
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds    The content of the "Subject" field will be ignored.
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds    vy 73,
1311da177e4SLinus Torvalds    Joerg Reuter	ampr-net: dl1bke@db0pra.ampr.org
1321da177e4SLinus Torvalds 		AX-25   : DL1BKE @ DB0ABH.#BAY.DEU.EU
1331da177e4SLinus Torvalds 		Internet: jreuter@yaina.de
1341da177e4SLinus Torvalds 		www     : http://yaina.de/jreuter
1351da177e4SLinus Torvalds */
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds /* ----------------------------------------------------------------------- */
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds #undef  SCC_LDELAY		/* slow it even a bit more down */
1401da177e4SLinus Torvalds #undef  SCC_DONT_CHECK		/* don't look if the SCCs you specified are available */
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds #define SCC_MAXCHIPS	4       /* number of max. supported chips */
1431da177e4SLinus Torvalds #define SCC_BUFSIZE	384     /* must not exceed 4096 */
1441da177e4SLinus Torvalds #undef	SCC_DEBUG
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds #define SCC_DEFAULT_CLOCK	4915200
1471da177e4SLinus Torvalds 				/* default pclock if nothing is specified */
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds /* ----------------------------------------------------------------------- */
1501da177e4SLinus Torvalds 
151b6459415SJakub Kicinski #include <linux/compat.h>
1521da177e4SLinus Torvalds #include <linux/module.h>
1531da177e4SLinus Torvalds #include <linux/errno.h>
1541da177e4SLinus Torvalds #include <linux/signal.h>
1551da177e4SLinus Torvalds #include <linux/timer.h>
1561da177e4SLinus Torvalds #include <linux/interrupt.h>
1571da177e4SLinus Torvalds #include <linux/ioport.h>
1581da177e4SLinus Torvalds #include <linux/string.h>
1591da177e4SLinus Torvalds #include <linux/in.h>
1601da177e4SLinus Torvalds #include <linux/fcntl.h>
1611da177e4SLinus Torvalds #include <linux/ptrace.h>
1621da177e4SLinus Torvalds #include <linux/delay.h>
1631da177e4SLinus Torvalds #include <linux/skbuff.h>
1641da177e4SLinus Torvalds #include <linux/netdevice.h>
1651da177e4SLinus Torvalds #include <linux/rtnetlink.h>
1661da177e4SLinus Torvalds #include <linux/if_ether.h>
1671da177e4SLinus Torvalds #include <linux/if_arp.h>
1681da177e4SLinus Torvalds #include <linux/socket.h>
1691da177e4SLinus Torvalds #include <linux/init.h>
1701da177e4SLinus Torvalds #include <linux/scc.h>
1711da177e4SLinus Torvalds #include <linux/ctype.h>
1721da177e4SLinus Torvalds #include <linux/kernel.h>
1731da177e4SLinus Torvalds #include <linux/proc_fs.h>
1741da177e4SLinus Torvalds #include <linux/seq_file.h>
1751da177e4SLinus Torvalds #include <linux/bitops.h>
1761da177e4SLinus Torvalds 
177457c4cbcSEric W. Biederman #include <net/net_namespace.h>
1781da177e4SLinus Torvalds #include <net/ax25.h>
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds #include <asm/irq.h>
1811da177e4SLinus Torvalds #include <asm/io.h>
1827c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds #include "z8530.h"
1851da177e4SLinus Torvalds 
186afa8c78bSAndi Kleen static const char banner[] __initconst = KERN_INFO \
187eb33ae24SHannes Eder 	"AX.25: Z8530 SCC driver version "VERSION".dl1bke\n";
1881da177e4SLinus Torvalds 
18941e9475cSKees Cook static void t_dwait(struct timer_list *t);
19041e9475cSKees Cook static void t_txdelay(struct timer_list *t);
19141e9475cSKees Cook static void t_tail(struct timer_list *t);
19241e9475cSKees Cook static void t_busy(struct timer_list *);
19341e9475cSKees Cook static void t_maxkeyup(struct timer_list *);
19441e9475cSKees Cook static void t_idle(struct timer_list *t);
1951da177e4SLinus Torvalds static void scc_tx_done(struct scc_channel *);
19641e9475cSKees Cook static void scc_start_tx_timer(struct scc_channel *,
19741e9475cSKees Cook 			       void (*)(struct timer_list *), unsigned long);
1981da177e4SLinus Torvalds static void scc_start_maxkeyup(struct scc_channel *);
1991da177e4SLinus Torvalds static void scc_start_defer(struct scc_channel *);
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds static void z8530_init(void);
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds static void init_channel(struct scc_channel *scc);
2041da177e4SLinus Torvalds static void scc_key_trx (struct scc_channel *scc, char tx);
2051da177e4SLinus Torvalds static void scc_init_timer(struct scc_channel *scc);
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds static int scc_net_alloc(const char *name, struct scc_channel *scc);
2081da177e4SLinus Torvalds static void scc_net_setup(struct net_device *dev);
2091da177e4SLinus Torvalds static int scc_net_open(struct net_device *dev);
2101da177e4SLinus Torvalds static int scc_net_close(struct net_device *dev);
2111da177e4SLinus Torvalds static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb);
21236e4d64aSStephen Hemminger static netdev_tx_t scc_net_tx(struct sk_buff *skb,
21336e4d64aSStephen Hemminger 			      struct net_device *dev);
21425ec92fbSArnd Bergmann static int scc_net_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
21525ec92fbSArnd Bergmann 				  void __user *data, int cmd);
2161da177e4SLinus Torvalds static int scc_net_set_mac_address(struct net_device *dev, void *addr);
2171da177e4SLinus Torvalds static struct net_device_stats * scc_net_get_stats(struct net_device *dev);
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds static unsigned char SCC_DriverName[] = "scc";
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds static struct irqflags { unsigned char used : 1; } Ivec[NR_IRQS];
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds static struct scc_channel SCC_Info[2 * SCC_MAXCHIPS];	/* information per channel */
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds static struct scc_ctrl {
2261da177e4SLinus Torvalds 	io_port chan_A;
2271da177e4SLinus Torvalds 	io_port chan_B;
2281da177e4SLinus Torvalds 	int irq;
2291da177e4SLinus Torvalds } SCC_ctrl[SCC_MAXCHIPS+1];
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds static unsigned char Driver_Initialized;
2321da177e4SLinus Torvalds static int Nchips;
2331da177e4SLinus Torvalds static io_port Vector_Latch;
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds /* ******************************************************************** */
2371da177e4SLinus Torvalds /* *			Port Access Functions			      * */
2381da177e4SLinus Torvalds /* ******************************************************************** */
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds /* These provide interrupt save 2-step access to the Z8530 registers */
2411da177e4SLinus Torvalds 
2421da177e4SLinus Torvalds static DEFINE_SPINLOCK(iolock);	/* Guards paired accesses */
2431da177e4SLinus Torvalds 
InReg(io_port port,unsigned char reg)2441da177e4SLinus Torvalds static inline unsigned char InReg(io_port port, unsigned char reg)
2451da177e4SLinus Torvalds {
2461da177e4SLinus Torvalds 	unsigned long flags;
2471da177e4SLinus Torvalds 	unsigned char r;
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds 	spin_lock_irqsave(&iolock, flags);
2501da177e4SLinus Torvalds #ifdef SCC_LDELAY
2511da177e4SLinus Torvalds 	Outb(port, reg);
2521da177e4SLinus Torvalds 	udelay(SCC_LDELAY);
2531da177e4SLinus Torvalds 	r=Inb(port);
2541da177e4SLinus Torvalds 	udelay(SCC_LDELAY);
2551da177e4SLinus Torvalds #else
2561da177e4SLinus Torvalds 	Outb(port, reg);
2571da177e4SLinus Torvalds 	r=Inb(port);
2581da177e4SLinus Torvalds #endif
2591da177e4SLinus Torvalds 	spin_unlock_irqrestore(&iolock, flags);
2601da177e4SLinus Torvalds 	return r;
2611da177e4SLinus Torvalds }
2621da177e4SLinus Torvalds 
OutReg(io_port port,unsigned char reg,unsigned char val)2631da177e4SLinus Torvalds static inline void OutReg(io_port port, unsigned char reg, unsigned char val)
2641da177e4SLinus Torvalds {
2651da177e4SLinus Torvalds 	unsigned long flags;
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 	spin_lock_irqsave(&iolock, flags);
2681da177e4SLinus Torvalds #ifdef SCC_LDELAY
2691da177e4SLinus Torvalds 	Outb(port, reg); udelay(SCC_LDELAY);
2701da177e4SLinus Torvalds 	Outb(port, val); udelay(SCC_LDELAY);
2711da177e4SLinus Torvalds #else
2721da177e4SLinus Torvalds 	Outb(port, reg);
2731da177e4SLinus Torvalds 	Outb(port, val);
2741da177e4SLinus Torvalds #endif
2751da177e4SLinus Torvalds 	spin_unlock_irqrestore(&iolock, flags);
2761da177e4SLinus Torvalds }
2771da177e4SLinus Torvalds 
wr(struct scc_channel * scc,unsigned char reg,unsigned char val)2781da177e4SLinus Torvalds static inline void wr(struct scc_channel *scc, unsigned char reg,
2791da177e4SLinus Torvalds 	unsigned char val)
2801da177e4SLinus Torvalds {
2811da177e4SLinus Torvalds 	OutReg(scc->ctrl, reg, (scc->wreg[reg] = val));
2821da177e4SLinus Torvalds }
2831da177e4SLinus Torvalds 
or(struct scc_channel * scc,unsigned char reg,unsigned char val)2841da177e4SLinus Torvalds static inline void or(struct scc_channel *scc, unsigned char reg, unsigned char val)
2851da177e4SLinus Torvalds {
2861da177e4SLinus Torvalds 	OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val));
2871da177e4SLinus Torvalds }
2881da177e4SLinus Torvalds 
cl(struct scc_channel * scc,unsigned char reg,unsigned char val)2891da177e4SLinus Torvalds static inline void cl(struct scc_channel *scc, unsigned char reg, unsigned char val)
2901da177e4SLinus Torvalds {
2911da177e4SLinus Torvalds 	OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val));
2921da177e4SLinus Torvalds }
2931da177e4SLinus Torvalds 
2941da177e4SLinus Torvalds /* ******************************************************************** */
2951da177e4SLinus Torvalds /* *			Some useful macros			      * */
2961da177e4SLinus Torvalds /* ******************************************************************** */
2971da177e4SLinus Torvalds 
scc_discard_buffers(struct scc_channel * scc)2981da177e4SLinus Torvalds static inline void scc_discard_buffers(struct scc_channel *scc)
2991da177e4SLinus Torvalds {
3001da177e4SLinus Torvalds 	unsigned long flags;
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds 	spin_lock_irqsave(&scc->lock, flags);
3031da177e4SLinus Torvalds 	if (scc->tx_buff != NULL)
3041da177e4SLinus Torvalds 	{
305*3727f742SYang Yingliang 		dev_kfree_skb_irq(scc->tx_buff);
3061da177e4SLinus Torvalds 		scc->tx_buff = NULL;
3071da177e4SLinus Torvalds 	}
3081da177e4SLinus Torvalds 
309b03efcfbSDavid S. Miller 	while (!skb_queue_empty(&scc->tx_queue))
310*3727f742SYang Yingliang 		dev_kfree_skb_irq(skb_dequeue(&scc->tx_queue));
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds 	spin_unlock_irqrestore(&scc->lock, flags);
3131da177e4SLinus Torvalds }
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds /* ******************************************************************** */
3181da177e4SLinus Torvalds /* *			Interrupt Service Routines		      * */
3191da177e4SLinus Torvalds /* ******************************************************************** */
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds /* ----> subroutines for the interrupt handlers <---- */
3231da177e4SLinus Torvalds 
scc_notify(struct scc_channel * scc,int event)3241da177e4SLinus Torvalds static inline void scc_notify(struct scc_channel *scc, int event)
3251da177e4SLinus Torvalds {
3261da177e4SLinus Torvalds 	struct sk_buff *skb;
3271da177e4SLinus Torvalds 	char *bp;
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds         if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA)
3301da177e4SLinus Torvalds 		return;
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 	skb = dev_alloc_skb(2);
3331da177e4SLinus Torvalds 	if (skb != NULL)
3341da177e4SLinus Torvalds 	{
3351da177e4SLinus Torvalds 		bp = skb_put(skb, 2);
3361da177e4SLinus Torvalds 		*bp++ = PARAM_HWEVENT;
3371da177e4SLinus Torvalds 		*bp++ = event;
3381da177e4SLinus Torvalds 		scc_net_rx(scc, skb);
3391da177e4SLinus Torvalds 	} else
3401da177e4SLinus Torvalds 		scc->stat.nospace++;
3411da177e4SLinus Torvalds }
3421da177e4SLinus Torvalds 
flush_rx_FIFO(struct scc_channel * scc)3431da177e4SLinus Torvalds static inline void flush_rx_FIFO(struct scc_channel *scc)
3441da177e4SLinus Torvalds {
3451da177e4SLinus Torvalds 	int k;
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	for (k=0; k<3; k++)
3481da177e4SLinus Torvalds 		Inb(scc->data);
3491da177e4SLinus Torvalds 
3501da177e4SLinus Torvalds 	if(scc->rx_buff != NULL)		/* did we receive something? */
3511da177e4SLinus Torvalds 	{
3521da177e4SLinus Torvalds 		scc->stat.rxerrs++;  /* then count it as an error */
3531da177e4SLinus Torvalds 		dev_kfree_skb_irq(scc->rx_buff);
3541da177e4SLinus Torvalds 		scc->rx_buff = NULL;
3551da177e4SLinus Torvalds 	}
3561da177e4SLinus Torvalds }
3571da177e4SLinus Torvalds 
start_hunt(struct scc_channel * scc)3581da177e4SLinus Torvalds static void start_hunt(struct scc_channel *scc)
3591da177e4SLinus Torvalds {
3601da177e4SLinus Torvalds 	if ((scc->modem.clocksrc != CLK_EXTERNAL))
3611da177e4SLinus Torvalds 		OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
3621da177e4SLinus Torvalds 	or(scc,R3,ENT_HM|RxENABLE);  /* enable the receiver, hunt mode */
3631da177e4SLinus Torvalds }
3641da177e4SLinus Torvalds 
3651da177e4SLinus Torvalds /* ----> four different interrupt handlers for Tx, Rx, changing of	*/
3661da177e4SLinus Torvalds /*       DCD/CTS and Rx/Tx errors					*/
3671da177e4SLinus Torvalds 
3681da177e4SLinus Torvalds /* Transmitter interrupt handler */
scc_txint(struct scc_channel * scc)3691da177e4SLinus Torvalds static inline void scc_txint(struct scc_channel *scc)
3701da177e4SLinus Torvalds {
3711da177e4SLinus Torvalds 	struct sk_buff *skb;
3721da177e4SLinus Torvalds 
3731da177e4SLinus Torvalds 	scc->stat.txints++;
3741da177e4SLinus Torvalds 	skb = scc->tx_buff;
3751da177e4SLinus Torvalds 
3761da177e4SLinus Torvalds 	/* send first octet */
3771da177e4SLinus Torvalds 
3781da177e4SLinus Torvalds 	if (skb == NULL)
3791da177e4SLinus Torvalds 	{
3801da177e4SLinus Torvalds 		skb = skb_dequeue(&scc->tx_queue);
3811da177e4SLinus Torvalds 		scc->tx_buff = skb;
3821da177e4SLinus Torvalds 		netif_wake_queue(scc->dev);
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds 		if (skb == NULL)
3851da177e4SLinus Torvalds 		{
3861da177e4SLinus Torvalds 			scc_tx_done(scc);
3871da177e4SLinus Torvalds 			Outb(scc->ctrl, RES_Tx_P);
3881da177e4SLinus Torvalds 			return;
3891da177e4SLinus Torvalds 		}
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds 		if (skb->len == 0)		/* Paranoia... */
3921da177e4SLinus Torvalds 		{
3931da177e4SLinus Torvalds 			dev_kfree_skb_irq(skb);
3941da177e4SLinus Torvalds 			scc->tx_buff = NULL;
3951da177e4SLinus Torvalds 			scc_tx_done(scc);
3961da177e4SLinus Torvalds 			Outb(scc->ctrl, RES_Tx_P);
3971da177e4SLinus Torvalds 			return;
3981da177e4SLinus Torvalds 		}
3991da177e4SLinus Torvalds 
4001da177e4SLinus Torvalds 		scc->stat.tx_state = TXS_ACTIVE;
4011da177e4SLinus Torvalds 
4021da177e4SLinus Torvalds 		OutReg(scc->ctrl, R0, RES_Tx_CRC);
4031da177e4SLinus Torvalds 						/* reset CRC generator */
4041da177e4SLinus Torvalds 		or(scc,R10,ABUNDER);		/* re-install underrun protection */
4051da177e4SLinus Torvalds 		Outb(scc->data,*skb->data);	/* send byte */
4061da177e4SLinus Torvalds 		skb_pull(skb, 1);
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds 		if (!scc->enhanced)		/* reset EOM latch */
4091da177e4SLinus Torvalds 			Outb(scc->ctrl,RES_EOM_L);
4101da177e4SLinus Torvalds 		return;
4111da177e4SLinus Torvalds 	}
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 	/* End Of Frame... */
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds 	if (skb->len == 0)
4161da177e4SLinus Torvalds 	{
4171da177e4SLinus Torvalds 		Outb(scc->ctrl, RES_Tx_P);	/* reset pending int */
4181da177e4SLinus Torvalds 		cl(scc, R10, ABUNDER);		/* send CRC */
4191da177e4SLinus Torvalds 		dev_kfree_skb_irq(skb);
4201da177e4SLinus Torvalds 		scc->tx_buff = NULL;
4211da177e4SLinus Torvalds 		scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */
4221da177e4SLinus Torvalds 		return;
4231da177e4SLinus Torvalds 	}
4241da177e4SLinus Torvalds 
4251da177e4SLinus Torvalds 	/* send octet */
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds 	Outb(scc->data,*skb->data);
4281da177e4SLinus Torvalds 	skb_pull(skb, 1);
4291da177e4SLinus Torvalds }
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds /* External/Status interrupt handler */
scc_exint(struct scc_channel * scc)4331da177e4SLinus Torvalds static inline void scc_exint(struct scc_channel *scc)
4341da177e4SLinus Torvalds {
4351da177e4SLinus Torvalds 	unsigned char status,changes,chg_and_stat;
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 	scc->stat.exints++;
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds 	status = InReg(scc->ctrl,R0);
4401da177e4SLinus Torvalds 	changes = status ^ scc->status;
4411da177e4SLinus Torvalds 	chg_and_stat = changes & status;
4421da177e4SLinus Torvalds 
4431da177e4SLinus Torvalds 	/* ABORT: generated whenever DCD drops while receiving */
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds 	if (chg_and_stat & BRK_ABRT)		/* Received an ABORT */
4461da177e4SLinus Torvalds 		flush_rx_FIFO(scc);
4471da177e4SLinus Torvalds 
4481da177e4SLinus Torvalds 	/* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */
4491da177e4SLinus Torvalds 
4501da177e4SLinus Torvalds 	if ((changes & SYNC_HUNT) && scc->kiss.softdcd)
4511da177e4SLinus Torvalds 	{
4521da177e4SLinus Torvalds 		if (status & SYNC_HUNT)
4531da177e4SLinus Torvalds 		{
4541da177e4SLinus Torvalds 			scc->dcd = 0;
4551da177e4SLinus Torvalds 			flush_rx_FIFO(scc);
4561da177e4SLinus Torvalds 			if ((scc->modem.clocksrc != CLK_EXTERNAL))
4571da177e4SLinus Torvalds 				OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
4581da177e4SLinus Torvalds 		} else {
4591da177e4SLinus Torvalds 			scc->dcd = 1;
4601da177e4SLinus Torvalds 		}
4611da177e4SLinus Torvalds 
4621da177e4SLinus Torvalds 		scc_notify(scc, scc->dcd? HWEV_DCD_OFF:HWEV_DCD_ON);
4631da177e4SLinus Torvalds 	}
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 	/* DCD: on = start to receive packet, off = ABORT condition */
4661da177e4SLinus Torvalds 	/* (a successfully received packet generates a special condition int) */
4671da177e4SLinus Torvalds 
4681da177e4SLinus Torvalds 	if((changes & DCD) && !scc->kiss.softdcd) /* DCD input changed state */
4691da177e4SLinus Torvalds 	{
4701da177e4SLinus Torvalds 		if(status & DCD)                /* DCD is now ON */
4711da177e4SLinus Torvalds 		{
4721da177e4SLinus Torvalds 			start_hunt(scc);
4731da177e4SLinus Torvalds 			scc->dcd = 1;
4741da177e4SLinus Torvalds 		} else {                        /* DCD is now OFF */
4751da177e4SLinus Torvalds 			cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */
4761da177e4SLinus Torvalds 			flush_rx_FIFO(scc);
4771da177e4SLinus Torvalds 			scc->dcd = 0;
4781da177e4SLinus Torvalds 		}
4791da177e4SLinus Torvalds 
4801da177e4SLinus Torvalds 		scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF);
4811da177e4SLinus Torvalds 	}
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds #ifdef notdef
4841da177e4SLinus Torvalds 	/* CTS: use external TxDelay (what's that good for?!)
4851da177e4SLinus Torvalds 	 * Anyway: If we _could_ use it (BayCom USCC uses CTS for
4861da177e4SLinus Torvalds 	 * own purposes) we _should_ use the "autoenable" feature
4871da177e4SLinus Torvalds 	 * of the Z8530 and not this interrupt...
4881da177e4SLinus Torvalds 	 */
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 	if (chg_and_stat & CTS)			/* CTS is now ON */
4911da177e4SLinus Torvalds 	{
4921da177e4SLinus Torvalds 		if (scc->kiss.txdelay == 0)	/* zero TXDELAY = wait for CTS */
4931da177e4SLinus Torvalds 			scc_start_tx_timer(scc, t_txdelay, 0);
4941da177e4SLinus Torvalds 	}
4951da177e4SLinus Torvalds #endif
4961da177e4SLinus Torvalds 
4971da177e4SLinus Torvalds 	if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM))
4981da177e4SLinus Torvalds 	{
4991da177e4SLinus Torvalds 		scc->stat.tx_under++;	  /* oops, an underrun! count 'em */
5001da177e4SLinus Torvalds 		Outb(scc->ctrl, RES_EXT_INT);	/* reset ext/status interrupts */
5011da177e4SLinus Torvalds 
5021da177e4SLinus Torvalds 		if (scc->tx_buff != NULL)
5031da177e4SLinus Torvalds 		{
5041da177e4SLinus Torvalds 			dev_kfree_skb_irq(scc->tx_buff);
5051da177e4SLinus Torvalds 			scc->tx_buff = NULL;
5061da177e4SLinus Torvalds 		}
5071da177e4SLinus Torvalds 
5081da177e4SLinus Torvalds 		or(scc,R10,ABUNDER);
5091da177e4SLinus Torvalds 		scc_start_tx_timer(scc, t_txdelay, 0);	/* restart transmission */
5101da177e4SLinus Torvalds 	}
5111da177e4SLinus Torvalds 
5121da177e4SLinus Torvalds 	scc->status = status;
5131da177e4SLinus Torvalds 	Outb(scc->ctrl,RES_EXT_INT);
5141da177e4SLinus Torvalds }
5151da177e4SLinus Torvalds 
5161da177e4SLinus Torvalds 
5171da177e4SLinus Torvalds /* Receiver interrupt handler */
scc_rxint(struct scc_channel * scc)5181da177e4SLinus Torvalds static inline void scc_rxint(struct scc_channel *scc)
5191da177e4SLinus Torvalds {
5201da177e4SLinus Torvalds 	struct sk_buff *skb;
5211da177e4SLinus Torvalds 
5221da177e4SLinus Torvalds 	scc->stat.rxints++;
5231da177e4SLinus Torvalds 
5241da177e4SLinus Torvalds 	if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF)
5251da177e4SLinus Torvalds 	{
5261da177e4SLinus Torvalds 		Inb(scc->data);		/* discard char */
5271da177e4SLinus Torvalds 		or(scc,R3,ENT_HM);	/* enter hunt mode for next flag */
5281da177e4SLinus Torvalds 		return;
5291da177e4SLinus Torvalds 	}
5301da177e4SLinus Torvalds 
5311da177e4SLinus Torvalds 	skb = scc->rx_buff;
5321da177e4SLinus Torvalds 
5331da177e4SLinus Torvalds 	if (skb == NULL)
5341da177e4SLinus Torvalds 	{
5351da177e4SLinus Torvalds 		skb = dev_alloc_skb(scc->stat.bufsize);
5361da177e4SLinus Torvalds 		if (skb == NULL)
5371da177e4SLinus Torvalds 		{
5381da177e4SLinus Torvalds 			scc->dev_stat.rx_dropped++;
5391da177e4SLinus Torvalds 			scc->stat.nospace++;
5401da177e4SLinus Torvalds 			Inb(scc->data);
5411da177e4SLinus Torvalds 			or(scc, R3, ENT_HM);
5421da177e4SLinus Torvalds 			return;
5431da177e4SLinus Torvalds 		}
5441da177e4SLinus Torvalds 
5451da177e4SLinus Torvalds 		scc->rx_buff = skb;
546634fef61SJohannes Berg 		skb_put_u8(skb, 0);	/* KISS data */
5471da177e4SLinus Torvalds 	}
5481da177e4SLinus Torvalds 
5491da177e4SLinus Torvalds 	if (skb->len >= scc->stat.bufsize)
5501da177e4SLinus Torvalds 	{
5511da177e4SLinus Torvalds #ifdef notdef
5521da177e4SLinus Torvalds 		printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n");
5531da177e4SLinus Torvalds #endif
5541da177e4SLinus Torvalds 		dev_kfree_skb_irq(skb);
5551da177e4SLinus Torvalds 		scc->rx_buff = NULL;
5561da177e4SLinus Torvalds 		Inb(scc->data);
5571da177e4SLinus Torvalds 		or(scc, R3, ENT_HM);
5581da177e4SLinus Torvalds 		return;
5591da177e4SLinus Torvalds 	}
5601da177e4SLinus Torvalds 
561634fef61SJohannes Berg 	skb_put_u8(skb, Inb(scc->data));
5621da177e4SLinus Torvalds }
5631da177e4SLinus Torvalds 
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds /* Receive Special Condition interrupt handler */
scc_spint(struct scc_channel * scc)5661da177e4SLinus Torvalds static inline void scc_spint(struct scc_channel *scc)
5671da177e4SLinus Torvalds {
5681da177e4SLinus Torvalds 	unsigned char status;
5691da177e4SLinus Torvalds 	struct sk_buff *skb;
5701da177e4SLinus Torvalds 
5711da177e4SLinus Torvalds 	scc->stat.spints++;
5721da177e4SLinus Torvalds 
5731da177e4SLinus Torvalds 	status = InReg(scc->ctrl,R1);		/* read receiver status */
5741da177e4SLinus Torvalds 
5751da177e4SLinus Torvalds 	Inb(scc->data);				/* throw away Rx byte */
5761da177e4SLinus Torvalds 	skb = scc->rx_buff;
5771da177e4SLinus Torvalds 
5781da177e4SLinus Torvalds 	if(status & Rx_OVR)			/* receiver overrun */
5791da177e4SLinus Torvalds 	{
5801da177e4SLinus Torvalds 		scc->stat.rx_over++;             /* count them */
5811da177e4SLinus Torvalds 		or(scc,R3,ENT_HM);               /* enter hunt mode for next flag */
5821da177e4SLinus Torvalds 
5831da177e4SLinus Torvalds 		if (skb != NULL)
5841da177e4SLinus Torvalds 			dev_kfree_skb_irq(skb);
5851da177e4SLinus Torvalds 		scc->rx_buff = skb = NULL;
5861da177e4SLinus Torvalds 	}
5871da177e4SLinus Torvalds 
5881da177e4SLinus Torvalds 	if(status & END_FR && skb != NULL)	/* end of frame */
5891da177e4SLinus Torvalds 	{
5901da177e4SLinus Torvalds 		/* CRC okay, frame ends on 8 bit boundary and received something ? */
5911da177e4SLinus Torvalds 
5921da177e4SLinus Torvalds 		if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0)
5931da177e4SLinus Torvalds 		{
5941da177e4SLinus Torvalds 			/* ignore last received byte (first of the CRC bytes) */
5951da177e4SLinus Torvalds 			skb_trim(skb, skb->len-1);
5961da177e4SLinus Torvalds 			scc_net_rx(scc, skb);
5971da177e4SLinus Torvalds 			scc->rx_buff = NULL;
5981da177e4SLinus Torvalds 			scc->stat.rxframes++;
5991da177e4SLinus Torvalds 		} else {				/* a bad frame */
6001da177e4SLinus Torvalds 			dev_kfree_skb_irq(skb);
6011da177e4SLinus Torvalds 			scc->rx_buff = NULL;
6021da177e4SLinus Torvalds 			scc->stat.rxerrs++;
6031da177e4SLinus Torvalds 		}
6041da177e4SLinus Torvalds 	}
6051da177e4SLinus Torvalds 
6061da177e4SLinus Torvalds 	Outb(scc->ctrl,ERR_RES);
6071da177e4SLinus Torvalds }
6081da177e4SLinus Torvalds 
6091da177e4SLinus Torvalds 
6101da177e4SLinus Torvalds /* ----> interrupt service routine for the Z8530 <---- */
6111da177e4SLinus Torvalds 
scc_isr_dispatch(struct scc_channel * scc,int vector)6121da177e4SLinus Torvalds static void scc_isr_dispatch(struct scc_channel *scc, int vector)
6131da177e4SLinus Torvalds {
6141da177e4SLinus Torvalds 	spin_lock(&scc->lock);
6151da177e4SLinus Torvalds 	switch (vector & VECTOR_MASK)
6161da177e4SLinus Torvalds 	{
6171da177e4SLinus Torvalds 		case TXINT: scc_txint(scc); break;
6181da177e4SLinus Torvalds 		case EXINT: scc_exint(scc); break;
6191da177e4SLinus Torvalds 		case RXINT: scc_rxint(scc); break;
6201da177e4SLinus Torvalds 		case SPINT: scc_spint(scc); break;
6211da177e4SLinus Torvalds 	}
6221da177e4SLinus Torvalds 	spin_unlock(&scc->lock);
6231da177e4SLinus Torvalds }
6241da177e4SLinus Torvalds 
6251da177e4SLinus Torvalds /* If the card has a latch for the interrupt vector (like the PA0HZP card)
6261da177e4SLinus Torvalds    use it to get the number of the chip that generated the int.
6271da177e4SLinus Torvalds    If not: poll all defined chips.
6281da177e4SLinus Torvalds  */
6291da177e4SLinus Torvalds 
6301da177e4SLinus Torvalds #define SCC_IRQTIMEOUT 30000
6311da177e4SLinus Torvalds 
scc_isr(int irq,void * dev_id)6327d12e780SDavid Howells static irqreturn_t scc_isr(int irq, void *dev_id)
6331da177e4SLinus Torvalds {
6341b36efe0SJeff Garzik 	int chip_irq = (long) dev_id;
6351da177e4SLinus Torvalds 	unsigned char vector;
6361da177e4SLinus Torvalds 	struct scc_channel *scc;
6371da177e4SLinus Torvalds 	struct scc_ctrl *ctrl;
6381da177e4SLinus Torvalds 	int k;
6391da177e4SLinus Torvalds 
6401da177e4SLinus Torvalds 	if (Vector_Latch)
6411da177e4SLinus Torvalds 	{
6421da177e4SLinus Torvalds 	    	for(k=0; k < SCC_IRQTIMEOUT; k++)
6431da177e4SLinus Torvalds     		{
6441da177e4SLinus Torvalds 			Outb(Vector_Latch, 0);      /* Generate INTACK */
6451da177e4SLinus Torvalds 
6461da177e4SLinus Torvalds 			/* Read the vector */
6471da177e4SLinus Torvalds 			if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break;
6481da177e4SLinus Torvalds 			if (vector & 0x01) break;
6491da177e4SLinus Torvalds 
6501da177e4SLinus Torvalds 		        scc=&SCC_Info[vector >> 3 ^ 0x01];
6511da177e4SLinus Torvalds 			if (!scc->dev) break;
6521da177e4SLinus Torvalds 
6531da177e4SLinus Torvalds 			scc_isr_dispatch(scc, vector);
6541da177e4SLinus Torvalds 
6551da177e4SLinus Torvalds 			OutReg(scc->ctrl,R0,RES_H_IUS);              /* Reset Highest IUS */
6561da177e4SLinus Torvalds 		}
6571da177e4SLinus Torvalds 
6581da177e4SLinus Torvalds 		if (k == SCC_IRQTIMEOUT)
6591da177e4SLinus Torvalds 			printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n");
6601da177e4SLinus Torvalds 
6611da177e4SLinus Torvalds 		return IRQ_HANDLED;
6621da177e4SLinus Torvalds 	}
6631da177e4SLinus Torvalds 
6641da177e4SLinus Torvalds 	/* Find the SCC generating the interrupt by polling all attached SCCs
6651da177e4SLinus Torvalds 	 * reading RR3A (the interrupt pending register)
6661da177e4SLinus Torvalds 	 */
6671da177e4SLinus Torvalds 
6681da177e4SLinus Torvalds 	ctrl = SCC_ctrl;
6691da177e4SLinus Torvalds 	while (ctrl->chan_A)
6701da177e4SLinus Torvalds 	{
6711b36efe0SJeff Garzik 		if (ctrl->irq != chip_irq)
6721da177e4SLinus Torvalds 		{
6731da177e4SLinus Torvalds 			ctrl++;
6741da177e4SLinus Torvalds 			continue;
6751da177e4SLinus Torvalds 		}
6761da177e4SLinus Torvalds 
6771da177e4SLinus Torvalds 		scc = NULL;
6781da177e4SLinus Torvalds 		for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++)
6791da177e4SLinus Torvalds 		{
6801da177e4SLinus Torvalds 			vector=InReg(ctrl->chan_B,R2);	/* Read the vector */
6811da177e4SLinus Torvalds 			if (vector & 0x01) break;
6821da177e4SLinus Torvalds 
6831da177e4SLinus Torvalds 			scc = &SCC_Info[vector >> 3 ^ 0x01];
6841da177e4SLinus Torvalds 		        if (!scc->dev) break;
6851da177e4SLinus Torvalds 
6861da177e4SLinus Torvalds 			scc_isr_dispatch(scc, vector);
6871da177e4SLinus Torvalds 		}
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds 		if (k == SCC_IRQTIMEOUT)
6901da177e4SLinus Torvalds 		{
6911da177e4SLinus Torvalds 			printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n");
6921da177e4SLinus Torvalds 			break;
6931da177e4SLinus Torvalds 		}
6941da177e4SLinus Torvalds 
6951da177e4SLinus Torvalds 		/* This looks weird and it is. At least the BayCom USCC doesn't
6961da177e4SLinus Torvalds 		 * use the Interrupt Daisy Chain, thus we'll have to start
6971da177e4SLinus Torvalds 		 * all over again to be sure not to miss an interrupt from
6981da177e4SLinus Torvalds 		 * (any of) the other chip(s)...
6991da177e4SLinus Torvalds 		 * Honestly, the situation *is* braindamaged...
7001da177e4SLinus Torvalds 		 */
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 		if (scc != NULL)
7031da177e4SLinus Torvalds 		{
7041da177e4SLinus Torvalds 			OutReg(scc->ctrl,R0,RES_H_IUS);
7051da177e4SLinus Torvalds 			ctrl = SCC_ctrl;
7061da177e4SLinus Torvalds 		} else
7071da177e4SLinus Torvalds 			ctrl++;
7081da177e4SLinus Torvalds 	}
7091da177e4SLinus Torvalds 	return IRQ_HANDLED;
7101da177e4SLinus Torvalds }
7111da177e4SLinus Torvalds 
7121da177e4SLinus Torvalds 
7131da177e4SLinus Torvalds 
7141da177e4SLinus Torvalds /* ******************************************************************** */
7151da177e4SLinus Torvalds /* *			Init Channel					*/
7161da177e4SLinus Torvalds /* ******************************************************************** */
7171da177e4SLinus Torvalds 
7181da177e4SLinus Torvalds 
7191da177e4SLinus Torvalds /* ----> set SCC channel speed <---- */
7201da177e4SLinus Torvalds 
set_brg(struct scc_channel * scc,unsigned int tc)7211da177e4SLinus Torvalds static inline void set_brg(struct scc_channel *scc, unsigned int tc)
7221da177e4SLinus Torvalds {
7231da177e4SLinus Torvalds 	cl(scc,R14,BRENABL);		/* disable baudrate generator */
7241da177e4SLinus Torvalds 	wr(scc,R12,tc & 255);		/* brg rate LOW */
7251da177e4SLinus Torvalds 	wr(scc,R13,tc >> 8);   		/* brg rate HIGH */
7261da177e4SLinus Torvalds 	or(scc,R14,BRENABL);		/* enable baudrate generator */
7271da177e4SLinus Torvalds }
7281da177e4SLinus Torvalds 
set_speed(struct scc_channel * scc)7291da177e4SLinus Torvalds static inline void set_speed(struct scc_channel *scc)
7301da177e4SLinus Torvalds {
7311da177e4SLinus Torvalds 	unsigned long flags;
7321da177e4SLinus Torvalds 	spin_lock_irqsave(&scc->lock, flags);
7331da177e4SLinus Torvalds 
7341da177e4SLinus Torvalds 	if (scc->modem.speed > 0)	/* paranoia... */
7351da177e4SLinus Torvalds 		set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2);
7361da177e4SLinus Torvalds 
7371da177e4SLinus Torvalds 	spin_unlock_irqrestore(&scc->lock, flags);
7381da177e4SLinus Torvalds }
7391da177e4SLinus Torvalds 
7401da177e4SLinus Torvalds 
7411da177e4SLinus Torvalds /* ----> initialize a SCC channel <---- */
7421da177e4SLinus Torvalds 
init_brg(struct scc_channel * scc)7431da177e4SLinus Torvalds static inline void init_brg(struct scc_channel *scc)
7441da177e4SLinus Torvalds {
7451da177e4SLinus Torvalds 	wr(scc, R14, BRSRC);				/* BRG source = PCLK */
7461da177e4SLinus Torvalds 	OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]);	/* DPLL source = BRG */
7471da177e4SLinus Torvalds 	OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]);	/* DPLL NRZI mode */
7481da177e4SLinus Torvalds }
7491da177e4SLinus Torvalds 
7501da177e4SLinus Torvalds /*
7511da177e4SLinus Torvalds  * Initialization according to the Z8530 manual (SGS-Thomson's version):
7521da177e4SLinus Torvalds  *
7531da177e4SLinus Torvalds  * 1. Modes and constants
7541da177e4SLinus Torvalds  *
7551da177e4SLinus Torvalds  * WR9	11000000	chip reset
7561da177e4SLinus Torvalds  * WR4	XXXXXXXX	Tx/Rx control, async or sync mode
7571da177e4SLinus Torvalds  * WR1	0XX00X00	select W/REQ (optional)
7581da177e4SLinus Torvalds  * WR2	XXXXXXXX	program interrupt vector
7591da177e4SLinus Torvalds  * WR3	XXXXXXX0	select Rx control
7601da177e4SLinus Torvalds  * WR5	XXXX0XXX	select Tx control
7611da177e4SLinus Torvalds  * WR6	XXXXXXXX	sync character
7621da177e4SLinus Torvalds  * WR7	XXXXXXXX	sync character
7631da177e4SLinus Torvalds  * WR9	000X0XXX	select interrupt control
7641da177e4SLinus Torvalds  * WR10	XXXXXXXX	miscellaneous control (optional)
7651da177e4SLinus Torvalds  * WR11	XXXXXXXX	clock control
7661da177e4SLinus Torvalds  * WR12	XXXXXXXX	time constant lower byte (optional)
7671da177e4SLinus Torvalds  * WR13	XXXXXXXX	time constant upper byte (optional)
7681da177e4SLinus Torvalds  * WR14	XXXXXXX0	miscellaneous control
7691da177e4SLinus Torvalds  * WR14	XXXSSSSS	commands (optional)
7701da177e4SLinus Torvalds  *
7711da177e4SLinus Torvalds  * 2. Enables
7721da177e4SLinus Torvalds  *
7731da177e4SLinus Torvalds  * WR14	000SSSS1	baud rate enable
7741da177e4SLinus Torvalds  * WR3	SSSSSSS1	Rx enable
7751da177e4SLinus Torvalds  * WR5	SSSS1SSS	Tx enable
7761da177e4SLinus Torvalds  * WR0	10000000	reset Tx CRG (optional)
7771da177e4SLinus Torvalds  * WR1	XSS00S00	DMA enable (optional)
7781da177e4SLinus Torvalds  *
7791da177e4SLinus Torvalds  * 3. Interrupt status
7801da177e4SLinus Torvalds  *
7811da177e4SLinus Torvalds  * WR15	XXXXXXXX	enable external/status
7821da177e4SLinus Torvalds  * WR0	00010000	reset external status
7831da177e4SLinus Torvalds  * WR0	00010000	reset external status twice
7841da177e4SLinus Torvalds  * WR1	SSSXXSXX	enable Rx, Tx and Ext/status
7851da177e4SLinus Torvalds  * WR9	000SXSSS	enable master interrupt enable
7861da177e4SLinus Torvalds  *
7871da177e4SLinus Torvalds  * 1 = set to one, 0 = reset to zero
7881da177e4SLinus Torvalds  * X = user defined, S = same as previous init
7891da177e4SLinus Torvalds  *
7901da177e4SLinus Torvalds  *
7911da177e4SLinus Torvalds  * Note that the implementation differs in some points from above scheme.
7921da177e4SLinus Torvalds  *
7931da177e4SLinus Torvalds  */
7941da177e4SLinus Torvalds 
init_channel(struct scc_channel * scc)7951da177e4SLinus Torvalds static void init_channel(struct scc_channel *scc)
7961da177e4SLinus Torvalds {
7971da177e4SLinus Torvalds 	del_timer(&scc->tx_t);
7981da177e4SLinus Torvalds 	del_timer(&scc->tx_wdog);
7991da177e4SLinus Torvalds 
8001da177e4SLinus Torvalds 	disable_irq(scc->irq);
8011da177e4SLinus Torvalds 
8021da177e4SLinus Torvalds 	wr(scc,R4,X1CLK|SDLC);		/* *1 clock, SDLC mode */
8031da177e4SLinus Torvalds 	wr(scc,R1,0);			/* no W/REQ operation */
8041da177e4SLinus Torvalds 	wr(scc,R3,Rx8|RxCRC_ENAB);	/* RX 8 bits/char, CRC, disabled */
8051da177e4SLinus Torvalds 	wr(scc,R5,Tx8|DTR|TxCRC_ENAB);	/* TX 8 bits/char, disabled, DTR */
8061da177e4SLinus Torvalds 	wr(scc,R6,0);			/* SDLC address zero (not used) */
8071da177e4SLinus Torvalds 	wr(scc,R7,FLAG);		/* SDLC flag value */
8081da177e4SLinus Torvalds 	wr(scc,R9,VIS);			/* vector includes status */
8091da177e4SLinus Torvalds 	wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */
8101da177e4SLinus Torvalds 	wr(scc,R14, 0);
8111da177e4SLinus Torvalds 
8121da177e4SLinus Torvalds 
8131da177e4SLinus Torvalds /* set clock sources:
8141da177e4SLinus Torvalds 
8151da177e4SLinus Torvalds    CLK_DPLL: normal halfduplex operation
8161da177e4SLinus Torvalds 
8171da177e4SLinus Torvalds 		RxClk: use DPLL
8181da177e4SLinus Torvalds 		TxClk: use DPLL
8191da177e4SLinus Torvalds 		TRxC mode DPLL output
8201da177e4SLinus Torvalds 
8211da177e4SLinus Torvalds    CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem)
8221da177e4SLinus Torvalds 
8231da177e4SLinus Torvalds   	        BayCom: 		others:
8241da177e4SLinus Torvalds 
8251da177e4SLinus Torvalds   	        TxClk = pin RTxC	TxClk = pin TRxC
8261da177e4SLinus Torvalds   	        RxClk = pin TRxC 	RxClk = pin RTxC
8271da177e4SLinus Torvalds 
8281da177e4SLinus Torvalds 
8291da177e4SLinus Torvalds    CLK_DIVIDER:
8301da177e4SLinus Torvalds    		RxClk = use DPLL
8311da177e4SLinus Torvalds    		TxClk = pin RTxC
8321da177e4SLinus Torvalds 
8331da177e4SLinus Torvalds    		BayCom:			others:
8341da177e4SLinus Torvalds    		pin TRxC = DPLL		pin TRxC = BRG
8351da177e4SLinus Torvalds    		(RxClk * 1)		(RxClk * 32)
8361da177e4SLinus Torvalds */
8371da177e4SLinus Torvalds 
8381da177e4SLinus Torvalds 
8391da177e4SLinus Torvalds 	switch(scc->modem.clocksrc)
8401da177e4SLinus Torvalds 	{
8411da177e4SLinus Torvalds 		case CLK_DPLL:
8421da177e4SLinus Torvalds 			wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
8431da177e4SLinus Torvalds 			init_brg(scc);
8441da177e4SLinus Torvalds 			break;
8451da177e4SLinus Torvalds 
8461da177e4SLinus Torvalds 		case CLK_DIVIDER:
8471da177e4SLinus Torvalds 			wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI);
8481da177e4SLinus Torvalds 			init_brg(scc);
8491da177e4SLinus Torvalds 			break;
8501da177e4SLinus Torvalds 
8511da177e4SLinus Torvalds 		case CLK_EXTERNAL:
8521da177e4SLinus Torvalds 			wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP);
8531da177e4SLinus Torvalds 			OutReg(scc->ctrl, R14, DISDPLL);
8541da177e4SLinus Torvalds 			break;
8551da177e4SLinus Torvalds 
8561da177e4SLinus Torvalds 	}
8571da177e4SLinus Torvalds 
8581da177e4SLinus Torvalds 	set_speed(scc);			/* set baudrate */
8591da177e4SLinus Torvalds 
8601da177e4SLinus Torvalds 	if(scc->enhanced)
8611da177e4SLinus Torvalds 	{
8621da177e4SLinus Torvalds 		or(scc,R15,SHDLCE|FIFOE);	/* enable FIFO, SDLC/HDLC Enhancements (From now R7 is R7') */
8631da177e4SLinus Torvalds 		wr(scc,R7,AUTOEOM);
8641da177e4SLinus Torvalds 	}
8651da177e4SLinus Torvalds 
8661da177e4SLinus Torvalds 	if(scc->kiss.softdcd || (InReg(scc->ctrl,R0) & DCD))
8671da177e4SLinus Torvalds 						/* DCD is now ON */
8681da177e4SLinus Torvalds 	{
8691da177e4SLinus Torvalds 		start_hunt(scc);
8701da177e4SLinus Torvalds 	}
8711da177e4SLinus Torvalds 
8721da177e4SLinus Torvalds 	/* enable ABORT, DCD & SYNC/HUNT interrupts */
8731da177e4SLinus Torvalds 
8741da177e4SLinus Torvalds 	wr(scc,R15, BRKIE|TxUIE|(scc->kiss.softdcd? SYNCIE:DCDIE));
8751da177e4SLinus Torvalds 
8761da177e4SLinus Torvalds 	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
8771da177e4SLinus Torvalds 	Outb(scc->ctrl,RES_EXT_INT);	/* must be done twice */
8781da177e4SLinus Torvalds 
8791da177e4SLinus Torvalds 	or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */
8801da177e4SLinus Torvalds 
8811da177e4SLinus Torvalds 	scc->status = InReg(scc->ctrl,R0);	/* read initial status */
8821da177e4SLinus Torvalds 
8831da177e4SLinus Torvalds 	or(scc,R9,MIE);			/* master interrupt enable */
8841da177e4SLinus Torvalds 
8851da177e4SLinus Torvalds 	scc_init_timer(scc);
8861da177e4SLinus Torvalds 
8871da177e4SLinus Torvalds 	enable_irq(scc->irq);
8881da177e4SLinus Torvalds }
8891da177e4SLinus Torvalds 
8901da177e4SLinus Torvalds 
8911da177e4SLinus Torvalds 
8921da177e4SLinus Torvalds 
8931da177e4SLinus Torvalds /* ******************************************************************** */
8941da177e4SLinus Torvalds /* *			SCC timer functions			      * */
8951da177e4SLinus Torvalds /* ******************************************************************** */
8961da177e4SLinus Torvalds 
8971da177e4SLinus Torvalds 
8981da177e4SLinus Torvalds /* ----> scc_key_trx sets the time constant for the baudrate
8991da177e4SLinus Torvalds          generator and keys the transmitter		     <---- */
9001da177e4SLinus Torvalds 
scc_key_trx(struct scc_channel * scc,char tx)9011da177e4SLinus Torvalds static void scc_key_trx(struct scc_channel *scc, char tx)
9021da177e4SLinus Torvalds {
9031da177e4SLinus Torvalds 	unsigned int time_const;
9041da177e4SLinus Torvalds 
9051da177e4SLinus Torvalds 	if (scc->brand & PRIMUS)
9061da177e4SLinus Torvalds 		Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0));
9071da177e4SLinus Torvalds 
9081da177e4SLinus Torvalds 	if (scc->modem.speed < 300)
9091da177e4SLinus Torvalds 		scc->modem.speed = 1200;
9101da177e4SLinus Torvalds 
9111da177e4SLinus Torvalds 	time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2;
9121da177e4SLinus Torvalds 
9131da177e4SLinus Torvalds 	disable_irq(scc->irq);
9141da177e4SLinus Torvalds 
9151da177e4SLinus Torvalds 	if (tx)
9161da177e4SLinus Torvalds 	{
9171da177e4SLinus Torvalds 		or(scc, R1, TxINT_ENAB);	/* t_maxkeyup may have reset these */
9181da177e4SLinus Torvalds 		or(scc, R15, TxUIE);
9191da177e4SLinus Torvalds 	}
9201da177e4SLinus Torvalds 
9211da177e4SLinus Torvalds 	if (scc->modem.clocksrc == CLK_DPLL)
9221da177e4SLinus Torvalds 	{				/* force simplex operation */
9231da177e4SLinus Torvalds 		if (tx)
9241da177e4SLinus Torvalds 		{
9251da177e4SLinus Torvalds #ifdef CONFIG_SCC_TRXECHO
9261da177e4SLinus Torvalds 			cl(scc, R3, RxENABLE|ENT_HM);	/* switch off receiver */
9271da177e4SLinus Torvalds 			cl(scc, R15, DCDIE|SYNCIE);	/* No DCD changes, please */
9281da177e4SLinus Torvalds #endif
9291da177e4SLinus Torvalds 			set_brg(scc, time_const);	/* reprogram baudrate generator */
9301da177e4SLinus Torvalds 
9311da177e4SLinus Torvalds 			/* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */
9321da177e4SLinus Torvalds 			wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR);
9331da177e4SLinus Torvalds 
9341da177e4SLinus Torvalds 			/* By popular demand: tx_inhibit */
9351da177e4SLinus Torvalds 			if (scc->kiss.tx_inhibit)
9361da177e4SLinus Torvalds 			{
9371da177e4SLinus Torvalds 				or(scc,R5, TxENAB);
9381da177e4SLinus Torvalds 				scc->wreg[R5] |= RTS;
9391da177e4SLinus Torvalds 			} else {
9401da177e4SLinus Torvalds 				or(scc,R5,RTS|TxENAB);	/* set the RTS line and enable TX */
9411da177e4SLinus Torvalds 			}
9421da177e4SLinus Torvalds 		} else {
9431da177e4SLinus Torvalds 			cl(scc,R5,RTS|TxENAB);
9441da177e4SLinus Torvalds 
9451da177e4SLinus Torvalds 			set_brg(scc, time_const);	/* reprogram baudrate generator */
9461da177e4SLinus Torvalds 
9471da177e4SLinus Torvalds 			/* DPLL -> Rx clk, DPLL -> Tx CLK, TRxC mode output, TRxC = DPLL */
9481da177e4SLinus Torvalds 			wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
9491da177e4SLinus Torvalds 
9501da177e4SLinus Torvalds #ifndef CONFIG_SCC_TRXECHO
9511da177e4SLinus Torvalds 			if (scc->kiss.softdcd)
9521da177e4SLinus Torvalds #endif
9531da177e4SLinus Torvalds 			{
9541da177e4SLinus Torvalds 				or(scc,R15, scc->kiss.softdcd? SYNCIE:DCDIE);
9551da177e4SLinus Torvalds 				start_hunt(scc);
9561da177e4SLinus Torvalds 			}
9571da177e4SLinus Torvalds 		}
9581da177e4SLinus Torvalds 	} else {
9591da177e4SLinus Torvalds 		if (tx)
9601da177e4SLinus Torvalds 		{
9611da177e4SLinus Torvalds #ifdef CONFIG_SCC_TRXECHO
9621da177e4SLinus Torvalds 			if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
9631da177e4SLinus Torvalds 			{
9641da177e4SLinus Torvalds 				cl(scc, R3, RxENABLE);
9651da177e4SLinus Torvalds 				cl(scc, R15, DCDIE|SYNCIE);
9661da177e4SLinus Torvalds 			}
9671da177e4SLinus Torvalds #endif
9681da177e4SLinus Torvalds 
9691da177e4SLinus Torvalds 			if (scc->kiss.tx_inhibit)
9701da177e4SLinus Torvalds 			{
9711da177e4SLinus Torvalds 				or(scc,R5, TxENAB);
9721da177e4SLinus Torvalds 				scc->wreg[R5] |= RTS;
9731da177e4SLinus Torvalds 			} else {
9741da177e4SLinus Torvalds 				or(scc,R5,RTS|TxENAB);	/* enable tx */
9751da177e4SLinus Torvalds 			}
9761da177e4SLinus Torvalds 		} else {
9771da177e4SLinus Torvalds 			cl(scc,R5,RTS|TxENAB);		/* disable tx */
9781da177e4SLinus Torvalds 
9791da177e4SLinus Torvalds 			if ((scc->kiss.fulldup == KISS_DUPLEX_HALF) &&
9801da177e4SLinus Torvalds #ifndef CONFIG_SCC_TRXECHO
9811da177e4SLinus Torvalds 			    scc->kiss.softdcd)
9821da177e4SLinus Torvalds #else
9831da177e4SLinus Torvalds 			    1)
9841da177e4SLinus Torvalds #endif
9851da177e4SLinus Torvalds 			{
9861da177e4SLinus Torvalds 				or(scc, R15, scc->kiss.softdcd? SYNCIE:DCDIE);
9871da177e4SLinus Torvalds 				start_hunt(scc);
9881da177e4SLinus Torvalds 			}
9891da177e4SLinus Torvalds 		}
9901da177e4SLinus Torvalds 	}
9911da177e4SLinus Torvalds 
9921da177e4SLinus Torvalds 	enable_irq(scc->irq);
9931da177e4SLinus Torvalds }
9941da177e4SLinus Torvalds 
9951da177e4SLinus Torvalds 
9961da177e4SLinus Torvalds /* ----> SCC timer interrupt handler and friends. <---- */
9971da177e4SLinus Torvalds 
__scc_start_tx_timer(struct scc_channel * scc,void (* handler)(struct timer_list * t),unsigned long when)99841e9475cSKees Cook static void __scc_start_tx_timer(struct scc_channel *scc,
99941e9475cSKees Cook 				 void (*handler)(struct timer_list *t),
100041e9475cSKees Cook 				 unsigned long when)
10011da177e4SLinus Torvalds {
10021da177e4SLinus Torvalds 	del_timer(&scc->tx_t);
10031da177e4SLinus Torvalds 
10041da177e4SLinus Torvalds 	if (when == 0)
10051da177e4SLinus Torvalds 	{
100641e9475cSKees Cook 		handler(&scc->tx_t);
10071da177e4SLinus Torvalds 	} else
10081da177e4SLinus Torvalds 	if (when != TIMER_OFF)
10091da177e4SLinus Torvalds 	{
1010841b86f3SKees Cook 		scc->tx_t.function = handler;
10111da177e4SLinus Torvalds 		scc->tx_t.expires = jiffies + (when*HZ)/100;
10121da177e4SLinus Torvalds 		add_timer(&scc->tx_t);
10131da177e4SLinus Torvalds 	}
10141da177e4SLinus Torvalds }
10151da177e4SLinus Torvalds 
scc_start_tx_timer(struct scc_channel * scc,void (* handler)(struct timer_list * t),unsigned long when)101641e9475cSKees Cook static void scc_start_tx_timer(struct scc_channel *scc,
101741e9475cSKees Cook 			       void (*handler)(struct timer_list *t),
101841e9475cSKees Cook 			       unsigned long when)
10191da177e4SLinus Torvalds {
10201da177e4SLinus Torvalds 	unsigned long flags;
10211da177e4SLinus Torvalds 
10221da177e4SLinus Torvalds 	spin_lock_irqsave(&scc->lock, flags);
10231da177e4SLinus Torvalds 	__scc_start_tx_timer(scc, handler, when);
10241da177e4SLinus Torvalds 	spin_unlock_irqrestore(&scc->lock, flags);
10251da177e4SLinus Torvalds }
10261da177e4SLinus Torvalds 
scc_start_defer(struct scc_channel * scc)10271da177e4SLinus Torvalds static void scc_start_defer(struct scc_channel *scc)
10281da177e4SLinus Torvalds {
10291da177e4SLinus Torvalds 	unsigned long flags;
10301da177e4SLinus Torvalds 
10311da177e4SLinus Torvalds 	spin_lock_irqsave(&scc->lock, flags);
10321da177e4SLinus Torvalds 	del_timer(&scc->tx_wdog);
10331da177e4SLinus Torvalds 
10341da177e4SLinus Torvalds 	if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF)
10351da177e4SLinus Torvalds 	{
1036841b86f3SKees Cook 		scc->tx_wdog.function = t_busy;
10371da177e4SLinus Torvalds 		scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer;
10381da177e4SLinus Torvalds 		add_timer(&scc->tx_wdog);
10391da177e4SLinus Torvalds 	}
10401da177e4SLinus Torvalds 	spin_unlock_irqrestore(&scc->lock, flags);
10411da177e4SLinus Torvalds }
10421da177e4SLinus Torvalds 
scc_start_maxkeyup(struct scc_channel * scc)10431da177e4SLinus Torvalds static void scc_start_maxkeyup(struct scc_channel *scc)
10441da177e4SLinus Torvalds {
10451da177e4SLinus Torvalds 	unsigned long flags;
10461da177e4SLinus Torvalds 
10471da177e4SLinus Torvalds 	spin_lock_irqsave(&scc->lock, flags);
10481da177e4SLinus Torvalds 	del_timer(&scc->tx_wdog);
10491da177e4SLinus Torvalds 
10501da177e4SLinus Torvalds 	if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF)
10511da177e4SLinus Torvalds 	{
1052841b86f3SKees Cook 		scc->tx_wdog.function = t_maxkeyup;
10531da177e4SLinus Torvalds 		scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup;
10541da177e4SLinus Torvalds 		add_timer(&scc->tx_wdog);
10551da177e4SLinus Torvalds 	}
10561da177e4SLinus Torvalds 	spin_unlock_irqrestore(&scc->lock, flags);
10571da177e4SLinus Torvalds }
10581da177e4SLinus Torvalds 
10591da177e4SLinus Torvalds /*
10601da177e4SLinus Torvalds  * This is called from scc_txint() when there are no more frames to send.
10611da177e4SLinus Torvalds  * Not exactly a timer function, but it is a close friend of the family...
10621da177e4SLinus Torvalds  */
10631da177e4SLinus Torvalds 
scc_tx_done(struct scc_channel * scc)10641da177e4SLinus Torvalds static void scc_tx_done(struct scc_channel *scc)
10651da177e4SLinus Torvalds {
10661da177e4SLinus Torvalds 	/*
10671da177e4SLinus Torvalds 	 * trx remains keyed in fulldup mode 2 until t_idle expires.
10681da177e4SLinus Torvalds 	 */
10691da177e4SLinus Torvalds 
10701da177e4SLinus Torvalds 	switch (scc->kiss.fulldup)
10711da177e4SLinus Torvalds 	{
10721da177e4SLinus Torvalds 		case KISS_DUPLEX_LINK:
10731da177e4SLinus Torvalds 			scc->stat.tx_state = TXS_IDLE2;
10741da177e4SLinus Torvalds 			if (scc->kiss.idletime != TIMER_OFF)
10751f1d47efSJulia Lawall 				scc_start_tx_timer(scc, t_idle,
10761f1d47efSJulia Lawall 						   scc->kiss.idletime*100);
10771da177e4SLinus Torvalds 			break;
10781da177e4SLinus Torvalds 		case KISS_DUPLEX_OPTIMA:
10791da177e4SLinus Torvalds 			scc_notify(scc, HWEV_ALL_SENT);
10801da177e4SLinus Torvalds 			break;
10811da177e4SLinus Torvalds 		default:
10821da177e4SLinus Torvalds 			scc->stat.tx_state = TXS_BUSY;
10831da177e4SLinus Torvalds 			scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
10841da177e4SLinus Torvalds 	}
10851da177e4SLinus Torvalds 
10861da177e4SLinus Torvalds 	netif_wake_queue(scc->dev);
10871da177e4SLinus Torvalds }
10881da177e4SLinus Torvalds 
10891da177e4SLinus Torvalds 
10901da177e4SLinus Torvalds static unsigned char Rand = 17;
10911da177e4SLinus Torvalds 
is_grouped(struct scc_channel * scc)10921da177e4SLinus Torvalds static inline int is_grouped(struct scc_channel *scc)
10931da177e4SLinus Torvalds {
10941da177e4SLinus Torvalds 	int k;
10951da177e4SLinus Torvalds 	struct scc_channel *scc2;
10961da177e4SLinus Torvalds 	unsigned char grp1, grp2;
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds 	grp1 = scc->kiss.group;
10991da177e4SLinus Torvalds 
11001da177e4SLinus Torvalds 	for (k = 0; k < (Nchips * 2); k++)
11011da177e4SLinus Torvalds 	{
11021da177e4SLinus Torvalds 		scc2 = &SCC_Info[k];
11031da177e4SLinus Torvalds 		grp2 = scc2->kiss.group;
11041da177e4SLinus Torvalds 
11051da177e4SLinus Torvalds 		if (scc2 == scc || !(scc2->dev && grp2))
11061da177e4SLinus Torvalds 			continue;
11071da177e4SLinus Torvalds 
11081da177e4SLinus Torvalds 		if ((grp1 & 0x3f) == (grp2 & 0x3f))
11091da177e4SLinus Torvalds 		{
11101da177e4SLinus Torvalds 			if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) )
11111da177e4SLinus Torvalds 				return 1;
11121da177e4SLinus Torvalds 
11131da177e4SLinus Torvalds 			if ( (grp1 & RXGROUP) && scc2->dcd )
11141da177e4SLinus Torvalds 				return 1;
11151da177e4SLinus Torvalds 		}
11161da177e4SLinus Torvalds 	}
11171da177e4SLinus Torvalds 	return 0;
11181da177e4SLinus Torvalds }
11191da177e4SLinus Torvalds 
11201da177e4SLinus Torvalds /* DWAIT and SLOTTIME expired
11211da177e4SLinus Torvalds  *
11221da177e4SLinus Torvalds  * fulldup == 0:  DCD is active or Rand > P-persistence: start t_busy timer
11231da177e4SLinus Torvalds  *                else key trx and start txdelay
11241da177e4SLinus Torvalds  * fulldup == 1:  key trx and start txdelay
11251da177e4SLinus Torvalds  * fulldup == 2:  mintime expired, reset status or key trx and start txdelay
11261da177e4SLinus Torvalds  */
11271da177e4SLinus Torvalds 
t_dwait(struct timer_list * t)112841e9475cSKees Cook static void t_dwait(struct timer_list *t)
11291da177e4SLinus Torvalds {
113041e9475cSKees Cook 	struct scc_channel *scc = from_timer(scc, t, tx_t);
11311da177e4SLinus Torvalds 
11321da177e4SLinus Torvalds 	if (scc->stat.tx_state == TXS_WAIT)	/* maxkeyup or idle timeout */
11331da177e4SLinus Torvalds 	{
1134b03efcfbSDavid S. Miller 		if (skb_queue_empty(&scc->tx_queue)) {	/* nothing to send */
11351da177e4SLinus Torvalds 			scc->stat.tx_state = TXS_IDLE;
11361da177e4SLinus Torvalds 			netif_wake_queue(scc->dev);	/* t_maxkeyup locked it. */
11371da177e4SLinus Torvalds 			return;
11381da177e4SLinus Torvalds 		}
11391da177e4SLinus Torvalds 
11401da177e4SLinus Torvalds 		scc->stat.tx_state = TXS_BUSY;
11411da177e4SLinus Torvalds 	}
11421da177e4SLinus Torvalds 
11431da177e4SLinus Torvalds 	if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
11441da177e4SLinus Torvalds 	{
11451da177e4SLinus Torvalds 		Rand = Rand * 17 + 31;
11461da177e4SLinus Torvalds 
11471da177e4SLinus Torvalds 		if (scc->dcd || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) )
11481da177e4SLinus Torvalds 		{
11491da177e4SLinus Torvalds 			scc_start_defer(scc);
11501da177e4SLinus Torvalds 			scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime);
11511da177e4SLinus Torvalds 			return ;
11521da177e4SLinus Torvalds 		}
11531da177e4SLinus Torvalds 	}
11541da177e4SLinus Torvalds 
11551da177e4SLinus Torvalds 	if ( !(scc->wreg[R5] & RTS) )
11561da177e4SLinus Torvalds 	{
11571da177e4SLinus Torvalds 		scc_key_trx(scc, TX_ON);
11581da177e4SLinus Torvalds 		scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
11591da177e4SLinus Torvalds 	} else {
11601da177e4SLinus Torvalds 		scc_start_tx_timer(scc, t_txdelay, 0);
11611da177e4SLinus Torvalds 	}
11621da177e4SLinus Torvalds }
11631da177e4SLinus Torvalds 
11641da177e4SLinus Torvalds 
11651da177e4SLinus Torvalds /* TXDELAY expired
11661da177e4SLinus Torvalds  *
11671da177e4SLinus Torvalds  * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog.
11681da177e4SLinus Torvalds  */
11691da177e4SLinus Torvalds 
t_txdelay(struct timer_list * t)117041e9475cSKees Cook static void t_txdelay(struct timer_list *t)
11711da177e4SLinus Torvalds {
117241e9475cSKees Cook 	struct scc_channel *scc = from_timer(scc, t, tx_t);
11731da177e4SLinus Torvalds 
11741da177e4SLinus Torvalds 	scc_start_maxkeyup(scc);
11751da177e4SLinus Torvalds 
11761da177e4SLinus Torvalds 	if (scc->tx_buff == NULL)
11771da177e4SLinus Torvalds 	{
11781da177e4SLinus Torvalds 		disable_irq(scc->irq);
11791da177e4SLinus Torvalds 		scc_txint(scc);
11801da177e4SLinus Torvalds 		enable_irq(scc->irq);
11811da177e4SLinus Torvalds 	}
11821da177e4SLinus Torvalds }
11831da177e4SLinus Torvalds 
11841da177e4SLinus Torvalds 
11851da177e4SLinus Torvalds /* TAILTIME expired
11861da177e4SLinus Torvalds  *
11871da177e4SLinus Torvalds  * switch off transmitter. If we were stopped by Maxkeyup restart
11881da177e4SLinus Torvalds  * transmission after 'mintime' seconds
11891da177e4SLinus Torvalds  */
11901da177e4SLinus Torvalds 
t_tail(struct timer_list * t)119141e9475cSKees Cook static void t_tail(struct timer_list *t)
11921da177e4SLinus Torvalds {
119341e9475cSKees Cook 	struct scc_channel *scc = from_timer(scc, t, tx_t);
11941da177e4SLinus Torvalds 	unsigned long flags;
11951da177e4SLinus Torvalds 
11961da177e4SLinus Torvalds 	spin_lock_irqsave(&scc->lock, flags);
11971da177e4SLinus Torvalds 	del_timer(&scc->tx_wdog);
11981da177e4SLinus Torvalds 	scc_key_trx(scc, TX_OFF);
11991da177e4SLinus Torvalds 	spin_unlock_irqrestore(&scc->lock, flags);
12001da177e4SLinus Torvalds 
12011da177e4SLinus Torvalds 	if (scc->stat.tx_state == TXS_TIMEOUT)		/* we had a timeout? */
12021da177e4SLinus Torvalds 	{
12031da177e4SLinus Torvalds 		scc->stat.tx_state = TXS_WAIT;
12041da177e4SLinus Torvalds 		scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
12051da177e4SLinus Torvalds 		return;
12061da177e4SLinus Torvalds 	}
12071da177e4SLinus Torvalds 
12081da177e4SLinus Torvalds 	scc->stat.tx_state = TXS_IDLE;
12091da177e4SLinus Torvalds 	netif_wake_queue(scc->dev);
12101da177e4SLinus Torvalds }
12111da177e4SLinus Torvalds 
12121da177e4SLinus Torvalds 
12131da177e4SLinus Torvalds /* BUSY timeout
12141da177e4SLinus Torvalds  *
12151da177e4SLinus Torvalds  * throw away send buffers if DCD remains active too long.
12161da177e4SLinus Torvalds  */
12171da177e4SLinus Torvalds 
t_busy(struct timer_list * t)121841e9475cSKees Cook static void t_busy(struct timer_list *t)
12191da177e4SLinus Torvalds {
122041e9475cSKees Cook 	struct scc_channel *scc = from_timer(scc, t, tx_wdog);
12211da177e4SLinus Torvalds 
12221da177e4SLinus Torvalds 	del_timer(&scc->tx_t);
12231da177e4SLinus Torvalds 	netif_stop_queue(scc->dev);	/* don't pile on the wabbit! */
12241da177e4SLinus Torvalds 
12251da177e4SLinus Torvalds 	scc_discard_buffers(scc);
12261da177e4SLinus Torvalds 	scc->stat.txerrs++;
12271da177e4SLinus Torvalds 	scc->stat.tx_state = TXS_IDLE;
12281da177e4SLinus Torvalds 
12291da177e4SLinus Torvalds 	netif_wake_queue(scc->dev);
12301da177e4SLinus Torvalds }
12311da177e4SLinus Torvalds 
12321da177e4SLinus Torvalds /* MAXKEYUP timeout
12331da177e4SLinus Torvalds  *
12341da177e4SLinus Torvalds  * this is our watchdog.
12351da177e4SLinus Torvalds  */
12361da177e4SLinus Torvalds 
t_maxkeyup(struct timer_list * t)123741e9475cSKees Cook static void t_maxkeyup(struct timer_list *t)
12381da177e4SLinus Torvalds {
123941e9475cSKees Cook 	struct scc_channel *scc = from_timer(scc, t, tx_wdog);
12401da177e4SLinus Torvalds 	unsigned long flags;
12411da177e4SLinus Torvalds 
12421da177e4SLinus Torvalds 	spin_lock_irqsave(&scc->lock, flags);
12431da177e4SLinus Torvalds 	/*
12441da177e4SLinus Torvalds 	 * let things settle down before we start to
12451da177e4SLinus Torvalds 	 * accept new data.
12461da177e4SLinus Torvalds 	 */
12471da177e4SLinus Torvalds 
12481da177e4SLinus Torvalds 	netif_stop_queue(scc->dev);
12491da177e4SLinus Torvalds 	scc_discard_buffers(scc);
12501da177e4SLinus Torvalds 
12511da177e4SLinus Torvalds 	del_timer(&scc->tx_t);
12521da177e4SLinus Torvalds 
12531da177e4SLinus Torvalds 	cl(scc, R1, TxINT_ENAB);	/* force an ABORT, but don't */
12541da177e4SLinus Torvalds 	cl(scc, R15, TxUIE);		/* count it. */
12551da177e4SLinus Torvalds 	OutReg(scc->ctrl, R0, RES_Tx_P);
12561da177e4SLinus Torvalds 
12571da177e4SLinus Torvalds 	spin_unlock_irqrestore(&scc->lock, flags);
12581da177e4SLinus Torvalds 
12591da177e4SLinus Torvalds 	scc->stat.txerrs++;
12601da177e4SLinus Torvalds 	scc->stat.tx_state = TXS_TIMEOUT;
12611da177e4SLinus Torvalds 	scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
12621da177e4SLinus Torvalds }
12631da177e4SLinus Torvalds 
12641da177e4SLinus Torvalds /* IDLE timeout
12651da177e4SLinus Torvalds  *
12661da177e4SLinus Torvalds  * in fulldup mode 2 it keys down the transmitter after 'idle' seconds
12671da177e4SLinus Torvalds  * of inactivity. We will not restart transmission before 'mintime'
12681da177e4SLinus Torvalds  * expires.
12691da177e4SLinus Torvalds  */
12701da177e4SLinus Torvalds 
t_idle(struct timer_list * t)127141e9475cSKees Cook static void t_idle(struct timer_list *t)
12721da177e4SLinus Torvalds {
127341e9475cSKees Cook 	struct scc_channel *scc = from_timer(scc, t, tx_t);
12741da177e4SLinus Torvalds 
12751da177e4SLinus Torvalds 	del_timer(&scc->tx_wdog);
12761da177e4SLinus Torvalds 
12771da177e4SLinus Torvalds 	scc_key_trx(scc, TX_OFF);
12781da177e4SLinus Torvalds 	if(scc->kiss.mintime)
12791da177e4SLinus Torvalds 		scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
12801da177e4SLinus Torvalds 	scc->stat.tx_state = TXS_WAIT;
12811da177e4SLinus Torvalds }
12821da177e4SLinus Torvalds 
scc_init_timer(struct scc_channel * scc)12831da177e4SLinus Torvalds static void scc_init_timer(struct scc_channel *scc)
12841da177e4SLinus Torvalds {
12851da177e4SLinus Torvalds 	unsigned long flags;
12861da177e4SLinus Torvalds 
12871da177e4SLinus Torvalds 	spin_lock_irqsave(&scc->lock, flags);
12881da177e4SLinus Torvalds 	scc->stat.tx_state = TXS_IDLE;
12891da177e4SLinus Torvalds 	spin_unlock_irqrestore(&scc->lock, flags);
12901da177e4SLinus Torvalds }
12911da177e4SLinus Torvalds 
12921da177e4SLinus Torvalds 
12931da177e4SLinus Torvalds /* ******************************************************************** */
12941da177e4SLinus Torvalds /* *			Set/get L1 parameters			      * */
12951da177e4SLinus Torvalds /* ******************************************************************** */
12961da177e4SLinus Torvalds 
12971da177e4SLinus Torvalds 
12981da177e4SLinus Torvalds /*
12991da177e4SLinus Torvalds  * this will set the "hardware" parameters through KISS commands or ioctl()
13001da177e4SLinus Torvalds  */
13011da177e4SLinus Torvalds 
13021da177e4SLinus Torvalds #define CAST(x) (unsigned long)(x)
13031da177e4SLinus Torvalds 
scc_set_param(struct scc_channel * scc,unsigned int cmd,unsigned int arg)13041da177e4SLinus Torvalds static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg)
13051da177e4SLinus Torvalds {
13061da177e4SLinus Torvalds 	switch (cmd)
13071da177e4SLinus Torvalds 	{
13081da177e4SLinus Torvalds 		case PARAM_TXDELAY:	scc->kiss.txdelay=arg;		break;
13091da177e4SLinus Torvalds 		case PARAM_PERSIST:	scc->kiss.persist=arg;		break;
13101da177e4SLinus Torvalds 		case PARAM_SLOTTIME:	scc->kiss.slottime=arg;		break;
13111da177e4SLinus Torvalds 		case PARAM_TXTAIL:	scc->kiss.tailtime=arg;		break;
13121da177e4SLinus Torvalds 		case PARAM_FULLDUP:	scc->kiss.fulldup=arg;		break;
13131da177e4SLinus Torvalds 		case PARAM_DTR:		break; /* does someone need this? */
13141da177e4SLinus Torvalds 		case PARAM_GROUP:	scc->kiss.group=arg;		break;
13151da177e4SLinus Torvalds 		case PARAM_IDLE:	scc->kiss.idletime=arg;		break;
13161da177e4SLinus Torvalds 		case PARAM_MIN:		scc->kiss.mintime=arg;		break;
13171da177e4SLinus Torvalds 		case PARAM_MAXKEY:	scc->kiss.maxkeyup=arg;		break;
13181da177e4SLinus Torvalds 		case PARAM_WAIT:	scc->kiss.waittime=arg;		break;
13191da177e4SLinus Torvalds 		case PARAM_MAXDEFER:	scc->kiss.maxdefer=arg;		break;
13201da177e4SLinus Torvalds 		case PARAM_TX:		scc->kiss.tx_inhibit=arg;	break;
13211da177e4SLinus Torvalds 
13221da177e4SLinus Torvalds 		case PARAM_SOFTDCD:
13231da177e4SLinus Torvalds 			scc->kiss.softdcd=arg;
13241da177e4SLinus Torvalds 			if (arg)
13251da177e4SLinus Torvalds 			{
13261da177e4SLinus Torvalds 				or(scc, R15, SYNCIE);
13271da177e4SLinus Torvalds 				cl(scc, R15, DCDIE);
13281da177e4SLinus Torvalds 				start_hunt(scc);
13291da177e4SLinus Torvalds 			} else {
13301da177e4SLinus Torvalds 				or(scc, R15, DCDIE);
13311da177e4SLinus Torvalds 				cl(scc, R15, SYNCIE);
13321da177e4SLinus Torvalds 			}
13331da177e4SLinus Torvalds 			break;
13341da177e4SLinus Torvalds 
13351da177e4SLinus Torvalds 		case PARAM_SPEED:
13361da177e4SLinus Torvalds 			if (arg < 256)
13371da177e4SLinus Torvalds 				scc->modem.speed=arg*100;
13381da177e4SLinus Torvalds 			else
13391da177e4SLinus Torvalds 				scc->modem.speed=arg;
13401da177e4SLinus Torvalds 
13411da177e4SLinus Torvalds 			if (scc->stat.tx_state == 0)	/* only switch baudrate on rx... ;-) */
13421da177e4SLinus Torvalds 				set_speed(scc);
13431da177e4SLinus Torvalds 			break;
13441da177e4SLinus Torvalds 
13451da177e4SLinus Torvalds 		case PARAM_RTS:
13461da177e4SLinus Torvalds 			if ( !(scc->wreg[R5] & RTS) )
13471da177e4SLinus Torvalds 			{
13486941727aSIlpo Järvinen 				if (arg != TX_OFF) {
13491da177e4SLinus Torvalds 					scc_key_trx(scc, TX_ON);
13501da177e4SLinus Torvalds 					scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
13516941727aSIlpo Järvinen 				}
13521da177e4SLinus Torvalds 			} else {
13531da177e4SLinus Torvalds 				if (arg == TX_OFF)
13541da177e4SLinus Torvalds 				{
13551da177e4SLinus Torvalds 					scc->stat.tx_state = TXS_BUSY;
13561da177e4SLinus Torvalds 					scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
13571da177e4SLinus Torvalds 				}
13581da177e4SLinus Torvalds 			}
13591da177e4SLinus Torvalds 			break;
13601da177e4SLinus Torvalds 
13611da177e4SLinus Torvalds 		case PARAM_HWEVENT:
13621da177e4SLinus Torvalds 			scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF);
13631da177e4SLinus Torvalds 			break;
13641da177e4SLinus Torvalds 
13651da177e4SLinus Torvalds 		default:		return -EINVAL;
13661da177e4SLinus Torvalds 	}
13671da177e4SLinus Torvalds 
13681da177e4SLinus Torvalds 	return 0;
13691da177e4SLinus Torvalds }
13701da177e4SLinus Torvalds 
13711da177e4SLinus Torvalds 
13721da177e4SLinus Torvalds 
scc_get_param(struct scc_channel * scc,unsigned int cmd)13731da177e4SLinus Torvalds static unsigned long scc_get_param(struct scc_channel *scc, unsigned int cmd)
13741da177e4SLinus Torvalds {
13751da177e4SLinus Torvalds 	switch (cmd)
13761da177e4SLinus Torvalds 	{
13771da177e4SLinus Torvalds 		case PARAM_TXDELAY:	return CAST(scc->kiss.txdelay);
13781da177e4SLinus Torvalds 		case PARAM_PERSIST:	return CAST(scc->kiss.persist);
13791da177e4SLinus Torvalds 		case PARAM_SLOTTIME:	return CAST(scc->kiss.slottime);
13801da177e4SLinus Torvalds 		case PARAM_TXTAIL:	return CAST(scc->kiss.tailtime);
13811da177e4SLinus Torvalds 		case PARAM_FULLDUP:	return CAST(scc->kiss.fulldup);
13821da177e4SLinus Torvalds 		case PARAM_SOFTDCD:	return CAST(scc->kiss.softdcd);
13831da177e4SLinus Torvalds 		case PARAM_DTR:		return CAST((scc->wreg[R5] & DTR)? 1:0);
13841da177e4SLinus Torvalds 		case PARAM_RTS:		return CAST((scc->wreg[R5] & RTS)? 1:0);
13851da177e4SLinus Torvalds 		case PARAM_SPEED:	return CAST(scc->modem.speed);
13861da177e4SLinus Torvalds 		case PARAM_GROUP:	return CAST(scc->kiss.group);
13871da177e4SLinus Torvalds 		case PARAM_IDLE:	return CAST(scc->kiss.idletime);
13881da177e4SLinus Torvalds 		case PARAM_MIN:		return CAST(scc->kiss.mintime);
13891da177e4SLinus Torvalds 		case PARAM_MAXKEY:	return CAST(scc->kiss.maxkeyup);
13901da177e4SLinus Torvalds 		case PARAM_WAIT:	return CAST(scc->kiss.waittime);
13911da177e4SLinus Torvalds 		case PARAM_MAXDEFER:	return CAST(scc->kiss.maxdefer);
13921da177e4SLinus Torvalds 		case PARAM_TX:		return CAST(scc->kiss.tx_inhibit);
13931da177e4SLinus Torvalds 		default:		return NO_SUCH_PARAM;
13941da177e4SLinus Torvalds 	}
13951da177e4SLinus Torvalds 
13961da177e4SLinus Torvalds }
13971da177e4SLinus Torvalds 
13981da177e4SLinus Torvalds #undef CAST
13991da177e4SLinus Torvalds 
14001da177e4SLinus Torvalds /* ******************************************************************* */
14011da177e4SLinus Torvalds /* *			Send calibration pattern		     * */
14021da177e4SLinus Torvalds /* ******************************************************************* */
14031da177e4SLinus Torvalds 
scc_stop_calibrate(struct timer_list * t)140441e9475cSKees Cook static void scc_stop_calibrate(struct timer_list *t)
14051da177e4SLinus Torvalds {
140641e9475cSKees Cook 	struct scc_channel *scc = from_timer(scc, t, tx_wdog);
14071da177e4SLinus Torvalds 	unsigned long flags;
14081da177e4SLinus Torvalds 
14091da177e4SLinus Torvalds 	spin_lock_irqsave(&scc->lock, flags);
14101da177e4SLinus Torvalds 	del_timer(&scc->tx_wdog);
14111da177e4SLinus Torvalds 	scc_key_trx(scc, TX_OFF);
14121da177e4SLinus Torvalds 	wr(scc, R6, 0);
14131da177e4SLinus Torvalds 	wr(scc, R7, FLAG);
14141da177e4SLinus Torvalds 	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
14151da177e4SLinus Torvalds 	Outb(scc->ctrl,RES_EXT_INT);
14161da177e4SLinus Torvalds 
14171da177e4SLinus Torvalds 	netif_wake_queue(scc->dev);
14181da177e4SLinus Torvalds 	spin_unlock_irqrestore(&scc->lock, flags);
14191da177e4SLinus Torvalds }
14201da177e4SLinus Torvalds 
14211da177e4SLinus Torvalds 
14221da177e4SLinus Torvalds static void
scc_start_calibrate(struct scc_channel * scc,int duration,unsigned char pattern)14231da177e4SLinus Torvalds scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern)
14241da177e4SLinus Torvalds {
14251da177e4SLinus Torvalds 	unsigned long flags;
14261da177e4SLinus Torvalds 
14271da177e4SLinus Torvalds 	spin_lock_irqsave(&scc->lock, flags);
14281da177e4SLinus Torvalds 	netif_stop_queue(scc->dev);
14291da177e4SLinus Torvalds 	scc_discard_buffers(scc);
14301da177e4SLinus Torvalds 
14311da177e4SLinus Torvalds 	del_timer(&scc->tx_wdog);
14321da177e4SLinus Torvalds 
1433841b86f3SKees Cook 	scc->tx_wdog.function = scc_stop_calibrate;
14341da177e4SLinus Torvalds 	scc->tx_wdog.expires = jiffies + HZ*duration;
14351da177e4SLinus Torvalds 	add_timer(&scc->tx_wdog);
14361da177e4SLinus Torvalds 
14371da177e4SLinus Torvalds 	/* This doesn't seem to work. Why not? */
14381da177e4SLinus Torvalds 	wr(scc, R6, 0);
14391da177e4SLinus Torvalds 	wr(scc, R7, pattern);
14401da177e4SLinus Torvalds 
14411da177e4SLinus Torvalds 	/*
14421da177e4SLinus Torvalds 	 * Don't know if this works.
14431da177e4SLinus Torvalds 	 * Damn, where is my Z8530 programming manual...?
14441da177e4SLinus Torvalds 	 */
14451da177e4SLinus Torvalds 
14461da177e4SLinus Torvalds 	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
14471da177e4SLinus Torvalds 	Outb(scc->ctrl,RES_EXT_INT);
14481da177e4SLinus Torvalds 
14491da177e4SLinus Torvalds 	scc_key_trx(scc, TX_ON);
14501da177e4SLinus Torvalds 	spin_unlock_irqrestore(&scc->lock, flags);
14511da177e4SLinus Torvalds }
14521da177e4SLinus Torvalds 
14531da177e4SLinus Torvalds /* ******************************************************************* */
14541da177e4SLinus Torvalds /* *		Init channel structures, special HW, etc...	     * */
14551da177e4SLinus Torvalds /* ******************************************************************* */
14561da177e4SLinus Torvalds 
14571da177e4SLinus Torvalds /*
14581da177e4SLinus Torvalds  * Reset the Z8530s and setup special hardware
14591da177e4SLinus Torvalds  */
14601da177e4SLinus Torvalds 
z8530_init(void)14611da177e4SLinus Torvalds static void z8530_init(void)
14621da177e4SLinus Torvalds {
14631da177e4SLinus Torvalds 	struct scc_channel *scc;
14641da177e4SLinus Torvalds 	int chip, k;
14651da177e4SLinus Torvalds 	unsigned long flags;
14661da177e4SLinus Torvalds 	char *flag;
14671da177e4SLinus Torvalds 
14681da177e4SLinus Torvalds 
14691da177e4SLinus Torvalds 	printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2);
14701da177e4SLinus Torvalds 
14711da177e4SLinus Torvalds 	flag=" ";
147260e4ad7aSYinghai Lu 	for (k = 0; k < nr_irqs; k++)
14731da177e4SLinus Torvalds 		if (Ivec[k].used)
14741da177e4SLinus Torvalds 		{
14751da177e4SLinus Torvalds 			printk("%s%d", flag, k);
14761da177e4SLinus Torvalds 			flag=",";
14771da177e4SLinus Torvalds 		}
14781da177e4SLinus Torvalds 	printk("\n");
14791da177e4SLinus Torvalds 
14801da177e4SLinus Torvalds 
14811da177e4SLinus Torvalds 	/* reset and pre-init all chips in the system */
14821da177e4SLinus Torvalds 	for (chip = 0; chip < Nchips; chip++)
14831da177e4SLinus Torvalds 	{
14841da177e4SLinus Torvalds 		scc=&SCC_Info[2*chip];
14851da177e4SLinus Torvalds 		if (!scc->ctrl) continue;
14861da177e4SLinus Torvalds 
14871da177e4SLinus Torvalds 		/* Special SCC cards */
14881da177e4SLinus Torvalds 
14891da177e4SLinus Torvalds 		if(scc->brand & EAGLE)			/* this is an EAGLE card */
14901da177e4SLinus Torvalds 			Outb(scc->special,0x08);	/* enable interrupt on the board */
14911da177e4SLinus Torvalds 
14921da177e4SLinus Torvalds 		if(scc->brand & (PC100 | PRIMUS))	/* this is a PC100/PRIMUS card */
14931da177e4SLinus Torvalds 			Outb(scc->special,scc->option);	/* set the MODEM mode (0x22) */
14941da177e4SLinus Torvalds 
14951da177e4SLinus Torvalds 
14961da177e4SLinus Torvalds 		/* Reset and pre-init Z8530 */
14971da177e4SLinus Torvalds 
14981da177e4SLinus Torvalds 		spin_lock_irqsave(&scc->lock, flags);
14991da177e4SLinus Torvalds 
15001da177e4SLinus Torvalds 		Outb(scc->ctrl, 0);
15011da177e4SLinus Torvalds 		OutReg(scc->ctrl,R9,FHWRES);		/* force hardware reset */
15021da177e4SLinus Torvalds 		udelay(100);				/* give it 'a bit' more time than required */
15031da177e4SLinus Torvalds 		wr(scc, R2, chip*16);			/* interrupt vector */
15041da177e4SLinus Torvalds 		wr(scc, R9, VIS);			/* vector includes status */
15051da177e4SLinus Torvalds 		spin_unlock_irqrestore(&scc->lock, flags);
15061da177e4SLinus Torvalds         }
15071da177e4SLinus Torvalds 
15081da177e4SLinus Torvalds 
15091da177e4SLinus Torvalds 	Driver_Initialized = 1;
15101da177e4SLinus Torvalds }
15111da177e4SLinus Torvalds 
15121da177e4SLinus Torvalds /*
15131da177e4SLinus Torvalds  * Allocate device structure, err, instance, and register driver
15141da177e4SLinus Torvalds  */
15151da177e4SLinus Torvalds 
scc_net_alloc(const char * name,struct scc_channel * scc)15161da177e4SLinus Torvalds static int scc_net_alloc(const char *name, struct scc_channel *scc)
15171da177e4SLinus Torvalds {
15181da177e4SLinus Torvalds 	int err;
15191da177e4SLinus Torvalds 	struct net_device *dev;
15201da177e4SLinus Torvalds 
1521c835a677STom Gundersen 	dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, scc_net_setup);
15221da177e4SLinus Torvalds 	if (!dev)
15231da177e4SLinus Torvalds 		return -ENOMEM;
15241da177e4SLinus Torvalds 
1525f4bdd264SWang Chen 	dev->ml_priv = scc;
15261da177e4SLinus Torvalds 	scc->dev = dev;
15271da177e4SLinus Torvalds 	spin_lock_init(&scc->lock);
152841e9475cSKees Cook 	timer_setup(&scc->tx_t, NULL, 0);
152941e9475cSKees Cook 	timer_setup(&scc->tx_wdog, NULL, 0);
15301da177e4SLinus Torvalds 
15311da177e4SLinus Torvalds 	err = register_netdevice(dev);
15321da177e4SLinus Torvalds 	if (err) {
15331da177e4SLinus Torvalds 		printk(KERN_ERR "%s: can't register network device (%d)\n",
15341da177e4SLinus Torvalds 		       name, err);
15351da177e4SLinus Torvalds 		free_netdev(dev);
15361da177e4SLinus Torvalds 		scc->dev = NULL;
15371da177e4SLinus Torvalds 		return err;
15381da177e4SLinus Torvalds 	}
15391da177e4SLinus Torvalds 
15401da177e4SLinus Torvalds 	return 0;
15411da177e4SLinus Torvalds }
15421da177e4SLinus Torvalds 
15431da177e4SLinus Torvalds 
15441da177e4SLinus Torvalds 
15451da177e4SLinus Torvalds /* ******************************************************************** */
15461da177e4SLinus Torvalds /* *			    Network driver methods		      * */
15471da177e4SLinus Torvalds /* ******************************************************************** */
15481da177e4SLinus Torvalds 
1549ff908cf8SStephen Hemminger static const struct net_device_ops scc_netdev_ops = {
1550ff908cf8SStephen Hemminger 	.ndo_open            = scc_net_open,
1551ff908cf8SStephen Hemminger 	.ndo_stop	     = scc_net_close,
1552ff908cf8SStephen Hemminger 	.ndo_start_xmit	     = scc_net_tx,
1553ff908cf8SStephen Hemminger 	.ndo_set_mac_address = scc_net_set_mac_address,
1554ff908cf8SStephen Hemminger 	.ndo_get_stats       = scc_net_get_stats,
155525ec92fbSArnd Bergmann 	.ndo_siocdevprivate  = scc_net_siocdevprivate,
1556ff908cf8SStephen Hemminger };
1557ff908cf8SStephen Hemminger 
15581da177e4SLinus Torvalds /* ----> Initialize device <----- */
15591da177e4SLinus Torvalds 
scc_net_setup(struct net_device * dev)15601da177e4SLinus Torvalds static void scc_net_setup(struct net_device *dev)
15611da177e4SLinus Torvalds {
15621da177e4SLinus Torvalds 	dev->tx_queue_len    = 16;	/* should be enough... */
15631da177e4SLinus Torvalds 
1564ff908cf8SStephen Hemminger 	dev->netdev_ops	     = &scc_netdev_ops;
15653b04dddeSStephen Hemminger 	dev->header_ops      = &ax25_header_ops;
15663b04dddeSStephen Hemminger 
15671da177e4SLinus Torvalds 	dev->flags      = 0;
15681da177e4SLinus Torvalds 
15691da177e4SLinus Torvalds 	dev->type = ARPHRD_AX25;
15701da177e4SLinus Torvalds 	dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
15711da177e4SLinus Torvalds 	dev->mtu = AX25_DEF_PACLEN;
15721da177e4SLinus Torvalds 	dev->addr_len = AX25_ADDR_LEN;
15731da177e4SLinus Torvalds 
157420c3d9e4SJakub Kicinski 	memcpy(dev->broadcast, &ax25_bcast,  AX25_ADDR_LEN);
157520c3d9e4SJakub Kicinski 	dev_addr_set(dev, (u8 *)&ax25_defaddr);
15761da177e4SLinus Torvalds }
15771da177e4SLinus Torvalds 
15781da177e4SLinus Torvalds /* ----> open network device <---- */
15791da177e4SLinus Torvalds 
scc_net_open(struct net_device * dev)15801da177e4SLinus Torvalds static int scc_net_open(struct net_device *dev)
15811da177e4SLinus Torvalds {
1582f4bdd264SWang Chen 	struct scc_channel *scc = (struct scc_channel *) dev->ml_priv;
15831da177e4SLinus Torvalds 
15841da177e4SLinus Torvalds 	if (!scc->init)
15851da177e4SLinus Torvalds 		return -EINVAL;
15861da177e4SLinus Torvalds 
15871da177e4SLinus Torvalds 	scc->tx_buff = NULL;
15881da177e4SLinus Torvalds 	skb_queue_head_init(&scc->tx_queue);
15891da177e4SLinus Torvalds 
15901da177e4SLinus Torvalds 	init_channel(scc);
15911da177e4SLinus Torvalds 
15921da177e4SLinus Torvalds 	netif_start_queue(dev);
15931da177e4SLinus Torvalds 	return 0;
15941da177e4SLinus Torvalds }
15951da177e4SLinus Torvalds 
15961da177e4SLinus Torvalds /* ----> close network device <---- */
15971da177e4SLinus Torvalds 
scc_net_close(struct net_device * dev)15981da177e4SLinus Torvalds static int scc_net_close(struct net_device *dev)
15991da177e4SLinus Torvalds {
1600f4bdd264SWang Chen 	struct scc_channel *scc = (struct scc_channel *) dev->ml_priv;
16011da177e4SLinus Torvalds 	unsigned long flags;
16021da177e4SLinus Torvalds 
16031da177e4SLinus Torvalds 	netif_stop_queue(dev);
16041da177e4SLinus Torvalds 
16051da177e4SLinus Torvalds 	spin_lock_irqsave(&scc->lock, flags);
16061da177e4SLinus Torvalds 	Outb(scc->ctrl,0);		/* Make sure pointer is written */
16071da177e4SLinus Torvalds 	wr(scc,R1,0);			/* disable interrupts */
16081da177e4SLinus Torvalds 	wr(scc,R3,0);
16091da177e4SLinus Torvalds 	spin_unlock_irqrestore(&scc->lock, flags);
16101da177e4SLinus Torvalds 
16111da177e4SLinus Torvalds 	del_timer_sync(&scc->tx_t);
16121da177e4SLinus Torvalds 	del_timer_sync(&scc->tx_wdog);
16131da177e4SLinus Torvalds 
16141da177e4SLinus Torvalds 	scc_discard_buffers(scc);
16151da177e4SLinus Torvalds 
16161da177e4SLinus Torvalds 	return 0;
16171da177e4SLinus Torvalds }
16181da177e4SLinus Torvalds 
16191da177e4SLinus Torvalds /* ----> receive frame, called from scc_rxint() <---- */
16201da177e4SLinus Torvalds 
scc_net_rx(struct scc_channel * scc,struct sk_buff * skb)16211da177e4SLinus Torvalds static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb)
16221da177e4SLinus Torvalds {
16231da177e4SLinus Torvalds 	if (skb->len == 0) {
16241da177e4SLinus Torvalds 		dev_kfree_skb_irq(skb);
16251da177e4SLinus Torvalds 		return;
16261da177e4SLinus Torvalds 	}
16271da177e4SLinus Torvalds 
16281da177e4SLinus Torvalds 	scc->dev_stat.rx_packets++;
16291da177e4SLinus Torvalds 	scc->dev_stat.rx_bytes += skb->len;
16301da177e4SLinus Torvalds 
163156cb5156SArnaldo Carvalho de Melo 	skb->protocol = ax25_type_trans(skb, scc->dev);
16321da177e4SLinus Torvalds 
16331da177e4SLinus Torvalds 	netif_rx(skb);
16341da177e4SLinus Torvalds }
16351da177e4SLinus Torvalds 
16361da177e4SLinus Torvalds /* ----> transmit frame <---- */
16371da177e4SLinus Torvalds 
scc_net_tx(struct sk_buff * skb,struct net_device * dev)163836e4d64aSStephen Hemminger static netdev_tx_t scc_net_tx(struct sk_buff *skb, struct net_device *dev)
16391da177e4SLinus Torvalds {
1640f4bdd264SWang Chen 	struct scc_channel *scc = (struct scc_channel *) dev->ml_priv;
16411da177e4SLinus Torvalds 	unsigned long flags;
16421da177e4SLinus Torvalds 	char kisscmd;
16431da177e4SLinus Torvalds 
16441d5da757SEric W. Biederman 	if (skb->protocol == htons(ETH_P_IP))
16451d5da757SEric W. Biederman 		return ax25_ip_xmit(skb);
16461d5da757SEric W. Biederman 
16471da177e4SLinus Torvalds 	if (skb->len > scc->stat.bufsize || skb->len < 2) {
16481da177e4SLinus Torvalds 		scc->dev_stat.tx_dropped++;	/* bogus frame */
16491da177e4SLinus Torvalds 		dev_kfree_skb(skb);
16506ed10654SPatrick McHardy 		return NETDEV_TX_OK;
16511da177e4SLinus Torvalds 	}
16521da177e4SLinus Torvalds 
16531da177e4SLinus Torvalds 	scc->dev_stat.tx_packets++;
16541da177e4SLinus Torvalds 	scc->dev_stat.tx_bytes += skb->len;
16551da177e4SLinus Torvalds 	scc->stat.txframes++;
16561da177e4SLinus Torvalds 
16571da177e4SLinus Torvalds 	kisscmd = *skb->data & 0x1f;
16581da177e4SLinus Torvalds 	skb_pull(skb, 1);
16591da177e4SLinus Torvalds 
16601da177e4SLinus Torvalds 	if (kisscmd) {
16611da177e4SLinus Torvalds 		scc_set_param(scc, kisscmd, *skb->data);
16621da177e4SLinus Torvalds 		dev_kfree_skb(skb);
16636ed10654SPatrick McHardy 		return NETDEV_TX_OK;
16641da177e4SLinus Torvalds 	}
16651da177e4SLinus Torvalds 
16661da177e4SLinus Torvalds 	spin_lock_irqsave(&scc->lock, flags);
16671da177e4SLinus Torvalds 
16681da177e4SLinus Torvalds 	if (skb_queue_len(&scc->tx_queue) > scc->dev->tx_queue_len) {
16691da177e4SLinus Torvalds 		struct sk_buff *skb_del;
16701da177e4SLinus Torvalds 		skb_del = skb_dequeue(&scc->tx_queue);
1671*3727f742SYang Yingliang 		dev_kfree_skb_irq(skb_del);
16721da177e4SLinus Torvalds 	}
16731da177e4SLinus Torvalds 	skb_queue_tail(&scc->tx_queue, skb);
1674860e9538SFlorian Westphal 	netif_trans_update(dev);
16751da177e4SLinus Torvalds 
16761da177e4SLinus Torvalds 
16771da177e4SLinus Torvalds 	/*
16781da177e4SLinus Torvalds 	 * Start transmission if the trx state is idle or
16791da177e4SLinus Torvalds 	 * t_idle hasn't expired yet. Use dwait/persistence/slottime
16801da177e4SLinus Torvalds 	 * algorithm for normal halfduplex operation.
16811da177e4SLinus Torvalds 	 */
16821da177e4SLinus Torvalds 
16831da177e4SLinus Torvalds 	if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2) {
16841da177e4SLinus Torvalds 		scc->stat.tx_state = TXS_BUSY;
16851da177e4SLinus Torvalds 		if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
16861da177e4SLinus Torvalds 			__scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime);
16871da177e4SLinus Torvalds 		else
16881da177e4SLinus Torvalds 			__scc_start_tx_timer(scc, t_dwait, 0);
16891da177e4SLinus Torvalds 	}
16901da177e4SLinus Torvalds 	spin_unlock_irqrestore(&scc->lock, flags);
16916ed10654SPatrick McHardy 	return NETDEV_TX_OK;
16921da177e4SLinus Torvalds }
16931da177e4SLinus Torvalds 
16941da177e4SLinus Torvalds /* ----> ioctl functions <---- */
16951da177e4SLinus Torvalds 
16961da177e4SLinus Torvalds /*
16971da177e4SLinus Torvalds  * SIOCSCCCFG		- configure driver	arg: (struct scc_hw_config *) arg
16981da177e4SLinus Torvalds  * SIOCSCCINI		- initialize driver	arg: ---
16991da177e4SLinus Torvalds  * SIOCSCCCHANINI	- initialize channel	arg: (struct scc_modem *) arg
17001da177e4SLinus Torvalds  * SIOCSCCSMEM		- set memory		arg: (struct scc_mem_config *) arg
17011da177e4SLinus Torvalds  * SIOCSCCGKISS		- get level 1 parameter	arg: (struct scc_kiss_cmd *) arg
17021da177e4SLinus Torvalds  * SIOCSCCSKISS		- set level 1 parameter arg: (struct scc_kiss_cmd *) arg
17031da177e4SLinus Torvalds  * SIOCSCCGSTAT		- get driver status	arg: (struct scc_stat *) arg
17041da177e4SLinus Torvalds  * SIOCSCCCAL		- send calib. pattern	arg: (struct scc_calibrate *) arg
17051da177e4SLinus Torvalds  */
17061da177e4SLinus Torvalds 
scc_net_siocdevprivate(struct net_device * dev,struct ifreq * ifr,void __user * arg,int cmd)170725ec92fbSArnd Bergmann static int scc_net_siocdevprivate(struct net_device *dev,
170825ec92fbSArnd Bergmann 				  struct ifreq *ifr, void __user *arg, int cmd)
17091da177e4SLinus Torvalds {
17101da177e4SLinus Torvalds 	struct scc_kiss_cmd kiss_cmd;
17111da177e4SLinus Torvalds 	struct scc_mem_config memcfg;
17121da177e4SLinus Torvalds 	struct scc_hw_config hwcfg;
17131da177e4SLinus Torvalds 	struct scc_calibrate cal;
1714f4bdd264SWang Chen 	struct scc_channel *scc = (struct scc_channel *) dev->ml_priv;
17151da177e4SLinus Torvalds 	int chan;
17161da177e4SLinus Torvalds 	unsigned char device_name[IFNAMSIZ];
17171da177e4SLinus Torvalds 
17181da177e4SLinus Torvalds 	if (!Driver_Initialized)
17191da177e4SLinus Torvalds 	{
17201da177e4SLinus Torvalds 		if (cmd == SIOCSCCCFG)
17211da177e4SLinus Torvalds 		{
17221da177e4SLinus Torvalds 			int found = 1;
17231da177e4SLinus Torvalds 
17241da177e4SLinus Torvalds 			if (!capable(CAP_SYS_RAWIO)) return -EPERM;
172525ec92fbSArnd Bergmann 			if (in_compat_syscall())
172625ec92fbSArnd Bergmann 				return -EOPNOTSUPP;
172725ec92fbSArnd Bergmann 
17281da177e4SLinus Torvalds 			if (!arg) return -EFAULT;
17291da177e4SLinus Torvalds 
17301da177e4SLinus Torvalds 			if (Nchips >= SCC_MAXCHIPS)
17311da177e4SLinus Torvalds 				return -EINVAL;
17321da177e4SLinus Torvalds 
17331da177e4SLinus Torvalds 			if (copy_from_user(&hwcfg, arg, sizeof(hwcfg)))
17341da177e4SLinus Torvalds 				return -EFAULT;
17351da177e4SLinus Torvalds 
17361da177e4SLinus Torvalds 			if (hwcfg.irq == 2) hwcfg.irq = 9;
17371da177e4SLinus Torvalds 
173860e4ad7aSYinghai Lu 			if (hwcfg.irq < 0 || hwcfg.irq >= nr_irqs)
17391da177e4SLinus Torvalds 				return -EINVAL;
17401da177e4SLinus Torvalds 
17411da177e4SLinus Torvalds 			if (!Ivec[hwcfg.irq].used && hwcfg.irq)
17421da177e4SLinus Torvalds 			{
17431b36efe0SJeff Garzik 				if (request_irq(hwcfg.irq, scc_isr,
17449714481eSMichael Opdenacker 						0, "AX.25 SCC",
17451b36efe0SJeff Garzik 						(void *)(long) hwcfg.irq))
17461da177e4SLinus Torvalds 					printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq);
17471da177e4SLinus Torvalds 				else
17481da177e4SLinus Torvalds 					Ivec[hwcfg.irq].used = 1;
17491da177e4SLinus Torvalds 			}
17501da177e4SLinus Torvalds 
17511da177e4SLinus Torvalds 			if (hwcfg.vector_latch && !Vector_Latch) {
17521da177e4SLinus Torvalds 				if (!request_region(hwcfg.vector_latch, 1, "scc vector latch"))
17531da177e4SLinus Torvalds 					printk(KERN_WARNING "z8530drv: warning, cannot reserve vector latch port 0x%lx\n, disabled.", hwcfg.vector_latch);
17541da177e4SLinus Torvalds 				else
17551da177e4SLinus Torvalds 					Vector_Latch = hwcfg.vector_latch;
17561da177e4SLinus Torvalds 			}
17571da177e4SLinus Torvalds 
17581da177e4SLinus Torvalds 			if (hwcfg.clock == 0)
17591da177e4SLinus Torvalds 				hwcfg.clock = SCC_DEFAULT_CLOCK;
17601da177e4SLinus Torvalds 
17611da177e4SLinus Torvalds #ifndef SCC_DONT_CHECK
17621da177e4SLinus Torvalds 
17631da177e4SLinus Torvalds 			if(request_region(hwcfg.ctrl_a, 1, "scc-probe"))
17641da177e4SLinus Torvalds 			{
17651da177e4SLinus Torvalds 				disable_irq(hwcfg.irq);
17661da177e4SLinus Torvalds 				Outb(hwcfg.ctrl_a, 0);
17671da177e4SLinus Torvalds 				OutReg(hwcfg.ctrl_a, R9, FHWRES);
17681da177e4SLinus Torvalds 				udelay(100);
17691da177e4SLinus Torvalds 				OutReg(hwcfg.ctrl_a,R13,0x55);		/* is this chip really there? */
17701da177e4SLinus Torvalds 				udelay(5);
17711da177e4SLinus Torvalds 
17721da177e4SLinus Torvalds 				if (InReg(hwcfg.ctrl_a,R13) != 0x55)
17731da177e4SLinus Torvalds 					found = 0;
17741da177e4SLinus Torvalds 				enable_irq(hwcfg.irq);
17751da177e4SLinus Torvalds 				release_region(hwcfg.ctrl_a, 1);
17761da177e4SLinus Torvalds 			}
17771da177e4SLinus Torvalds 			else
17781da177e4SLinus Torvalds 				found = 0;
17791da177e4SLinus Torvalds #endif
17801da177e4SLinus Torvalds 
17811da177e4SLinus Torvalds 			if (found)
17821da177e4SLinus Torvalds 			{
17831da177e4SLinus Torvalds 				SCC_Info[2*Nchips  ].ctrl = hwcfg.ctrl_a;
17841da177e4SLinus Torvalds 				SCC_Info[2*Nchips  ].data = hwcfg.data_a;
17851da177e4SLinus Torvalds 				SCC_Info[2*Nchips  ].irq  = hwcfg.irq;
17861da177e4SLinus Torvalds 				SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b;
17871da177e4SLinus Torvalds 				SCC_Info[2*Nchips+1].data = hwcfg.data_b;
17881da177e4SLinus Torvalds 				SCC_Info[2*Nchips+1].irq  = hwcfg.irq;
17891da177e4SLinus Torvalds 
17901da177e4SLinus Torvalds 				SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a;
17911da177e4SLinus Torvalds 				SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b;
17921da177e4SLinus Torvalds 				SCC_ctrl[Nchips].irq    = hwcfg.irq;
17931da177e4SLinus Torvalds 			}
17941da177e4SLinus Torvalds 
17951da177e4SLinus Torvalds 
17961da177e4SLinus Torvalds 			for (chan = 0; chan < 2; chan++)
17971da177e4SLinus Torvalds 			{
17981da177e4SLinus Torvalds 				sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan);
17991da177e4SLinus Torvalds 
18001da177e4SLinus Torvalds 				SCC_Info[2*Nchips+chan].special = hwcfg.special;
18011da177e4SLinus Torvalds 				SCC_Info[2*Nchips+chan].clock = hwcfg.clock;
18021da177e4SLinus Torvalds 				SCC_Info[2*Nchips+chan].brand = hwcfg.brand;
18031da177e4SLinus Torvalds 				SCC_Info[2*Nchips+chan].option = hwcfg.option;
18041da177e4SLinus Torvalds 				SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc;
18051da177e4SLinus Torvalds 
18061da177e4SLinus Torvalds #ifdef SCC_DONT_CHECK
18071da177e4SLinus Torvalds 				printk(KERN_INFO "%s: data port = 0x%3.3x  control port = 0x%3.3x\n",
18081da177e4SLinus Torvalds 					device_name,
18091da177e4SLinus Torvalds 					SCC_Info[2*Nchips+chan].data,
18101da177e4SLinus Torvalds 					SCC_Info[2*Nchips+chan].ctrl);
18111da177e4SLinus Torvalds 
18121da177e4SLinus Torvalds #else
18131da177e4SLinus Torvalds 				printk(KERN_INFO "%s: data port = 0x%3.3lx  control port = 0x%3.3lx -- %s\n",
18141da177e4SLinus Torvalds 					device_name,
18151da177e4SLinus Torvalds 					chan? hwcfg.data_b : hwcfg.data_a,
18161da177e4SLinus Torvalds 					chan? hwcfg.ctrl_b : hwcfg.ctrl_a,
18171da177e4SLinus Torvalds 					found? "found" : "missing");
18181da177e4SLinus Torvalds #endif
18191da177e4SLinus Torvalds 
18201da177e4SLinus Torvalds 				if (found)
18211da177e4SLinus Torvalds 				{
18221da177e4SLinus Torvalds 					request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl");
18231da177e4SLinus Torvalds 					request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data");
18241da177e4SLinus Torvalds 					if (Nchips+chan != 0 &&
18251da177e4SLinus Torvalds 					    scc_net_alloc(device_name,
18261da177e4SLinus Torvalds 							  &SCC_Info[2*Nchips+chan]))
18271da177e4SLinus Torvalds 					    return -EINVAL;
18281da177e4SLinus Torvalds 				}
18291da177e4SLinus Torvalds 			}
18301da177e4SLinus Torvalds 
18311da177e4SLinus Torvalds 			if (found) Nchips++;
18321da177e4SLinus Torvalds 
18331da177e4SLinus Torvalds 			return 0;
18341da177e4SLinus Torvalds 		}
18351da177e4SLinus Torvalds 
18361da177e4SLinus Torvalds 		if (cmd == SIOCSCCINI)
18371da177e4SLinus Torvalds 		{
18381da177e4SLinus Torvalds 			if (!capable(CAP_SYS_RAWIO))
18391da177e4SLinus Torvalds 				return -EPERM;
18401da177e4SLinus Torvalds 
18411da177e4SLinus Torvalds 			if (Nchips == 0)
18421da177e4SLinus Torvalds 				return -EINVAL;
18431da177e4SLinus Torvalds 
18441da177e4SLinus Torvalds 			z8530_init();
18451da177e4SLinus Torvalds 			return 0;
18461da177e4SLinus Torvalds 		}
18471da177e4SLinus Torvalds 
18481da177e4SLinus Torvalds 		return -EINVAL;	/* confuse the user */
18491da177e4SLinus Torvalds 	}
18501da177e4SLinus Torvalds 
18511da177e4SLinus Torvalds 	if (!scc->init)
18521da177e4SLinus Torvalds 	{
18531da177e4SLinus Torvalds 		if (cmd == SIOCSCCCHANINI)
18541da177e4SLinus Torvalds 		{
18551da177e4SLinus Torvalds 			if (!capable(CAP_NET_ADMIN)) return -EPERM;
18561da177e4SLinus Torvalds 			if (!arg) return -EINVAL;
18571da177e4SLinus Torvalds 
18581da177e4SLinus Torvalds 			scc->stat.bufsize   = SCC_BUFSIZE;
18591da177e4SLinus Torvalds 
18601da177e4SLinus Torvalds 			if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem)))
18611da177e4SLinus Torvalds 				return -EINVAL;
18621da177e4SLinus Torvalds 
18631da177e4SLinus Torvalds 			/* default KISS Params */
18641da177e4SLinus Torvalds 
18651da177e4SLinus Torvalds 			if (scc->modem.speed < 4800)
18661da177e4SLinus Torvalds 			{
18671da177e4SLinus Torvalds 				scc->kiss.txdelay = 36;		/* 360 ms */
18681da177e4SLinus Torvalds 				scc->kiss.persist = 42;		/* 25% persistence */			/* was 25 */
18691da177e4SLinus Torvalds 				scc->kiss.slottime = 16;	/* 160 ms */
18701da177e4SLinus Torvalds 				scc->kiss.tailtime = 4;		/* minimal reasonable value */
18711da177e4SLinus Torvalds 				scc->kiss.fulldup = 0;		/* CSMA */
18721da177e4SLinus Torvalds 				scc->kiss.waittime = 50;	/* 500 ms */
18731da177e4SLinus Torvalds 				scc->kiss.maxkeyup = 10;	/* 10 s */
18741da177e4SLinus Torvalds 				scc->kiss.mintime = 3;		/* 3 s */
18751da177e4SLinus Torvalds 				scc->kiss.idletime = 30;	/* 30 s */
18761da177e4SLinus Torvalds 				scc->kiss.maxdefer = 120;	/* 2 min */
18771da177e4SLinus Torvalds 				scc->kiss.softdcd = 0;		/* hardware dcd */
18781da177e4SLinus Torvalds 			} else {
18791da177e4SLinus Torvalds 				scc->kiss.txdelay = 10;		/* 100 ms */
18801da177e4SLinus Torvalds 				scc->kiss.persist = 64;		/* 25% persistence */			/* was 25 */
18811da177e4SLinus Torvalds 				scc->kiss.slottime = 8;		/* 160 ms */
18821da177e4SLinus Torvalds 				scc->kiss.tailtime = 1;		/* minimal reasonable value */
18831da177e4SLinus Torvalds 				scc->kiss.fulldup = 0;		/* CSMA */
18841da177e4SLinus Torvalds 				scc->kiss.waittime = 50;	/* 500 ms */
18851da177e4SLinus Torvalds 				scc->kiss.maxkeyup = 7;		/* 7 s */
18861da177e4SLinus Torvalds 				scc->kiss.mintime = 3;		/* 3 s */
18871da177e4SLinus Torvalds 				scc->kiss.idletime = 30;	/* 30 s */
18881da177e4SLinus Torvalds 				scc->kiss.maxdefer = 120;	/* 2 min */
18891da177e4SLinus Torvalds 				scc->kiss.softdcd = 0;		/* hardware dcd */
18901da177e4SLinus Torvalds 			}
18911da177e4SLinus Torvalds 
18921da177e4SLinus Torvalds 			scc->tx_buff = NULL;
18931da177e4SLinus Torvalds 			skb_queue_head_init(&scc->tx_queue);
18941da177e4SLinus Torvalds 			scc->init = 1;
18951da177e4SLinus Torvalds 
18961da177e4SLinus Torvalds 			return 0;
18971da177e4SLinus Torvalds 		}
18981da177e4SLinus Torvalds 
18991da177e4SLinus Torvalds 		return -EINVAL;
19001da177e4SLinus Torvalds 	}
19011da177e4SLinus Torvalds 
19021da177e4SLinus Torvalds 	switch(cmd)
19031da177e4SLinus Torvalds 	{
19041da177e4SLinus Torvalds 		case SIOCSCCRESERVED:
19051da177e4SLinus Torvalds 			return -ENOIOCTLCMD;
19061da177e4SLinus Torvalds 
19071da177e4SLinus Torvalds 		case SIOCSCCSMEM:
19081da177e4SLinus Torvalds 			if (!capable(CAP_SYS_RAWIO)) return -EPERM;
19091da177e4SLinus Torvalds 			if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg)))
19101da177e4SLinus Torvalds 				return -EINVAL;
19111da177e4SLinus Torvalds 			scc->stat.bufsize   = memcfg.bufsize;
19121da177e4SLinus Torvalds 			return 0;
19131da177e4SLinus Torvalds 
19141da177e4SLinus Torvalds 		case SIOCSCCGSTAT:
19151da177e4SLinus Torvalds 			if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat)))
19161da177e4SLinus Torvalds 				return -EINVAL;
19171da177e4SLinus Torvalds 			return 0;
19181da177e4SLinus Torvalds 
19191da177e4SLinus Torvalds 		case SIOCSCCGKISS:
19201da177e4SLinus Torvalds 			if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
19211da177e4SLinus Torvalds 				return -EINVAL;
19221da177e4SLinus Torvalds 			kiss_cmd.param = scc_get_param(scc, kiss_cmd.command);
19231da177e4SLinus Torvalds 			if (copy_to_user(arg, &kiss_cmd, sizeof(kiss_cmd)))
19241da177e4SLinus Torvalds 				return -EINVAL;
19251da177e4SLinus Torvalds 			return 0;
19261da177e4SLinus Torvalds 
19271da177e4SLinus Torvalds 		case SIOCSCCSKISS:
19281da177e4SLinus Torvalds 			if (!capable(CAP_NET_ADMIN)) return -EPERM;
19291da177e4SLinus Torvalds 			if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
19301da177e4SLinus Torvalds 				return -EINVAL;
19311da177e4SLinus Torvalds 			return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param);
19321da177e4SLinus Torvalds 
19331da177e4SLinus Torvalds 		case SIOCSCCCAL:
19341da177e4SLinus Torvalds 			if (!capable(CAP_SYS_RAWIO)) return -EPERM;
19351da177e4SLinus Torvalds 			if (!arg || copy_from_user(&cal, arg, sizeof(cal)) || cal.time == 0)
19361da177e4SLinus Torvalds 				return -EINVAL;
19371da177e4SLinus Torvalds 
19381da177e4SLinus Torvalds 			scc_start_calibrate(scc, cal.time, cal.pattern);
19391da177e4SLinus Torvalds 			return 0;
19401da177e4SLinus Torvalds 
19411da177e4SLinus Torvalds 		default:
19421da177e4SLinus Torvalds 			return -ENOIOCTLCMD;
19431da177e4SLinus Torvalds 
19441da177e4SLinus Torvalds 	}
19451da177e4SLinus Torvalds 
19461da177e4SLinus Torvalds 	return -EINVAL;
19471da177e4SLinus Torvalds }
19481da177e4SLinus Torvalds 
19491da177e4SLinus Torvalds /* ----> set interface callsign <---- */
19501da177e4SLinus Torvalds 
scc_net_set_mac_address(struct net_device * dev,void * addr)19511da177e4SLinus Torvalds static int scc_net_set_mac_address(struct net_device *dev, void *addr)
19521da177e4SLinus Torvalds {
19531da177e4SLinus Torvalds 	struct sockaddr *sa = (struct sockaddr *) addr;
1954ea52a0b5SJakub Kicinski 	dev_addr_set(dev, sa->sa_data);
19551da177e4SLinus Torvalds 	return 0;
19561da177e4SLinus Torvalds }
19571da177e4SLinus Torvalds 
19581da177e4SLinus Torvalds /* ----> get statistics <---- */
19591da177e4SLinus Torvalds 
scc_net_get_stats(struct net_device * dev)19601da177e4SLinus Torvalds static struct net_device_stats *scc_net_get_stats(struct net_device *dev)
19611da177e4SLinus Torvalds {
1962f4bdd264SWang Chen 	struct scc_channel *scc = (struct scc_channel *) dev->ml_priv;
19631da177e4SLinus Torvalds 
19641da177e4SLinus Torvalds 	scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over;
19651da177e4SLinus Torvalds 	scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under;
19661da177e4SLinus Torvalds 	scc->dev_stat.rx_fifo_errors = scc->stat.rx_over;
19671da177e4SLinus Torvalds 	scc->dev_stat.tx_fifo_errors = scc->stat.tx_under;
19681da177e4SLinus Torvalds 
19691da177e4SLinus Torvalds 	return &scc->dev_stat;
19701da177e4SLinus Torvalds }
19711da177e4SLinus Torvalds 
19721da177e4SLinus Torvalds /* ******************************************************************** */
19731da177e4SLinus Torvalds /* *		dump statistics to /proc/net/z8530drv		      * */
19741da177e4SLinus Torvalds /* ******************************************************************** */
19751da177e4SLinus Torvalds 
19761da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
19771da177e4SLinus Torvalds 
scc_net_seq_idx(loff_t pos)19781da177e4SLinus Torvalds static inline struct scc_channel *scc_net_seq_idx(loff_t pos)
19791da177e4SLinus Torvalds {
19801da177e4SLinus Torvalds 	int k;
19811da177e4SLinus Torvalds 
19821da177e4SLinus Torvalds 	for (k = 0; k < Nchips*2; ++k) {
19831da177e4SLinus Torvalds 		if (!SCC_Info[k].init)
19841da177e4SLinus Torvalds 			continue;
19851da177e4SLinus Torvalds 		if (pos-- == 0)
19861da177e4SLinus Torvalds 			return &SCC_Info[k];
19871da177e4SLinus Torvalds 	}
19881da177e4SLinus Torvalds 	return NULL;
19891da177e4SLinus Torvalds }
19901da177e4SLinus Torvalds 
scc_net_seq_start(struct seq_file * seq,loff_t * pos)19911da177e4SLinus Torvalds static void *scc_net_seq_start(struct seq_file *seq, loff_t *pos)
19921da177e4SLinus Torvalds {
19931da177e4SLinus Torvalds 	return *pos ? scc_net_seq_idx(*pos - 1) : SEQ_START_TOKEN;
19941da177e4SLinus Torvalds 
19951da177e4SLinus Torvalds }
19961da177e4SLinus Torvalds 
scc_net_seq_next(struct seq_file * seq,void * v,loff_t * pos)19971da177e4SLinus Torvalds static void *scc_net_seq_next(struct seq_file *seq, void *v, loff_t *pos)
19981da177e4SLinus Torvalds {
19991da177e4SLinus Torvalds 	unsigned k;
20001da177e4SLinus Torvalds 	struct scc_channel *scc = v;
20011da177e4SLinus Torvalds 	++*pos;
20021da177e4SLinus Torvalds 
20031da177e4SLinus Torvalds 	for (k = (v == SEQ_START_TOKEN) ? 0 : (scc - SCC_Info)+1;
20041da177e4SLinus Torvalds 	     k < Nchips*2; ++k) {
20051da177e4SLinus Torvalds 		if (SCC_Info[k].init)
20061da177e4SLinus Torvalds 			return &SCC_Info[k];
20071da177e4SLinus Torvalds 	}
20081da177e4SLinus Torvalds 	return NULL;
20091da177e4SLinus Torvalds }
20101da177e4SLinus Torvalds 
scc_net_seq_stop(struct seq_file * seq,void * v)20111da177e4SLinus Torvalds static void scc_net_seq_stop(struct seq_file *seq, void *v)
20121da177e4SLinus Torvalds {
20131da177e4SLinus Torvalds }
20141da177e4SLinus Torvalds 
scc_net_seq_show(struct seq_file * seq,void * v)20151da177e4SLinus Torvalds static int scc_net_seq_show(struct seq_file *seq, void *v)
20161da177e4SLinus Torvalds {
20171da177e4SLinus Torvalds 	if (v == SEQ_START_TOKEN) {
20181da177e4SLinus Torvalds 		seq_puts(seq, "z8530drv-"VERSION"\n");
20191da177e4SLinus Torvalds 	} else if (!Driver_Initialized) {
20201da177e4SLinus Torvalds 		seq_puts(seq, "not initialized\n");
20211da177e4SLinus Torvalds 	} else if (!Nchips) {
20221da177e4SLinus Torvalds 		seq_puts(seq, "chips missing\n");
20231da177e4SLinus Torvalds 	} else {
20241da177e4SLinus Torvalds 		const struct scc_channel *scc = v;
20251da177e4SLinus Torvalds 		const struct scc_stat *stat = &scc->stat;
20261da177e4SLinus Torvalds 		const struct scc_kiss *kiss = &scc->kiss;
20271da177e4SLinus Torvalds 
20281da177e4SLinus Torvalds 
20291da177e4SLinus Torvalds 		/* dev	data ctrl irq clock brand enh vector special option
20301da177e4SLinus Torvalds 		 *	baud nrz clocksrc softdcd bufsize
20311da177e4SLinus Torvalds 		 *	rxints txints exints spints
20321da177e4SLinus Torvalds 		 *	rcvd rxerrs over / xmit txerrs under / nospace bufsize
20331da177e4SLinus Torvalds 		 *	txd pers slot tail ful wait min maxk idl defr txof grp
20341da177e4SLinus Torvalds 		 *	W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
20351da177e4SLinus Torvalds 		 *	R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ##
20361da177e4SLinus Torvalds 		 */
20371da177e4SLinus Torvalds 
20381da177e4SLinus Torvalds 		seq_printf(seq, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n",
20391da177e4SLinus Torvalds 				scc->dev->name,
20401da177e4SLinus Torvalds 				scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand,
20411da177e4SLinus Torvalds 				scc->enhanced, Vector_Latch, scc->special,
20421da177e4SLinus Torvalds 				scc->option);
20431da177e4SLinus Torvalds 		seq_printf(seq, "\t%lu %d %d %d %d\n",
20441da177e4SLinus Torvalds 				scc->modem.speed, scc->modem.nrz,
20451da177e4SLinus Torvalds 				scc->modem.clocksrc, kiss->softdcd,
20461da177e4SLinus Torvalds 				stat->bufsize);
20471da177e4SLinus Torvalds 		seq_printf(seq, "\t%lu %lu %lu %lu\n",
20481da177e4SLinus Torvalds 				stat->rxints, stat->txints, stat->exints, stat->spints);
20491da177e4SLinus Torvalds 		seq_printf(seq, "\t%lu %lu %d / %lu %lu %d / %d %d\n",
20501da177e4SLinus Torvalds 				stat->rxframes, stat->rxerrs, stat->rx_over,
20511da177e4SLinus Torvalds 				stat->txframes, stat->txerrs, stat->tx_under,
20521da177e4SLinus Torvalds 				stat->nospace,  stat->tx_state);
20531da177e4SLinus Torvalds 
20541da177e4SLinus Torvalds #define K(x) kiss->x
20551da177e4SLinus Torvalds 		seq_printf(seq, "\t%d %d %d %d %d %d %d %d %d %d %d %d\n",
20561da177e4SLinus Torvalds 				K(txdelay), K(persist), K(slottime), K(tailtime),
20571da177e4SLinus Torvalds 				K(fulldup), K(waittime), K(mintime), K(maxkeyup),
20581da177e4SLinus Torvalds 				K(idletime), K(maxdefer), K(tx_inhibit), K(group));
20591da177e4SLinus Torvalds #undef K
20601da177e4SLinus Torvalds #ifdef SCC_DEBUG
20611da177e4SLinus Torvalds 		{
20621da177e4SLinus Torvalds 			int reg;
20631da177e4SLinus Torvalds 
20641da177e4SLinus Torvalds 		seq_printf(seq, "\tW ");
20651da177e4SLinus Torvalds 			for (reg = 0; reg < 16; reg++)
20661da177e4SLinus Torvalds 				seq_printf(seq, "%2.2x ", scc->wreg[reg]);
20671da177e4SLinus Torvalds 			seq_printf(seq, "\n");
20681da177e4SLinus Torvalds 
20691da177e4SLinus Torvalds 		seq_printf(seq, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1));
20701da177e4SLinus Torvalds 			for (reg = 3; reg < 8; reg++)
20711da177e4SLinus Torvalds 				seq_printf(seq, "%2.2x ", InReg(scc->ctrl, reg));
20721da177e4SLinus Torvalds 			seq_printf(seq, "XX ");
20731da177e4SLinus Torvalds 			for (reg = 9; reg < 16; reg++)
20741da177e4SLinus Torvalds 				seq_printf(seq, "%2.2x ", InReg(scc->ctrl, reg));
20751da177e4SLinus Torvalds 			seq_printf(seq, "\n");
20761da177e4SLinus Torvalds 		}
20771da177e4SLinus Torvalds #endif
20781da177e4SLinus Torvalds 		seq_putc(seq, '\n');
20791da177e4SLinus Torvalds 	}
20801da177e4SLinus Torvalds 
20811da177e4SLinus Torvalds         return 0;
20821da177e4SLinus Torvalds }
20831da177e4SLinus Torvalds 
20844101dec9SJan Engelhardt static const struct seq_operations scc_net_seq_ops = {
20851da177e4SLinus Torvalds 	.start  = scc_net_seq_start,
20861da177e4SLinus Torvalds 	.next   = scc_net_seq_next,
20871da177e4SLinus Torvalds 	.stop   = scc_net_seq_stop,
20881da177e4SLinus Torvalds 	.show   = scc_net_seq_show,
20891da177e4SLinus Torvalds };
20901da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */
20911da177e4SLinus Torvalds 
20921da177e4SLinus Torvalds 
20931da177e4SLinus Torvalds /* ******************************************************************** */
20941da177e4SLinus Torvalds /* * 			Init SCC driver 			      * */
20951da177e4SLinus Torvalds /* ******************************************************************** */
20961da177e4SLinus Torvalds 
scc_init_driver(void)20971da177e4SLinus Torvalds static int __init scc_init_driver (void)
20981da177e4SLinus Torvalds {
20991da177e4SLinus Torvalds 	char devname[IFNAMSIZ];
21001da177e4SLinus Torvalds 
21011da177e4SLinus Torvalds 	printk(banner);
21021da177e4SLinus Torvalds 
21031da177e4SLinus Torvalds 	sprintf(devname,"%s0", SCC_DriverName);
21041da177e4SLinus Torvalds 
21051da177e4SLinus Torvalds 	rtnl_lock();
21061da177e4SLinus Torvalds 	if (scc_net_alloc(devname, SCC_Info)) {
21071da177e4SLinus Torvalds 		rtnl_unlock();
21081da177e4SLinus Torvalds 		printk(KERN_ERR "z8530drv: cannot initialize module\n");
21091da177e4SLinus Torvalds 		return -EIO;
21101da177e4SLinus Torvalds 	}
21111da177e4SLinus Torvalds 	rtnl_unlock();
21121da177e4SLinus Torvalds 
2113fddda2b7SChristoph Hellwig 	proc_create_seq("z8530drv", 0, init_net.proc_net, &scc_net_seq_ops);
21141da177e4SLinus Torvalds 
21151da177e4SLinus Torvalds 	return 0;
21161da177e4SLinus Torvalds }
21171da177e4SLinus Torvalds 
scc_cleanup_driver(void)21181da177e4SLinus Torvalds static void __exit scc_cleanup_driver(void)
21191da177e4SLinus Torvalds {
21201da177e4SLinus Torvalds 	io_port ctrl;
21211da177e4SLinus Torvalds 	int k;
21221da177e4SLinus Torvalds 	struct scc_channel *scc;
21231da177e4SLinus Torvalds 	struct net_device *dev;
21241da177e4SLinus Torvalds 
21251da177e4SLinus Torvalds 	if (Nchips == 0 && (dev = SCC_Info[0].dev))
21261da177e4SLinus Torvalds 	{
21271da177e4SLinus Torvalds 		unregister_netdev(dev);
21281da177e4SLinus Torvalds 		free_netdev(dev);
21291da177e4SLinus Torvalds 	}
21301da177e4SLinus Torvalds 
21311da177e4SLinus Torvalds 	/* Guard against chip prattle */
21321da177e4SLinus Torvalds 	local_irq_disable();
21331da177e4SLinus Torvalds 
21341da177e4SLinus Torvalds 	for (k = 0; k < Nchips; k++)
21351da177e4SLinus Torvalds 		if ( (ctrl = SCC_ctrl[k].chan_A) )
21361da177e4SLinus Torvalds 		{
21371da177e4SLinus Torvalds 			Outb(ctrl, 0);
21381da177e4SLinus Torvalds 			OutReg(ctrl,R9,FHWRES);	/* force hardware reset */
21391da177e4SLinus Torvalds 			udelay(50);
21401da177e4SLinus Torvalds 		}
21411da177e4SLinus Torvalds 
21421da177e4SLinus Torvalds 	/* To unload the port must be closed so no real IRQ pending */
214360e4ad7aSYinghai Lu 	for (k = 0; k < nr_irqs ; k++)
21441da177e4SLinus Torvalds 		if (Ivec[k].used) free_irq(k, NULL);
21451da177e4SLinus Torvalds 
21461da177e4SLinus Torvalds 	local_irq_enable();
21471da177e4SLinus Torvalds 
21481da177e4SLinus Torvalds 	/* Now clean up */
21491da177e4SLinus Torvalds 	for (k = 0; k < Nchips*2; k++)
21501da177e4SLinus Torvalds 	{
21511da177e4SLinus Torvalds 		scc = &SCC_Info[k];
21521da177e4SLinus Torvalds 		if (scc->ctrl)
21531da177e4SLinus Torvalds 		{
21541da177e4SLinus Torvalds 			release_region(scc->ctrl, 1);
21551da177e4SLinus Torvalds 			release_region(scc->data, 1);
21561da177e4SLinus Torvalds 		}
21571da177e4SLinus Torvalds 		if (scc->dev)
21581da177e4SLinus Torvalds 		{
21591da177e4SLinus Torvalds 			unregister_netdev(scc->dev);
21601da177e4SLinus Torvalds 			free_netdev(scc->dev);
21611da177e4SLinus Torvalds 		}
21621da177e4SLinus Torvalds 	}
21631da177e4SLinus Torvalds 
21641da177e4SLinus Torvalds 
21651da177e4SLinus Torvalds 	if (Vector_Latch)
21661da177e4SLinus Torvalds 		release_region(Vector_Latch, 1);
21671da177e4SLinus Torvalds 
2168ece31ffdSGao feng 	remove_proc_entry("z8530drv", init_net.proc_net);
21691da177e4SLinus Torvalds }
21701da177e4SLinus Torvalds 
21711da177e4SLinus Torvalds MODULE_AUTHOR("Joerg Reuter <jreuter@yaina.de>");
21721da177e4SLinus Torvalds MODULE_DESCRIPTION("AX.25 Device Driver for Z8530 based HDLC cards");
21731da177e4SLinus Torvalds MODULE_LICENSE("GPL");
21741da177e4SLinus Torvalds module_init(scc_init_driver);
21751da177e4SLinus Torvalds module_exit(scc_cleanup_driver);
2176