android kernel initialization process console

Original link: http://www.cnblogs.com/james1207/p/3253853.html


For our android platform, the console has been assigned to the serial port 1, and therefore the initialization process is to configure the console output to the serial port 1

For kernel console is initialized before mount the file system, because there is no serial port device file, the file can not be accessed by opening the serial device can only access the hardware directly, more accessible manner similar to the bare metal.

The following formal point of view

Board initialization process of
android \ kernel_imx \ arch \ arm \ mach-mx6 \ board-mx6q_sabresd.c

MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board")
/* Maintainer: Freescale Semiconductor, Inc. */
.boot_params = MX6_PHYS_OFFSET + 0x100,
.fixup = fixup_mxc_board,
.map_io = mx6_map_io,
.init_irq = mx6_init_irq,
.init_machine = mx6_sabresd_board_init,
.timer = &mx6_sabresd_timer,
.reserve = mx6q_sabresd_reserve,
MACHINE_END





Which has a clock initialization mx6_sabresd_timer we look at its definition

static struct sys_timer mx6_sabresd_timer = {
.init   = mx6_sabresd_timer_init,
};
static void __init mx6_sabresd_timer_init(void)
{
struct clk *uart_clk;
#ifdef CONFIG_LOCAL_TIMERS
twd_base = ioremap(LOCAL_TWD_ADDR, SZ_256);
BUG_ON(!twd_base);
#endif
mx6_clocks_init(32768, 24000000, 0, 0);


uart_clk = clk_get_sys("imx-uart.0", NULL);
early_console_setup(UART1_BASE_ADDR, uart_clk);
}



Here you can see the call early_console_setup (UART1_BASE_ADDR, uart_clk);
the console before the function is to mount the file system initialization function. Now I began to analyze the function
android \ kernel_imx \ arch \ arm \ plat-mxc \ cpu.c

/**
 * early_console_setup - setup debugging console
 *
 * Consoles started here require little enough setup that we can start using
 * them very early in the boot process, either right after the machine
 * vector initialization, or even before if the drivers can detect their hw.
 *
 * Returns non-zero if a console couldn't be setup.
 * This function is developed based on
 * early_console_setup function as defined in arch/ia64/kernel/setup.c
 * 这个注释里写的很清楚,在设备驱动执行之前,为了调试错误的需要我们
 * 需要在启动的最初就初始化控制台
 */
void __init early_console_setup(unsigned long base, struct clk *clk)
{
#ifdef CONFIG_SERIAL_IMX_CONSOLE
mxc_early_serial_console_init(base, clk);
#endif
}
这里调用mxc_early_serial_console_init(base, clk);
android\kernel_imx\drivers\tty\serial、mxc_uart_early.c
int __init mxc_early_serial_console_init(unsigned long base, struct clk *clk)
{
mxc_early_device.clk = clk;
mxc_early_device.port.mapbase = base;


register_console(&mxc_early_uart_console);
return 0;
}



Here you can see register_console (& mxc_early_uart_console); a device that is registered to the console,
at the beginning of this device must be registered to access the bare metal of the way, so we focused look at this device

static struct console mxc_early_uart_console __initdata = {
.name = "ttymxc",
.write = early_mxcuart_console_write,
.setup = mxc_early_uart_setup,
.flags = CON_PRINTBUFFER | CON_BOOT,
.index = -1,
};



Device Access Interface This device provides
.write = early_mxcuart_console_write, is a serial transmission function
.setup = mxc_early_uart_setup, serial port initialization function
.flags = CON_PRINTBUFFER | CON_BOOT, is the console flag, CON_BOOT show these things a boot console device
also console device before the device file to mount that is


, we analyze the initialization function and data transmission function following
initialization function

static int __init mxc_early_uart_setup(struct console *console, char *options)
{

struct mxc_early_uart_device *device = &mxc_early_device;
struct uart_port *port = &device->port;
int length;

if (device->port.membase || device->port.iobase)  
return -ENODEV;


/* Enable Early MXC UART Clock */
clk_enable(device->clk);//初始化总线时钟


port->uartclk = 5600000;
port->iotype = UPIO_MEM;
port->membase = ioremap(port->mapbase, SZ_4K);//串口寄存器内存映射


if (options) {
device->baud = simple_strtoul(options, NULL, 0);
length = min(strlen(options), sizeof(device->options));
strncpy(device->options, options, length);
} else {
device->baud = probe_baud(port);
snprintf(device->options, sizeof(device->options), "%u",
device->baud);
}
printk(KERN_INFO
      "MXC_Early serial console at MMIO 0x%x (options '%s')\n",
      port->mapbase, device->options);
return 0;
}


In fact, seen from this initialization functions it has done a lot to fill in the data structure mxc_early_device work, and these data

Searched all the code did not use, so these things does not make sense, the code official to this point is not very good. However, because we have already initialized uboot

So even if there is no serial port initialization In fact, the serial port can also be use.

Here two sentences really useful
clk_enable (device-> clk); // initialize the bus clock
port-> membase = ioremap (port-> mapbase, SZ_4K); // port memory-mapped registers
but no register map at the end of any serial register initialization, it is also very strange, we take a close look found that
the register initialization code written in the data transmission function in particular to analyze why we send function
early_mxcuart_console_write

/*!
 * This function is called to write the console messages through the UART port.
 *
 * @param   co    the console structure
 * @param   s     the log message to be written to the UART
 * @param   count length of the message
 */
void __init early_mxcuart_console_write(struct console *co, const char *s,
u_int count)
{
struct uart_port *port = &mxc_early_device.port;
unsigned int status, oldcr1, oldcr2, oldcr3, cr2, cr3;


/*
* First save the control registers and then disable the interrupts
*/
oldcr1 = readl(port->membase + MXC_UARTUCR1);   //读取当前三个串口控制寄存器的值
oldcr2 = readl(port->membase + MXC_UARTUCR2);
oldcr3 = readl(port->membase + MXC_UARTUCR3);
cr2 =
   oldcr2 & ~(MXC_UARTUCR2_ATEN | MXC_UARTUCR2_RTSEN | //初始化串口寄存器数值
      MXC_UARTUCR2_ESCI);
cr3 =
   oldcr3 & ~(MXC_UARTUCR3_DCD | MXC_UARTUCR3_RI |
      MXC_UARTUCR3_DTRDEN);
writel(MXC_UARTUCR1_UARTEN, port->membase + MXC_UARTUCR1);  //使能串口
writel(cr2, port->membase + MXC_UARTUCR2);  //配置寄存器
writel(cr3, port->membase + MXC_UARTUCR3);


/* Transmit string */
uart_console_write(port, s, count, mxcuart_console_write_char); //发送数据


/*
* Finally, wait for the transmitter to become empty等待发送完成
*/
do {
status = readl(port->membase + MXC_UARTUSR2);
} while (!(status & MXC_UARTUSR2_TXDC));


/*
* Restore the control registers
*/
writel(oldcr1, port->membase + MXC_UARTUCR1);//恢复串口寄存器数值
writel(oldcr2, port->membase + MXC_UARTUCR2);
writel(oldcr3, port->membase + MXC_UARTUCR3);
}



From reading this function, it first holds the value of the serial port control register, and then initialized to comply with the console, after you send data, and restore the original data
The purpose of this is, if we load the serial driver, then it there might upset the console, and after the system starts
we are not active serial port driver configuration, so the best way is, each time you send data to reconfigure the serial port to send and then finished restore the previous configuration.
To here the first part of the console initialization has been completed. We can not know the file system, the console is how it works.

Reproduced in: https: //www.cnblogs.com/james1207/p/3253853.html

Guess you like

Origin blog.csdn.net/weixin_30415113/article/details/94987824