am335x uart分析
/************************************************************ * am335x uart分析 * 本文记录am335x uart驱动的注册过程。 * 参考: * http://www.cnblogs.com/helloworldtoyou/p/5385595.html * 涉及文件: * arch/arm/mach-omap2/board-am335xevm.c * drivers/tty/serial/omap-serial.c * * Tony Liu, 2016-5-2, Shenzhen ***********************************************************/ MACHINE_START(AM335XEVM, "am335xevm") /* Maintainer: Texas Instruments */ .atag_offset = 0x100, .map_io = am335x_evm_map_io, .init_early = am33xx_init_early, .init_irq = ti81xx_init_irq, .handle_irq = omap3_intc_handle_irq, .timer = &omap3_am33xx_timer, .init_machine = am335x_evm_init, ---+ MACHINE_END | | static void __init am335x_evm_init(void) <--+ { am33xx_cpuidle_init(); am33xx_mux_init(board_mux); omap_serial_init(); am335x_evm_i2c_init(); omap_sdrc_init(NULL, NULL); usb_musb_init(&musb_board_data); omap_board_config = am335x_evm_config; omap_board_config_size = ARRAY_SIZE(am335x_evm_config); daughter_brd_detected = false; setup_xxx_xxxx(); --+ ...... | } | | V static void setup_xxx_xxxx(void) { /*which doesn't have Write Protect pin LAN8710A_PHY_ID */ am335x_mmc[0].gpio_wp = -EINVAL; int ret; //配置引脚复用 _configure_device(EVM_SK, xxx_xxxx_dev_cfg, PROFILE_NONE); -----+ | | } | | V | static void _configure_device(int evm_id, struct evm_dev_cfg *dev_cfg, | int profile) | { | int i; | | am335x_evm_set_id(evm_id); | | if (profile == PROFILE_NONE) { | for (i = 0; dev_cfg->device_init != NULL; dev_cfg++) { | if (dev_cfg->device_on == DEV_ON_BASEBOARD) | dev_cfg->device_init(evm_id, profile); | else if (daughter_brd_detected == true) | dev_cfg->device_init(evm_id, profile); | } | } else { | for (i = 0; dev_cfg->device_init != NULL; dev_cfg++) { | if (dev_cfg->profile & profile) { | if (dev_cfg->device_on == DEV_ON_BASEBOARD) | dev_cfg->device_init(evm_id, profile); | else if (daughter_brd_detected == true) | dev_cfg->device_init(evm_id, profile); | } | } | } | } | | static struct evm_dev_cfg xxx_xxxx_dev_cfg[] = { <--------+ ...... {uart0_init, DEV_ON_BASEBOARD, PROFILE_ALL},//fixed ---+ {uart1_init, DEV_ON_BASEBOARD, PROFILE_ALL},//fixed | {uart4_init, DEV_ON_BASEBOARD, PROFILE_ALL},//fixed | ...... | {NULL, 0, 0}, | }; | | static void uart0_init(int evm_id, int profile) <---+ { setup_pin_mux(uart0_pin_mux); ------+ return; | | } | | V | static void setup_pin_mux(struct pinmux_config *pin_mux) | { | int i; | | for (i = 0; pin_mux->string_name != NULL; pin_mux++) | omap_mux_init_signal(pin_mux->string_name, pin_mux->val); | } | | static struct pinmux_config uart0_pin_mux[] = { <-----+ {"uart0_rxd.uart0_rxd", OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP}, {"uart0_txd.uart0_txd", OMAP_MUX_MODE0 | AM33XX_PULL_ENBL}, {"uart0_ctsn.uart0_ctsn", OMAP_MUX_MODE0 | AM33XX_PULL_ENBL}, {NULL, 0}, }; drivers/tty/serial/omap-serial.c static int __init serial_omap_init(void) { int ret; ret = uart_register_driver(&serial_omap_reg); -------+ if (ret != 0) | return ret; | ret = platform_driver_register(&serial_omap_driver); --+ | if (ret != 0) | | uart_unregister_driver(&serial_omap_reg); | | return ret; | | } | | | | static struct uart_driver serial_omap_reg = { | | .owner = THIS_MODULE, | | .driver_name = "OMAP-SERIAL", | | .dev_name = OMAP_SERIAL_NAME, | | .nr = OMAP_MAX_HSUART_PORTS, | | .cons = OMAP_CONSOLE, | | }; | | | | #define OMAP_SERIAL_NAME "ttyO" | | #define OMAP_MAX_HSUART_PORTS 6 | | | | #define OMAP_CONSOLE (&serial_omap_console) | | | | static struct console serial_omap_console = { | <------+ .name = OMAP_SERIAL_NAME, | .write = serial_omap_console_write, | .device = uart_console_device, | .setup = serial_omap_console_setup, | .flags = CON_PRINTBUFFER, | .index = -1, | .data = &serial_omap_reg, | }; | | static struct platform_driver serial_omap_driver = { <--+ .probe = serial_omap_probe, .remove = serial_omap_remove, .driver = { .name = DRIVER_NAME, .pm = &serial_omap_dev_pm_ops, .of_match_table = of_match_ptr(omap_serial_of_match), }, }; static int serial_omap_probe(struct platform_device *pdev) { struct uart_omap_port *up; struct resource *mem, *irq, *dma_tx, *dma_rx; struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data; int ret = -ENOSPC; if (pdev->dev.of_node) omap_up_info = of_get_uart_port_info(&pdev->dev); ...... up->pdev = pdev; up->port.dev = &pdev->dev; up->port.type = PORT_OMAP; up->port.iotype = UPIO_MEM; up->port.irq = irq->start; up->port.regshift = 2; up->port.fifosize = 64; up->port.ops = &serial_omap_pops; --------+ | if (pdev->dev.of_node) | up->port.line = of_alias_get_id(pdev->dev.of_node, "serial"); | else | up->port.line = pdev->id; | | if (up->port.line < 0) { | dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n", | up->port.line); | ret = -ENODEV; | goto err; | } | | sprintf(up->name, "OMAP UART%d", up->port.line); | up->port.mapbase = mem->start; | up->port.membase = ioremap(mem->start, resource_size(mem)); | if (!up->port.membase) { | dev_err(&pdev->dev, "can't ioremap UART\n"); | ret = -ENOMEM; | goto err; | } | | up->port.flags = omap_up_info->flags; | up->port.uartclk = omap_up_info->uartclk; | if (!up->port.uartclk) { | up->port.uartclk = DEFAULT_CLK_SPEED; | dev_warn(&pdev->dev, "No clock speed specified: using default:" | "%d\n", DEFAULT_CLK_SPEED); | } | up->uart_dma.uart_base = mem->start; | up->errata = omap_up_info->errata; | | if (omap_up_info->dma_enabled) { | up->uart_dma.uart_dma_tx = dma_tx->start; | up->uart_dma.uart_dma_rx = dma_rx->start; | up->use_dma = 1; | up->uart_dma.rx_buf_size = omap_up_info->dma_rx_buf_size; | up->uart_dma.rx_timeout = omap_up_info->dma_rx_timeout; | up->uart_dma.rx_poll_rate = omap_up_info->dma_rx_poll_rate; | spin_lock_init(&(up->uart_dma.tx_lock)); | spin_lock_init(&(up->uart_dma.rx_lock)); | up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE; | up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE; | } | | up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; | up->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; | pm_qos_add_request(&up->pm_qos_request, | PM_QOS_CPU_DMA_LATENCY, up->latency); | serial_omap_uart_wq = create_singlethread_workqueue(up->name); | INIT_WORK(&up->qos_work, serial_omap_uart_qos_work); | | pm_runtime_use_autosuspend(&pdev->dev); | pm_runtime_set_autosuspend_delay(&pdev->dev, | omap_up_info->autosuspend_timeout); | | pm_runtime_irq_safe(&pdev->dev); | pm_runtime_enable(&pdev->dev); | pm_runtime_get_sync(&pdev->dev); | | ui[up->port.line] = up; | serial_omap_add_console_port(up); | | ret = uart_add_one_port(&serial_omap_reg, &up->port); --------------+ | if (ret != 0) | | goto do_release_region; | | | | pm_runtime_put(&pdev->dev); | | platform_set_drvdata(pdev, up); | | return 0; | | err: | | dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n", | | pdev->id, __func__, ret); | | do_release_region: | | release_mem_region(mem->start, resource_size(mem)); | | return ret; | | } | | | | static struct uart_ops serial_omap_pops = { <--------|--+ .tx_empty = serial_omap_tx_empty, | .set_mctrl = serial_omap_set_mctrl, | .get_mctrl = serial_omap_get_mctrl, | .stop_tx = serial_omap_stop_tx, | .start_tx = serial_omap_start_tx, | .stop_rx = serial_omap_stop_rx, | .enable_ms = serial_omap_enable_ms, | .break_ctl = serial_omap_break_ctl, | .startup = serial_omap_startup, | .shutdown = serial_omap_shutdown, | .set_termios = serial_omap_set_termios, | .pm = serial_omap_pm, | .type = serial_omap_type, | .release_port = serial_omap_release_port, | .request_port = serial_omap_request_port, | .config_port = serial_omap_config_port, | .verify_port = serial_omap_verify_port, | #ifdef CONFIG_CONSOLE_POLL | .poll_put_char = serial_omap_poll_put_char, | .poll_get_char = serial_omap_poll_get_char, | #endif | }; | | int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) <-+ { struct uart_state *state; struct tty_port *port; int ret = 0; struct device *tty_dev; BUG_ON(in_interrupt()); if (uport->line >= drv->nr) return -EINVAL; state = drv->state + uport->line; port = &state->port; mutex_lock(&port_mutex); mutex_lock(&port->mutex); if (state->uart_port) { ret = -EINVAL; goto out; } state->uart_port = uport; state->pm_state = -1; uport->cons = drv->cons; uport->state = state; /* * If this port is a console, then the spinlock is already * initialised. */ if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) { spin_lock_init(&uport->lock); lockdep_set_class(&uport->lock, &port_lock_key); } uart_configure_port(drv, state, uport); /* * Register the port whether it's detected or not. This allows * setserial to be used to alter this ports parameters. */ tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev); -----+ if (likely(!IS_ERR(tty_dev))) { | device_init_wakeup(tty_dev, 1); | device_set_wakeup_enable(tty_dev, 0); | } else | printk(KERN_ERR "Cannot register tty device on line %d\n", | uport->line); | | /* | * Ensure UPF_DEAD is not set. | */ | uport->flags &= ~UPF_DEAD; | | out: | mutex_unlock(&port->mutex); | mutex_unlock(&port_mutex); | | return ret; | } | | struct device *tty_register_device(struct tty_driver *driver, unsigned index, <---+ struct device *device) { char name[64]; dev_t dev = MKDEV(driver->major, driver->minor_start) + index; if (index >= driver->num) { printk(KERN_ERR "Attempt to register invalid tty line number " " (%d).\n", index); return ERR_PTR(-EINVAL); } if (driver->type == TTY_DRIVER_TYPE_PTY) pty_line_name(driver, index, name); else tty_line_name(driver, index, name); ---+ | return device_create(tty_class, device, dev, NULL, name); | } | // 设备名 | static void tty_line_name(struct tty_driver *driver, int index, char *p) <--+ { sprintf(p, "%s%d", driver->name, index + driver->name_base); }