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#include <arm/mp.h>
30#include <arm/cpu_data.h>
31#include <arm/machine_cpu.h>
32#include <arm/machine_routines.h>
33#include <arm/misc_protos.h>
34#include <vm/vm_kern.h>
35#include <console/video_console.h>
36#include <console/serial_protos.h>
37#include <kern/kalloc.h>
38
39static struct {
40	char	*buffer;
41	int	len;
42	int	used;
43	char	*write_ptr;
44	char	*read_ptr;
45	decl_simple_lock_data(,read_lock);
46	decl_simple_lock_data(,write_lock);
47} console_ring;
48
49hw_lock_data_t cnputc_lock;
50static volatile long console_output = 0;
51
52typedef struct console_buf {
53	char	*buf_base;
54	char	*buf_end;
55	char	*buf_ptr;
56#define CPU_BUFFER_LEN	(256 - 3*(sizeof(char*)))
57	char	buf[CPU_BUFFER_LEN];
58} console_buf_t;
59
60static void _serial_putc(int, int, int);
61
62struct console_ops cons_ops[] = {
63	{
64		.putc = _serial_putc,
65		.getc = _serial_getc,
66	},
67	{
68		.putc = vcputc,
69		.getc = vcgetc,
70	},
71};
72
73uint32_t nconsops = (sizeof cons_ops / sizeof cons_ops[0]);
74
75uint32_t cons_ops_index = VC_CONS_OPS;
76
77/* This macro polls for pending TLB flushes while spinning on a lock
78 */
79#define SIMPLE_LOCK_NO_INTRS(l)				\
80MACRO_BEGIN						\
81	boolean_t istate = ml_get_interrupts_enabled();	\
82	while (!simple_lock_try((l)))			\
83	{						\
84		if (!istate)				\
85			handle_pending_TLB_flushes();	\
86		cpu_pause();				\
87	}						\
88MACRO_END
89
90void
91console_init(void)
92{
93	int	ret;
94
95	console_ring.len = PAGE_SIZE;
96	ret = kmem_alloc(kernel_map, (vm_offset_t *) &console_ring.buffer,
97			 console_ring.len);
98	if (ret != KERN_SUCCESS)
99		panic("console_ring_init() "
100		      "failed to allocate ring buffer, error %d\n", ret);
101	console_ring.used = 0;
102	console_ring.read_ptr = console_ring.buffer;
103	console_ring.write_ptr = console_ring.buffer;
104	simple_lock_init(&console_ring.read_lock, 0);
105	simple_lock_init(&console_ring.write_lock, 0);
106	hw_lock_init(&cnputc_lock);
107}
108
109void *
110console_cpu_alloc(__unused boolean_t boot_processor)
111{
112	int		ret;
113	console_buf_t	*cbp;
114
115	ret = kmem_alloc(kernel_map, (vm_offset_t *) &cbp,
116				sizeof(console_buf_t));
117	if (ret != KERN_SUCCESS) {
118		printf("console_cpu_alloc() "
119		      "failed to allocate cpu buffer, error=%d\n", ret);
120		return NULL;
121	}
122
123	cbp->buf_base = (char *) &cbp->buf;
124	cbp->buf_ptr = cbp->buf_base;
125	cbp->buf_end = cbp->buf_base + CPU_BUFFER_LEN;
126
127	return (void *) cbp;
128}
129
130void
131console_cpu_free(void *buf)
132{
133	if (buf != NULL)
134		kfree((void *) buf, sizeof(console_buf_t));
135}
136
137/* So we can re-write the serial device functions at boot-time */
138void
139console_set_serial_ops( struct console_ops *newops )
140{
141	cons_ops[SERIAL_CONS_OPS] = *newops;
142}
143
144static inline int
145console_ring_space(void)
146{
147	return console_ring.len - console_ring.used;
148}
149
150static boolean_t
151console_ring_put(char ch)
152{
153	if (console_ring.used < console_ring.len) {
154		console_ring.used++;;
155		*console_ring.write_ptr++ = ch;
156		if (console_ring.write_ptr - console_ring.buffer
157		    == console_ring.len)
158			console_ring.write_ptr = console_ring.buffer;
159		return TRUE;
160	} else {
161		return FALSE;
162	}
163}
164
165static int
166console_ring_get(void)
167{
168	char	ch = 0;
169
170	if (console_ring.used > 0) {
171		console_ring.used--;
172		ch = *console_ring.read_ptr++;
173		if (console_ring.read_ptr - console_ring.buffer
174		    == console_ring.len)
175			console_ring.read_ptr = console_ring.buffer;
176	}
177	return (int) ch;
178}
179
180static inline void
181cpu_buffer_put(console_buf_t *cbp, char ch)
182{
183	if (ch != '\0' && cbp->buf_ptr < cbp->buf_end)
184		*(cbp->buf_ptr++) = ch;
185}
186
187static inline void
188_cnputc(char c)
189{
190	/* The console device output routines are assumed to be
191	 * non-reentrant.
192	 */
193	cons_ops[cons_ops_index].putc(0, 0, c);
194	if (c == '\n')
195            cons_ops[cons_ops_index].putc(0, 0, '\r');
196}
197
198void cnputc_unbuffered(char c) {
199	_cnputc(c);
200}
201
202void
203cnputcusr(char c)
204{
205	/* Spin (with pre-emption enabled) waiting for console_ring_try_empty()
206	 * to complete output. There is a small window here where we could
207	 * end up with a stale value of console_output, but it's unlikely,
208	 * and _cnputc(), which outputs to the console device, is internally
209	 * synchronized. There's something of a conflict between the
210	 * character-at-a-time (with pre-emption enabled) unbuffered
211	 * output model here, and the buffered output from cnputc(),
212	 * whose consumers include printf() ( which outputs a sequence
213	 * with pre-emption disabled, and should be safe to call with
214	 * interrupts off); we don't want to disable pre-emption indefinitely
215	 * here, and spinlocks and mutexes are inappropriate.
216	 */
217	while (console_output != 0);
218
219	_cnputc(c);
220}
221
222static void
223console_ring_try_empty(void)
224{
225	boolean_t state = ml_get_interrupts_enabled();
226	/*
227	 * Try to get the read lock on the ring buffer to empty it.
228	 * If this fails someone else is already emptying...
229	 */
230	if (!simple_lock_try(&console_ring.read_lock))
231		return;
232	/* Indicate that we're in the process of writing a block of data
233	 * to the console.
234	 */
235	atomic_incl(&console_output, 1);
236	for (;;) {
237		char	ch;
238		if (!state)
239			handle_pending_TLB_flushes();
240   	 	SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock);
241		ch = console_ring_get();
242    		simple_unlock(&console_ring.write_lock);
243		if (ch == 0)
244			break;
245		_cnputc(ch);
246	}
247	atomic_decl(&console_output, 1);
248	simple_unlock(&console_ring.read_lock);
249}
250
251void
252cnputc(char c)
253{
254	console_buf_t	*cbp;
255	/* Put directly if console ring is not initialized */
256	_cnputc(c);
257	return;
258}
259
260int _serial_getc(__unused int a, __unused int b, boolean_t wait, __unused boolean_t raw)
261{
262    int c;
263    do {
264        c = serial_getc();
265    } while (wait && c < 0);
266
267    return c;
268}
269
270static void _serial_putc(__unused int a, __unused int b, int c)
271{
272    serial_putc(c);
273}
274
275
276int
277cngetc(void)
278{
279	return cons_ops[cons_ops_index].getc(0, 0,
280					     TRUE, FALSE);
281}
282
283int
284cnmaygetc(void)
285{
286	return cons_ops[cons_ops_index].getc(0, 0,
287					     FALSE, FALSE);
288}
289
290int
291vcgetc(__unused int l,
292       __unused int u,
293       __unused boolean_t wait,
294       __unused boolean_t raw)
295{
296		return( 0);
297}
298