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