aboutsummaryrefslogtreecommitdiff
path: root/arch/microblaze/kernel/early_printk.c
blob: aba1f9a97d5d38aa64f31f4965be988ab1d597f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/*
 * Early printk support for Microblaze.
 *
 * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu>
 * Copyright (C) 2007-2009 PetaLogix
 * Copyright (C) 2003-2006 Yasushi SHOJI <yashi@atmark-techno.com>
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License. See the file "COPYING" in the main directory of this archive
 * for more details.
 */

#include <linux/console.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/tty.h>
#include <linux/io.h>
#include <asm/processor.h>
#include <linux/fcntl.h>
#include <asm/setup.h>
#include <asm/prom.h>

static u32 early_console_initialized;
static u32 base_addr;

#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
static void early_printk_uartlite_putc(char c)
{
	/*
	 * Limit how many times we'll spin waiting for TX FIFO status.
	 * This will prevent lockups if the base address is incorrectly
	 * set, or any other issue on the UARTLITE.
	 * This limit is pretty arbitrary, unless we are at about 10 baud
	 * we'll never timeout on a working UART.
	 */

	unsigned retries = 1000000;
	/* read status bit - 0x8 offset */
	while (--retries && (in_be32(base_addr + 8) & (1 << 3)))
		;

	/* Only attempt the iowrite if we didn't timeout */
	/* write to TX_FIFO - 0x4 offset */
	if (retries)
		out_be32(base_addr + 4, c & 0xff);
}

static void early_printk_uartlite_write(struct console *unused,
					const char *s, unsigned n)
{
	while (*s && n-- > 0) {
		if (*s == '\n')
			early_printk_uartlite_putc('\r');
		early_printk_uartlite_putc(*s);
		s++;
	}
}

static struct console early_serial_uartlite_console = {
	.name = "earlyser",
	.write = early_printk_uartlite_write,
	.flags = CON_PRINTBUFFER | CON_BOOT,
	.index = -1,
};
#endif /* CONFIG_SERIAL_UARTLITE_CONSOLE */

#ifdef CONFIG_SERIAL_8250_CONSOLE
static void early_printk_uart16550_putc(char c)
{
	/*
	 * Limit how many times we'll spin waiting for TX FIFO status.
	 * This will prevent lockups if the base address is incorrectly
	 * set, or any other issue on the UARTLITE.
	 * This limit is pretty arbitrary, unless we are at about 10 baud
	 * we'll never timeout on a working UART.
	 */

	#define UART_LSR_TEMT	0x40 /* Transmitter empty */
	#define UART_LSR_THRE	0x20 /* Transmit-hold-register empty */
	#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)

	unsigned retries = 10000;

	while (--retries &&
		!((in_be32(base_addr + 0x14) & BOTH_EMPTY) == BOTH_EMPTY))
		;

	if (retries)
		out_be32(base_addr, c & 0xff);
}

static void early_printk_uart16550_write(struct console *unused,
					const char *s, unsigned n)
{
	while (*s && n-- > 0) {
		if (*s == '\n')
			early_printk_uart16550_putc('\r');
		early_printk_uart16550_putc(*s);
		s++;
	}
}

static struct console early_serial_uart16550_console = {
	.name = "earlyser",
	.write = early_printk_uart16550_write,
	.flags = CON_PRINTBUFFER | CON_BOOT,
	.index = -1,
};
#endif /* CONFIG_SERIAL_8250_CONSOLE */

static struct console *early_console;

void early_printk(const char *fmt, ...)
{
	char buf[512];
	int n;
	va_list ap;

	if (early_console_initialized) {
		va_start(ap, fmt);
		n = vscnprintf(buf, 512, fmt, ap);
		early_console->write(early_console, buf, n);
		va_end(ap);
	}
}

int __init setup_early_printk(char *opt)
{
	int version = 0;

	if (early_console_initialized)
		return 1;

	base_addr = of_early_console(&version);
	if (base_addr) {
#ifdef CONFIG_MMU
		early_console_reg_tlb_alloc(base_addr);
#endif
		switch (version) {
#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
		case UARTLITE:
			printk(KERN_INFO "Early console on uartlite "
						"at 0x%08x\n", base_addr);
			early_console = &early_serial_uartlite_console;
			break;
#endif
#ifdef CONFIG_SERIAL_8250_CONSOLE
		case UART16550:
			printk(KERN_INFO "Early console on uart16650 "
						"at 0x%08x\n", base_addr);
			early_console = &early_serial_uart16550_console;
			break;
#endif
		default:
			printk(KERN_INFO  "Unsupported early console %d\n",
								version);
			return 1;
		}

		register_console(early_console);
		early_console_initialized = 1;
		return 0;
	}
	return 1;
}

/* Remap early console to virtual address and do not allocate one TLB
 * only for early console because of performance degression */
void __init remap_early_printk(void)
{
	if (!early_console_initialized || !early_console)
		return;
	printk(KERN_INFO "early_printk_console remapping from 0x%x to ",
								base_addr);
	base_addr = (u32) ioremap(base_addr, PAGE_SIZE);
	printk(KERN_CONT "0x%x\n", base_addr);

#ifdef CONFIG_MMU
	/*
	 * Early console is on the top of skipped TLB entries
	 * decrease tlb_skip size ensure that hardcoded TLB entry will be
	 * used by generic algorithm
	 * FIXME check if early console mapping is on the top by rereading
	 * TLB entry and compare baseaddr
	 *  mts  rtlbx, (tlb_skip - 1)
	 *  nop
	 *  mfs  rX, rtlblo
	 *  nop
	 *  cmp rX, orig_base_addr
	 */
	tlb_skip -= 1;
#endif
}

void __init disable_early_printk(void)
{
	if (!early_console_initialized || !early_console)
		return;
	printk(KERN_WARNING "disabling early console\n");
	unregister_console(early_console);
	early_console_initialized = 0;
}