3.3. Getting the console to work

3.3.1. Forcing the kernel to boot our-way

Once we discovered the kernel was indeed booting, but the console wasn't printing, it was time to begin. First, we forced the kernel to boot using a specified configuration for the serial port, in our case 9600n1, and did not allow any command line options or boot time considerations etc.

The first place to go is drivers/char/tty_io.c, to console_init(). This function determines the console configuration at startup. Here's a small part of it:

memset(, 0, sizeof(struct termios));
memcpy(tty_std_termios.c_cc, INIT_C_CC, NCCS);
tty_std_termios.c_iflag = ICRNL | IGNPAR;
tty_std_termios.c_oflag = OPOST | ONLCR;
tty_std_termios.c_cflag = CLOCAL | B9600 | CS8 | CREAD;
tty_std_termios.c_cflag &= ~(CRTSCTS);
tty_std_termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
tty_std_termios.c_iflag = ICRNL | IXON;
tty_std_termios.c_oflag = OPOST | ONLCR;
tty_std_termios.c_cflag = B38400 | CS8 | CREAD | HUPCL;
tty_std_termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
The first (naive) thing we tried, was to configure the console the way we wanted. Of course, this didn't help us much ;-)

Disappointed but not discouraged, we remembered that we didn't have a bootloader yet, and that we didn't really know if any option was being passed on to the kernel. "Maybe the kernel gets some garbage for command line?" we (again, naively) thought. So we tried to stop the kernel from parsing command-line options, and manually inserted our command line. This didn't help us much ;-)

3.3.2. Non-standard hardware - just say no!

At that point, we didn't have a console, but we had time. So we dove a bit deeper into the console issues. Looking at drivers/char/serial.c, we came across serial_console_setup(). This function, apart from parsing command-line options, also configures the serial port by writing directly to it. Our hardware people decided it was a good time to let us know that our serial port wasn't standard. The lines that are used for flow control were not connected. We decided to remark-out the following line, which sets the RTS and DTR lines high, because we just didn't have them.
serial_out(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
Ofcourse, this didn't help us much :-( The lesson learned here was check, check, check your hardware!. Custom boards might not be standard, and the porting will go a lot quicker if you know about it.

3.3.3. Let there be light: calculating baud rate

Finally, we decided to check the baudrate. Did Linux mean what we thought it meant when it said 9600? Possibly not, since we didn't know how it computed that value. We've noticed that the file(s) include/asm-ppc/pmppc_serial.h (replace pmppc with your board name) included a definition of BAUDBASE, which is later used for everything regarding serial ports. It was computed using the board's local bus frequency, bus clock to system clock ratio etc. This seemed wrong, so we checked out what the base baud was in a vxWorks system we had running on the board, and changed it to:

/*
 * system clock = 33Mhz, serial clock = system clock / 4
 * the following must hold: (divisor * BaudRate) == (System clock / 64)
 */
#define BASE_BAUD (33000000 / 4 / 16)
A quick compilation, and a reboot later we had a booting kernel visible through our serial port. Success!