AM335X 串口驱动学习(2)-基于linux3.8内核

在上一节中主要了解串口驱动的一些重要数据结构,这节主要来了解串口驱动的初始化过程。
对于AM335X UART 驱动来说,主要关心的是driver/tty/serial/omap_serial.c这个文件。

串口驱动初始化过程

首先找到驱动入口点module_init:module_init(serial_omap_init)

#include <linux/module.h>
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;
}
  1. uart_register_driver(&serial_omap_reg),注册了serial_omap_reg这个uart_driver平台设备驱动;
  2. platform_driver_register(&serial_omap_driver),注册了serial_omap_driver这个平台驱动。
    注册平台驱动时,平台总线会将平台驱动和系统中的平台设备拿来一一匹配,如果发现相匹配的,那么就会去调用平台驱动的probe函数
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),/*of_match_ptr函数将设备omap_serial_of_match与驱动serial_omap_driver 联系起来*/
    },
};
#if defined(CONFIG_OF)
static const struct of_device_id omap_serial_of_match[] = {
    { .compatible = "ti,omap2-uart" },
    { .compatible = "ti,omap3-uart" },
    { .compatible = "ti,omap4-uart" },
    {},
};
MODULE_DEVICE_TABLE(of, omap_serial_of_match);
#endif

接着调用 serial_omap_probe()

#include <linux/module.h>

static int serial_omap_probe(struct platform_device *pdev)
{
    struct uart_omap_port   *up; /*uart_omap_port 封装了uart_port*/
    struct resource     *mem, *irq;
    struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data;
    int ret;

    if (pdev->dev.of_node)/*设备树还是板级配置*/
        omap_up_info = of_get_uart_port_info(&pdev->dev);/*获取 "clock-frequency" */

    mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*获取内存*/
    if (!mem) {
        dev_err(&pdev->dev, "no mem resource?\n");
        return -ENODEV;
    }

    irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);/*获取IRQ资源*/
    if (!irq) {
        dev_err(&pdev->dev, "no irq resource?\n");
        return -ENODEV;
    }

    if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem),
                pdev->dev.driver->name)) {
        dev_err(&pdev->dev, "memory region already claimed\n");
        return -EBUSY;
    }

    if (gpio_is_valid(omap_up_info->DTR_gpio) &&
        omap_up_info->DTR_present) {
        ret = gpio_request(omap_up_info->DTR_gpio, "omap-serial");
        if (ret < 0)
            return ret;
        ret = gpio_direction_output(omap_up_info->DTR_gpio,
                        omap_up_info->DTR_inverted);
        if (ret < 0)
            return ret;
    }

    up = devm_kzalloc(&pdev->dev, sizeof(*up), GFP_KERNEL);
    if (!up)
        return -ENOMEM;

    if (gpio_is_valid(omap_up_info->DTR_gpio) &&
        omap_up_info->DTR_present) {
        up->DTR_gpio = omap_up_info->DTR_gpio;
        up->DTR_inverted = omap_up_info->DTR_inverted;
    } else
        up->DTR_gpio = -EINVAL;
    up->DTR_active = 0;

    up->dev = &pdev->dev;
    up->port.dev = &pdev->dev;/*让端口uart_port的成员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_port_line;
    }

    up->pins = devm_pinctrl_get_select_default(&pdev->dev);
    if (IS_ERR(up->pins)) {
        dev_warn(&pdev->dev, "did not get pins for uart%i error: %li\n",
             up->port.line, PTR_ERR(up->pins));
        up->pins = NULL;
    }

    sprintf(up->name, "OMAP UART%d", up->port.line);
    up->port.mapbase = mem->start;
    up->port.membase = devm_ioremap(&pdev->dev, mem->start,
                        resource_size(mem));
    if (!up->port.membase) {
        dev_err(&pdev->dev, "can't ioremap UART\n");
        ret = -ENOMEM;
        goto err_ioremap;
    }

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

    platform_set_drvdata(pdev, up);/*将uart_omap_port (up)保存成平台总线设备的私有数据。以后再要使用它时只需调用platform_get_drvdata()就可以了*/
    pm_runtime_enable(&pdev->dev);
    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_get_sync(&pdev->dev);

    omap_serial_fill_features_erratas(up);

    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 err_add_port;

    pm_runtime_mark_last_busy(up->dev);
    pm_runtime_put_autosuspend(up->dev);
    return 0;

err_add_port:
    pm_runtime_put(&pdev->dev);
    pm_runtime_disable(&pdev->dev);
err_ioremap:
err_port_line:
    dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n",
                pdev->id, __func__, ret);
    return ret;
}

(待续)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值