patch-2.2.18 linux/drivers/char/atari_SCC.c

Next file: linux/drivers/char/atari_SCC.h
Previous file: linux/drivers/char/atari_SCC.README
Back to the patch index
Back to the overall index

diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/drivers/char/atari_SCC.c linux/drivers/char/atari_SCC.c
@@ -0,0 +1,2624 @@
+/*
+ * drivers/char/atari_SCC.c: Atari SCC serial ports implementation
+ *
+ * Copyright 1994-95 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
+ *
+ * Some parts were taken from the (incomplete) SCC driver by Lars Brinkhoff
+ * <f93labr@dd.chalmers.se>
+ *
+ * Adapted to 1.2 by Andreas Schwab
+ *
+ * Adapted to support MVME147, MVME162 and BVME6000 by Richard Hirst
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ * Description/Notes:
+ *
+ *  - This driver currently handles the asynchronous modes of the SCC ports
+ *    only. Synchronous operation or packet modes aren't implemented yet.
+ *
+ *  - Since there are many variations how the SCC can be integrated, the
+ *    driver offers the possibility to provide the frequencies attached to the
+ *    various clock inputs via an ioctl (along with an externally calculated
+ *    baud table).
+ *
+ *  - I haven't spent much time for optimizations yet...
+ *
+ *  - Channel A is accessible via two different devices: ttyS3 and ttyS4. The
+ *    former is the RS232 "Serial2" port, the latter the RS422 "LAN" port.
+ *    Only one of these devices can be open at one time.
+ *
+ *  - ++TeSche 12/96: DMA support for channel A, see atari_SCC.README for details
+ *    send comments/problems to: itschere@techfak.uni-bielefeld.de
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/m68kserial.h>
+#include <linux/tqueue.h>
+#include <linux/malloc.h>
+
+#include <asm/uaccess.h>
+#include <asm/setup.h>
+#ifdef CONFIG_MVME147_SCC
+#include <asm/mvme147hw.h>
+#endif
+#ifdef CONFIG_MVME162_SCC
+#include <asm/mvme16xhw.h>
+#endif
+#ifdef CONFIG_BVME6000_SCC
+#include <asm/bvme6000hw.h>
+#endif
+#ifdef CONFIG_ATARI
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#endif
+#include <asm/irq.h>
+#include <asm/atari_SCCserial.h>
+
+#include "atari_SCC.h"
+
+
+#if defined CONFIG_ATARI_SCC || defined CONFIG_ATARI_SCC_MODULE
+#define ENABLE_ATARI_SCC
+#endif
+
+#define	DEBUG_INT	0x01
+#define	DEBUG_INIT	0x02
+#define	DEBUG_THROTTLE	0x04
+#define	DEBUG_INFO	0x08
+#define	DEBUG_SPEED	0x10
+#define	DEBUG_OPEN	0x20
+#define	DEBUG_OVERRUNS	0x40
+/* warning: DEBUG_DMA will lead to a driver which is not able to operate at
+ * more than 19200 bps without causing overruns!
+ */
+#define DEBUG_DMA	0x80
+
+#define	DEBUG_ALL	0xffffffff
+#define	DEBUG_NONE	0
+
+#define	DEBUG	DEBUG_NONE
+
+#define CHANNEL_A	0
+#define CHANNEL_B	1
+
+
+/* Shadows for all SCC write registers */
+static unsigned char SCC_shadow[2][16];
+
+/* Location to access for SCC register access delay */
+static volatile unsigned char *scc_del;
+
+/* To keep track of STATUS_REG state for detection of Ext/Status int source */
+static unsigned char SCC_last_status_reg[2];
+
+/* This array tells the clocks connected to RTxC or TRxC, resp., (2nd
+ * index) for each channel (1st index).
+ *
+ * This table is initialzed for the TT. If we run on another machine,
+ * the values are changed by the initialization function.
+ */
+
+static unsigned SCC_clocks[2][2] = {
+	  /* RTxC */			/* TRxC */
+	{ SCC_BAUD_BASE_PCLK4,	SCC_BAUD_BASE_NONE },	/* Channel A */
+	{ SCC_BAUD_BASE_TIMC,	SCC_BAUD_BASE_BCLK }	/* Channel B */
+};
+
+/* The SCC's master clock (as variable, in case someone has unusual
+ * hardware)
+ */
+
+static unsigned SCC_PCLK = SCC_BAUD_BASE_PCLK;
+
+
+/* BRG values for the standard speeds and the various clock sources */
+
+typedef struct {
+	unsigned	clksrc;		/* clock source to use or -1 for not possible */
+	unsigned	div;		/* divisor: 1, 2 and 4 correspond to
+					 * direct 1:16, 1:32 and 1:64 modes,
+					 * divisors >= 4 yield a BRG value of
+					 * div/2-2 (in 1:16 mode)
+					 */
+} BAUD_ENTRY;
+
+/* A pointer for each channel to the current baud table */
+static BAUD_ENTRY *SCC_baud_table[2];
+
+/* Baud table format:
+ *
+ * Each entry consists of the clock source (CLK_RTxC, CLK_TRxC or
+ * CLK_PCLK) and a divisor. The following rules apply to the divisor:
+ *
+ *   - CLK_RTxC: 1 or even (1, 2 and 4 are the direct modes, > 4 use
+ *               the BRG)
+ *
+ *   - CLK_TRxC: 1, 2 or 4 (no BRG, only direct modes possible)
+ *
+ *   - CLK_PCLK: >= 4 and even (no direct modes, only BRG)
+ *
+ */
+
+#ifdef ENABLE_ATARI_SCC
+/* This table is used if RTxC = 3.672 MHz. This is the case for TT's
+ * channel A and for both channels on the Mega STE/Falcon. (TRxC is unused)
+ */
+
+static BAUD_ENTRY bdtab_norm[20] = {
+	/* B0      */ { 0, 0 },
+	/* B50     */ { CLK_RTxC, 4590 },
+	/* B75     */ { CLK_RTxC, 3060 },
+	/* B110    */ { CLK_PCLK, 4576 },
+	/* B134    */ { CLK_PCLK, 3756 },
+	/* B150    */ { CLK_RTxC, 1530 },
+	/* B200    */ { CLK_PCLK, 2516 },
+	/* B300    */ { CLK_PCLK, 1678 },
+	/* B600    */ { CLK_PCLK, 838 },
+	/* B1200   */ { CLK_PCLK, 420 },
+	/* B1800   */ { CLK_PCLK, 280 },
+	/* B2400   */ { CLK_PCLK, 210 },
+	/* B4800   */ { CLK_RTxC, 48 },
+	/* B9600   */ { CLK_RTxC, 24 },
+	/* B19200  */ { CLK_RTxC, 12 },
+	/* B38400  */ { CLK_RTxC, 6 },   /* #15 spd_extra */
+	/* B57600  */ { CLK_RTxC, 4 },   /* #16 spd_hi */
+	/* B115200 */ { CLK_RTxC, 2 },   /* #17 spd_vhi */
+	/* B230400 */ { CLK_RTxC, 1 },   /* #18 spd_shi */
+	/* B460800 */ { 0, 0 }           /* #19 spd_warp: Impossible */
+};
+
+/* This is a special table for the TT channel B with 307.2 kHz at RTxC
+ * and 2.4576 MHz at TRxC
+ */
+static BAUD_ENTRY bdtab_TTChB[20] = {
+	/* B0      */ { 0, 0 },
+	/* B50     */ { CLK_RTxC, 384 },
+	/* B75     */ { CLK_RTxC, 256 },
+	/* B110    */ { CLK_PCLK, 4576 },
+	/* B134    */ { CLK_PCLK, 3756 },
+	/* B150    */ { CLK_RTxC, 128 },
+	/* B200    */ { CLK_RTxC, 96 },
+	/* B300    */ { CLK_RTxC, 64 },
+	/* B600    */ { CLK_RTxC, 32 },
+	/* B1200   */ { CLK_RTxC, 16 },
+	/* B1800   */ { CLK_PCLK, 280 },
+	/* B2400   */ { CLK_RTxC, 8 },
+	/* B4800   */ { CLK_RTxC, 4 },
+	/* B9600   */ { CLK_RTxC, 2 },
+	/* B19200  */ { CLK_RTxC, 1 },
+	/* B38400  */ { CLK_TRxC, 4 },
+	/* B57600  */ { CLK_TRxC, 2 }, /* 57600 is not possible, use 76800 instead */
+	/* B115200 */ { CLK_TRxC, 1 }, /* 115200 is not possible, use 153600 instead */
+	/* B230400 */ { 0, 0 },        /* #18 spd_shi: Impossible  */
+	/* B460800 */ { 0, 0 }         /* #19 spd_warp: Impossible */
+};
+#endif
+
+#ifdef CONFIG_MVME147_SCC
+/* This table is used if RTxC = pCLK = 5 MHz. This is the case for MVME147
+ */
+static BAUD_ENTRY bdtab_m147[19] = {
+	/* B0      */ { 0, 0 },
+	/* B50     */ { CLK_PCLK, 6250 },
+	/* B75     */ { CLK_PCLK, 4166 },
+	/* B110    */ { CLK_PCLK, 2814 },
+	/* B134    */ { CLK_PCLK, 2322 },
+	/* B150    */ { CLK_PCLK, 2084 },
+	/* B200    */ { CLK_PCLK, 1562 },
+	/* B300    */ { CLK_PCLK, 1040 },
+	/* B600    */ { CLK_PCLK, 520 },
+	/* B1200   */ { CLK_PCLK, 260 },
+	/* B1800   */ { CLK_PCLK, 194 },
+	/* B2400   */ { CLK_PCLK, 130 },
+	/* B4800   */ { CLK_PCLK, 64 },
+	/* B9600   */ { CLK_PCLK, 32 },
+	/* B19200  */ { CLK_PCLK, 16 },
+	/* B38400  */ { CLK_PCLK, 8 },
+	/* B57600  */ { CLK_PCLK, 4 },
+	/* B115200 */ { CLK_PCLK, 2 },
+	/* B230400 */ { CLK_PCLK, 1 },   /* #18 spd_shi */
+};
+#endif
+
+#ifdef CONFIG_MVME162_SCC
+/* This table is used if RTxC = pCLK = 10 MHz. This is the case for MVME162
+ */
+static BAUD_ENTRY bdtab_mvme[20] = {
+	/* B0      */ { 0, 0 },
+	/* B50     */ { CLK_PCLK, 12500 },
+	/* B75     */ { CLK_PCLK, 8332 },
+	/* B110    */ { CLK_PCLK, 5682 },
+	/* B134    */ { CLK_PCLK, 4646 },
+	/* B150    */ { CLK_PCLK, 4166 },
+	/* B200    */ { CLK_PCLK, 3124 },
+	/* B300    */ { CLK_PCLK, 2082 },
+	/* B600    */ { CLK_PCLK, 1042 },
+	/* B1200   */ { CLK_PCLK, 520 },
+	/* B1800   */ { CLK_PCLK, 390 },
+	/* B2400   */ { CLK_PCLK, 260 },
+	/* B4800   */ { CLK_PCLK, 130 },
+	/* B9600   */ { CLK_PCLK, 64 },
+	/* B19200  */ { CLK_PCLK, 32 },
+	/* B38400  */ { CLK_PCLK, 16 },
+	/* B57600  */ { CLK_PCLK, 8 },
+	/* B115200 */ { CLK_PCLK, 4 },
+	/* B230400 */ { CLK_PCLK, 2 },   /* #18 spd_shi */
+	/* B460800 */ { CLK_PCLK, 1 }    /* #19 spd_warp */
+};
+#endif
+
+#ifdef CONFIG_BVME6000_SCC
+
+/* This table is used if RTxC = 7.3728 MHz. This is the case for BVMs
+ */
+static BAUD_ENTRY bdtab_bvme[18] = {
+	/* B0      */ { 0, 0 },
+	/* B50     */ { CLK_RTxC, 9216 },
+	/* B75     */ { CLK_RTxC, 6144 },
+	/* B110    */ { CLK_RTxC, 4188 },
+	/* B134    */ { CLK_RTxC, 3424 },
+	/* B150    */ { CLK_RTxC, 3072 },
+	/* B200    */ { CLK_RTxC, 2304 },
+	/* B300    */ { CLK_RTxC, 1536 },
+	/* B600    */ { CLK_RTxC, 768 },
+	/* B1200   */ { CLK_RTxC, 384 },
+	/* B1800   */ { CLK_RTxC, 256 },
+	/* B2400   */ { CLK_RTxC, 192 },
+	/* B4800   */ { CLK_RTxC, 96 },
+	/* B9600   */ { CLK_RTxC, 48 },
+	/* B19200  */ { CLK_RTxC, 24 },
+	/* B38400  */ { CLK_RTxC, 12 },
+	/* B57600  */ { CLK_RTxC, 8 },
+	/* B115200 */ { CLK_RTxC, 4 }
+};
+#endif
+
+
+/* User settable tables */
+static BAUD_ENTRY bdtab_usr[2][20];
+
+
+/*
+   Here are the values to compute the tables above. For each base
+   clock, the BRG values for the common bps rates are listed. The
+   divisor is (BRG+2)*2. For each clock, the 1:16 and 1:32 are also
+   usable (and the BRG isn't used). 1:64 is the same as BRG with
+   k==0. If more than clock source was possible for a bps rate I've
+   choosen the one with the smallest error.
+
+   For 307.2 kHz == base 19200:
+     50    bps -> 190
+     75    bps -> 126
+     110   bps -> 85 (really 110.34 bps, error 0.31 %)
+     134   bps -> 70 (really 133.33 bps, error 0.49 %)
+     150   bps -> 62
+     200   bps -> 46
+     300   bps -> 30
+     600   bps -> 14
+     1200  bps -> 6
+     1800  bps -> 3 (really 1920 bps, error 6.7 %)
+     2400  bps -> 2
+     4800  bps -> 0
+
+   For 2.4576 MHz == base 153600:
+     50    bps -> 1534
+     75    bps -> 1022
+     110   bps -> 696 (really 110.03 bps, error 0.027 %)
+     134   bps -> 571 (really 134.03 bps, error 0.022 %)
+     150   bps -> 510
+     200   bps -> 382
+     300   bps -> 254
+     600   bps -> 126
+     1200  bps -> 62
+     1800  bps -> 41 (really 1786.1 bps, error 0.77 %)
+     2400  bps -> 30
+     4800  bps -> 14
+     9600  bps -> 6
+     19200 bps -> 2
+     38400 bps -> 0
+
+   For 3.672 MHz == base 229500:
+     50    bps -> 2293
+     75    bps -> 1528
+     110   bps -> 1041
+     134   bps -> 854
+     150   bps -> 763
+     200   bps -> 572
+     300   bps -> 381
+     600   bps -> 189
+     1200  bps -> 94
+     1800  bps -> 62
+     2400  bps -> 46
+     4800  bps -> 22
+     9600  bps -> 10
+     19200 bps -> 4
+     38400 bps -> 1
+     57600 bps -> 0
+
+   For 8.053976 MHz == base 503374:
+	  0    	 bps -> 0
+	  50     bps -> 5032
+	  75     bps -> 3354
+	  110    bps -> 2286
+	  134    bps -> 1876
+	  150	 bps -> 1676
+	  200	 bps -> 1256
+	  300	 bps -> 837
+	  600	 bps -> 417
+	  1200   bps -> 208
+	  1800   bps -> 138
+	  2400   bps -> 103
+	  4800   bps -> 50
+	  9600   bps -> 24
+	  19200  bps -> 11
+	  31500  bps -> 6 (really 31461 bps)
+	  50000  bps -> 3
+	  125000 bps -> 0
+
+*/
+
+
+/* Is channel A switchable between two hardware ports? (Serial2/LAN) */
+
+#define	SCCA_SWITCH_SERIAL2_ONLY	0	/* only connected to Serial2 */
+#define	SCCA_SWITCH_LAN_ONLY		1	/* only connected to LAN */
+#define	SCCA_SWITCH_BOTH		2	/* connected to both, switch by
+						 * IO7 in the PSG */
+
+static int SCC_chan_a_switchable;
+
+/* Is channel A (two ports!) already open? */
+static int SCC_chan_a_open;
+
+/* For which line has channel A been opened? */
+static int SCC_chan_a_line;
+
+/* info pointer for SCC_chan_a_line */
+static struct m68k_async_struct *SCC_chan_a_info;
+
+/* Are the register addresses for the channels reversed? (B before A). This is
+ * the case for the ST_ESCC. */
+static int ChannelsReversed;
+
+/* This macro sets up the 'info' pointer for the interrupt functions of
+ * channel A. It addresses the following problem: The isrs were registered
+ * with callback_data == &rs_table[3] (= Serial2). But they can also be for
+ * &rs_table[4] (LAN), if this port is the open one. SETUP_INFO() thus
+ * advances the pointer if info->line == 3.
+ */
+
+#define DEFAULT_CHANNEL_B_LINE		1 /* ttyS1 */
+#define DEFAULT_CHANNEL_A232_LINE	3 /* ttyS3 */
+#define DEFAULT_CHANNEL_A422_LINE	4 /* ttyS4 */
+
+static int chb_line = -1, cha232_line = -1, cha422_line = -1;
+
+#ifndef CONFIG_ATARI_SCC_DMA
+#define scca_dma  0        /* No DMA support */
+#else
+static int scca_dma = 0;   /* whether DMA is supported at all */
+
+/* ++TeSche: these next few things are for DMA support on channel A. both
+ * BUFFERS and BUFSIZE must be a power of two (because of speed reasons)!
+ */
+#define SCCA_DMA_BUFFERS 8
+#define SCCA_DMA_BUFSIZE 2048
+typedef struct {
+	u_char *buf, *pbuf, *err;
+	int inbuf;
+	short cntOver, cntPar, cntFrame;
+	unsigned active:1;   /* whether a DMA transfer is using this buffer */
+	unsigned needsFlushing:1;   /* whether it's dirty (even when inbuf==0) */
+} DMABUF;
+static DMABUF scca_dma_buf[SCCA_DMA_BUFFERS];
+static DMABUF *scca_dma_head, *scca_dma_tail;
+static DMABUF *scca_dma_end = &scca_dma_buf[SCCA_DMA_BUFFERS];
+
+#endif
+
+#define	SETUP_INFO(info)						\
+	do {										\
+		if (info->line == cha232_line)			\
+			info = SCC_chan_a_info;				\
+	} while(0)
+
+
+/***************************** Prototypes *****************************/
+
+#ifdef ENABLE_ATARI_SCC
+static void SCC_init_port( struct m68k_async_struct *info, int type, int channel );
+#endif
+#ifdef CONFIG_MVME147_SCC
+static void m147_init_port( struct m68k_async_struct *info, int type, int channel );
+#endif
+#ifdef CONFIG_MVME162_SCC
+static void mvme_init_port( struct m68k_async_struct *info, int type, int channel );
+#endif
+#ifdef CONFIG_BVME6000_SCC
+static void bvme_init_port( struct m68k_async_struct *info, int type, int channel );
+#endif
+#ifdef MODULE
+static void SCC_deinit_port( struct m68k_async_struct *info, int channel );
+#endif
+static void SCC_rx_int (int irq, void *data, struct pt_regs *fp);
+static void SCC_spcond_int (int irq, void *data, struct pt_regs *fp);
+static void SCC_tx_int (int irq, void *data, struct pt_regs *fp);
+static void SCC_stat_int (int irq, void *data, struct pt_regs *fp);
+#ifdef ENABLE_ATARI_SCC
+static void SCC_ri_int (int irq, void *data, struct pt_regs *fp);
+#endif
+static int SCC_check_open( struct m68k_async_struct *info, struct tty_struct
+                           *tty, struct file *file );
+static void SCC_init( struct m68k_async_struct *info );
+static void SCC_deinit( struct m68k_async_struct *info, int leave_dtr );
+static void SCC_enab_tx_int( struct m68k_async_struct *info, int enab_flag );
+static int SCC_check_custom_divisor( struct m68k_async_struct *info, int baud_base,
+				    int divisor );
+static void SCC_change_speed( struct m68k_async_struct *info );
+static int SCC_clocksrc( unsigned baud_base, unsigned channel );
+static void SCC_throttle( struct m68k_async_struct *info, int status );
+static void SCC_set_break( struct m68k_async_struct *info, int break_flag );
+static void SCC_get_serial_info( struct m68k_async_struct *info, struct
+				serial_struct *retinfo );
+static unsigned int SCC_get_modem_info( struct m68k_async_struct *info );
+static int SCC_set_modem_info( struct m68k_async_struct *info, int new_dtr, int
+			      new_rts );
+static int SCC_ioctl( struct tty_struct *tty, struct file *file, struct
+		     m68k_async_struct *info, unsigned int cmd, unsigned long
+		     arg );
+static void SCC_stop_receive (struct m68k_async_struct *info);
+static int SCC_trans_empty (struct m68k_async_struct *info);
+#ifdef CONFIG_ATARI_SCC_DMA
+static void SCC_timer_int (int irq, void *data, struct pt_regs *fp);
+static void SCC_dma_int (int irq, void *data, struct pt_regs *fp);
+#endif
+
+/************************* End of Prototypes **************************/
+
+
+static SERIALSWITCH SCC_switch = {
+	SCC_init, SCC_deinit, SCC_enab_tx_int,
+	SCC_check_custom_divisor, SCC_change_speed,
+	SCC_throttle, SCC_set_break,
+	SCC_get_serial_info, SCC_get_modem_info,
+	SCC_set_modem_info, SCC_ioctl, SCC_stop_receive, SCC_trans_empty,
+	SCC_check_open
+};
+
+extern int atari_SCC_init_done;
+extern int atari_SCC_reset_done;
+
+
+#ifdef CONFIG_BVME6000_SCC
+
+int bvme_SCC_init( void )
+{
+	struct serial_struct req;
+	int nr = 0;
+
+	if (!MACH_IS_BVME6000)
+		return (-ENODEV);
+
+	scc_del = (unsigned char *)0;
+
+	SCC_chan_a_switchable = SCCA_SWITCH_SERIAL2_ONLY;
+	SCC_PCLK = SCC_BAUD_BASE_BVME_PCLK;
+
+	/* General initialization */
+	ChannelsReversed = 8;
+	SCC_chan_a_open = 0;
+
+	req.line = DEFAULT_CHANNEL_B_LINE;
+	req.type = SER_SCC_BVME;
+	req.port = BVME_SCC_B_ADDR;
+	if ((chb_line = register_serial( &req )) >= 0) {
+		bvme_init_port( &rs_table[chb_line], req.type, CHANNEL_B );
+		++nr;
+	}
+	else
+		printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel B\n", req.line );
+
+	/* Init channel A, RS232 part (Serial2) */
+	req.line = 0;
+	req.type = SER_SCC_BVME;
+	req.port = BVME_SCC_A_ADDR;
+	if ((cha232_line = register_serial( &req )) >= 0) {
+		bvme_init_port( &rs_table[cha232_line], req.type, CHANNEL_A );
+		++nr;
+	}
+	else
+		printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );
+
+	return( nr > 0 ? 0 : -ENODEV );
+}
+
+
+static void bvme_init_port( struct m68k_async_struct *info, int type, int channel )
+{
+	static int called = 0, ch_a_inited = 0;
+	SCC_ACCESS_INIT(info);
+
+	info->sw = &SCC_switch;
+
+	/* set ISRs, but don't enable interrupts yet (done in init());
+	 */
+	if (channel == CHANNEL_B || !ch_a_inited) {
+		request_irq(channel ? BVME_IRQ_SCCB_TX : BVME_IRQ_SCCA_TX,
+		            SCC_tx_int, BVME_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B TX" : "SCC-A TX", info);
+		request_irq(channel ? BVME_IRQ_SCCB_STAT : BVME_IRQ_SCCA_STAT,
+		            SCC_stat_int, BVME_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B status" : "SCC-A status", info);
+		request_irq(channel ? BVME_IRQ_SCCB_RX : BVME_IRQ_SCCA_RX,
+		            SCC_rx_int, BVME_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B RX" : "SCC-A RX", info);
+		request_irq(channel ? BVME_IRQ_SCCB_SPCOND : BVME_IRQ_SCCA_SPCOND,
+		            SCC_spcond_int, BVME_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B special cond" : "SCC-A special cond", info);
+
+	}
+
+	/* Hardware initialization */
+
+	if (!called) {
+		/* Set the interrupt vector */
+		SCCwrite( INT_VECTOR_REG, BVME_IRQ_SCC_BASE );
+
+		/* Interrupt parameters: vector includes status, status low */
+		SCCwrite( MASTER_INT_CTRL, MIC_VEC_INCL_STAT );
+
+		/* Set the baud tables */
+		SCC_baud_table[CHANNEL_A] = bdtab_bvme;
+		SCC_baud_table[CHANNEL_B] = bdtab_bvme;
+
+		/* Set the clocks */
+		SCC_clocks[CHANNEL_A][CLK_RTxC] = SCC_BAUD_BASE_BVME;
+		SCC_clocks[CHANNEL_A][CLK_TRxC] = SCC_BAUD_BASE_NONE;
+		SCC_clocks[CHANNEL_B][CLK_RTxC] = SCC_BAUD_BASE_BVME;
+		SCC_clocks[CHANNEL_B][CLK_TRxC] = SCC_BAUD_BASE_NONE;
+
+		SCCmod( MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB );
+	}
+
+	/* disable interrupts for this channel */
+	SCCwrite( INT_AND_DMA_REG, 0 );
+
+	called = 1;
+	if (CHANNR(info) == CHANNEL_A) ch_a_inited = 1;
+}
+
+#endif
+
+
+#ifdef CONFIG_MVME147_SCC
+
+int m147_SCC_init( void )
+{
+	struct serial_struct req;
+	int nr = 0;
+
+	if (!MACH_IS_MVME147)
+		return (-ENODEV);
+
+	scc_del = (unsigned char *)0;
+
+	SCC_chan_a_switchable = SCCA_SWITCH_SERIAL2_ONLY;
+	SCC_PCLK = SCC_BAUD_BASE_M147_PCLK;
+
+	/* General initialization */
+	ChannelsReversed = 2;
+	SCC_chan_a_open = 0;
+
+	req.line = DEFAULT_CHANNEL_B_LINE;
+	req.type = SER_SCC_MVME;
+	req.port = M147_SCC_B_ADDR;
+	if ((chb_line = register_serial( &req )) >= 0) {
+		m147_init_port( &rs_table[chb_line], req.type, CHANNEL_B );
+		++nr;
+	}
+	else
+		printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel B\n", req.line );
+
+	/* Init channel A, RS232 part (Serial2) */
+	req.line = 0;
+	req.type = SER_SCC_MVME;
+	req.port = M147_SCC_A_ADDR;
+	if ((cha232_line = register_serial( &req )) >= 0) {
+		m147_init_port( &rs_table[cha232_line], req.type, CHANNEL_A );
+		++nr;
+	}
+	else
+		printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );
+        /*
+         * Ensure interrupts are enabled in the PCC chip
+         */
+        m147_pcc->serial_cntrl=PCC_LEVEL_SERIAL|PCC_INT_ENAB;
+
+	return( nr > 0 ? 0 : -ENODEV );
+}
+
+
+static void m147_init_port( struct m68k_async_struct *info, int type, int channel )
+{
+	static int called = 0, ch_a_inited = 0;
+	SCC_ACCESS_INIT(info);
+
+	info->sw = &SCC_switch;
+
+	/* set ISRs, but don't enable interrupts yet (done in init());
+	 */
+	if (channel == CHANNEL_B || !ch_a_inited) {
+		request_irq(channel ? MVME147_IRQ_SCCB_TX : MVME147_IRQ_SCCA_TX,
+		            SCC_tx_int, MVME147_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B TX" : "SCC-A TX", info);
+		request_irq(channel ? MVME147_IRQ_SCCB_STAT : MVME147_IRQ_SCCA_STAT,
+		            SCC_stat_int, MVME147_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B status" : "SCC-A status", info);
+		request_irq(channel ? MVME147_IRQ_SCCB_RX : MVME147_IRQ_SCCA_RX,
+		            SCC_rx_int, MVME147_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B RX" : "SCC-A RX", info);
+		request_irq(channel ? MVME147_IRQ_SCCB_SPCOND : MVME147_IRQ_SCCA_SPCOND,
+		            SCC_spcond_int, MVME147_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B special cond" : "SCC-A special cond", info);
+
+	}
+
+	/* Hardware initialization */
+
+	if (!called) {
+		/* Set the interrupt vector */
+		SCCwrite( INT_VECTOR_REG, MVME147_IRQ_SCC_BASE );
+
+		/* Interrupt parameters: vector includes status, status low */
+		SCCwrite( MASTER_INT_CTRL, MIC_VEC_INCL_STAT );
+
+		/* Set the baud tables */
+		SCC_baud_table[CHANNEL_A] = bdtab_m147;
+		SCC_baud_table[CHANNEL_B] = bdtab_m147;
+
+		/* Set the clocks */
+		SCC_clocks[CHANNEL_A][CLK_RTxC] = SCC_BAUD_BASE_M147;
+		SCC_clocks[CHANNEL_A][CLK_TRxC] = SCC_BAUD_BASE_NONE;
+		SCC_clocks[CHANNEL_B][CLK_RTxC] = SCC_BAUD_BASE_M147;
+		SCC_clocks[CHANNEL_B][CLK_TRxC] = SCC_BAUD_BASE_NONE;
+
+		SCCmod( MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB );
+	}
+
+	/* disable interrupts for this channel */
+	SCCwrite( INT_AND_DMA_REG, 0 );
+
+	called = 1;
+	if (CHANNR(info) == CHANNEL_A) ch_a_inited = 1;
+}
+
+#endif
+
+#ifdef CONFIG_MVME162_SCC
+
+int mvme_SCC_init( void )
+{
+	struct serial_struct req;
+	int nr = 0;
+
+	if (!MACH_IS_MVME16x || !(mvme16x_config & MVME16x_CONFIG_GOT_SCCA))
+		return (-ENODEV);
+
+	scc_del = (unsigned char *)0;
+
+	SCC_chan_a_switchable = SCCA_SWITCH_SERIAL2_ONLY;
+	SCC_PCLK = SCC_BAUD_BASE_MVME_PCLK;
+
+	/* General initialization */
+	ChannelsReversed = 4;
+	SCC_chan_a_open = 0;
+
+	req.line = DEFAULT_CHANNEL_B_LINE;
+	req.type = SER_SCC_MVME;
+	req.port = MVME_SCC_B_ADDR;
+	if ((chb_line = register_serial( &req )) >= 0) {
+		mvme_init_port( &rs_table[chb_line], req.type, CHANNEL_B );
+		++nr;
+	}
+	else
+		printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel B\n", req.line );
+
+	/* Init channel A, RS232 part (Serial2) */
+	req.line = 0;
+	req.type = SER_SCC_MVME;
+	req.port = MVME_SCC_A_ADDR;
+	if ((cha232_line = register_serial( &req )) >= 0) {
+		mvme_init_port( &rs_table[cha232_line], req.type, CHANNEL_A );
+		++nr;
+	}
+	else
+		printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );
+        /*
+         * Ensure interrupts are enabled in the MC2 chip
+         */
+        *(volatile char *)0xfff4201d = 0x14;
+
+	return( nr > 0 ? 0 : -ENODEV );
+}
+
+
+static void mvme_init_port( struct m68k_async_struct *info, int type, int channel )
+{
+	static int called = 0, ch_a_inited = 0;
+	SCC_ACCESS_INIT(info);
+
+	info->sw = &SCC_switch;
+
+	/* set ISRs, but don't enable interrupts yet (done in init());
+	 */
+	if (channel == CHANNEL_B || !ch_a_inited) {
+		request_irq(channel ? MVME162_IRQ_SCCB_TX : MVME162_IRQ_SCCA_TX,
+		            SCC_tx_int, MVME162_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B TX" : "SCC-A TX", info);
+		request_irq(channel ? MVME162_IRQ_SCCB_STAT : MVME162_IRQ_SCCA_STAT,
+		            SCC_stat_int, MVME162_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B status" : "SCC-A status", info);
+		request_irq(channel ? MVME162_IRQ_SCCB_RX : MVME162_IRQ_SCCA_RX,
+		            SCC_rx_int, MVME162_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B RX" : "SCC-A RX", info);
+		request_irq(channel ? MVME162_IRQ_SCCB_SPCOND : MVME162_IRQ_SCCA_SPCOND,
+		            SCC_spcond_int, MVME162_IRQ_TYPE_PRIO,
+		            channel ? "SCC-B special cond" : "SCC-A special cond", info);
+
+	}
+
+	/* Hardware initialization */
+
+	if (!called) {
+		/* Set the interrupt vector */
+		SCCwrite( INT_VECTOR_REG, MVME162_IRQ_SCC_BASE );
+
+		/* Interrupt parameters: vector includes status, status low */
+		SCCwrite( MASTER_INT_CTRL, MIC_VEC_INCL_STAT );
+
+		/* Set the baud tables */
+		SCC_baud_table[CHANNEL_A] = bdtab_mvme;
+		SCC_baud_table[CHANNEL_B] = bdtab_mvme;
+
+		/* Set the clocks */
+		SCC_clocks[CHANNEL_A][CLK_RTxC] = SCC_BAUD_BASE_MVME;
+		SCC_clocks[CHANNEL_A][CLK_TRxC] = SCC_BAUD_BASE_NONE;
+		SCC_clocks[CHANNEL_B][CLK_RTxC] = SCC_BAUD_BASE_MVME;
+		SCC_clocks[CHANNEL_B][CLK_TRxC] = SCC_BAUD_BASE_NONE;
+
+		SCCmod( MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB );
+	}
+
+	/* disable interrupts for this channel */
+	SCCwrite( INT_AND_DMA_REG, 0 );
+
+	called = 1;
+	if (CHANNR(info) == CHANNEL_A) ch_a_inited = 1;
+}
+
+#endif
+
+#ifdef ENABLE_ATARI_SCC
+
+int atari_SCC_init( void )
+{
+	struct serial_struct req;
+	int escc = ATARIHW_PRESENT(ST_ESCC);
+	int nr = 0;
+	extern char m68k_debug_device[];
+
+	/* SCC present at all? */
+	if (!(ATARIHW_PRESENT(SCC) || ATARIHW_PRESENT(ST_ESCC)))
+		return( -ENODEV );
+
+	scc_del = &mfp.par_dt_reg;
+
+#ifdef CONFIG_ATARI_SCC_DMA
+	/* strengthen the condition a bit to be on the safer side...
+	 */
+	scca_dma = ATARIHW_PRESENT(SCC_DMA) && ATARIHW_PRESENT (TT_MFP);
+#endif
+
+	/* Channel A is switchable on the TT, MegaSTE and Medusa (extension), i.e.
+	 * all machines with an SCC except the Falcon. If there's a machine where
+	 * channel A is fixed to a RS-232 Serial2, add code to set to
+	 * SCCA_SWITCH_SERIAL2_ONLY.
+	 */
+	if (MACH_IS_FALCON)
+		SCC_chan_a_switchable = SCCA_SWITCH_LAN_ONLY;
+	else if (ATARIHW_PRESENT(TT_MFP) || MACH_IS_MSTE)
+		SCC_chan_a_switchable = SCCA_SWITCH_BOTH;
+	else
+		SCC_chan_a_switchable = SCCA_SWITCH_SERIAL2_ONLY;
+
+	/* General initialization */
+	ChannelsReversed = escc ? 4 : 0;
+	SCC_chan_a_open = 0;
+
+	/* Init channel B */
+	if (!strcmp( m68k_debug_device, "ser2" ))
+		printk(KERN_NOTICE "SCC channel B: used as debug device\n" );
+	else {
+		req.line = DEFAULT_CHANNEL_B_LINE;
+		req.type = SER_SCC_NORM;
+		req.port = (int)(escc ? &st_escc.cha_b_ctrl : &scc.cha_b_ctrl);
+		if ((chb_line = register_serial( &req )) >= 0) {
+			SCC_init_port( &rs_table[chb_line], req.type, CHANNEL_B );
+			++nr;
+		}
+		else
+			printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel B\n", req.line );
+	}
+
+	/* Init channel A, RS232 part (Serial2) */
+	if (SCC_chan_a_switchable != SCCA_SWITCH_LAN_ONLY) {
+		req.line = DEFAULT_CHANNEL_A232_LINE;
+		req.type = scca_dma ? SER_SCC_DMA : SER_SCC_NORM;
+		req.port = (int)(escc ? &st_escc.cha_a_ctrl : &scc.cha_a_ctrl);
+		if ((cha232_line = register_serial( &req )) >= 0) {
+			SCC_init_port( &rs_table[cha232_line], req.type, CHANNEL_A );
+			++nr;
+		}
+		else
+			printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );
+	}
+
+	/* Init channel A, RS422 part (LAN) */
+	if (SCC_chan_a_switchable != SCCA_SWITCH_SERIAL2_ONLY) {
+		req.line = DEFAULT_CHANNEL_A422_LINE;
+		req.type = scca_dma ? SER_SCC_DMA : SER_SCC_NORM;
+		req.port = (int)(escc ? &st_escc.cha_a_ctrl : &scc.cha_a_ctrl);
+		if ((cha422_line = register_serial( &req )) >= 0) {
+			SCC_init_port( &rs_table[cha422_line], req.type, CHANNEL_A );
+			++nr;
+		}
+		else
+			printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );
+	}
+
+	return( nr > 0 ? 0 : -ENODEV );
+}
+
+
+static void SCC_init_port( struct m68k_async_struct *info, int type, int channel )
+{
+	static int called = 0, ch_a_inited = 0;
+	SCC_ACCESS_INIT(info);
+
+	info->sw = &SCC_switch;
+
+	/* set ISRs, but don't enable interrupts yet (done in init());
+	 * All interrupts are of type PRIORITIZED, which means they can be
+	 * interrupted by all level 6 ints, but not by another SCC (or other level
+	 * 5) int. I see no races with any MFP int, but I'm not quite sure yet
+	 * whether longer delays in between the two-stage SCC register access can
+	 * break things...
+	 */
+	if (channel == CHANNEL_B || !ch_a_inited) {
+		request_irq(channel ? IRQ_SCCB_TX : IRQ_SCCA_TX,
+		            SCC_tx_int, IRQ_TYPE_PRIO,
+		            channel ? "SCC-B TX" : "SCC-A TX", info);
+		request_irq(channel ? IRQ_SCCB_STAT : IRQ_SCCA_STAT,
+		            SCC_stat_int, IRQ_TYPE_PRIO,
+		            channel ? "SCC-B status" : "SCC-A status", info);
+		request_irq(channel ? IRQ_SCCB_RX : IRQ_SCCA_RX,
+		            SCC_rx_int, IRQ_TYPE_PRIO,
+		            channel ? "SCC-B RX" : "SCC-A RX", info);
+		request_irq(channel ? IRQ_SCCB_SPCOND : IRQ_SCCA_SPCOND,
+		            SCC_spcond_int, IRQ_TYPE_PRIO,
+		            channel ? "SCC-B special cond" : "SCC-A special cond", info);
+
+		if (channel != 0 && ATARIHW_PRESENT (TT_MFP))
+			request_irq(IRQ_TT_MFP_RI, SCC_ri_int, IRQ_TYPE_SLOW,
+			            "TT-MFP ring indicator (modem 2)", info);
+
+#ifdef CONFIG_ATARI_SCC_DMA
+		if (channel == CHANNEL_A && !ch_a_inited && type == SER_SCC_DMA && scca_dma) {
+
+			int i, size = SCCA_DMA_BUFFERS * SCCA_DMA_BUFSIZE;
+
+			if (!(scca_dma_buf[0].err = kmalloc (size, GFP_KERNEL))) {
+				printk ("SCC-A: Cannot allocate buffers, DMA support disabled\n");
+				scca_dma = 0;
+			}
+
+			if (scca_dma) {
+				size = (size + PAGE_SIZE - 1) >> 12;
+				if (!(scca_dma_buf[0].buf = (u_char *)__get_dma_pages (GFP_KERNEL, size))) {
+					printk ("SCC-A: Cannot allocate buffers, DMA support disabled\n");
+					kfree (scca_dma_buf[0].err);
+					scca_dma = 0;
+				}
+			}
+
+			if (scca_dma)
+			  if (tt_mfp.int_en_a & tt_mfp.int_mk_a & 0x20) {
+				  printk ("SCC-A: TT_MFP Timer A already in use, DMA support disabled\n");
+				  free_pages ((unsigned long)scca_dma_buf[0].buf, size);
+				  kfree (scca_dma_buf[0].err);
+				  scca_dma = 0;
+			  }
+
+			if (scca_dma) {
+
+				printk ("SCC-A: using %d buffers a %d bytes for DMA\n", SCCA_DMA_BUFFERS, SCCA_DMA_BUFSIZE);
+
+				size = SCCA_DMA_BUFSIZE;
+				for (i=1; i<SCCA_DMA_BUFFERS; i++) {
+					scca_dma_buf[i].buf = scca_dma_buf[0].buf + size;
+					scca_dma_buf[i].err = scca_dma_buf[0].err + size;
+					size += SCCA_DMA_BUFSIZE;
+				}
+
+				for (i=0; i<SCCA_DMA_BUFFERS; i++)
+				  scca_dma_buf[i].pbuf = (char *)virt_to_phys(scca_dma_buf[i].buf);
+
+				tt_mfp.int_en_a &= ~0x20;
+				tt_mfp.int_pn_a = ~0x20;
+				tt_mfp.tim_ct_a = 0x00;
+				tt_mfp.tim_dt_a = 0x00;
+				request_irq (IRQ_TT_MFP_TIMA, SCC_timer_int, IRQ_TYPE_SLOW,
+							 "SCC-A Timer", info);
+				tt_mfp.int_mk_a |= 0x20;
+				tt_mfp.int_en_a |= 0x20;
+				tt_mfp.tim_ct_a = 0x07;
+
+				/* timer now runs with 1/200 pre-divisor and 1/256 data
+				 * counter, say 1/51200 at 2.4576 MHz, say 48 Hz. seems to be
+				 * the slowest frequency possible.
+				 */
+				request_irq (IRQ_TT_MFP_SCC, SCC_dma_int, IRQ_TYPE_PRIO,
+							 "SCC-A DMA", info);
+			}
+		}
+#endif
+	}
+
+	/* Hardware initialization */
+
+	if (!called) {
+		/* Before accessing the SCC the first time, do a read to the
+		 * control register to reset the internal pointers
+		 */
+		SCCread( STATUS_REG );
+
+		if (!atari_SCC_reset_done) {
+			/* The master reset with additional delay */
+			SCCwrite( MASTER_INT_CTRL, MIC_HARD_RESET );
+			udelay(40);
+			atari_SCC_reset_done = 1;
+		}
+
+		/* Set the interrupt vector, 0x60 is standard Atari */
+		SCCwrite( INT_VECTOR_REG, 0x60 );
+
+		/* Interrupt parameters: vector includes status, status low */
+		SCCwrite( MASTER_INT_CTRL, MIC_VEC_INCL_STAT );
+
+		/* to do only once, too: If a TT-MFP is present, initialize its timer
+		 * C that is connected to RTxC of channel B */
+		if (ATARIHW_PRESENT(TT_MFP)) {
+			/* If on a TT, program the TT-MFP's timer C to 307200 kHz
+			 * for RTxC on channel B
+			 */
+			tt_mfp.tim_ct_cd = (tt_mfp.tim_ct_cd & ~0x70) | 0x10; /* 1:4 mode */
+			tt_mfp.tim_dt_c  = 1;
+			/* make sure the timer interrupt is disabled, the timer is used
+			 * only for generating a clock */
+			atari_turnoff_irq( IRQ_TT_MFP_TIMC );
+
+			/* Set the baud tables */
+			SCC_baud_table[CHANNEL_A] = bdtab_norm;
+			SCC_baud_table[CHANNEL_B] = bdtab_TTChB;
+
+			/* The clocks are already initialzed for the TT. */
+		}
+		else {
+			/* Set the baud tables */
+			SCC_baud_table[CHANNEL_A] = bdtab_norm;
+			SCC_baud_table[CHANNEL_B] = bdtab_norm;
+
+			/* Set the clocks; only RTxCB is different compared to the TT */
+			SCC_clocks[CHANNEL_B][CLK_RTxC] = SCC_BAUD_BASE_PCLK4;
+		}
+
+		SCCmod( MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB );
+	}
+
+	/* disable interrupts for this channel */
+	SCCwrite( INT_AND_DMA_REG, 0 );
+
+	called = 1;
+	if (CHANNR(info) == CHANNEL_A) ch_a_inited = 1;
+}
+
+#endif
+
+#ifdef MODULE
+static void SCC_deinit_port( struct m68k_async_struct *info, int channel )
+{
+#ifdef ENABLE_ATARI_SCC
+	if (MACH_IS_ATARI) {
+		free_irq(channel ? IRQ_SCCB_TX : IRQ_SCCA_TX, info);
+		free_irq(channel ? IRQ_SCCB_STAT : IRQ_SCCA_STAT, info);
+		free_irq(channel ? IRQ_SCCB_RX : IRQ_SCCA_RX, info);
+		free_irq(channel ? IRQ_SCCB_SPCOND : IRQ_SCCA_SPCOND, info);
+		if (channel != 0 && ATARIHW_PRESENT (TT_MFP))
+			free_irq(IRQ_TT_MFP_RI, info);
+
+#ifdef CONFIG_ATARI_SCC_DMA
+		if (channel == CHANNEL_A && scca_dma) {
+			tt_mfp.int_en_a &= ~0x20;
+			tt_mfp.int_pn_a = ~0x20;
+			tt_mfp.int_mk_a &= ~0x20;
+			free_irq(IRQ_TT_MFP_SCC, info);
+			free_irq(IRQ_TT_MFP_TIMA, info);
+			free_pages ((unsigned long)scca_dma_buf[0].buf,
+				(SCCA_DMA_BUFFERS * SCCA_DMA_BUFSIZE + PAGE_SIZE - 1) >> 12);
+			kfree (scca_dma_buf[0].err);
+		}
+#endif
+	}
+#endif
+#ifdef CONFIG_MVME147
+	if (MACH_IS_MVME147) {
+		free_irq(channel ? MVME147_IRQ_SCCB_TX : MVME147_IRQ_SCCA_TX, info);
+		free_irq(channel ? MVME147_IRQ_SCCB_STAT : MVME147_IRQ_SCCA_STAT, info);
+		free_irq(channel ? MVME147_IRQ_SCCB_RX : MVME147_IRQ_SCCA_RX, info);
+		free_irq(channel ? MVME147_IRQ_SCCB_SPCOND : MVME147_IRQ_SCCA_SPCOND,
+			info);
+	}
+#endif
+#ifdef CONFIG_MVME16x
+	if (MACH_IS_MVME16x) {
+		free_irq(channel ? MVME162_IRQ_SCCB_TX : MVME162_IRQ_SCCA_TX, info);
+		free_irq(channel ? MVME162_IRQ_SCCB_STAT : MVME162_IRQ_SCCA_STAT, info);
+		free_irq(channel ? MVME162_IRQ_SCCB_RX : MVME162_IRQ_SCCA_RX, info);
+		free_irq(channel ? MVME162_IRQ_SCCB_SPCOND : MVME162_IRQ_SCCA_SPCOND,
+			info);
+	}
+#endif
+#ifdef CONFIG_BVME6000
+	if (MACH_IS_BVME6000) {
+		free_irq(channel ? BVME_IRQ_SCCB_TX : BVME_IRQ_SCCA_TX, info);
+		free_irq(channel ? BVME_IRQ_SCCB_STAT : BVME_IRQ_SCCA_STAT, info);
+		free_irq(channel ? BVME_IRQ_SCCB_RX : BVME_IRQ_SCCA_RX, info);
+		free_irq(channel ? BVME_IRQ_SCCB_SPCOND : BVME_IRQ_SCCA_SPCOND,
+			info);
+	}
+#endif
+}
+#endif
+
+
+#ifdef CONFIG_ATARI_SCC_DMA
+
+/*****************************************************************************/
+
+/*
+ * TeSche's high-speed debugging helpers. it has proven (not only at this
+ * place) that ordinary printk()s cause much more problems than they solve when
+ * debugging interrupt handlers. these ones are not nice, but fast.
+ */
+
+#if DEBUG & DEBUG_DMA
+
+#define DEBUGBUFSIZE 1024
+static char debugBuf[DEBUGBUFSIZE];
+static char *debugPtr = &debugBuf[0];
+static char *debugEndPtr = &debugBuf[DEBUGBUFSIZE-1];
+
+static inline void debugString (char *s)
+{
+	while (*s && (debugPtr != debugEndPtr))
+	  *debugPtr++ = *s++;
+}
+
+static inline void debugInt (int i)
+{
+	char *tmp = "0123456789";   /* maxint (unsigned) = 4294967296, 10 digits */
+	short cnt = 10;
+
+	while (--cnt > 0) {
+		tmp[cnt] = '0' + (i % 10);
+		i /= 10;
+	}
+
+	while (*tmp == '0')
+	  tmp++;
+
+	while (*tmp && (debugPtr != debugEndPtr))
+	  *debugPtr++ = *tmp++;
+}
+
+static char int2hex[] = "0123456789abcdef";
+
+static inline void debugHex (unsigned int h)
+{
+	char *tmp = "01234567";
+	short cnt = 8;
+
+	while (--cnt > 0) {
+		tmp[cnt] = int2hex[h & 15];
+		h >>= 4;
+	}
+
+	while (*tmp == '0')
+	  tmp++;
+
+	while (*tmp && (debugPtr != debugEndPtr))
+	  *debugPtr++ = *tmp++;
+}
+
+static inline void debugFlush (void)
+{
+	if (debugPtr != &debugBuf[0]) {
+		*debugPtr = 0;
+		printk ("%s\n", debugBuf);
+		debugPtr = &debugBuf[0];
+	}
+}
+
+#endif /* DEBUG & DEBUG_DMA */
+
+
+/*****************************************************************************/
+
+/*
+ * these functions are for DMA support. they all assume that they're called
+ * with INTs off so that they can play with their data structures undisturbed.
+ */
+
+static ulong dmaStartAddr = 0;   /* 0 means DMA not running */
+static ulong dmaSize;
+
+
+/* start DMA on current (scca_head) write buffer
+ */
+static inline void dma_start (void)
+{
+	if ((dmaSize = SCCA_DMA_BUFSIZE - scca_dma_head->inbuf) > 0) {
+		dmaStartAddr = (ulong)(scca_dma_head->pbuf + scca_dma_head->inbuf); /* needs no virt_to_phys() */
+#if DEBUG & DEBUG_DMA
+		debugString ("[start@0x");
+		debugHex (dmaStartAddr & 0xffff);
+		debugString ("/");
+		debugInt (dmaSize);
+		debugString ("] ");
+#endif
+		tt_scc_dma.dma_ctrl &= ~3;
+		__asm__ __volatile__ ("movep.l %0,%1@(0)\n\t"
+							  "movep.l %2,%3@(0)\n\t"
+							  : /* no outputs */
+							  : "d"(dmaStartAddr), "a"(&tt_scc_dma.dma_addr_hi),
+							  "d"(dmaSize), "a"(&tt_scc_dma.dma_cnt_hi)
+							  : "memory");
+		tt_scc_dma.dma_ctrl |= 2;
+	} else {
+		dmaStartAddr = 0;
+	}
+}
+
+
+/* stop DMA, read restbytes and adjust buffer counters
+ */
+static inline void dma_stop (void)
+{
+	register short rest;
+	ulong size = 0, endaddr;
+	unsigned char *from = (unsigned char *)&tt_scc_dma.dma_restdata, *to;
+
+	if (!dmaStartAddr)
+	  return;
+
+	tt_scc_dma.dma_ctrl &= ~3;   /* stop DMA */
+
+	/* ++TeSche: I've had tremendous problems with looking at dma_addr to see
+	 * how many bytes were received rather than looking at dma_cnt. To me it
+	 * looks like there are cases when dma_addr is not properly updated when
+	 * DMA is aborted, so I ended up with calculating less bytes than actually
+	 * were received. Dma_cnt seems to be ok for me, so this is the way I go.
+	 *
+	 * sigh, yet one more unspecified hardware feature? is it at all anywhere
+	 * specified what happens when a DMA is aborted before it ends?
+	 */
+	__asm__ __volatile__ ("movep.l %1@(0),%0\n\t"
+						  : "=d"(size)
+						  : "a"(&tt_scc_dma.dma_cnt_hi)
+						  : "memory");
+	size = dmaSize - size;
+
+#if DEBUG & DEBUG_DMA
+	__asm__ __volatile__ ("movep.l %1@(0),%0\n\t"
+						  : "=d"(endaddr)
+						  : "a"(&tt_scc_dma.dma_addr_hi)
+						  : "memory");
+	if (endaddr - dmaStartAddr != size) {
+		debugString ("[size=");
+		debugInt (size);
+		debugString (",addr=");
+		debugInt (endaddr-dmaStartAddr);
+		debugString ("] ");
+	}
+#endif
+
+	endaddr = dmaStartAddr + size;
+
+	if ((dmaStartAddr & ~3) != (endaddr & ~3)) {
+		/* at least one long was written. lower two bits of endaddress are
+		 * number of restbytes. write them left-justified to the long the
+		 * endaddress is in.
+		 */
+		rest = endaddr & 3;
+		to = scca_dma_head->buf + (endaddr & (SCCA_DMA_BUFSIZE-1) & ~3);   /* needs no PTOV */
+	} else {
+		/* no long written. number of restbytes is endaddress -
+		 * startaddress. write them to the startaddress.
+		 */
+		rest = size;   /* must and will be 0..3 */
+		from += dmaStartAddr & 3;
+		to = scca_dma_head->buf + (dmaStartAddr & (SCCA_DMA_BUFSIZE-1));   /* needs no PTOV */
+	}
+
+#if DEBUG & DEBUG_DMA
+	debugString ("[stop@0x");
+	debugHex (endaddr & 0xffff);
+	debugString ("/");
+	debugInt (size);
+	if (rest) {
+		debugString (",rest=");
+		debugInt (rest);
+		debugString ("@0x");
+		debugHex ((unsigned int)from);
+		debugString ("->");
+		debugHex (((unsigned int)to) & 0xffff);
+	}
+	debugString ("] ");
+#endif
+
+	while (--rest >= 0)
+	  *to++ = *from++;
+
+	scca_dma_head->inbuf += size;
+}
+
+
+/* used by the SPCOND INT handler to deliver error codes. it's not very clean,
+ * maybe overwrites older values, but since that only happens when something
+ * has already gone wrong I don't consider this a problem.
+ */
+static inline void dma_fake_receive (u_char data, u_char err)
+{
+	scca_dma_head->buf[scca_dma_head->inbuf] = data;
+	scca_dma_head->err[scca_dma_head->inbuf] = err;
+
+	if (scca_dma_head->inbuf < SCCA_DMA_BUFSIZE-1)
+	  scca_dma_head->inbuf++;
+
+	switch (err) {
+	  case TTY_OVERRUN:
+		scca_dma_head->cntOver++;
+		break;
+	  case TTY_PARITY:
+		scca_dma_head->cntPar++;
+		break;
+	  case TTY_FRAME:
+		scca_dma_head->cntFrame++;
+	}
+}
+
+
+/*****************************************************************************/
+
+/*
+ * these functions are for high-level DMA support. they make no assumptions
+ * about current INT status when called.
+ */
+
+/* can't be called more than once due to tqueue handling + clever (?:) variable
+ * design -> no cli/sti.
+ */
+static void SCC_flush (struct tty_struct *tty)
+{
+	int loops = 0;
+	static int highWater = (95 * SCCA_DMA_BUFSIZE) / 100;
+
+	/* a potential endless loop, but that's *really* unlikely...
+	 */
+	while (scca_dma_tail->needsFlushing) {
+
+		/* ...anyway, be save
+		 */
+		if (++loops > SCCA_DMA_BUFFERS) {
+			printk ("SCC-A: flush loop overrun\n");
+			break;
+		}
+
+#if DEBUG & DEBUG_DMA
+		if (scca_dma_tail->cntOver || scca_dma_tail->cntPar || scca_dma_tail->cntFrame) {
+			debugString ("[ovr=");
+			debugInt (scca_dma_tail->cntOver);
+			debugString (",par=");
+			debugInt (scca_dma_tail->cntPar);
+			debugString (",frm=");
+			debugInt (scca_dma_tail->cntFrame);
+			debugString ("] ");
+		}
+#else
+		if (scca_dma_tail->cntOver || scca_dma_tail->cntPar || scca_dma_tail->cntFrame)
+		  printk ("SCC-A: %d overrun, %d parity, %d frame errors\n",
+				  scca_dma_tail->cntOver, scca_dma_tail->cntPar, scca_dma_tail->cntFrame);
+#endif
+
+		if (scca_dma_tail->inbuf > highWater)
+		  printk ("SCC-A: warning: buffer usage: %d/%d chars\n", scca_dma_tail->inbuf, SCCA_DMA_BUFSIZE);
+
+#if 0
+		if (scca_dma_tail->inbuf > 1) {
+			scca_dma_tail->buf[0] |= 0x1;
+			scca_dma_tail->buf[scca_dma_tail->inbuf-1] |= 0x2;
+		}
+#endif
+
+		tty->ldisc.receive_buf (tty, scca_dma_tail->buf, scca_dma_tail->err, scca_dma_tail->inbuf);
+
+		scca_dma_tail->cntOver = 0;
+		scca_dma_tail->cntPar = 0;
+		scca_dma_tail->cntFrame = 0;
+		scca_dma_tail->inbuf = 0;
+		scca_dma_tail->needsFlushing = 0;
+
+		if (++scca_dma_tail == scca_dma_end)
+		  scca_dma_tail = scca_dma_buf;
+	}
+
+#if DEBUG & DEBUG_DMA
+	debugFlush ();
+#endif
+}
+
+
+static struct tq_struct SCC_flush_tqueue = {
+	NULL,		/* next */
+	0,			/* sync */
+	(void (*)(void*)) SCC_flush,  /* routine, must have (void *) arg... */
+	NULL		/* data */
+};
+
+
+/* the 48Hz timer to flush data. runs in fact only every 4th call, say at 12Hz.
+ */
+static void SCC_timer_int (int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	static int delay = 4;
+	ulong flags;
+	SCC_ACCESS_INIT(info);
+
+	/* if 'fp' is NULL we're called from SCC_dma_int, in which case we must
+	 * respond immediately!
+	 */
+	if (fp && --delay > 0)
+	  return;
+
+#if DEBUG & DEBUG_DMA
+	delay = 100;   /* 0.48Hz for better debugging, no more than 19k2b! */
+#else
+	delay = 4;   /* 12Hz delivery frequency (960 bytes / delivery @ 115k2b) */
+#endif
+
+	if (!SCC_flush_tqueue.data)
+	  return;   /* no program listening... */
+
+	save_flags (flags);
+	cli ();
+
+	if (scca_dma_head->active) {
+		SCCmod (INT_AND_DMA_REG, ~(IDR_RX_INT_MASK|IDR_WAITREQ_ENAB), 0x00);
+		dma_stop ();
+		scca_dma_head->needsFlushing = 1;
+		scca_dma_head->active = 0;
+		if (++scca_dma_head == scca_dma_end)
+		  scca_dma_head = scca_dma_buf;
+	}
+
+	if (!scca_dma_head->needsFlushing) {
+		scca_dma_head->active = 1;
+		dma_start ();
+		SCCmod (INT_AND_DMA_REG, 0xff, IDR_RX_INT_SPCOND|IDR_WAITREQ_ENAB);
+		/* this must *happen* after re-starting DMA for speed reasons.
+		 */
+		memset (scca_dma_head->err, 0x00, SCCA_DMA_BUFSIZE);
+	} else {
+		printk ("SCC-A: fatal buffer overflow, data lost!\n");
+	}
+
+	queue_task (&SCC_flush_tqueue, &tq_immediate);
+	mark_bh (IMMEDIATE_BH);
+
+	restore_flags (flags);
+}
+
+
+/* DMA finished before timer occured?
+ */
+static void SCC_dma_int (int irq, void *data, struct pt_regs *fp)
+{
+	printk ("SCC-A: DMA-INT occured, data lost!\n");
+#if 0
+	/* is there any reason why we should call this? if the timer INT was
+	 * delayed so long that this happened then this INT was delayed too, so
+	 * it's already too late.
+	 */
+	SCC_timer_int (irq, (struct m68k_async_struct *)data, NULL);
+#endif
+}
+
+/*****************************************************************************/
+
+#endif
+
+
+#if DEBUG & DEBUG_OVERRUNS
+static int SCC_ch_cnt[2] = { 0, 0 }, SCC_ch_ovrrun[2] = { 0, 0 };
+#endif
+
+static void SCC_rx_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	unsigned char	ch;
+	SCC_ACCESS_INIT(info);
+
+	SETUP_INFO(info);
+
+	ch = SCCread_NB( RX_DATA_REG );
+#if DEBUG & DEBUG_INT
+	printk( "SCC ch %d rx int: char %02x\n", CHANNR(info), ch );
+#endif
+	rs_receive_char (info, ch, 0);
+#if DEBUG & DEBUG_OVERRUNS
+	{ int channel = CHANNR(info);
+	  if (++SCC_ch_cnt[channel] == 10000) {
+		  printk( "SCC ch. %d: overrun rate %d.%02d\n", channel,
+				  SCC_ch_ovrrun[channel] / 100,
+				  SCC_ch_ovrrun[channel] % 100 );
+		  SCC_ch_cnt[channel] = SCC_ch_ovrrun[channel] = 0;
+	  }
+	}
+#endif
+
+	/* Check if another character is already ready; in that case, the
+	 * spcond_int() function must be used, because this character may have an
+	 * error condition that isn't signalled by the interrupt vector used!
+	 */
+	if (SCCread( INT_PENDING_REG ) &
+	    (CHANNR(info) == CHANNEL_A ? IPR_A_RX : IPR_B_RX)) {
+		SCC_spcond_int (0, info, 0);
+		return;
+	}
+
+#ifndef ATARI_USE_SOFTWARE_EOI
+	SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET );
+#endif
+}
+
+
+static void SCC_spcond_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	unsigned char	stat, ch, err;
+	int		int_pending_mask = CHANNR(info) == CHANNEL_A ?
+			                   IPR_A_RX : IPR_B_RX;
+#ifdef CONFIG_ATARI_SCC_DMA
+	int isdma = (CHANNR(info) == CHANNEL_A) && scca_dma;
+	ulong flags = 0;
+#endif
+	SCC_ACCESS_INIT(info);
+	
+	SETUP_INFO(info);
+
+#ifdef CONFIG_ATARI_SCC_DMA
+	if (isdma) {
+		save_flags (flags);
+		cli ();
+		SCCmod (INT_AND_DMA_REG, ~(IDR_RX_INT_MASK|IDR_WAITREQ_ENAB), 0);
+		dma_stop ();
+	}
+#endif
+
+	do {
+		stat = SCCread( SPCOND_STATUS_REG );
+		ch = SCCread_NB(RX_DATA_REG);
+#if DEBUG & DEBUG_INT
+		printk( "SCC ch %d spcond int: char %02x stat %02x\n",
+			   CHANNR(info), ch, stat );
+#endif
+
+		if (stat & SCSR_RX_OVERRUN)
+			err = TTY_OVERRUN;
+		else if (stat & SCSR_PARITY_ERR)
+			err = TTY_PARITY;
+		else if (stat & SCSR_CRC_FRAME_ERR)
+			err = TTY_FRAME;
+		else
+			err = 0;
+
+#ifdef CONFIG_ATARI_SCC_DMA
+		if (isdma)
+		  dma_fake_receive (ch, err);
+		else
+#endif
+		  rs_receive_char (info, ch, err);
+
+		/* ++TeSche: *All* errors have to be cleared manually,
+		 * else the condition persists for the next chars
+		 */
+		if (err)
+		  SCCwrite(COMMAND_REG, CR_ERROR_RESET);
+
+#if DEBUG & DEBUG_OVERRUNS
+		{ int channel = CHANNR(info);
+		  if (err == TTY_OVERRUN) SCC_ch_ovrrun[channel]++;
+		  if (++SCC_ch_cnt[channel] == 10000) {
+			  printk( "SCC ch. %d: overrun rate %d.%02d %%\n", channel,
+					  SCC_ch_ovrrun[channel] / 100,
+					  SCC_ch_ovrrun[channel] % 100 );
+			  SCC_ch_cnt[channel] = SCC_ch_ovrrun[channel] = 0;
+		  }
+	    }
+#endif
+
+	} while( SCCread( INT_PENDING_REG ) & int_pending_mask );
+
+#ifdef CONFIG_ATARI_SCC_DMA
+	if (isdma) {
+		dma_start ();
+		SCCmod (INT_AND_DMA_REG, 0xff, IDR_RX_INT_SPCOND|IDR_WAITREQ_ENAB);
+		restore_flags (flags);
+	}
+#endif
+
+#ifndef ATARI_USE_SOFTWARE_EOI
+	SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET );
+#endif
+}
+
+
+#ifdef ENABLE_ATARI_SCC
+static void SCC_ri_int(int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	/* update input line counter */
+	info->icount.rng++;
+	wake_up_interruptible(&info->delta_msr_wait);
+}
+#endif
+
+
+static void SCC_tx_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	int ch;
+	SCC_ACCESS_INIT(info);
+
+	SETUP_INFO(info);
+
+	while( (SCCread_NB( STATUS_REG ) & SR_TX_BUF_EMPTY) &&
+		   (ch = rs_get_tx_char( info )) >= 0 ) {
+		SCCwrite( TX_DATA_REG, ch );
+#if DEBUG & DEBUG_INT
+		printk( "SCC ch. %d tx int: sent char %02x\n", CHANNR(info), ch );
+#endif
+	}
+
+	if (rs_no_more_tx( info )) {
+		/* disable tx interrupts */
+		SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
+		SCCwrite( COMMAND_REG, CR_TX_PENDING_RESET );   /* disable tx_int on next tx underrun? */
+#if DEBUG & DEBUG_INT
+		printk ("SCC ch %d tx int: no more chars after %d sent\n",
+				CHANNR (info), total);
+#endif
+	}
+
+#ifndef ATARI_USE_SOFTWARE_EOI
+	SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET );
+#endif
+}
+
+
+static void SCC_stat_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct m68k_async_struct *info = data;
+	unsigned channel = CHANNR(info);
+	unsigned char	last_sr, sr, changed;
+	SCC_ACCESS_INIT(info);
+
+	SETUP_INFO(info);
+
+	last_sr = SCC_last_status_reg[channel];
+	sr = SCC_last_status_reg[channel] = SCCread_NB( STATUS_REG );
+	changed = last_sr ^ sr;
+#if DEBUG & DEBUG_INT
+	printk( "SCC ch %d stat int: sr=%02x last_sr=%02x\n",
+			CHANNR(info), sr, last_sr );
+#endif
+
+	if (changed & SR_DCD)
+		rs_dcd_changed( info, sr & SR_DCD );
+
+	if (changed & SR_CTS) {
+#if DEBUG & DEBUG_THROTTLE
+		printk( "SCC ch. %d: now CTS=%d\n", CHANNR(info), !!(sr & SR_CTS) );
+#endif
+		rs_check_cts( info, sr & SR_CTS );
+	}
+
+	if (changed & SR_SYNC_ABORT) { /* Data Set Ready */
+		/* update input line counter */
+		info->icount.dsr++;
+		wake_up_interruptible(&info->delta_msr_wait);
+	}
+
+	SCCwrite( COMMAND_REG, CR_EXTSTAT_RESET );
+#ifndef ATARI_USE_SOFTWARE_EOI
+	SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET );
+#endif
+}
+
+
+static int SCC_check_open( struct m68k_async_struct *info, struct tty_struct *tty,
+			  struct file *file )
+{
+	/* If channel A is opened, check if one of the compounded ports (ttyS3 and
+	 * ttyS4) is already open, else activate the appropriate port hardware.
+	 */
+
+#if DEBUG & DEBUG_OPEN
+	printk( "SCC: about to open channel %d as line %d\n",
+			CHANNR(info), info->line );
+#endif
+
+	if (CHANNR(info) == CHANNEL_A) {
+
+		if (SCC_chan_a_open) {
+			if (SCC_chan_a_line != info->line) {
+#if DEBUG & DEBUG_OPEN
+				printk("SCC: channel 0 was already open\n");
+#endif
+				return -EBUSY;
+			}
+			else
+				return 0;
+		}
+
+		if ((info->line == cha232_line &&
+		     SCC_chan_a_switchable == SCCA_SWITCH_LAN_ONLY) ||
+		    (info->line == cha422_line &&
+		     SCC_chan_a_switchable == SCCA_SWITCH_SERIAL2_ONLY))
+			return( -ENODEV );
+
+		SCC_chan_a_open = 1;
+		SCC_chan_a_line = info->line;
+		SCC_chan_a_info = &rs_table[info->line];
+#ifdef ENABLE_ATARI_SCC
+		if (SCC_chan_a_switchable == SCCA_SWITCH_BOTH) {
+			unsigned long flags; 
+			unsigned char tmp;
+
+			save_flags(flags);
+			cli();
+			sound_ym.rd_data_reg_sel = 14;
+			tmp = sound_ym.rd_data_reg_sel;
+			sound_ym.wd_data = (info->line == cha232_line
+					    ? tmp | 0x80
+					    : tmp & 0x7f);
+#if DEBUG & DEBUG_OPEN
+			printk( "SCC: set PSG IO7 to %02x (was %02x)\n",
+			       (info->line & 1) ? (tmp | 0x80) : (tmp & 0x7f),
+			       tmp );
+#endif
+			restore_flags(flags);
+		}
+#endif
+	}
+	return( 0 );
+}
+
+
+static void SCC_init( struct m68k_async_struct *info )
+{
+	int i, channel = CHANNR(info);
+	unsigned long	flags;
+	SCC_ACCESS_INIT(info);
+#ifdef ENABLE_ATARI_SCC
+	static const struct {
+		unsigned reg, val;
+	} init_tab[] = {
+		/* no parity, 1 stop bit, async, 1:16 */
+		{ AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x64 },
+		/* parity error is special cond, ints disabled, no DMA */
+		{ INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB },
+		/* Rx 8 bits/char, no auto enable, Rx off */
+		{ RX_CTRL_REG, RCR_CHSIZE_8 },
+		/* DTR off, Tx 8 bits/char, RTS off, Tx off */
+		{ TX_CTRL_REG, TCR_CHSIZE_8 },
+		/* special features off */
+		{ AUX2_CTRL_REG, 0 },
+		/* RTxC is XTAL, TRxC is input, both clocks = RTxC */
+		{ CLK_CTRL_REG, CCR_TRxCOUT_XTAL | CCR_TXCLK_RTxC | CCR_RXCLK_RTxC },
+		{ DPLL_CTRL_REG, 0 },
+		/* Start Rx */
+		{ RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },
+		/* Start Tx */
+		{ TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },
+		/* Ext/Stat ints: CTS, DCD, SYNC (DSR) */
+		{ INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT },
+		/* Reset Ext/Stat ints */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* ...again */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* Rx int always, TX int off, Ext/Stat int on */
+		{ INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB |
+		  IDR_PARERR_AS_SPCOND | IDR_RX_INT_ALL }
+	};
+#ifdef CONFIG_ATARI_SCC_DMA
+	static const struct {
+		unsigned reg, val;
+	} init_withdma_tab[] = {
+		/* no parity, 1 stop bit, async, 1:16 */
+		{ AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x64 },
+		/* parity error is special cond, ints disabled, DMA receive but disabled */
+		{ INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB |
+			IDR_WAITREQ_RX | IDR_WAITREQ_IS_REQ},
+		/* Rx 8 bits/char, no auto enable, Rx off */
+		{ RX_CTRL_REG, RCR_CHSIZE_8 },
+		/* DTR off, Tx 8 bits/char, RTS off, Tx off */
+		{ TX_CTRL_REG, TCR_CHSIZE_8 },
+		/* special features off */
+		{ AUX2_CTRL_REG, 0 },
+		/* RTxC is XTAL, TRxC is input, both clocks = RTxC */
+		{ CLK_CTRL_REG, CCR_TRxCOUT_XTAL | CCR_TXCLK_RTxC | CCR_RXCLK_RTxC },
+		{ DPLL_CTRL_REG, 0 },
+		/* Start Rx */
+		{ RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },
+		/* Start Tx */
+		{ TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },
+		/* Ext/Stat ints: CTS, DCD, SYNC (DSR) */
+		{ INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT },
+		/* Reset Ext/Stat ints */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* ...again */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* parity error is special cond, Tx & SPcond ints enabled, Rx int disabled, DMA receive but disabled */
+		{ INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB | IDR_PARERR_AS_SPCOND |
+			IDR_RX_INT_DISAB | IDR_WAITREQ_RX | IDR_WAITREQ_IS_REQ}
+	};
+#endif
+#endif
+#ifdef CONFIG_MVME147_SCC
+	static const struct {
+		unsigned reg, val;
+	} m147_init_tab[] = {
+		/* Values for MVME147 */
+		/* no parity, 1 stop bit, async, 1:16 */
+		{ AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 },
+		/* parity error is special cond, ints disabled, no DMA */
+		{ INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB },
+		/* Rx 8 bits/char, no auto enable, Rx off */
+		{ RX_CTRL_REG, RCR_CHSIZE_8 },
+		/* DTR off, Tx 8 bits/char, RTS off, Tx off */
+		{ TX_CTRL_REG, TCR_CHSIZE_8 },
+		/* special features off */
+		{ AUX2_CTRL_REG, 0 },
+		{ CLK_CTRL_REG, CCR_RXCLK_BRG | CCR_TXCLK_BRG },
+		{ DPLL_CTRL_REG, DCR_BRG_ENAB | DCR_BRG_USE_PCLK },
+		/* Start Rx */
+		{ RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },
+		/* Start Tx */
+		{ TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },
+		/* Ext/Stat ints: CTS, DCD, SYNC (DSR) */
+		{ INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT },
+		/* Reset Ext/Stat ints */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* ...again */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* Rx int always, TX int off, Ext/Stat int on */
+		{ INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB |
+		  IDR_PARERR_AS_SPCOND | IDR_RX_INT_ALL }
+	};
+#endif
+#ifdef CONFIG_MVME162_SCC
+	static const struct {
+		unsigned reg, val;
+	} mvme_init_tab[] = {
+		/* Values for MVME162 */
+		/* no parity, 1 stop bit, async, 1:16 */
+		{ AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 },
+		/* parity error is special cond, ints disabled, no DMA */
+		{ INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB },
+		/* Rx 8 bits/char, no auto enable, Rx off */
+		{ RX_CTRL_REG, RCR_CHSIZE_8 },
+		/* DTR off, Tx 8 bits/char, RTS off, Tx off */
+		{ TX_CTRL_REG, TCR_CHSIZE_8 },
+		/* special features off */
+		{ AUX2_CTRL_REG, 0 },
+		{ CLK_CTRL_REG, CCR_RXCLK_BRG | CCR_TXCLK_BRG },
+		{ DPLL_CTRL_REG, DCR_BRG_ENAB | DCR_BRG_USE_PCLK },
+		/* Start Rx */
+		{ RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },
+		/* Start Tx */
+		{ TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },
+		/* Ext/Stat ints: CTS, DCD, SYNC (DSR) */
+		{ INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT },
+		/* Reset Ext/Stat ints */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* ...again */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* Rx int always, TX int off, Ext/Stat int on */
+		{ INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB |
+		  IDR_PARERR_AS_SPCOND | IDR_RX_INT_ALL }
+	};
+#endif
+#ifdef CONFIG_BVME6000_SCC
+	static const struct {
+		unsigned reg, val;
+	} bvme_init_tab[] = {
+		/* Values for BVME6000 */
+		/* no parity, 1 stop bit, async, 1:16 */
+		{ AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 },
+		/* parity error is special cond, ints disabled, no DMA */
+		{ INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB },
+		/* Rx 8 bits/char, no auto enable, Rx off */
+		{ RX_CTRL_REG, RCR_CHSIZE_8 },
+		/* DTR off, Tx 8 bits/char, RTS off, Tx off */
+		{ TX_CTRL_REG, TCR_CHSIZE_8 },
+		/* special features off */
+		{ AUX2_CTRL_REG, 0 },
+		{ CLK_CTRL_REG, CCR_RTxC_XTAL | CCR_RXCLK_BRG | CCR_TXCLK_BRG },
+		{ DPLL_CTRL_REG, DCR_BRG_ENAB },
+		/* Start Rx */
+		{ RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },
+		/* Start Tx */
+		{ TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },
+		/* Ext/Stat ints: CTS, DCD, SYNC (DSR) */
+		{ INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT },
+		/* Reset Ext/Stat ints */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* ...again */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* Rx int always, TX int off, Ext/Stat int on */
+		{ INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB |
+		  IDR_PARERR_AS_SPCOND | IDR_RX_INT_ALL }
+	};
+#endif
+	save_flags(flags);
+	cli();
+
+	if (!MACH_IS_MVME16x && !MACH_IS_BVME6000 && !MACH_IS_MVME147) {
+		SCCmod( MASTER_INT_CTRL, 0x3f,
+			channel == 0 ? MIC_CH_A_RESET : MIC_CH_B_RESET );
+		udelay(40); /* extra delay after a reset */
+	}
+
+#ifdef ENABLE_ATARI_SCC
+	if (MACH_IS_ATARI) {
+#ifdef CONFIG_ATARI_SCC_DMA
+		if (channel == CHANNEL_A && scca_dma) {
+
+			for (i=0; i<sizeof(init_withdma_tab)/sizeof(*init_withdma_tab); ++i)
+				  SCCwrite( init_withdma_tab[i].reg, init_withdma_tab[i].val );
+
+			/* enable SCC-DMA INTs
+			 */
+			tt_mfp.int_en_b &= ~4;
+			tt_mfp.active_edge |= 4;
+			tt_mfp.int_pn_b = ~4;
+			tt_mfp.int_mk_b |= 4;
+			tt_mfp.int_en_b |= 4;
+
+			/* init and start dma
+			 */
+			scca_dma_head = scca_dma_tail = scca_dma_buf;
+			for (i=0; i<SCCA_DMA_BUFFERS; i++) {
+				scca_dma_buf[i].inbuf = 0;
+				scca_dma_buf[i].cntOver = 0;
+				scca_dma_buf[i].cntPar = 0;
+				scca_dma_buf[i].cntFrame = 0;
+				scca_dma_buf[i].active = 0;
+				scca_dma_buf[i].needsFlushing = 0;
+				memset (scca_dma_buf[i].err, 0, SCCA_DMA_BUFSIZE);
+			}
+
+			scca_dma_head->active = 1;
+			dma_start ();
+			SCCmod (INT_AND_DMA_REG, 0xff, IDR_RX_INT_SPCOND|IDR_WAITREQ_ENAB);
+
+			SCC_flush_tqueue.data = ((struct m68k_async_struct *)info)->tty;
+
+		} else
+#endif
+		{
+			for (i=0; i<sizeof(init_tab)/sizeof(*init_tab); ++i)
+				SCCwrite( init_tab[i].reg, init_tab[i].val );
+		}
+	}
+#endif
+#ifdef CONFIG_MVME147_SCC
+	if (MACH_IS_MVME147) {
+		for (i=0; i<sizeof(m147_init_tab)/sizeof(*m147_init_tab); ++i)
+			SCCwrite( m147_init_tab[i].reg, m147_init_tab[i].val );
+	}
+#endif
+#ifdef CONFIG_MVME162_SCC
+	if (MACH_IS_MVME16x) {
+		for (i=0; i<sizeof(mvme_init_tab)/sizeof(*mvme_init_tab); ++i)
+			SCCwrite( mvme_init_tab[i].reg, mvme_init_tab[i].val );
+	}
+#endif
+#ifdef CONFIG_BVME6000_SCC
+	if (MACH_IS_BVME6000) {
+		for (i=0; i<sizeof(bvme_init_tab)/sizeof(*bvme_init_tab); ++i)
+			SCCwrite( bvme_init_tab[i].reg, bvme_init_tab[i].val );
+	}
+#endif
+
+	/* remember status register for detection of DCD and CTS changes */
+	SCC_last_status_reg[channel] = SCCread( STATUS_REG );
+	restore_flags(flags);
+#if DEBUG & DEBUG_INIT
+	printk( "SCC channel %d inited\n", CHANNR(info) );
+#endif
+#if DEBUG & DEBUG_OPEN
+	printk( "SCC channel %d opened\n", CHANNR(info) );
+#endif
+	MOD_INC_USE_COUNT;
+}
+
+
+static void SCC_deinit( struct m68k_async_struct *info, int leave_dtr )
+{
+	unsigned long	flags, timeout;
+	SCC_ACCESS_INIT(info);
+
+#if DEBUG & DEBUG_INIT
+	printk( "SCC channel %d about to be deinited\n", CHANNR(info) );
+#endif
+
+	if (MACH_IS_MVME147 && CHANNR(info) == CHANNEL_A)
+		return;		/* Channel A is our console */
+	if (MACH_IS_MVME16x && CHANNR(info) == CHANNEL_A)
+		return;		/* 162Bug uses channel A */
+	if (MACH_IS_BVME6000 && CHANNR(info) == CHANNEL_A)
+		return;		/* BVMbug uses channel A */
+
+	save_flags(flags);
+	cli();
+
+#ifdef CONFIG_ATARI_SCC_DMA
+	if (CHANNR(info) == CHANNEL_A && scca_dma) {
+		SCC_flush_tqueue.data = NULL;
+		SCCmod (INT_AND_DMA_REG, ~(IDR_RX_INT_MASK|IDR_WAITREQ_ENAB), 0);
+		dma_stop ();
+		tt_mfp.int_en_b &= ~4; /* disable TT-MFP INTs */
+		/* ++TeSche: for the moment we don't care if there's still data unflushed */
+	}
+#endif
+
+	/* disable interrupts */
+	SCCmod( INT_AND_DMA_REG, ~(IDR_EXTSTAT_INT_ENAB | IDR_TX_INT_ENAB |
+							   IDR_RX_INT_SPCOND), 0 );
+
+	/* disable Rx */
+	SCCmod( RX_CTRL_REG, ~RCR_RX_ENAB, 0 );
+
+	/* disable Transmitter */
+	SCCmod( TX_CTRL_REG, ~TCR_TX_ENAB, 0 );
+
+	/* wait until character is completely sent */
+	timeout = jiffies + 50;
+	restore_flags(flags);
+	while( !(SCCread( SPCOND_STATUS_REG ) & SCSR_ALL_SENT) ) {
+		if (time_after(jiffies, timeout)) break;
+	}
+	save_flags(flags);
+	cli();
+
+	/* drop RTS and maybe DTR */
+	SCCmod( TX_CTRL_REG, ~(TCR_RTS | (leave_dtr ? 0 : TCR_DTR)), 0 );
+
+	restore_flags(flags);
+#if DEBUG & DEBUG_INIT
+	printk( "SCC channel %d deinited\n", CHANNR(info) );
+#endif
+
+	if (CHANNR(info) == CHANNEL_A)
+		SCC_chan_a_open = 0;
+#if DEBUG & DEBUG_OPEN
+	printk( "SCC channel %d closed, chanAlock now = %d\n",
+			CHANNR(info), SCC_chan_a_open );
+#endif
+	MOD_DEC_USE_COUNT;
+}
+
+
+static void SCC_enab_tx_int( struct m68k_async_struct *info, int enab_flag )
+{
+	unsigned long	flags;
+	unsigned char	iadr;
+	SCC_ACCESS_INIT(info);
+
+	save_flags(flags);
+	cli();
+
+	iadr = SCCread( INT_AND_DMA_REG );
+	if (!!(iadr & IDR_TX_INT_ENAB) != !!enab_flag) {
+		SCCwrite(INT_AND_DMA_REG, iadr ^ IDR_TX_INT_ENAB);
+#if DEBUG & DEBUG_INT
+		printk("SCC ch %d: tx int %sabled\n", CHANNR(info),
+		       enab_flag ? "en" : "dis");
+#endif
+		if (enab_flag)
+		  /* restart the transmitter */
+		  SCC_tx_int (0, info, 0);
+	}
+
+	restore_flags(flags);
+}
+
+
+static int SCC_check_custom_divisor( struct m68k_async_struct *info,
+				    int baud_base, int divisor )
+{
+	int		clksrc;
+
+	clksrc = SCC_clocksrc (baud_base, CHANNR (info));
+	if (clksrc < 0)
+		/* invalid baud base */
+		return( -1 );
+
+	/* check for maximum (BRG values start from 4 with step 2) */
+	if (divisor/2-2 > 65535)
+		return( -1 );
+
+	switch( clksrc ) {
+
+	  case CLK_PCLK:
+		/* The master clock can only be used with the BRG, divisors
+		 * range from 4 and must be a multiple of 2
+		 */
+		return( !(divisor >= 4 && (divisor & 1) == 0) );
+
+	  case CLK_RTxC:
+		/* The RTxC clock can either be used for the direct 1:16, 1:32
+		 * or 1:64 modes (divisors 1, 2 or 4, resp.) or with the BRG
+		 * (divisors from 4 and a multiple of 2)
+		 */
+		return( !(divisor >= 1 && (divisor == 1 || (divisor & 1) == 0)) );
+
+	  case CLK_TRxC:
+		/* The TRxC clock can only be used for direct 1:16, 1:32 or
+		 * 1:64 modes
+		 */
+		return( !(divisor == 1 || divisor == 2 || divisor == 4) );
+
+	}
+	return( -1 );
+}
+
+
+static void SCC_change_speed( struct m68k_async_struct *info )
+{
+	/* the SCC has char sizes 5,7,6,8 in that order! */
+	static int chsize_map[4] = { 0, 2, 1, 3 };
+	unsigned cflag, baud, chsize, aflags;
+	unsigned channel, div = 0, clkmode, brgmode, brgval;
+	int clksrc = 0;
+	unsigned long flags;
+	SCC_ACCESS_INIT(info);
+
+	if (!info->tty || !info->tty->termios) return;
+
+	channel = CHANNR(info);
+
+	if (MACH_IS_MVME147 && channel == CHANNEL_A)
+		return;		/* Settings controlled by 147Bug */
+	if (MACH_IS_MVME16x && channel == CHANNEL_A)
+		return;		/* Settings controlled by 162Bug */
+	if (MACH_IS_BVME6000 && channel == CHANNEL_A)
+		return;		/* Settings controlled by BVMBug */
+
+	cflag  = info->tty->termios->c_cflag;
+	baud   = cflag & CBAUD;
+	chsize = (cflag & CSIZE) >> 4;
+	aflags = info->flags & ASYNC_SPD_MASK;
+
+	if (cflag & CRTSCTS)
+		info->flags |= ASYNC_CTS_FLOW;
+	else
+		info->flags &= ~ASYNC_CTS_FLOW;
+	if (cflag & CLOCAL)
+		info->flags &= ~ASYNC_CHECK_CD;
+	else
+		info->flags |= ASYNC_CHECK_CD;
+
+#if DEBUG & DEBUG_SPEED
+	printk( "SCC channel %d: doing new settings:\n", CHANNR(info) );
+	printk( "  baud=%d chsize=%d aflags=%04x base_baud=%d divisor=%d\n",
+			baud, chsize, aflags, info->baud_base, info->custom_divisor );
+#endif
+
+	if (baud == 0 && !aflags) {
+		/* speed == 0 -> drop DTR */
+		save_flags(flags);
+		cli();
+		SCCmod( TX_CTRL_REG, ~TCR_DTR, 0 );
+		restore_flags(flags);
+		return;
+	}
+
+	if (baud & CBAUDEX) {
+		baud &= ~CBAUDEX;
+		if (baud < 1 || baud > (MACH_IS_MVME16x ? 2 : 4))
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			baud += 15;
+	}
+	if (baud == 15 && aflags) {
+		switch( aflags) {
+		  case ASYNC_SPD_HI:
+			baud = 16;
+			break;
+		  case ASYNC_SPD_VHI:
+			baud = 17;
+			break;
+		  case ASYNC_SPD_SHI:
+			baud = 18;
+			break;
+		  case ASYNC_SPD_WARP:
+			baud = 19;
+			break;
+		  case ASYNC_SPD_CUST:
+			/* Custom divisor: Compute clock source from the base_baud
+			 * field */
+			if ((clksrc = SCC_clocksrc( info->baud_base, channel )) < 0)
+				/* This shouldn't happen... the baud_base has been checked
+				 * before by check_custom_divisor() */
+				return;
+			div = info->custom_divisor;
+		}
+	}
+
+	if (!div) {
+		if (baud > 19) baud = 19;
+		clksrc = SCC_baud_table[channel][baud].clksrc;
+		div = SCC_baud_table[channel][baud].div;
+		if(!div)
+		{
+			printk(" SCC_change_speed: divisor = 0 !!!");
+			return;
+		}
+	}
+
+	/* compute the SCC's clock source, clock mode, BRG mode and BRG
+	 * value from clksrc and div
+	 */
+	if (div <= 4) {
+		clkmode = (div == 1 ? A1CR_CLKMODE_x16 :
+			   div == 2 ? A1CR_CLKMODE_x32 :
+				      A1CR_CLKMODE_x64);
+		clksrc  = (clksrc == CLK_RTxC
+			   ? CCR_TXCLK_RTxC | CCR_RXCLK_RTxC
+			   : CCR_TXCLK_TRxC | CCR_RXCLK_TRxC);
+		brgmode = 0; /* off */
+		brgval  = 0;
+	}
+	else {
+		brgval  = div/2 - 2;
+		brgmode = (DCR_BRG_ENAB |
+			   (clksrc == CLK_PCLK ? DCR_BRG_USE_PCLK : 0));
+		clkmode = A1CR_CLKMODE_x16;
+		clksrc  = CCR_TXCLK_BRG | CCR_RXCLK_BRG;
+	}
+
+	/* Now we have all parameters and can go to set them: */
+	save_flags(flags);
+	cli();
+#if DEBUG & DEBUG_SPEED
+	printk( "  brgval=%d brgmode=%02x clkmode=%02x clksrc=%02x\n",
+			brgval, brgmode, clkmode, clksrc );
+#endif
+
+	/* receiver's character size */
+	SCCmod( RX_CTRL_REG, ~RCR_CHSIZE_MASK, chsize_map[chsize] << 6 );
+#if DEBUG & DEBUG_SPEED
+	printk( "  RX_CTRL_REG <- %02x\n", SCCread( RX_CTRL_REG ) );
+#endif
+
+	/* parity and stop bits (both, Tx and Rx) and clock mode */
+	SCCmod (AUX1_CTRL_REG,
+		~(A1CR_PARITY_MASK | A1CR_MODE_MASK | A1CR_CLKMODE_MASK),
+		((cflag & PARENB
+		  ? (cflag & PARODD ? A1CR_PARITY_ODD : A1CR_PARITY_EVEN)
+		  : A1CR_PARITY_NONE)
+		 | (cflag & CSTOPB ? A1CR_MODE_ASYNC_2 : A1CR_MODE_ASYNC_1)
+		 | clkmode));
+#if DEBUG & DEBUG_SPEED
+	printk( "  AUX1_CTRL_REG <- %02x\n", SCCread( AUX1_CTRL_REG ) );
+#endif
+
+	/* sender's character size */
+	/* Set DTR for valid baud rates! Tnx to jds@kom.auc.dk */
+	SCCmod( TX_CTRL_REG, ~TCR_CHSIZE_MASK, chsize_map[chsize] << 5 | TCR_DTR );
+#if DEBUG & DEBUG_SPEED
+	printk( "  TX_CTRL_REG <- %02x\n", SCCread( TX_CTRL_REG ) );
+#endif
+
+	/* clock sources */
+	SCCmod( CLK_CTRL_REG, ~(CCR_TXCLK_MASK | CCR_RXCLK_MASK), clksrc );
+#if DEBUG & DEBUG_SPEED
+	printk( "  CLK_CTRL_REG <- %02x\n", SCCread( CLK_CTRL_REG ) );
+#endif
+
+	/* disable BRG before changing the value */
+	SCCmod( DPLL_CTRL_REG, ~DCR_BRG_ENAB, 0 );
+
+	/* BRG value */
+	SCCwrite( TIMER_LOW_REG, brgval & 0xff );
+	SCCwrite( TIMER_HIGH_REG, (brgval >> 8) & 0xff );
+
+	/* BRG enable and clock source */
+	SCCmod( DPLL_CTRL_REG, ~(DCR_BRG_ENAB | DCR_BRG_USE_PCLK), brgmode );
+#if DEBUG & DEBUG_SPEED
+	printk( "  TIMER_LOW_REG <- %02x\n", SCCread( TIMER_LOW_REG ) );
+	printk( "  TIMER_HIGH_REG <- %02x\n", SCCread( TIMER_HIGH_REG ) );
+#endif
+#if DEBUG & DEBUG_SPEED
+	printk( "  DPLL_CTRL_REG <- %02x\n", SCCread( DPLL_CTRL_REG ) );
+#endif
+
+	restore_flags(flags);
+}
+
+
+static int SCC_clocksrc( unsigned baud_base, unsigned channel )
+{
+	if (baud_base == SCC_PCLK)
+		return( CLK_PCLK );
+	else if (SCC_clocks[channel][CLK_RTxC] != SCC_BAUD_BASE_NONE &&
+		 baud_base == SCC_clocks[channel][CLK_RTxC])
+		return( CLK_RTxC );
+	else if (SCC_clocks[channel][CLK_TRxC] != SCC_BAUD_BASE_NONE &&
+		 baud_base == SCC_clocks[channel][CLK_TRxC])
+		return( CLK_TRxC );
+	else
+		return( -1 );
+}
+
+static void SCC_throttle( struct m68k_async_struct *info, int status )
+{
+	unsigned long	flags;
+	SCC_ACCESS_INIT(info);
+
+#if DEBUG & DEBUG_THROTTLE
+	printk( "SCC channel %d: throttle %s\n",
+	       CHANNR(info), status ? "full" : "avail" );
+#endif
+	save_flags(flags);
+	cli();
+
+	if (status)
+		SCCmod( TX_CTRL_REG, ~TCR_RTS, 0 );
+	else
+		SCCmod( TX_CTRL_REG, 0xff, TCR_RTS );
+
+#if DEBUG & DEBUG_THROTTLE
+	printk( "  now TX_CTRL_REG = %02x\n", SCCread( TX_CTRL_REG ) );
+#endif
+
+	restore_flags(flags);
+}
+
+
+static void SCC_set_break( struct m68k_async_struct *info, int break_flag )
+{
+	unsigned long	flags;
+	SCC_ACCESS_INIT(info);
+
+	save_flags(flags);
+	cli();
+
+	if (break_flag) {
+		SCCmod( TX_CTRL_REG, 0xff, TCR_SEND_BREAK );
+	} else {
+		SCCmod( TX_CTRL_REG, ~TCR_SEND_BREAK, 0 );
+	}
+
+	restore_flags(flags);
+}
+
+
+static void SCC_get_serial_info( struct m68k_async_struct *info,
+				struct serial_struct *retinfo )
+{
+	retinfo->baud_base = info->baud_base;
+	retinfo->custom_divisor = info->custom_divisor;
+}
+
+
+static unsigned int SCC_get_modem_info( struct m68k_async_struct *info )
+{
+	unsigned	sr, tcr, ri = 0, dsr = 0;
+	unsigned long	flags;
+	SCC_ACCESS_INIT(info);
+	
+	save_flags(flags);
+	cli();
+	sr = SCCread( STATUS_REG );
+	tcr = SCCread( TX_CTRL_REG );
+	restore_flags(flags);
+#if DEBUG & DEBUG_INFO
+	printk( "SCC channel %d: get info, sr=%02x tcr=%02x\n",
+			CHANNR(info), sr, tcr );
+#endif
+#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_BVME6000_SCC) || defined(CONFIG_MVME147_SCC)
+	if (MACH_IS_MVME147 || MACH_IS_MVME16x || MACH_IS_BVME6000) {
+		ri = 0;
+		dsr = sr & SR_SYNC_ABORT ? TIOCM_DSR : 0;
+	}
+#endif
+#ifdef ENABLE_ATARI_SCC
+	if (MACH_IS_ATARI) {
+		if (CHANNR (info) == 0)
+			ri = 0;
+		else if (ATARIHW_PRESENT (TT_MFP))
+			ri = tt_mfp.par_dt_reg & (1 << 3) ? 0 : TIOCM_RNG;
+		else
+			ri = mfp.par_dt_reg & (1 << 6) ? 0 : TIOCM_RNG;
+
+		if (ATARIHW_PRESENT (ST_ESCC))
+			dsr = st_escc_dsr & (1 << (3 - CHANNR(info))) ? TIOCM_DSR : 0;
+		else
+			dsr = sr & SR_SYNC_ABORT ? TIOCM_DSR : 0;
+	}
+#endif
+	return (((tcr & TCR_RTS) ? TIOCM_RTS : 0) |
+		((tcr & TCR_DTR) ? TIOCM_DTR : 0) |
+		((sr & SR_DCD ) ? TIOCM_CAR : 0) |
+		((sr & SR_CTS ) ? TIOCM_CTS : 0) |
+		dsr | ri);
+}
+
+
+static int SCC_set_modem_info( struct m68k_async_struct *info,
+			      int new_dtr, int new_rts )
+{
+	unsigned long	flags;
+	SCC_ACCESS_INIT(info);
+	
+	save_flags(flags);
+	cli();
+
+	if (new_dtr == 0) {
+		SCCmod( TX_CTRL_REG, ~TCR_DTR, 0 );
+	} else if (new_dtr == 1) {
+		SCCmod( TX_CTRL_REG, 0xff, TCR_DTR );
+	}
+
+	if (new_rts == 0) {
+		SCCmod( TX_CTRL_REG, ~TCR_RTS, 0 );
+	} else if (new_rts == 1) {
+		SCCmod( TX_CTRL_REG, 0xff, TCR_RTS );
+	}
+
+#if DEBUG & DEBUG_INFO
+	printk( "SCC channel %d: set info (dtr=%d,rts=%d), now tcr=%02x\n",
+	       CHANNR(info), new_dtr, new_rts, SCCread( TX_CTRL_REG ) );
+#endif
+
+	restore_flags(flags);
+	return( 0 );
+}
+
+static void SCC_stop_receive (struct m68k_async_struct *info)
+{
+	SCC_ACCESS_INIT(info);
+	
+#ifdef CONFIG_ATARI_SCC_DMA
+	dma_stop ();
+#endif
+
+	/* disable Rx interrupts */
+	SCCmod (INT_AND_DMA_REG, ~IDR_RX_INT_MASK, 0);
+
+	/* disable Rx */
+	if (!((MACH_IS_MVME16x || MACH_IS_BVME6000) && CHANNR(info) == CHANNEL_A))
+		SCCmod (RX_CTRL_REG, ~RCR_RX_ENAB, 0);
+}
+
+static int SCC_trans_empty (struct m68k_async_struct *info)
+{
+	SCC_ACCESS_INIT(info);
+	
+	return (SCCread (SPCOND_STATUS_REG) & SCSR_ALL_SENT) != 0;
+}
+
+static int SCC_ioctl( struct tty_struct *tty, struct file *file,
+		     struct m68k_async_struct *info, unsigned int cmd,
+		     unsigned long arg )
+{
+	struct atari_SCCserial *cp = (void *)arg;
+	int error;
+	unsigned channel = CHANNR(info), i, clk, div, rtxc, trxc, pclk;
+
+	switch( cmd ) {
+
+	  case TIOCGATSCC:
+
+		error = verify_area( VERIFY_WRITE, (void *)arg,
+				    sizeof(struct atari_SCCserial) );
+		if (error)
+			return error;
+
+		put_user(SCC_clocks[channel][CLK_RTxC], &cp->RTxC_base);
+		put_user(SCC_clocks[channel][CLK_TRxC], &cp->TRxC_base);
+		put_user(SCC_PCLK, &cp->PCLK_base);
+		copy_to_user(cp->baud_table, SCC_baud_table[channel] + 1,
+			     sizeof(cp->baud_table));
+
+		return( 0 );
+
+	  case TIOCSATSCC:
+
+		if (!suser()) return( -EPERM );
+
+		error = verify_area(VERIFY_READ, (void *)arg,
+				    sizeof(struct atari_SCCserial) );
+		if (error)
+			return error;
+
+		get_user(rtxc, &cp->RTxC_base);
+		get_user(trxc, &cp->TRxC_base);
+		get_user(pclk, &cp->PCLK_base);
+
+		if (pclk == SCC_BAUD_BASE_NONE)
+			/* This is really not possible :-) */
+			return( -EINVAL );
+
+		/* Check the baud table for consistency */
+		for( i = 0; i < sizeof(cp->baud_table)/sizeof(cp->baud_table[0]); ++i ) {
+
+			get_user(clk, &cp->baud_table[i].clksrc);
+			get_user(div, &cp->baud_table[i].divisor);
+
+			switch( clk ) {
+			  case CLK_RTxC:
+				if (rtxc == SCC_BAUD_BASE_NONE)
+					return( -EINVAL );
+				if (((div & 1) && div != 1) ||
+				    (div >= 4 && div/2-2 > 65535))
+					return( -EINVAL );
+				break;
+			  case CLK_TRxC:
+				if (trxc == SCC_BAUD_BASE_NONE)
+					return( -EINVAL );
+				if (div != 1 && div != 2 && div != 4)
+					return( -EINVAL );
+				break;
+			  case CLK_PCLK:
+				if (div < 4 || (div & 1) || div/2-2 > 65535)
+					return( -EINVAL );
+				break;
+			  default:
+				/* invalid valid clock source */
+				return( -EINVAL );
+			}
+		}
+
+		/* After all the checks, set the values */
+
+		SCC_clocks[channel][CLK_RTxC] = rtxc;
+		SCC_clocks[channel][CLK_TRxC] = trxc;
+		SCC_PCLK = pclk;
+
+		copy_from_user(bdtab_usr[channel] + 1, cp->baud_table,
+			       sizeof(cp->baud_table));
+		/* Now use the user supplied baud table */
+		SCC_baud_table[channel] = bdtab_usr[channel];
+
+		return( 0 );
+
+	  case TIOCDATSCC:
+
+		if (!suser()) return( -EPERM );
+#ifdef ENABLE_ATARI_SCC
+		if (!MACH_IS_ATARI)
+			return 0; /* XXX */
+
+		if (ATARIHW_PRESENT(TT_MFP)) {
+			SCC_clocks[channel][CLK_RTxC] =
+				(channel == CHANNEL_A) ?
+					SCC_BAUD_BASE_PCLK4 :
+					SCC_BAUD_BASE_TIMC;
+			SCC_clocks[channel][CLK_TRxC] =
+				(channel == CHANNEL_A) ?
+					SCC_BAUD_BASE_NONE :
+					SCC_BAUD_BASE_BCLK;
+		}
+		else {
+			SCC_clocks[channel][CLK_RTxC] = SCC_BAUD_BASE_PCLK4;
+			SCC_clocks[channel][CLK_TRxC] =
+				(channel == CHANNEL_A) ?
+					SCC_BAUD_BASE_NONE :
+					SCC_BAUD_BASE_BCLK;
+		}
+
+		SCC_PCLK = SCC_BAUD_BASE_PCLK;
+		SCC_baud_table[channel] =
+			((ATARIHW_PRESENT(TT_MFP) && channel == 1) ?
+			 bdtab_TTChB : bdtab_norm);
+#endif
+		return( 0 );
+
+	}
+	return( -ENOIOCTLCMD );
+}
+
+
+
+
+#ifdef MODULE
+int init_module(void)
+{
+#ifdef ENABLE_ATARI_SCC
+	if (MACH_IS_ATARI)
+		return atari_SCC_init();
+#endif
+	return -ENODEV;
+}
+
+void cleanup_module(void)
+{
+	if (chb_line >= 0) {
+		SCC_deinit_port( &rs_table[chb_line], CHANNEL_B );
+		unregister_serial( chb_line );
+	}
+
+    /* ++Juergen Starek: use proper structure to deinitialize port
+     *                   because atari_free_irq relies on the valid
+     *                   `dev_id` parameter!
+     *			  If we use only the cha232_line, unloading a 
+     *			  module causes a damaged irq list!
+     */
+	/* We must deinit channel A only once! ++Andreas. */
+	if (cha232_line >= 0)
+		SCC_deinit_port(&rs_table[cha232_line], CHANNEL_A);
+	else if (cha422_line >= 0)
+		SCC_deinit_port(&rs_table[cha422_line], CHANNEL_A);
+
+	if (cha232_line >= 0)
+		unregister_serial( cha232_line );
+	if (cha422_line >= 0)
+		unregister_serial( cha422_line );
+}
+#endif
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  tab-width: 4
+ * End:
+ */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)