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