- 代码来自:http://blog.youkuaiyun.com/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 所对应的资源 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这个串口文件。
Linux串口驱动分析初始化
最新推荐文章于 2025-03-31 14:48:46 发布