0、前言
深圳亿道电子技术有限公司自主研发的EELiod高校教学开发平台采用Intel PXA架构的PXA270,在Linux内核中,有比较成熟的基于Intel PXA架构的UART驱动,现对该驱动作一点分析,这对于理解、掌握以及编写UART驱动有一定的借鉴作用。
基于Linux-2.6.28的PXA架构UART硬件相关的驱动代码位于:/drivers/serial/pxa.c。与UART硬件无关的驱动代码位于/drivers/Serial/Serial_core.c,另外与整个PXA架构相关的代码位于arch/arm/Mach-pxa/Devices.c。从这三个文件入手,基本上可以开始分析基于PXA架构的UART驱动。
一、 PXA UART串口驱动初始化
linux-2.6.28/drivers/serial/pxa.c
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;
}
在UART初始化函数中,首先调用uart_register_driver(struct uart_driver *drv)函数来注册UART驱动,在这函数里它会分配一个tty_driver对象,并初始化tty_operations为serial_pxa_pops, 这是serial-core.c提供的统一的UART操作函数。下面为serial_pxa_reg结构体的定义。
static struct uart_driver serial_pxa_reg = {
.owner = THIS_MODULE,
.driver_name = “PXA serial”,
.dev_name = “ttyS”,
.major = TTY_MAJOR, //major=4
.minor = 64,
.nr = 4,
.cons = PXA_CONSOLE,
};
从serial_pxa_reg结构体中,UART使用的设备名为ttySn(n=0~3),主设备号major为4,次设备号minor为64,串口数nr为4,当然PXA270只有三个串口(FFUART、BTUART、STUART),但是PXA255/26x 微处理器还附加了一个HWUART;当用户编译内核选择SERIAL_PXA_CONSOLE时,UART驱动中将增加终端控制台输出结构体PXA_CONSOLE
#ifdef CONFIG_SERIAL_PXA_CONSOLE
…….
static struct console serial_pxa_console = {
.name = “ttyS”,
.write = serial_pxa_console_write,
.device = uart_console_device,
.setup = serial_pxa_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &serial_pxa_reg,
};
#define PXA_CONSOLE &serial_pxa_console
#else
#define PXA_CONSOLE NULL
#endif
初始化函数然后调用平台驱动注册函数platform_driver_register(struct platform_driver *drv)注册UART驱动,该函数实际上定义有关“伪总线”(pseudo-bus)驱动的相关操作,Linux-2.6.x内核为简化一些片上系统(SoC)系统的集成外设和传统PC接插件的驱动接口,提出了“伪总线”的概念。
linux-2.6.28/drivers/Base/ Platform.c
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume;
if (drv->pm)
drv->driver.pm = &drv->pm->base;
return driver_register(&drv->driver);
}
platform_driver_register函数中的serial_pxa_driver变量定义如下:该结构体实际上定义驱动探测(probe)、移除(remove)、挂起(suspend)、重启(resume)等相关函数操作:/drivers/serial/pxa.c文件本质上完成对所有这些函数的实现。
static struct platform_driver serial_pxa_driver = {
.probe = serial_pxa_probe,
.remove = serial_pxa_remove,
.suspend = serial_pxa_suspend,
.resume = serial_pxa_resume,
.driver = {
.name = “pxa2xx-uart”,
.owner = THIS_MODULE,
},
};
1、UART驱动侦测函数(probe)
static int serial_pxa_probe(struct platform_device *dev)
{
struct uart_pxa_port *sport;
struct resource *mmres, *irqres;
int ret;
fficeffice” />
mmres = platform_get_resource(dev, IORESOURCE_MEM, 0);
irqres = platform_get_resource(dev, IORESOURCE_IRQ, 0);
if (!mmres || !irqres)
return -ENODEV;
sport = kzalloc(sizeof(struct uart_pxa_port), GFP_KERNEL);
if (!sport)
return -ENOMEM;
sport->clk = clk_get(&dev->dev, “UARTCLK”);
if (IS_ERR(sport->clk)) {
ret = PTR_ERR(sport->clk);
goto err_free;
}
sport->port.type = PORT_PXA;
sport->port.iotype = UPIO_MEM;
sport->port.mapbase = mmres->start;
sport->port.irq = irqres->start;
sport->port.fifosize = 64;
sport->port.ops = &serial_pxa_pops;
sport->port.line = dev->id;
sport->port.dev = &dev->dev;
sport->port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
sport->port.uartclk = clk_get_rate(sport->clk);
/*
* Is it worth keeping this?
*/
if (mmres->start == __PREG(FFUART))
sport->name = “FFUART”;
else if (mmres->start == __PREG(BTUART))
sport->name = “BTUART”;
else if (mmres->start == __PREG(STUART))
sport->name = “STUART”;
else if (mmres->start == __PREG(HWUART))
sport->name = “HWUART”;
else
sport->name = “???”;
sport->port.membase = ioremap(mmres->start, mmres->end – mmres->start + 1);
if (!sport->port.membase) {
ret = -ENOMEM;
goto err_clk;
}
serial_pxa_ports[dev->id] = sport;
uart_add_one_port(&serial_pxa_reg, &sport->port);
platform_set_drvdata(dev, sport);
return 0;
err_clk:
clk_put(sport->clk);
err_free:
kfree(sport);
return ret;
}
初始化函数然后调用平台驱动注册函数platform_driver_register注册UART驱动时会调用UART驱动侦测函数serial_pxa_probe,并且传入serial_pxa_driver参数,从下面调试信息可以看出,linux操作系统中进行平台初始化时便调用了注册UART的platform_driver_register函数。而且三次调用该函数,也就是说,操作系统至少有三处跟串口初始化有关。
基于Linux-2.6.28的 EELiod平台UART驱动分析(2)” src=”http://cdn.verydemo.com/upload/2013_05_01/13673638012221.png” alt=”[原创]基于Linux-2.6.28的 EELiod平台UART驱动分析(2)” width=”549″ height=”358″ />