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);
}

 

posted @ 2016-05-02 15:18  SuperTao1024  阅读(1199)  评论(0编辑  收藏  举报