Linux串列埠驅動分析初始化
阿新 • • 發佈:2019-01-09
<pre name="code" class="cpp">/** * 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這個串列埠檔案。