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