forked from Minki/linux
e02db0aa3e
This patch fix the problem with rootfs on JFFS2 with early printk console turned on. The origin version used TLB63 for temporary early printk mapping. The code expect that kernel is not able to use all 64 TLB entries till early printk console is remapped by ioremap. After that temporary mapping on TLB63 is silently lost. This expectation give the opportunity to have early console pretty early. Microblaze systems with JFFS2 rootfs with early printk console turned on used more than 64 TLB entries before kernel can remap early console. Based on that kernel does access to bad area because early printk mapping is rewritten. This patch introduces tlb_skip variable which dynamically stores number of skipped TLB entries from the TLB0. skip_tlb=2 means that TLB0 and TLB1 should be skipped. MICROBLAZE_TLB_SKIP defines how many TLB is skipped at the kernel start. They can be used for user purpose. TLB 63 is used for temporary LMB mapping (MICROBLAZE_LMB_TLB_ID). Also clean TLBLO when kernel starts. For specific kernel sizes kernel can use just one TLB. Detect this case and use the second TLB for general purpose. Change _tlbia function to flush TLB entries from tlb_skip to TLB_SIZE. Export tlb_skip size through debugfs. Signed-off-by: Michal Simek <monstr@monstr.eu>
202 lines
5.0 KiB
C
202 lines
5.0 KiB
C
/*
|
|
* 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);
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
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;
|
|
}
|