pxa2128 linux kernel console_init 控制台初始化


// 首先系统启动时, 函数指针会调用con_init(), 代码在drivers/tty/vt/vt.c,函数指针代码如下: 
/*
 * Initialize the console device. This is called *early*, so
 * we can't necessarily depend on lots of kernel help here.
 * Just do some early initializations, and do the complex setup
 * later.
 */
void __init console_init(void)
{
initcall_t *call;


/* Setup the default TTY line discipline. */
tty_ldisc_begin();


/*
* set up the console device so that later boot sequences can
* inform about problems etc..
*/
call = __con_initcall_start;
while (call < __con_initcall_end) {
(*call)();
call++;
}
}
// 对于我使用的pxa平台pxa.c中的uart驱动并未使用console_initcall(serial_pxa_init);而是用了下面的方式, 如下:
int __init serial_pxa_init(void)
{
        int ret;


        ret = uart_register_driver(&serial_pxa_reg);
        if (ret != 0)
                return ret;


        ret = platform_driver_register(&serial_pxa_driver);
        if (ret != 0)
                uart_unregister_driver(&serial_pxa_reg);


        return ret;
}


void __exit serial_pxa_exit(void)
{
        platform_driver_unregister(&serial_pxa_driver);
        uart_unregister_driver(&serial_pxa_reg);
}


module_init(serial_pxa_init);
/* 此方式为常规的设备驱动模块注册,所以按照这个段的链接顺序遍历函数指针并后续探测设备.
下面我们具体看下log信息:
[    0.000000] ----------console_init----------
[    0.000000] -------func pointer run-------start= c0019848, end=  (null) 
再看下地址0xc0019848的函数是什么,利用readelf(arm的)来看下是什么? 如下:
33064: c0019848   612 FUNC    LOCAL  DEFAULT    1 con_init
这样其实这里只有一个函数,就是con_init了.
然后到uart正真注册的时候将会把他替换掉,如下:
serial_pxa_probe --->   uart_add_one_port    --->    uart_configure_port    --->   register_console
这样uart就注册上了,其他tty相关的这里就不讲述了.
下面我们看看上层printf打印驱动中所需要的函数部分: */
struct uart_ops serial_pxa_pops = {
.tx_empty = serial_pxa_tx_empty,
.set_mctrl = serial_pxa_set_mctrl,
.get_mctrl = serial_pxa_get_mctrl,
.stop_tx = serial_pxa_stop_tx,
.start_tx = serial_pxa_start_tx,
.stop_rx = serial_pxa_stop_rx,
.enable_ms = serial_pxa_enable_ms,
.break_ctl = serial_pxa_break_ctl,
.startup = serial_pxa_startup,
.shutdown = serial_pxa_shutdown,
.set_termios = serial_pxa_set_termios,
.pm = serial_pxa_pm,
.type = serial_pxa_type,
.release_port = serial_pxa_release_port,
.request_port = serial_pxa_request_port,
.config_port = serial_pxa_config_port,
.verify_port = serial_pxa_verify_port,
};
/* 这个便是驱动代码给上面注册的函数了, 先看下第一个函数吧:serial_pxa_startup*/
static int serial_pxa_startup(struct uart_port *port)
{
struct uart_pxa_port *up = (struct uart_pxa_port *)port;
unsigned long flags;
int retval;


if (port->line == 3) /* HWUART */
up->mcr |= UART_MCR_AFE;
else
up->mcr = 0;


up->port.uartclk = clk_get_rate(up->clk);


/*
* Allocate the IRQ
*/
retval = request_irq(up->port.irq, serial_pxa_irq, 0, up->name, up);
if (retval)
return retval;


/*
* Clear the FIFO buffers and disable them.
* (they will be reenabled in set_termios())
*/
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
serial_out(up, UART_FCR, 0);


/*
* Clear the interrupt registers.
*/
(void) serial_in(up, UART_LSR);
(void) serial_in(up, UART_RX);
(void) serial_in(up, UART_IIR);
(void) serial_in(up, UART_MSR);


/*
* Now, initialize the UART
*/
serial_out(up, UART_LCR, UART_LCR_WLEN8);


spin_lock_irqsave(&up->port.lock, flags);
up->port.mctrl |= TIOCM_OUT2;
serial_pxa_set_mctrl(&up->port, up->port.mctrl);
spin_unlock_irqrestore(&up->port.lock, flags);


/*
* Finally, enable interrupts.  Note: Modem status interrupts
* are set via set_termios(), which will be occurring imminently
* anyway, so we don't enable them here.
*/
if (up->dma_enable) {
uart_pxa_dma_init(up);
up->rx_stop = 0;
pxa_uart_receive_dma_start(up);
up->ier = UART_IER_DMAE | UART_IER_UUE | UART_IER_RTOIE;
tasklet_init(&up->tklet, uart_task_action, (unsigned long)up);
} else {
up->ier = UART_IER_RLSI | UART_IER_RDI |
UART_IER_RTOIE | UART_IER_UUE;
}
serial_out(up, UART_IER, up->ier);


/*
* And clear the interrupt registers again for luck.
*/
(void) serial_in(up, UART_LSR);
(void) serial_in(up, UART_RX);
(void) serial_in(up, UART_IIR);
(void) serial_in(up, UART_MSR);


return 0;
}


/* 要读明白这块代码首先需要把uart的寄存器部分手册再仔细阅读下,然后再来看会比较好,或者边看代码边看手册,首先得到了uart的时钟,然后请求中断,中断处理函数内容如下:*/
static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id)
{
struct uart_pxa_port *up = dev_id;
unsigned int iir, lsr;


iir = serial_in(up, UART_IIR);
if (iir & UART_IIR_NO_INT)
return IRQ_NONE;


/* timer is not active */
if (!mod_timer(&up->pxa_timer, jiffies + PXA_TIMER_TIMEOUT)) {
#ifdef CONFIG_PXA95x
dvfm_disable_lowpower(up->dvfm_dev_idx[PXA_UART_RX]);
#elif defined(CONFIG_WAKELOCK) && defined(CONFIG_CPU_PXA910)
wake_lock(&up->idle_lock[PXA_UART_RX]);
#else
pm_qos_update_request(&up->qos_idle[PXA_UART_RX],
PM_QOS_CONSTRAINT);
#endif
}


lsr = serial_in(up, UART_LSR);
if (up->dma_enable) {
if (UART_LSR_FIFOE & lsr)
pxa_uart_receive_dma_err(up, &lsr);


if (iir & UART_IIR_TOD)
dma_receive_chars(up, &lsr);
} else {
if (lsr & UART_LSR_DR)
receive_chars(up, &lsr);


check_modem_status(up);
if (lsr & UART_LSR_THRE) {
transmit_chars(up);
/* wait Tx empty */
while (!serial_pxa_tx_empty(\
(struct uart_port *)dev_id))
;
}
}


return IRQ_HANDLED;
}
/*
中断处理函数中先获得中断验证寄存器的内容,然后验证是否有中断等待处理(pending),没有的话直接返回;
假如有的话那么继续下去。
      定时器部分就不看了,因为那部分代码我也没太看懂,有高手的话补充下... ...
      下面读LSR寄存器(这个寄存器被读数据状态发送状态),所以非常重要,有一些发送过程中的错误在这里一般会有记录。所以下面就是根据一些触发中断后的状态信息去做相应处理了,如下:
假设dma启用的话,先判断是否FIFI错误,手册上说是至少一个校验错误,帧错误,或者是break indication for any of the chars in the FIFO,这个寄存器在所有错误字节被从FIFO中读后才被reset。所以下面就调用相应的错误处理了。这里就不展开了,下面的代码都是一系列的错误处理。
下面再判断是否是FIFO模式下Time out interrupt,是的话那么调用dma_receive_chars,这是启用DMA的情况,那么不启用DMA的情况这个函数变为receive_chars,然后就是调用check_modem_status这个函数了,这个函数其实很简单就是读MSR寄存器,然后检查寄存器的字段是否改变了, 对于CTS字段改变的结果是或者交换数据或者停止,不启用DMA就到这里了。
这个处理函数还有些具体函数被展开,只能以后再慢慢补充。




下面回到serial_pxa_startup这个函数,注册中断完事后,下面注释里面也讲的很清楚了,然后再清除中断寄存器,再然后就是这个函数serial_out(up, UART_LCR, UART_LCR_WLEN8);了,他很简单就是指定word length select,有7位和8位,这里指定了8位。下面明天再续了,今天没时间写了。
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值