1/* 2 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* 29 * @OSF_COPYRIGHT@ 30 */ 31/* 32 * @APPLE_FREE_COPYRIGHT@ 33 */ 34/* 35 * Mach Operating System 36 * Copyright (c) 1991,1990,1989 Carnegie Mellon University 37 * All Rights Reserved. 38 * 39 * Permission to use, copy, modify and distribute this software and its 40 * documentation is hereby granted, provided that both the copyright 41 * notice and this permission notice appear in all copies of the 42 * software, derivative works or modified versions, and any portions 43 * thereof, and that both notices appear in supporting documentation. 44 * 45 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 46 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 47 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 48 * 49 * Carnegie Mellon requests users of this software to return to 50 * 51 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 52 * School of Computer Science 53 * Carnegie Mellon University 54 * Pittsburgh PA 15213-3890 55 * 56 * any improvements or extensions that they make and grant Carnegie Mellon 57 * the rights to redistribute these changes. 58 */ 59/* 60 */ 61/* 62 * File: scc_8530_hdw.c 63 * Author: Alessandro Forin, Carnegie Mellon University 64 * Date: 6/91 65 * 66 * Hardware-level operations for the SCC Serial Line Driver 67 */ 68 69#define NSCC 1 /* Number of serial chips, two ports per chip. */ 70#if NSCC > 0 71 72#include <mach_kdb.h> 73#include <platforms.h> 74#include <kern/spl.h> 75#include <mach/std_types.h> 76#include <types.h> 77#include <sys/syslog.h> 78#include <kern/thread.h> 79#include <ppc/misc_protos.h> 80#include <ppc/proc_reg.h> 81#include <ppc/exception.h> 82#include <ppc/Firmware.h> 83#include <ppc/serial_io.h> 84#include <ppc/scc_8530.h> 85 86#if MACH_KDB 87#include <machine/db_machdep.h> 88#endif /* MACH_KDB */ 89 90#define kdebug_state() (1) 91#define delay(x) { volatile int _d_; for (_d_ = 0; _d_ < (10000*x); _d_++) ; } 92 93#define NSCC_LINE 2 /* 2 ttys per chip */ 94 95#define SCC_DMA_TRANSFERS 0 96 97struct scc_tty scc_tty[NSCC_LINE]; 98 99#define scc_tty_for(chan) (&scc_tty[chan]) 100/* #define scc_unit(dev_no) (dev_no) */ 101 102#define scc_dev_no(chan) ((chan)^0x01) 103#define scc_chan(dev_no) ((dev_no)^0x01) 104 105int serial_initted = 0; 106unsigned int scc_parm_done = 0; 107 108static struct scc_byte { 109 unsigned char reg; 110 unsigned char val; 111} scc_init_hw[] = { 112 113 {9, 0x80}, 114 {4, 0x44}, 115 {3, 0xC0}, 116 {5, 0xE2}, 117 {2, 0x00}, 118 {10, 0x00}, 119 {11, 0x50}, 120 {12, 0x0A}, 121 {13, 0x00}, 122 {3, 0xC1}, 123 {5, 0xEA}, 124 {14, 0x01}, 125 {15, 0x00}, 126 {0, 0x10}, 127 {0, 0x10}, 128#if 0 129 {1, 0x12}, /* int or Rx, Tx int enable */ 130#else 131 {1, 0x10}, /* int or Rx, no Tx int enable */ 132#endif 133 {9, 0x0A} 134}; 135 136static int scc_init_hw_count = sizeof(scc_init_hw)/sizeof(scc_init_hw[0]); 137 138enum scc_error {SCC_ERR_NONE, SCC_ERR_PARITY, SCC_ERR_BREAK, SCC_ERR_OVERRUN}; 139 140 141/* 142 * BRG formula is: 143 * ClockFrequency (115200 for Power Mac) 144 * BRGconstant = --------------------------- - 2 145 * BaudRate 146 */ 147 148#define SERIAL_CLOCK_FREQUENCY (115200*2) /* Power Mac value */ 149#define convert_baud_rate(rate) ((((SERIAL_CLOCK_FREQUENCY) + (rate)) / (2 * (rate))) - 2) 150 151#define DEFAULT_SPEED 57600 152#define DEFAULT_PORT0_SPEED 1200 153#define DEFAULT_FLAGS (TF_LITOUT|TF_ECHO) 154 155int scc_param(struct scc_tty *tp); 156 157 158struct scc_softc scc_softc[NSCC]; 159caddr_t scc_std[NSCC] = { (caddr_t) 0}; 160 161 162#define SCC_RR1_ERRS (SCC_RR1_FRAME_ERR|SCC_RR1_RX_OVERRUN|SCC_RR1_PARITY_ERR) 163#define SCC_RR3_ALL (SCC_RR3_RX_IP_A|SCC_RR3_TX_IP_A|SCC_RR3_EXT_IP_A|\ 164 SCC_RR3_RX_IP_B|SCC_RR3_TX_IP_B|SCC_RR3_EXT_IP_B) 165 166#define DEBUG_SCC 167#undef DEBUG_SCC 168 169#ifdef DEBUG_SCC 170static int total_chars, total_ints, total_overruns, total_errors, num_ints, max_chars; 171static int chars_received[8]; 172static int __SCC_STATS = 0; 173static int max_in_q = 0; 174static int max_out_q = 0; 175#endif 176 177DECL_FUNNEL(, scc_funnel) /* funnel to serialize the SCC driver */ 178boolean_t scc_funnel_initted = FALSE; 179#define SCC_FUNNEL scc_funnel 180#define SCC_FUNNEL_INITTED scc_funnel_initted 181 182 183/* 184 * Adapt/Probe/Attach functions 185 */ 186boolean_t scc_uses_modem_control = FALSE;/* patch this with adb */ 187decl_simple_lock_data(,scc_stomp) 188 189/* This is called VERY early on in the init and therefore has to have 190 * hardcoded addresses of the serial hardware control registers. The 191 * serial line may be needed for console and debugging output before 192 * anything else takes place 193 */ 194 195void 196initialize_serial( caddr_t scc_phys_base, int32_t serial_baud ) 197{ 198 int i, chan, bits; 199 scc_regmap_t regs; 200 DECL_FUNNEL_VARS 201 202 assert( scc_phys_base ); 203 204 if (!SCC_FUNNEL_INITTED) { 205 FUNNEL_INIT(&SCC_FUNNEL, master_processor); 206 SCC_FUNNEL_INITTED = TRUE; 207 } 208 FUNNEL_ENTER(&SCC_FUNNEL); 209 210 if (serial_initted) { 211 FUNNEL_EXIT(&SCC_FUNNEL); 212 return; 213 } 214 215 simple_lock_init(&scc_stomp, FALSE); 216 217 if (serial_baud == -1) serial_baud = DEFAULT_SPEED; 218 219 scc_softc[0].full_modem = TRUE; 220 221 scc_std[0] = scc_phys_base; 222 223 regs = scc_softc[0].regs = (scc_regmap_t)scc_std[0]; 224 225 for (chan = 0; chan < NSCC_LINE; chan++) { 226 if (chan == 1) 227 scc_init_hw[0].val = 0x80; 228 229 for (i = 0; i < scc_init_hw_count; i++) { 230 scc_write_reg(regs, chan, 231 scc_init_hw[i].reg, scc_init_hw[i].val); 232 } 233 } 234 235 /* Call probe so we are ready very early for remote gdb and for serial 236 console output if appropriate. */ 237 if (scc_probe(serial_baud)) { 238 for (i = 0; i < NSCC_LINE; i++) { 239 scc_softc[0].softr[i].wr5 = SCC_WR5_DTR | SCC_WR5_RTS; 240 scc_param(scc_tty_for(i)); 241 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */ 242 scc_write_reg(regs, i, 9, SCC_WR9_NV); 243 244 scc_read_reg_zero(regs, 0, bits);/* Clear the status */ 245 } 246 scc_parm_done = 1; 247 } 248 249 serial_initted = TRUE; 250 251 FUNNEL_EXIT(&SCC_FUNNEL); 252 return; 253} 254 255int 256scc_probe(int32_t serial_baud) 257{ 258 scc_softc_t scc; 259 int i; 260 scc_regmap_t regs; 261 spl_t s; 262 DECL_FUNNEL_VARS 263 264 if (!SCC_FUNNEL_INITTED) { 265 FUNNEL_INIT(&SCC_FUNNEL, master_processor); 266 SCC_FUNNEL_INITTED = TRUE; 267 } 268 FUNNEL_ENTER(&SCC_FUNNEL); 269 270 /* Readjust the I/O address to handling 271 * new memory mappings. 272 */ 273 274 regs = (scc_regmap_t)scc_std[0]; 275 276 if (regs == (scc_regmap_t) 0) { 277 FUNNEL_EXIT(&SCC_FUNNEL); 278 return 0; 279 } 280 281 scc = &scc_softc[0]; 282 scc->regs = regs; 283 284 s = splhigh(); 285 286 for (i = 0; i < NSCC_LINE; i++) { 287 register struct scc_tty *tp; 288 tp = scc_tty_for(i); 289 tp->t_addr = (char*)(0x80000000L + (i&1)); 290 /* Set default values. These will be overridden on 291 open but are needed if the port will be used 292 independently of the Mach interfaces, e.g., for 293 gdb or for a serial console. */ 294 if (i == 0) { 295 tp->t_ispeed = DEFAULT_PORT0_SPEED; 296 tp->t_ospeed = DEFAULT_PORT0_SPEED; 297 } else { 298 tp->t_ispeed = serial_baud; 299 tp->t_ospeed = serial_baud; 300 } 301 tp->t_flags = DEFAULT_FLAGS; 302 scc->softr[i].speed = -1; 303 304 /* do min buffering */ 305 tp->t_state |= TS_MIN; 306 307 tp->t_dev = scc_dev_no(i); 308 } 309 310 splx(s); 311 312 FUNNEL_EXIT(&SCC_FUNNEL); 313 return 1; 314} 315 316/* 317 * Get a char from a specific SCC line 318 * [this is only used for console&screen purposes] 319 * must be splhigh since it may be called from another routine under spl 320 */ 321 322int 323scc_getc(__unused int unit, int line, boolean_t wait, __unused boolean_t raw) 324{ 325 scc_regmap_t regs; 326 unsigned char c, value; 327 int rcvalue; 328 spl_t s = splhigh(); 329 DECL_FUNNEL_VARS 330 331 FUNNEL_ENTER(&SCC_FUNNEL); 332 333 334 simple_lock(&scc_stomp); 335 regs = scc_softc[0].regs; 336 337 /* 338 * wait till something available 339 * 340 */ 341again: 342 rcvalue = 0; 343 while (1) { 344 scc_read_reg_zero(regs, line, value); 345 346 if (value & SCC_RR0_RX_AVAIL) 347 break; 348 349 if (!wait) { 350 simple_unlock(&scc_stomp); 351 splx(s); 352 FUNNEL_EXIT(&SCC_FUNNEL); 353 return -1; 354 } 355 } 356 357 /* 358 * if nothing found return -1 359 */ 360 361 scc_read_reg(regs, line, SCC_RR1, value); 362 scc_read_data(regs, line, c); 363 364#if MACH_KDB 365 if (console_is_serial() && 366 c == ('_' & 0x1f)) { 367 /* Drop into the debugger */ 368 simple_unlock(&scc_stomp); 369 Debugger("Serial Line Request"); 370 simple_lock(&scc_stomp); 371 scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS); 372 if (wait) { 373 goto again; 374 } 375 simple_unlock(&scc_stomp); 376 splx(s); 377 FUNNEL_EXIT(&SCC_FUNNEL); 378 return -1; 379 } 380#endif /* MACH_KDB */ 381 382 /* 383 * bad chars not ok 384 */ 385 if (value&(SCC_RR1_PARITY_ERR | SCC_RR1_RX_OVERRUN | SCC_RR1_FRAME_ERR)) { 386 scc_write_reg(regs, line, SCC_RR0, SCC_RESET_ERROR); 387 388 if (wait) { 389 scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS); 390 goto again; 391 } 392 } 393 394 scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS); 395 396 simple_unlock(&scc_stomp); 397 splx(s); 398 399 FUNNEL_EXIT(&SCC_FUNNEL); 400 return c; 401} 402 403 404/* 405 * This front-ends scc_getc to make some intel changes easier 406 */ 407 408int _serial_getc(int unit, int line, boolean_t wait, boolean_t raw) { 409 410 return(scc_getc(unit, line, wait, raw)); 411 412} 413 414/* 415 * Put a char on a specific SCC line 416 * use splhigh since we might be doing a printf in high spl'd code 417 */ 418 419void 420scc_putc(__unused int unit, int line, int c) 421{ 422 scc_regmap_t regs; 423 spl_t s; 424 unsigned char value; 425 DECL_FUNNEL_VARS 426 427 428 if (disable_serial_output) 429 return; 430 431 s = splhigh(); 432 FUNNEL_ENTER(&SCC_FUNNEL); 433 simple_lock(&scc_stomp); 434 435 regs = scc_softc[0].regs; 436 437 do { 438 scc_read_reg(regs, line, SCC_RR0, value); 439 if (value & SCC_RR0_TX_EMPTY) 440 break; 441 delay(1); 442 } while (1); 443 444 scc_write_data(regs, line, c); 445/* wait for it to swallow the char ? */ 446 447 do { 448 scc_read_reg(regs, line, SCC_RR0, value); 449 if (value & SCC_RR0_TX_EMPTY) 450 break; 451 } while (1); 452 scc_write_reg(regs, line, SCC_RR0, SCC_RESET_HIGHEST_IUS); 453 simple_unlock(&scc_stomp); 454 455 splx(s); 456 457 FUNNEL_EXIT(&SCC_FUNNEL); 458} 459 460 461void 462powermac_scc_set_datum(scc_regmap_t regs, unsigned int offset, unsigned char value) 463{ 464 volatile unsigned char *address = (unsigned char *) regs + offset; 465 466 assert(FUNNEL_IN_USE(&SCC_FUNNEL)); 467 468 *address = value; 469 eieio(); 470 471 assert(FUNNEL_IN_USE(&SCC_FUNNEL)); 472} 473 474unsigned char 475powermac_scc_get_datum(scc_regmap_t regs, unsigned int offset) 476{ 477 volatile unsigned char *address = (unsigned char *) regs + offset; 478 unsigned char value; 479 480 assert(FUNNEL_IN_USE(&SCC_FUNNEL)); 481 482 value = *address; eieio(); 483 return value; 484 485 assert(FUNNEL_IN_USE(&SCC_FUNNEL)); 486} 487 488int 489scc_param(struct scc_tty *tp) 490{ 491 scc_regmap_t regs; 492 unsigned char value; 493 unsigned short speed_value; 494 int bits, chan; 495 spl_t s; 496 struct scc_softreg *sr; 497 scc_softc_t scc; 498 499 assert(FUNNEL_IN_USE(&SCC_FUNNEL)); 500 501 s = splhigh(); 502 simple_lock(&scc_stomp); 503 504 chan = scc_chan(tp->t_dev); 505 scc = &scc_softc[0]; 506 regs = scc->regs; 507 508 sr = &scc->softr[chan]; 509 510 /* Do a quick check to see if the hardware needs to change */ 511 if ((sr->flags & (TF_ODDP|TF_EVENP)) == (tp->t_flags & (TF_ODDP|TF_EVENP)) 512 && sr->speed == (unsigned long)tp->t_ispeed) { 513 assert(FUNNEL_IN_USE(&SCC_FUNNEL)); 514 simple_unlock(&scc_stomp); 515 splx(s); 516 return 0; 517 } 518 519 if(scc_parm_done) { 520 521 scc_write_reg(regs, chan, 3, SCC_WR3_RX_8_BITS|SCC_WR3_RX_ENABLE); 522 sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE; 523 scc_write_reg(regs, chan, 1, sr->wr1); 524 scc_write_reg(regs, chan, 15, SCC_WR15_ENABLE_ESCC); 525 scc_write_reg(regs, chan, 7, SCC_WR7P_RX_FIFO); 526 scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR); 527 scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP); 528 scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP); 529 scc_write_reg(regs, chan, 9, SCC_WR9_MASTER_IE|SCC_WR9_NV); 530 scc_read_reg_zero(regs, 0, bits); 531 sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE; 532 scc_write_reg(regs, chan, 1, sr->wr1); 533 scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR); 534 simple_unlock(&scc_stomp); 535 splx(s); 536 return 0; 537 } 538 539 sr->flags = tp->t_flags; 540 sr->speed = tp->t_ispeed; 541 542 543 if (tp->t_ispeed == 0) { 544 sr->wr5 &= ~SCC_WR5_DTR; 545 scc_write_reg(regs, chan, 5, sr->wr5); 546 simple_unlock(&scc_stomp); 547 splx(s); 548 549 assert(FUNNEL_IN_USE(&SCC_FUNNEL)); 550 return 0; 551 } 552 553 554#if SCC_DMA_TRANSFERS 555 if (scc->dma_initted & (1<<chan)) 556 scc->dma_ops->scc_dma_reset_rx(chan); 557#endif 558 559 value = SCC_WR4_1_STOP; 560 561 /* 562 * For 115K the clocking divide changes to 64.. to 230K will 563 * start at the normal clock divide 16. 564 * 565 * However, both speeds will pull from a different clocking 566 * source 567 */ 568 569 if (tp->t_ispeed == 115200) 570 value |= SCC_WR4_CLK_x32; 571 else 572 value |= SCC_WR4_CLK_x16 ; 573 574 /* .. and parity */ 575 if ((tp->t_flags & (TF_ODDP | TF_EVENP)) == TF_EVENP) 576 value |= (SCC_WR4_EVEN_PARITY | SCC_WR4_PARITY_ENABLE); 577 else if ((tp->t_flags & (TF_ODDP | TF_EVENP)) == TF_ODDP) 578 value |= SCC_WR4_PARITY_ENABLE; 579 580 /* set it now, remember it must be first after reset */ 581 sr->wr4 = value; 582 583 /* Program Parity, and Stop bits */ 584 scc_write_reg(regs, chan, 4, sr->wr4); 585 586 /* Setup for 8 bits */ 587 scc_write_reg(regs, chan, 3, SCC_WR3_RX_8_BITS); 588 589 // Set DTR, RTS, and transmitter bits/character. 590 sr->wr5 = SCC_WR5_TX_8_BITS | SCC_WR5_RTS | SCC_WR5_DTR; 591 592 scc_write_reg(regs, chan, 5, sr->wr5); 593 594 scc_write_reg(regs, chan, 14, 0); /* Disable baud rate */ 595 596 /* Setup baud rate 57.6Kbps, 115K, 230K should all yeild 597 * a converted baud rate of zero 598 */ 599 speed_value = convert_baud_rate(tp->t_ispeed); 600 601 if (speed_value == 0xffff) 602 speed_value = 0; 603 604 scc_set_timing_base(regs, chan, speed_value); 605 606 if (tp->t_ispeed == 115200 || tp->t_ispeed == 230400) { 607 /* Special case here.. change the clock source*/ 608 scc_write_reg(regs, chan, 11, 0); 609 /* Baud rate generator is disabled.. */ 610 } else { 611 scc_write_reg(regs, chan, 11, SCC_WR11_RCLK_BAUDR|SCC_WR11_XTLK_BAUDR); 612 /* Enable the baud rate generator */ 613 scc_write_reg(regs, chan, 14, SCC_WR14_BAUDR_ENABLE); 614 } 615 616 617 scc_write_reg(regs, chan, 3, SCC_WR3_RX_8_BITS|SCC_WR3_RX_ENABLE); 618 619 620 sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE; 621 scc_write_reg(regs, chan, 1, sr->wr1); 622 scc_write_reg(regs, chan, 15, SCC_WR15_ENABLE_ESCC); 623 scc_write_reg(regs, chan, 7, SCC_WR7P_RX_FIFO); 624 scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR); 625 626 627 /* Clear out any pending external or status interrupts */ 628 scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP); 629 scc_write_reg(regs, chan, 0, SCC_RESET_EXT_IP); 630 //scc_write_reg(regs, chan, 0, SCC_RESET_ERROR); 631 632 /* Enable SCC interrupts (how many interrupts are to this thing?!?) */ 633 scc_write_reg(regs, chan, 9, SCC_WR9_MASTER_IE|SCC_WR9_NV); 634 635 scc_read_reg_zero(regs, 0, bits);/* Clear the status */ 636 637#if SCC_DMA_TRANSFERS 638 if (scc->dma_initted & (1<<chan)) { 639 scc->dma_ops->scc_dma_start_rx(chan); 640 scc->dma_ops->scc_dma_setup_8530(chan); 641 } else 642#endif 643 { 644 sr->wr1 = SCC_WR1_RXI_FIRST_CHAR | SCC_WR1_EXT_IE; 645 scc_write_reg(regs, chan, 1, sr->wr1); 646 scc_write_reg(regs, chan, 0, SCC_IE_NEXT_CHAR); 647 } 648 649 sr->wr5 |= SCC_WR5_TX_ENABLE; 650 scc_write_reg(regs, chan, 5, sr->wr5); 651 652 simple_unlock(&scc_stomp); 653 splx(s); 654 655 assert(FUNNEL_IN_USE(&SCC_FUNNEL)); 656 return 0; 657 658} 659#endif /* NSCC > 0 */ 660