aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/scc.h
blob: 93998f5baff576d9a68dcbd4077aa1ad37be1c95 (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
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
/*
 * atari_SCC.h: Definitions for the Am8530 Serial Communications Controller
 *
 * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
 *
 * 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.
 *
 */


#ifndef _SCC_H
#define _SCC_H

#include <linux/delay.h>

/* Special configuration ioctls for the Atari SCC5380 Serial
 * Communications Controller
 */

/* ioctl command codes */

#define TIOCGATSCC	0x54c0	/* get SCC configuration */
#define TIOCSATSCC	0x54c1	/* set SCC configuration */
#define TIOCDATSCC	0x54c2	/* reset configuration to defaults */

/* Clock sources */

#define CLK_RTxC	0
#define CLK_TRxC	1
#define CLK_PCLK	2

/* baud_bases for the common clocks in the Atari. These are the real
 * frequencies divided by 16.
 */
   
#define SCC_BAUD_BASE_TIMC	19200	/* 0.3072 MHz from TT-MFP, Timer C */
#define SCC_BAUD_BASE_BCLK	153600	/* 2.4576 MHz */
#define SCC_BAUD_BASE_PCLK4	229500	/* 3.6720 MHz */
#define SCC_BAUD_BASE_PCLK	503374	/* 8.0539763 MHz */
#define SCC_BAUD_BASE_NONE	0	/* for not connected or unused
					 * clock sources */

/* The SCC clock configuration structure */

struct scc_clock_config {
	unsigned	RTxC_base;	/* base_baud of RTxC */
	unsigned	TRxC_base;	/* base_baud of TRxC */
	unsigned	PCLK_base;	/* base_baud of PCLK, both channels! */
	struct {
		unsigned clksrc;	/* CLK_RTxC, CLK_TRxC or CLK_PCLK */
		unsigned divisor;	/* divisor for base baud, valid values:
					 * see below */
	} baud_table[17];		/* For 50, 75, 110, 135, 150, 200, 300,
					 * 600, 1200, 1800, 2400, 4800, 9600,
					 * 19200, 38400, 57600 and 115200 bps.
					 * The last two could be replaced by
					 * other rates > 38400 if they're not
					 * possible.
					 */
};

/* The following divisors are valid:
 *
 *   - 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)
 *
 */

struct scc_port {
	struct gs_port		gs;
	volatile unsigned char	*ctrlp;
	volatile unsigned char	*datap;
	int			x_char;		/* xon/xoff character */
	int			c_dcd;
	int			channel;
	struct scc_port		*port_a;	/* Reference to port A and B */
	struct scc_port		*port_b;	/*   structs for reg access  */
};

#define SCC_MAGIC	0x52696368

/***********************************************************************/
/*                                                                     */
/*                             Register Names                          */
/*                                                                     */
/***********************************************************************/

/* The SCC documentation gives no explicit names to the registers,
 * they're just called WR0..15 and RR0..15. To make the source code
 * better readable and make the transparent write reg read access (see
 * below) possible, I christen them here with self-invented names.
 * Note that (real) read registers are assigned numbers 16..31. WR7'
 * has number 33.
 */

#define	COMMAND_REG		0	/* wo */
#define	INT_AND_DMA_REG		1	/* wo */
#define	INT_VECTOR_REG		2	/* rw, common to both channels */
#define	RX_CTRL_REG		3	/* rw */
#define	AUX1_CTRL_REG		4	/* rw */
#define	TX_CTRL_REG		5	/* rw */
#define	SYNC_ADR_REG		6	/* wo */
#define	SYNC_CHAR_REG		7	/* wo */
#define	SDLC_OPTION_REG		33	/* wo */
#define	TX_DATA_REG		8	/* wo */
#define	MASTER_INT_CTRL		9	/* wo, common to both channels */
#define	AUX2_CTRL_REG		10	/* rw */
#define	CLK_CTRL_REG		11	/* wo */
#define	TIMER_LOW_REG		12	/* rw */
#define	TIMER_HIGH_REG		13	/* rw */
#define	DPLL_CTRL_REG		14	/* wo */
#define	INT_CTRL_REG		15	/* rw */

#define	STATUS_REG		16	/* ro */
#define	SPCOND_STATUS_REG	17	/* wo */
/* RR2 is WR2 for Channel A, Channel B gives vector + current status: */
#define	CURR_VECTOR_REG		18	/* Ch. B only, Ch. A for rw */
#define	INT_PENDING_REG		19	/* Channel A only! */
/* RR4 is WR4, if b6(MR7') == 1 */
/* RR5 is WR5, if b6(MR7') == 1 */
#define	FS_FIFO_LOW_REG		22	/* ro */
#define	FS_FIFO_HIGH_REG	23	/* ro */
#define	RX_DATA_REG		24	/* ro */
/* RR9 is WR3, if b6(MR7') == 1 */
#define	DPLL_STATUS_REG		26	/* ro */
/* RR11 is WR10, if b6(MR7') == 1 */
/* RR12 is WR12 */
/* RR13 is WR13 */
/* RR14 not present */
/* RR15 is WR15 */


/***********************************************************************/
/*                                                                     */
/*                             Register Values                         */
/*                                                                     */
/***********************************************************************/


/* WR0: COMMAND_REG "CR" */

#define	CR_RX_CRC_RESET		0x40
#define	CR_TX_CRC_RESET		0x80
#define	CR_TX_UNDERRUN_RESET	0xc0

#define	CR_EXTSTAT_RESET	0x10
#define	CR_SEND_ABORT		0x18
#define	CR_ENAB_INT_NEXT_RX	0x20
#define	CR_TX_PENDING_RESET	0x28
#define	CR_ERROR_RESET		0x30
#define	CR_HIGHEST_IUS_RESET	0x38


/* WR1: INT_AND_DMA_REG "IDR" */

#define	IDR_EXTSTAT_INT_ENAB	0x01
#define	IDR_TX_INT_ENAB		0x02
#define	IDR_PARERR_AS_SPCOND	0x04

#define	IDR_RX_INT_DISAB	0x00
#define	IDR_RX_INT_FIRST	0x08
#define	IDR_RX_INT_ALL		0x10
#define	IDR_RX_INT_SPCOND	0x18
#define	IDR_RX_INT_MASK		0x18

#define	IDR_WAITREQ_RX		0x20
#define	IDR_WAITREQ_IS_REQ	0x40
#define	IDR_WAITREQ_ENAB	0x80


/* WR3: RX_CTRL_REG "RCR" */

#define	RCR_RX_ENAB		0x01
#define	RCR_DISCARD_SYNC_CHARS	0x02
#define	RCR_ADDR_SEARCH		0x04
#define	RCR_CRC_ENAB		0x08
#define	RCR_SEARCH_MODE		0x10
#define	RCR_AUTO_ENAB_MODE	0x20

#define	RCR_CHSIZE_MASK		0xc0
#define	RCR_CHSIZE_5		0x00
#define	RCR_CHSIZE_6		0x40
#define	RCR_CHSIZE_7		0x80
#define	RCR_CHSIZE_8		0xc0


/* WR4: AUX1_CTRL_REG "A1CR" */

#define	A1CR_PARITY_MASK	0x03
#define	A1CR_PARITY_NONE	0x00
#define	A1CR_PARITY_ODD		0x01
#define	A1CR_PARITY_EVEN	0x03

#define	A1CR_MODE_MASK		0x0c
#define	A1CR_MODE_SYNCR		0x00
#define	A1CR_MODE_ASYNC_1	0x04
#define	A1CR_MODE_ASYNC_15	0x08
#define	A1CR_MODE_ASYNC_2	0x0c

#define	A1CR_SYNCR_MODE_MASK	0x30
#define	A1CR_SYNCR_MONOSYNC	0x00
#define	A1CR_SYNCR_BISYNC	0x10
#define	A1CR_SYNCR_SDLC		0x20
#define	A1CR_SYNCR_EXTCSYNC	0x30

#define	A1CR_CLKMODE_MASK	0xc0
#define	A1CR_CLKMODE_x1		0x00
#define	A1CR_CLKMODE_x16	0x40
#define	A1CR_CLKMODE_x32	0x80
#define	A1CR_CLKMODE_x64	0xc0


/* WR5: TX_CTRL_REG "TCR" */

#define	TCR_TX_CRC_ENAB		0x01
#define	TCR_RTS			0x02
#define	TCR_USE_CRC_CCITT	0x00
#define	TCR_USE_CRC_16		0x04
#define	TCR_TX_ENAB		0x08
#define	TCR_SEND_BREAK		0x10

#define	TCR_CHSIZE_MASK		0x60
#define	TCR_CHSIZE_5		0x00
#define	TCR_CHSIZE_6		0x20
#define	TCR_CHSIZE_7		0x40
#define	TCR_CHSIZE_8		0x60

#define	TCR_DTR			0x80


/* WR7': SLDC_OPTION_REG "SOR" */

#define	SOR_AUTO_TX_ENAB	0x01
#define	SOR_AUTO_EOM_RESET	0x02
#define	SOR_AUTO_RTS_MODE	0x04
#define	SOR_NRZI_DISAB_HIGH	0x08
#define	SOR_ALT_DTRREQ_TIMING	0x10
#define	SOR_READ_CRC_CHARS	0x20
#define	SOR_EXTENDED_REG_ACCESS	0x40


/* WR9: MASTER_INT_CTRL "MIC" */

#define	MIC_VEC_INCL_STAT	0x01
#define	MIC_NO_VECTOR		0x02
#define	MIC_DISAB_LOWER_CHAIN	0x04
#define	MIC_MASTER_INT_ENAB	0x08
#define	MIC_STATUS_HIGH		0x10
#define	MIC_IGN_INTACK		0x20

#define	MIC_NO_RESET		0x00
#define	MIC_CH_A_RESET		0x40
#define	MIC_CH_B_RESET		0x80
#define	MIC_HARD_RESET		0xc0


/* WR10: AUX2_CTRL_REG "A2CR" */

#define	A2CR_SYNC_6		0x01
#define	A2CR_LOOP_MODE		0x02
#define	A2CR_ABORT_ON_UNDERRUN	0x04
#define	A2CR_MARK_IDLE		0x08
#define	A2CR_GO_ACTIVE_ON_POLL	0x10

#define	A2CR_CODING_MASK	0x60
#define	A2CR_CODING_NRZ		0x00
#define	A2CR_CODING_NRZI	0x20
#define	A2CR_CODING_FM1		0x40
#define	A2CR_CODING_FM0		0x60

#define	A2CR_PRESET_CRC_1	0x80


/* WR11: CLK_CTRL_REG "CCR" */

#define	CCR_TRxCOUT_MASK	0x03
#define	CCR_TRxCOUT_XTAL	0x00
#define	CCR_TRxCOUT_TXCLK	0x01
#define	CCR_TRxCOUT_BRG		0x02
#define	CCR_TRxCOUT_DPLL	0x03

#define	CCR_TRxC_OUTPUT		0x04

#define	CCR_TXCLK_MASK		0x18
#define	CCR_TXCLK_RTxC		0x00
#define	CCR_TXCLK_TRxC		0x08
#define	CCR_TXCLK_BRG		0x10
#define	CCR_TXCLK_DPLL		0x18

#define	CCR_RXCLK_MASK		0x60
#define	CCR_RXCLK_RTxC		0x00
#define	CCR_RXCLK_TRxC		0x20
#define	CCR_RXCLK_BRG		0x40
#define	CCR_RXCLK_DPLL		0x60

#define	CCR_RTxC_XTAL		0x80


/* WR14: DPLL_CTRL_REG "DCR" */

#define	DCR_BRG_ENAB		0x01
#define	DCR_BRG_USE_PCLK	0x02
#define	DCR_DTRREQ_IS_REQ	0x04
#define	DCR_AUTO_ECHO		0x08
#define	DCR_LOCAL_LOOPBACK	0x10

#define	DCR_DPLL_EDGE_SEARCH	0x20
#define	DCR_DPLL_ERR_RESET	0x40
#define	DCR_DPLL_DISAB		0x60
#define	DCR_DPLL_CLK_BRG	0x80
#define	DCR_DPLL_CLK_RTxC	0xa0
#define	DCR_DPLL_FM		0xc0
#define	DCR_DPLL_NRZI		0xe0


/* WR15: INT_CTRL_REG "ICR" */

#define	ICR_OPTIONREG_SELECT	0x01
#define	ICR_ENAB_BRG_ZERO_INT	0x02
#define	ICR_USE_FS_FIFO		0x04
#define	ICR_ENAB_DCD_INT	0x08
#define	ICR_ENAB_SYNC_INT	0x10
#define	ICR_ENAB_CTS_INT	0x20
#define	ICR_ENAB_UNDERRUN_INT	0x40
#define	ICR_ENAB_BREAK_INT	0x80


/* RR0: STATUS_REG "SR" */

#define	SR_CHAR_AVAIL		0x01
#define	SR_BRG_ZERO		0x02
#define	SR_TX_BUF_EMPTY		0x04
#define	SR_DCD			0x08
#define	SR_SYNC_ABORT		0x10
#define	SR_CTS			0x20
#define	SR_TX_UNDERRUN		0x40
#define	SR_BREAK		0x80


/* RR1: SPCOND_STATUS_REG "SCSR" */

#define	SCSR_ALL_SENT		0x01
#define	SCSR_RESIDUAL_MASK	0x0e
#define	SCSR_PARITY_ERR		0x10
#define	SCSR_RX_OVERRUN		0x20
#define	SCSR_CRC_FRAME_ERR	0x40
#define	SCSR_END_OF_FRAME	0x80


/* RR3: INT_PENDING_REG "IPR" */

#define	IPR_B_EXTSTAT		0x01
#define	IPR_B_TX		0x02
#define	IPR_B_RX		0x04
#define	IPR_A_EXTSTAT		0x08
#define	IPR_A_TX		0x10
#define	IPR_A_RX		0x20


/* RR7: FS_FIFO_HIGH_REG "FFHR" */

#define	FFHR_CNT_MASK		0x3f
#define	FFHR_IS_FROM_FIFO	0x40
#define	FFHR_FIFO_OVERRUN	0x80


/* RR10: DPLL_STATUS_REG "DSR" */

#define	DSR_ON_LOOP		0x02
#define	DSR_ON_LOOP_SENDING	0x10
#define	DSR_TWO_CLK_MISSING	0x40
#define	DSR_ONE_CLK_MISSING	0x80

/***********************************************************************/
/*                                                                     */
/*                             Register Access                         */
/*                                                                     */
/***********************************************************************/


/* The SCC needs 3.5 PCLK cycles recovery time between to register
 * accesses. PCLK runs with 8 MHz on an Atari, so this delay is 3.5 *
 * 125 ns = 437.5 ns. This is too short for udelay().
 * 10/16/95: A tstb mfp.par_dt_reg takes 600ns (sure?) and thus should be
 * quite right
 */

#define scc_reg_delay() \
    do {			\
	if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147)	\
		__asm__ __volatile__ ( " nop; nop");			\
	else if (MACH_IS_ATARI)						\
		__asm__ __volatile__ ( "tstb %0" : : "g" (*_scc_del) : "cc" );\
    } while (0)

static unsigned char scc_shadow[2][16];

/* The following functions should relax the somehow complicated
 * register access of the SCC. _SCCwrite() stores all written values
 * (except for WR0 and WR8) in shadow registers for later recall. This
 * removes the burden of remembering written values as needed. The
 * extra work of storing the value doesn't count, since a delay is
 * needed after a SCC access anyway. Additionally, _SCCwrite() manages
 * writes to WR0 and WR8 differently, because these can be accessed
 * directly with less overhead. Another special case are WR7 and WR7'.
 * _SCCwrite automatically checks what of this registers is selected
 * and changes b0 of WR15 if needed.
 * 
 * _SCCread() for standard read registers is straightforward, except
 * for RR2 (split into two "virtual" registers: one for the value
 * written to WR2 (from the shadow) and one for the vector including
 * status from RR2, Ch. B) and RR3. The latter must be read from
 * Channel A, because it reads as all zeros on Ch. B. RR0 and RR8 can
 * be accessed directly as before.
 * 
 * The two inline function contain complicated switch statements. But
 * I rely on regno and final_delay being constants, so gcc can reduce
 * the whole stuff to just some assembler statements.
 * 
 * _SCCwrite and _SCCread aren't intended to be used directly under
 * normal circumstances. The macros SCCread[_ND] and SCCwrite[_ND] are
 * for that purpose. They assume that a local variable 'port' is
 * declared and pointing to the port's scc_struct entry. The
 * variants with "_NB" appended should be used if no other SCC
 * accesses follow immediately (within 0.5 usecs). They just skip the
 * final delay nops.
 * 
 * Please note that accesses to SCC registers should only take place
 * when interrupts are turned off (at least if SCC interrupts are
 * enabled). Otherwise, an interrupt could interfere with the
 * two-stage accessing process.
 *
 */


static __inline__ void _SCCwrite(
	struct scc_port *port,
	unsigned char *shadow,
	volatile unsigned char *_scc_del,
	int regno,
	unsigned char val, int final_delay )
{
	switch( regno ) {

	  case COMMAND_REG:
		/* WR0 can be written directly without pointing */
		*port->ctrlp = val;
		break;

	  case SYNC_CHAR_REG:
		/* For WR7, first set b0 of WR15 to 0, if needed */
		if (shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT) {
			*port->ctrlp = 15;
			shadow[INT_CTRL_REG] &= ~ICR_OPTIONREG_SELECT;
			scc_reg_delay();
			*port->ctrlp = shadow[INT_CTRL_REG];
			scc_reg_delay();
		}
		goto normal_case;
		
	  case SDLC_OPTION_REG:
		/* For WR7', first set b0 of WR15 to 1, if needed */
		if (!(shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT)) {
			*port->ctrlp = 15;
			shadow[INT_CTRL_REG] |= ICR_OPTIONREG_SELECT;
			scc_reg_delay();
			*port->ctrlp = shadow[INT_CTRL_REG];
			scc_reg_delay();
		}
		*port->ctrlp = 7;
		shadow[8] = val;	/* WR7' shadowed at WR8 */
		scc_reg_delay();
		*port->ctrlp = val;
		break;

	  case TX_DATA_REG:		/* WR8 */
		/* TX_DATA_REG can be accessed directly on some h/w */
		if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147)
		{
			*port->ctrlp = regno;
			scc_reg_delay();
			*port->ctrlp = val;
		}
		else
			*port->datap = val;
		break;

	  case MASTER_INT_CTRL:
		*port->ctrlp = regno;
		val &= 0x3f;	/* bits 6..7 are the reset commands */
		scc_shadow[0][regno] = val;
		scc_reg_delay();
		*port->ctrlp = val;
		break;

	  case DPLL_CTRL_REG:
		*port->ctrlp = regno;
		val &= 0x1f;			/* bits 5..7 are the DPLL commands */
		shadow[regno] = val;
		scc_reg_delay();
		*port->ctrlp = val;
		break;

	  case 1 ... 6:	
	  case 10 ... 13:
	  case 15:
	  normal_case:
		*port->ctrlp = regno;
		shadow[regno] = val;
		scc_reg_delay();
		*port->ctrlp = val;
		break;
		
	  default:
		printk( "Bad SCC write access to WR%d\n", regno );
		break;
		
	}

	if (final_delay)
		scc_reg_delay();
}


static __inline__ unsigned char _SCCread(
	struct scc_port *port,
	unsigned char *shadow,
	volatile unsigned char *_scc_del,
	int regno, int final_delay )
{
	unsigned char rv;

	switch( regno ) {

		/* --- real read registers --- */
	  case STATUS_REG:
		rv = *port->ctrlp;
		break;

	  case INT_PENDING_REG:
		/* RR3: read only from Channel A! */
		port = port->port_a;
		goto normal_case;

	  case RX_DATA_REG:
		/* RR8 can be accessed directly on some h/w */
		if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147)
		{
			*port->ctrlp = 8;
			scc_reg_delay();
			rv = *port->ctrlp;
		}
		else
			rv = *port->datap;
		break;

	  case CURR_VECTOR_REG:
		/* RR2 (vector including status) from Ch. B */
		port = port->port_b;
		goto normal_case;
		
		/* --- reading write registers: access the shadow --- */
	  case 1 ... 7:
	  case 10 ... 15:
		return shadow[regno]; /* no final delay! */

		/* WR7' is special, because it is shadowed at the place of WR8 */
	  case SDLC_OPTION_REG:
		return shadow[8]; /* no final delay! */

		/* WR9 is special too, because it is common for both channels */
	  case MASTER_INT_CTRL:
		return scc_shadow[0][9]; /* no final delay! */

	  default:
		printk( "Bad SCC read access to %cR%d\n", (regno & 16) ? 'R' : 'W',
				regno & ~16 );
		break;
		
	  case SPCOND_STATUS_REG:
	  case FS_FIFO_LOW_REG:
	  case FS_FIFO_HIGH_REG:
	  case DPLL_STATUS_REG:
	  normal_case:
		*port->ctrlp = regno & 0x0f;
		scc_reg_delay();
		rv = *port->ctrlp;
		break;
		
	}

	if (final_delay)
		scc_reg_delay();
	return rv;
}

#define SCC_ACCESS_INIT(port)						\
	unsigned char *_scc_shadow = &scc_shadow[port->channel][0]

#define	SCCwrite(reg,val)	_SCCwrite(port,_scc_shadow,scc_del,(reg),(val),1)
#define	SCCwrite_NB(reg,val)	_SCCwrite(port,_scc_shadow,scc_del,(reg),(val),0)
#define	SCCread(reg)		_SCCread(port,_scc_shadow,scc_del,(reg),1)
#define	SCCread_NB(reg)		_SCCread(port,_scc_shadow,scc_del,(reg),0)

#define SCCmod(reg,and,or)	SCCwrite((reg),(SCCread(reg)&(and))|(or))

#endif /* _SCC_H */