您的位置:首页 > 运维架构 > Linux

Linux串口驱动分析初始化

2015-04-27 22:18 369 查看
代码来自:http://blog.csdn.net/longwang155069/article/details/42712551

* uart分析

*

* 其实串口分析就两个重要的文件: S3c2440.c Samsung.c

*

* **/

/*1. 首先从Samsung.c的模块初始化函数看起*/

static int __init s3c24xx_serial_modinit(void)

{

int ret;

ret = uart_register_driver(&s3c24xx_uart_drv);

if (ret < 0) {

printk(KERN_ERR "failed to register UART driver\n");

return -1;

}

return 0;

}

/*参数是需要程序员自己定义 */

/** s3c2440.c **/

static struct s3c24xx_uart_info s3c2440_uart_inf = {

.name = "Samsung S3C2440 UART",

.type = PORT_S3C2440, //端口类型

.fifosize = 64, //FIFO缓冲区大小

.rx_fifomask = S3C2440_UFSTAT_RXMASK,

.rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,

.rx_fifofull = S3C2440_UFSTAT_RXFULL,

.tx_fifofull = S3C2440_UFSTAT_TXFULL,

.tx_fifomask = S3C2440_UFSTAT_TXMASK,

.tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,

.get_clksrc = s3c2440_serial_getsource,

.set_clksrc = s3c2440_serial_setsource,

.reset_port = s3c2440_serial_resetport,

};

/*platform 驱动定义: s3c2440_serial_driver*/

static struct platform_driver s3c2440_serial_driver = {

.probe = s3c2440_serial_probe, //匹配后设备调用

.remove = __devexit_p(s3c24xx_serial_remove),

.driver = {

.name = "s3c2440-uart", //驱动的名字

.owner = THIS_MODULE,

},

};

/*分析uart_register_driver函数*/

/* 这个函数的主要作用是初始化tty_driver结构体。 其实tty_driver的主要成员都是通过传进来的参数drv赋值的

* 其次是初始化uart_driver中的uart_state成员。

*

* uart_state是一个结构体包含了tty_port和uart_port。 而uart_port主要是和硬件相关。 其实每一个串口都对应一个uart_port结构

* 这里只初始化了tty_port结构。而uart_port是在等设备匹配上后在设备的ops中的probe函数中初始化

*/

int uart_register_driver(struct uart_driver *drv)

{

/*

* Maybe we should be using a slab cache for this, especially if

* we have a large number of ports to handle.

*/

drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);

if (!drv->state)

goto out;

normal = alloc_tty_driver(drv->nr);

if (!normal)

goto out_kfree;

drv->tty_driver = normal;

normal->owner = drv->owner;

normal->driver_name = drv->driver_name;

normal->name = drv->dev_name;

normal->major = drv->major;

normal->minor_start = drv->minor;

normal->type = TTY_DRIVER_TYPE_SERIAL;

normal->subtype = SERIAL_TYPE_NORMAL;

normal->init_termios = tty_std_termios;

normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;

normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;

normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;

normal->driver_state = drv;

tty_set_operations(normal, &uart_ops);

/*

* Initialise the UART state(s).

*/

for (i = 0; i < drv->nr; i++) {

struct uart_state *state = drv->state + i;

struct tty_port *port = &state->port;

tty_port_init(port);

port->ops = &uart_port_ops;

port->close_delay = 500; /* .5 seconds */

port->closing_wait = 30000; /* 30 seconds */

tasklet_init(&state->tlet, uart_tasklet_action,

(unsigned long)state);

}

retval = tty_register_driver(normal);

}

/* 当设备与驱动匹配后,会调用此函数。

*

* probe函数一般就是做的硬件相关的初始化。

*

* 其实uart_port的初始化也是通过s3c24xx_uart_port来初始化的。

* */

int s3c24xx_serial_probe(struct platform_device *dev,

struct s3c24xx_uart_info *info)

{

struct s3c24xx_uart_port *ourport;

int ret;

dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index);

/*取得相应的端口*/

ourport = &s3c24xx_serial_ports[probe_index];

probe_index++;

dbg("%s: initialising port %p...\n", __func__, ourport);

/*初始化端口*/

ret = s3c24xx_serial_init_port(ourport, info, dev);

if (ret < 0)

goto probe_err;

dbg("%s: adding port\n", __func__);

/*这个函数的主要作用是: 将uart_port和uart_driver关联起来*/

uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);

platform_set_drvdata(dev, &ourport->port);

ret = device_create_file(&dev->dev, &dev_attr_clock_source);

if (ret < 0)

printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__);

ret = s3c24xx_serial_cpufreq_register(ourport);

if (ret < 0)

dev_err(&dev->dev, "failed to add cpufreq notifier\n");

return 0;

probe_err:

return ret;

}

/*

* 端口初始化函数

* */

static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,

struct s3c24xx_uart_info *info,

struct platform_device *platdev)

{

struct uart_port *port = &ourport->port;

struct s3c2410_uartcfg *cfg;

struct resource *res;

int ret;

dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev);

if (platdev == NULL)

return -ENODEV;

/*获取设备的资源,详细解析见下面*/

cfg = s3c24xx_dev_to_cfg(&platdev->dev);

if (port->mapbase != 0)

return 0;

/*判断是否大于总数4个*/

if (cfg->hwport > CONFIG_SERIAL_SAMSUNG_UARTS) {

printk(KERN_ERR "%s: port %d bigger than %d\n", __func__,

cfg->hwport, CONFIG_SERIAL_SAMSUNG_UARTS);

return -ERANGE;

}

port->dev = &platdev->dev;

ourport->info = info;

/*初始化port的大小 = 64*/

ourport->port.fifosize = info->fifosize;

dbg("s3c24xx_serial_init_port: %p (hw %d)...\n", port, cfg->hwport);

port->uartclk = 1;

/*判断是否开启流控*/

if (cfg->uart_flags & UPF_CONS_FLOW) {

dbg("s3c24xx_serial_init_port: enabling flow control\n");

port->flags |= UPF_CONS_FLOW;

}

/*获取uart资源*/

res = platform_get_resource(platdev, IORESOURCE_MEM, 0);

if (res == NULL) {

printk(KERN_ERR "failed to find memory resource for uart\n");

return -EINVAL;

}

dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);

/*初始化基地址等*/

port->mapbase = res->start;

port->membase = S3C_VA_UART + (res->start & 0xfffff);

ret = platform_get_irq(platdev, 0);

if (ret < 0)

port->irq = 0;

else {

port->irq = ret;

ourport->rx_irq = ret;

ourport->tx_irq = ret + 1;

}

ret = platform_get_irq(platdev, 1);

if (ret > 0)

ourport->tx_irq = ret;

/*获取时钟*/

ourport->clk = clk_get(&platdev->dev, "uart");

dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n",

port->mapbase, port->membase, port->irq,

ourport->rx_irq, ourport->tx_irq, port->uartclk);

/* reset the fifos (and setup the uart) */

s3c24xx_serial_resetport(port, cfg);

return 0;

}

#define s3c24xx_dev_to_cfg(__dev) (struct s3c2410_uartcfg *)((__dev)->platform_data)

/*

* cfg = s3c24xx_dev_to_cfg(&platdev->dev)

*

* 那么cfg到底是如何获得?

*

* 搜索s3c2410_uartcfg得到:

*/

#define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK

#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB

#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE

static struct s3c2410_uartcfg smdk2440_uartcfgs[] __initdata = {

[0] = {

.hwport = 0,

.flags = 0,

.ucon = 0x3c5,

.ulcon = 0x03,

.ufcon = 0x51,

},

[1] = {

.hwport = 1,

.flags = 0,

.ucon = 0x3c5,

.ulcon = 0x03,

.ufcon = 0x51,

},

[2] = {

.hwport = 2,

.flags = 0,

.ucon = 0x3c5,

.ulcon = 0x43,

.ufcon = 0x51,

}

};

void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no)

{

if (cpu == NULL)

return;

if (cpu->init_uarts == NULL) {

printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n");

} else

(cpu->init_uarts)(cfg, no);

}

/*初始化系统上的串口资源*/

void __init s3c24xx_init_uartdevs(char *name,

struct s3c24xx_uart_resources *res,

struct s3c2410_uartcfg *cfg, int no)

{

struct platform_device *platdev;

struct s3c2410_uartcfg *cfgptr = uart_cfgs;

struct s3c24xx_uart_resources *resp;

int uart;

memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no);

for (uart = 0; uart < no; uart++, cfg++, cfgptr++) {

platdev = s3c24xx_uart_src[cfgptr->hwport];

resp = res + cfgptr->hwport;

s3c24xx_uart_devs[uart] = platdev;

platdev->name = name;

platdev->resource = resp->resources;

platdev->num_resources = resp->nr_resources;

/*从这里获取platform*/

platdev->dev.platform_data = cfgptr;

}

nr_uarts = no;

}

/*uart 所
12fd7
对应的资源 platform_get_resourced调用**/

/* Serial port registrations */

static struct resource s3c2410_uart0_resource[] = {

[0] = {

.start = S3C2410_PA_UART0,

.end = S3C2410_PA_UART0 + 0x3fff,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = IRQ_S3CUART_RX0,

.end = IRQ_S3CUART_ERR0,

.flags = IORESOURCE_IRQ,

}

};

static struct resource s3c2410_uart1_resource[] = {

[0] = {

.start = S3C2410_PA_UART1,

.end = S3C2410_PA_UART1 + 0x3fff,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = IRQ_S3CUART_RX1,

.end = IRQ_S3CUART_ERR1,

.flags = IORESOURCE_IRQ,

}

};

static struct resource s3c2410_uart2_resource[] = {

[0] = {

.start = S3C2410_PA_UART2,

.end = S3C2410_PA_UART2 + 0x3fff,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = IRQ_S3CUART_RX2,

.end = IRQ_S3CUART_ERR2,

.flags = IORESOURCE_IRQ,

}

};

static struct resource s3c2410_uart3_resource[] = {

[0] = {

.start = S3C2443_PA_UART3,

.end = S3C2443_PA_UART3 + 0x3fff,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = IRQ_S3CUART_RX3,

.end = IRQ_S3CUART_ERR3,

.flags = IORESOURCE_IRQ,

},

};

struct s3c24xx_uart_resources s3c2410_uart_resources[] __initdata = {

[0] = {

.resources = s3c2410_uart0_resource,

.nr_resources = ARRAY_SIZE(s3c2410_uart0_resource),

},

[1] = {

.resources = s3c2410_uart1_resource,

.nr_resources = ARRAY_SIZE(s3c2410_uart1_resource),

},

[2] = {

.resources = s3c2410_uart2_resource,

.nr_resources = ARRAY_SIZE(s3c2410_uart2_resource),

},

[3] = {

.resources = s3c2410_uart3_resource,

.nr_resources = ARRAY_SIZE(s3c2410_uart3_resource),

},

};

总结: 修改驱动需要设计的数据结构

1. uart_driver:用于初始化tty_driver

2. s3c24xx_uart_port: 用于初始化uart_port

3. s3c24xx_serial_ops: 硬件的操作集

4. s3c24xx_uart_info: 用于初始化uart_port

其实也就是:编写S3c2440.c这个串口文件。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: