scc.h 16.4 KB
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 st_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 */