summaryrefslogtreecommitdiffstats
path: root/include/asm-m68k/serial.h
blob: 607b408ec790b9f093c8f70f03cc1e149ca22f0f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
/*
 * include/linux/serial.h
 *
 * Copyright (C) 1992 by Theodore Ts'o.
 * 
 * Redistribution of this file is permitted under the terms of the GNU 
 * Public License (GPL)
 */

#ifndef _M68K_SERIAL_H
#define _M68K_SERIAL_H


/* m68k serial port types are numbered from 100 to avoid interference
 * with the PC types (1..4)
 */
#define PORT_UNKNOWN	0
#define PORT_8250	1
#define PORT_16450	2
#define PORT_16550	3
#define PORT_16550A	4
#define PORT_CIRRUS     5
#define PORT_16650V2	7
#define PORT_16750	8

#define SER_SCC_NORM	100	/* standard SCC channel */
#define	SER_SCC_DMA	101	/* SCC channel with DMA support */
#define	SER_MFP_CTRL	102	/* standard MFP port with modem control signals */
#define	SER_MFP_BARE	103	/* MFP port without modem controls */
#define	SER_MIDI	104	/* Atari MIDI */
#define	SER_AMIGA	105	/* Amiga built-in serial port */
#define SER_IOEXT	106	/* Amiga GVP IO-Extender (16c552) */
#define SER_MFC_III	107	/* Amiga BSC Multiface Card III (MC68681) */
#define SER_WHIPPET	108	/* Amiga Hisoft Whippet PCMCIA (16c550B) */

struct serial_struct {
	int	type;
	int	line;
	int	port;
	int	irq;
	int	flags;
	int	xmit_fifo_size;
	int	custom_divisor;
	int	baud_base;
	unsigned short	close_delay;
	char	reserved_char[2];
	int	hub6;
	unsigned short	closing_wait; /* time to wait before closing */
	unsigned short	closing_wait2; /* no longer used... */
	int	reserved[4];
};

/*
 * For the close wait times, 0 means wait forever for serial port to
 * flush its output.  65535 means don't wait at all.
 */
#define ASYNC_CLOSING_WAIT_INF	0
#define ASYNC_CLOSING_WAIT_NONE	65535

/* This function tables does the abstraction from the underlying
 * hardware:
 *
 *   init(): Initialize the port as necessary, set RTS and DTR and
 *      enable interrupts. It does not need to set the speed and other
 *      parameters, because change_speed() is called, too.
 *   deinit(): Stop and shutdown the port (e.g. disable interrupts, ...)
 *   enab_tx_int(): Enable or disable the Tx Buffer Empty interrupt
 *      independently from other interrupt sources. If the int is
 *      enabled, the transmitter should also be restarted, i.e. if there
 *      are any chars to be sent, they should be put into the Tx
 *      register. The real en/disabling of the interrupt may be a no-op
 *      if there is no way to do this or it is too complex. This Tx ints
 *      are just disabled to save some interrupts if the transmitter is
 *      stopped anyway. But the restarting must be implemented!
 *   check_custom_divisor(): Check the given custom divisor for legality
 *      and return 0 if OK, non-zero otherwise.
 *   change_speed(): Set port speed, character size, number of stop
 *      bits and parity from the termios structure. If the user wants
 *      to set the speed with a custom divisor, he is required to
 *      check the baud_base first!
 *   throttle(): Set or clear the RTS line according to 'status'.
 *   set_break(): Set or clear the 'Send a Break' flag.
 *   get_serial_info(): Fill in the baud_base and custom_divisor
 *      fields of a serial_struct. It may also modify other fields, if
 *      needed.
 *   get_modem_info(): Return the status of RTS, DTR, DCD, RI, DSR and CTS.
 *   set_modem_info(): Set the status of RTS and DTR according to
 *      'new_dtr' and 'new_rts', resp. 0 = clear, 1 = set, -1 = don't change
 *   ioctl(): Process any port-specific ioctl's. This pointer may be
 *      NULL, if the port has no own ioctl's.
 *   stop_receive(): Turn off the Rx part of the port, so no more characters
 *      will be received. This is called before shutting the port down.
 *   trans_empty(): Return !=0 if there are no more characters still to be
 *      sent out (Tx buffer register and FIFOs empty)
 *   check_open(): Is called before the port is opened. The driver can check
 *      if that's ok and return an error code, or keep track of the opening
 *      even before init() is called. Use deinit() for matching closing of the
 *      port.
 *
 */

struct async_struct;

typedef struct {
	void (*init)( struct async_struct *info );
	void (*deinit)( struct async_struct *info, int leave_dtr );
	void (*enab_tx_int)( struct async_struct *info, int enab_flag );
	int  (*check_custom_divisor)( struct async_struct *info, int baud_base,
				     int divisor );
	void (*change_speed)( struct async_struct *info );
	void (*throttle)( struct async_struct *info, int status );
	void (*set_break)( struct async_struct *info, int break_flag );
	void (*get_serial_info)( struct async_struct *info,
				struct serial_struct *retinfo );
	unsigned int (*get_modem_info)( struct async_struct *info );
	int  (*set_modem_info)( struct async_struct *info, int new_dtr,
			       int new_rts );
	int  (*ioctl)( struct tty_struct *tty, struct file *file,
		      struct async_struct *info, unsigned int cmd,
		      unsigned long arg );
	void (*stop_receive)( struct async_struct *info );
	int  (*trans_empty)( struct async_struct *info );
	int  (*check_open)( struct async_struct *info, struct tty_struct *tty,
			   struct file *file );
} SERIALSWITCH;

/*
 * Definitions for async_struct (and serial_struct) flags field
 */
#define ASYNC_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes 
				   on the callout port */
#define ASYNC_FOURPORT  0x0002	/* Set OU1, OUT2 per AST Fourport settings */
#define ASYNC_SAK	0x0004	/* Secure Attention Key (Orange book) */
#define ASYNC_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */

#define ASYNC_SPD_MASK	0x1030
#define ASYNC_SPD_HI	0x0010	/* Use 56000 instead of 38400 bps */

#define ASYNC_SPD_VHI	0x0020  /* Use 115200 instead of 38400 bps */
#define ASYNC_SPD_CUST	0x0030  /* Use user-specified divisor */

#define ASYNC_SKIP_TEST	0x0040 /* Skip UART test during autoconfiguration */
#define ASYNC_AUTO_IRQ  0x0080 /* Do automatic IRQ during autoconfiguration */
#define ASYNC_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
#define ASYNC_PGRP_LOCKOUT    0x0200 /* Lock out cua opens based on pgrp */
#define ASYNC_CALLOUT_NOHUP   0x0400 /* Don't do hangups for cua device */

#define ASYNC_HARDPPS_CD	0x0800	/* Call hardpps when CD goes high  */

#define ASYNC_SPD_SHI	0x1000	/* Use 230400 instead of 38400 bps */
#define ASYNC_SPD_WARP	0x1010	/* Use 460800 instead of 38400 bps */

#define ASYNC_FLAGS	0x1FFF	/* Possible legal async flags */
#define ASYNC_USR_MASK 0x1430	/* Legal flags that non-privileged
				 * users can set or reset */

/* Internal flags used only by drivers/char/m68kserial.c */
#define ASYNC_INITIALIZED	0x80000000 /* Serial port was initialized */
#define ASYNC_CALLOUT_ACTIVE	0x40000000 /* Call out device is active */
#define ASYNC_NORMAL_ACTIVE	0x20000000 /* Normal device is active */
#define ASYNC_BOOT_AUTOCONF	0x10000000 /* Autoconfigure port on bootup */
#define ASYNC_CLOSING		0x08000000 /* Serial port is closing */
#define ASYNC_CTS_FLOW		0x04000000 /* Do CTS flow control */
#define ASYNC_CHECK_CD		0x02000000 /* i.e., CLOCAL */

#define ASYNC_INTERNAL_FLAGS	0xFF000000 /* Internal flags */

/*
 * Serial input interrupt line counters -- external structure
 * Four lines can interrupt: CTS, DSR, RI, DCD
 */
struct serial_icounter_struct {
	int cts, dsr, rng, dcd;
	int rx, tx;
	int frame, overrun, parity, brk;
	int buf_overrun;
	int reserved[9];
};


#ifdef __KERNEL__
/*
 * This is our internal structure for each serial port's state.
 * 
 * Many fields are paralleled by the structure used by the serial_struct
 * structure.
 *
 * For definitions of the flags field, see tty.h
 */

#include <linux/termios.h>
#include <linux/tqueue.h>

/*
 * Counters of the input lines (CTS, DSR, RI, CD) interrupts
 */
struct async_icount {
	__u32	cts, dsr, rng, dcd, tx, rx;
	__u32	frame, parity, overrun, brk;
	__u32	buf_overrun;
};

struct async_struct {
	int			magic;
	int			baud_base;
	int			port;
	int			irq;
	int			flags; 		/* defined in tty.h */
	int			hub6;		/* HUB6 plus one */
	int			type;
	struct tty_struct 	*tty;
	int			read_status_mask;
	int			ignore_status_mask;
	int			timeout;
	int			xmit_fifo_size;
	int			custom_divisor;
	int			x_char;	/* xon/xoff character */
	int			close_delay;
	unsigned short		closing_wait;
	unsigned short		closing_wait2;
	int			IER; 	/* Interrupt Enable Register */
	int			MCR; 	/* Modem control register */
	int			MCR_noint; /* MCR with interrupts off */
	unsigned long		event;
	unsigned long		last_active;
	int			line;
	int			count;	    /* # of fd on device */
	int			blocked_open; /* # of blocked opens */
	long			session; /* Session of opening process */
	long			pgrp; /* pgrp of opening process */
	unsigned char 		*xmit_buf;
	int			xmit_head;
	int			xmit_tail;
	int			xmit_cnt;
	struct tq_struct	tqueue;
	struct tq_struct	tqueue_hangup;
	struct termios		normal_termios;
	struct termios		callout_termios;
	struct wait_queue	*open_wait;
	struct wait_queue	*close_wait;
	struct wait_queue	*delta_msr_wait;
	struct async_icount	icount;	/* kernel counters for the 4 input interrupts */
	struct async_struct	*next_port; /* For the linked list */
	struct async_struct	*prev_port;
	void			*board_base; /* board-base address for use with
						boards carrying several UART's,
						like some Amiga boards. */
	unsigned short		nr_uarts;    /* UART-counter, that indicates
						how many UART's there are on
						the board.  If the board has a
						IRQ-register, this can be used
						to check if any of the uarts,
						on the board has requested an
						interrupt, instead of checking
						IRQ-registers on all UART's */
	SERIALSWITCH		*sw;		/* functions to manage this port */
};

#define SERIAL_MAGIC 0x5301

/*
 * The size of the serial xmit buffer is 1 page, or 4096 bytes
 */
#define SERIAL_XMIT_SIZE 4096

/*
 * Events are used to schedule things to happen at timer-interrupt
 * time, instead of at rs interrupt time.
 */
#define RS_EVENT_WRITE_WAKEUP	0

/* number of characters left in xmit buffer before we ask for more */
#define WAKEUP_CHARS 256

/* Export to allow PCMCIA to use this - Dave Hinds */
extern int register_serial(struct serial_struct *req);
extern void unregister_serial(int line);
extern struct async_struct rs_table[];
extern task_queue tq_serial;


/*
 * This routine is used by the interrupt handler to schedule
 * processing in the software interrupt portion of the driver.
 */
static __inline__ void rs_sched_event(struct async_struct *info, int event)
{
	info->event |= 1 << event;
	queue_task(&info->tqueue, &tq_serial);
	mark_bh(SERIAL_BH);
}

static __inline__ void rs_receive_char( struct async_struct *info,
					    int ch, int err )
{
	struct tty_struct *tty = info->tty;
	
	if (tty->flip.count >= TTY_FLIPBUF_SIZE)
		return;
	tty->flip.count++;
	if (err == TTY_BREAK) {
		if (info->flags & ASYNC_SAK)
			do_SAK(tty);
	}
	*tty->flip.flag_buf_ptr++ = err;
	*tty->flip.char_buf_ptr++ = ch;
	info->icount.rx++;
	queue_task(&tty->flip.tqueue, &tq_timer);
}

static __inline__ int rs_get_tx_char( struct async_struct *info )
{
	unsigned char ch;
	
	if (info->x_char) {
		ch = info->x_char;
		info->icount.tx++;
		info->x_char = 0;
		return( ch );
	}

	if (info->xmit_cnt <= 0 || info->tty->stopped || info->tty->hw_stopped)
		return( -1 );

	ch = info->xmit_buf[info->xmit_tail++];
	info->xmit_tail &= SERIAL_XMIT_SIZE - 1;
	info->icount.tx++;
	if (--info->xmit_cnt < WAKEUP_CHARS)
		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
	return( ch );
}

static __inline__ int rs_no_more_tx( struct async_struct *info )
{
	return( info->xmit_cnt <= 0 ||
			info->tty->stopped ||
			info->tty->hw_stopped );
}

static __inline__ void rs_dcd_changed( struct async_struct *info, int dcd )

{
	/* update input line counter */
	info->icount.dcd++;
	wake_up_interruptible(&info->delta_msr_wait);

	if (info->flags & ASYNC_CHECK_CD) {
#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
		printk("ttyS%d CD now %s...", info->line,
		       dcd ? "on" : "off");
#endif		
		if (dcd) {
			wake_up_interruptible(&info->open_wait);
		} else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
			     (info->flags & ASYNC_CALLOUT_NOHUP))) {
#ifdef SERIAL_DEBUG_OPEN
			printk("scheduling hangup...");
#endif
			queue_task(&info->tqueue_hangup, &tq_scheduler);
		}
	}
}


void rs_stop( struct tty_struct *tty );
void rs_start( struct tty_struct *tty );

static __inline__ void rs_check_cts( struct async_struct *info, int cts )
{
	/* update input line counter */
	info->icount.cts++;
	wake_up_interruptible(&info->delta_msr_wait);
	
	if ((info->flags & ASYNC_CTS_FLOW) && info->tty)
		if (info->tty->hw_stopped) {
			if (cts) {
#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
				printk("CTS tx start...");
#endif
				info->tty->hw_stopped = 0;
				rs_start( info->tty );
				rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
				return;
			}
		} else {
			if (!cts) {
				info->tty->hw_stopped = 1;
				rs_stop( info->tty );
			}
		}
		
}


#endif /* __KERNEL__ */

#endif /* _M68K_SERIAL_H */