medfield 平台 UART 驱动

本文介绍了一种基于HSU的串口驱动实现方法,包括PCI设备的探测与移除、串口配置、DMA通道初始化及电源管理等核心功能。通过详细解析代码,展示了如何为三个UART串口设置基本参数,并实现串口操作和控制台功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

位于 /drivers/serial/mfd.c 中

1. 初始化:

    static int __init hsu_pci_init(void)
    {
        int ret;
        hsu_global_init();                              //初始化
        ret = uart_register_driver(&serial_hsu_reg);   //注册UART 驱动
        if (ret)
            return ret;
        return pci_register_driver(&hsu_pci_driver);   //注册pci驱动
    }
    static void __exit hsu_pci_exit(void)
    {
        pci_unregister_driver(&hsu_pci_driver);
        uart_unregister_driver(&serial_hsu_reg);
        hsu_debugfs_remove(phsu);
        kfree(phsu);
    }
    module_init(hsu_pci_init);
    module_exit(hsu_pci_exit);
    MODULE_LICENSE("GPL v2");
    MODULE_ALIAS("platform:medfield-hsu");
2. hsu_global_init(), 初始化3个UART 串口。

    static void hsu_global_init(void)
    {
        struct hsu_port *hsu;
        struct uart_hsu_port *uport;
        struct hsu_dma_chan *dchan;
        int i, ret;
        hsu = kzalloc(sizeof(struct hsu_port), GFP_KERNEL);
        if (!hsu)
            return;
        /* Get basic io resource and map it */
        hsu->paddr = 0xffa28000;
        hsu->iolen = 0x1000;
        if (!(request_mem_region(hsu->paddr, hsu->iolen, "HSU global")))
            pr_warning("HSU: error in request mem region\n");
        hsu->reg = ioremap_nocache((unsigned long)hsu->paddr, hsu->iolen);
        if (!hsu->reg) {
            pr_err("HSU: error in ioremap\n");
            ret = -ENOMEM;
            goto err_free_region;
        }
        /* Initialise the 3 UART ports */
        uport = hsu->port;
        for (i = 0; i < 3; i++) {
            uport->port.type = PORT_MFD;
            uport->port.iotype = UPIO_MEM;
            uport->port.mapbase = (resource_size_t)hsu->paddr
                        + HSU_PORT_REG_OFFSET
                        + i * HSU_PORT_REG_LENGTH;
            uport->port.membase = hsu->reg + HSU_PORT_REG_OFFSET
                        + i * HSU_PORT_REG_LENGTH;
            sprintf(uport->name, "hsu_port%d", i);
            uport->port.fifosize = 64;
            uport->port.ops = &serial_hsu_pops;
            uport->port.line = i;
            uport->port.flags = UPF_IOREMAP;
            /* set the scalable maxim support rate to 2746800 bps */
            uport->port.uartclk = 115200 * 24 * 16;
            uport->running = 0;
            uport->txc = &hsu->chans[i * 2];
            uport->rxc = &hsu->chans[i * 2 + 1];
            serial_hsu_ports[i] = uport;
            uport->index = i;
            uport++;
       }
        /* Initialise 6 dma channels */
        dchan = hsu->chans;
        for (i = 0; i < 6; i++) {
            dchan->id = i;
            dchan->dirt = (i & 0x1) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
            dchan->uport = &hsu->port[i/2];
            dchan->reg = hsu->reg + HSU_DMA_CHANS_REG_OFFSET +
                    i * HSU_DMA_CHANS_REG_LENGTH;
            /* Work around for RX */
            if (dmarx_need_timer() && dchan->dirt == DMA_FROM_DEVICE) {
                init_timer(&dchan->rx_timer);
                dchan->rx_timer.function = hsu_dma_rx_timeout;
                dchan->rx_timer.data = (unsigned long)dchan;
            }
            dchan++;
        }
        phsu = hsu;
        hsu_debugfs_init(hsu);
        return;
    err_free_region:
        release_mem_region(hsu->paddr, hsu->iolen);
        kfree(hsu);
        return;
    }
3.  串口信息配置:
    static struct uart_driver serial_hsu_reg = {
        .owner = THIS_MODULE,
        .driver_name = "MFD serial",
        .dev_name = "ttyMFD",
        .major = TTY_MAJOR,
        .minor = 128,
        .nr = 3,
    };
4. pci 驱动信息:
    static struct pci_driver hsu_pci_driver = {
        .name =        "HSU serial",
        .id_table =    pci_ids,
        .probe =    serial_hsu_probe,
        .remove =    __devexit_p(serial_hsu_remove),
        .suspend =    serial_hsu_suspend,
        .resume    =    serial_hsu_resume,
    };
5.  serial_hsu_probe :
    static int serial_hsu_probe(struct pci_dev *pdev,
                    const struct pci_device_id *ent)
    {
        struct uart_hsu_port *uport;
        int index, ret;
        printk(KERN_INFO "HSU: found PCI Serial controller(ID: %04x:%04x)\n",
            pdev->vendor, pdev->device);
        switch (pdev->device) {
        case 0x081B:
            index = 0;
            break;
        case 0x081C:
            index = 1;
            break;
        case 0x081D:
            index = 2;
            break;
        case 0x081E:
            /* internal DMA controller */
            index = 3;
            break;
        default:
            dev_err(&pdev->dev, "HSU: out of index!");
            return -ENODEV;
        }
        ret = pci_enable_device(pdev);
        if (ret)
            return ret;
        if (index == 3) {
            /* DMA controller */
            ret = request_irq(pdev->irq, dma_irq, 0, "hsu_dma", phsu);
            if (ret) {
                dev_err(&pdev->dev, "can not get IRQ\n");
                goto err_disable;
            }
            pci_set_drvdata(pdev, phsu);
        } else {
            /* UART port 0~2 */
            uport = &phsu->port[index];
            uport->port.irq = pdev->irq;
            uport->port.dev = &pdev->dev;
            uport->dev = &pdev->dev;
            ret = request_irq(pdev->irq, port_irq, 0, uport->name, uport);
            if (ret) {
                dev_err(&pdev->dev, "can not get IRQ\n");
                goto err_disable;
            }
            uart_add_one_port(&serial_hsu_reg, &uport->port);
    #ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE
            if (index == 1) {
                register_console(&serial_hsu_console);
                uport->port.cons = &serial_hsu_console;
            }
    #endif
            pci_set_drvdata(pdev, uport);
        }
        return 0;
    err_disable:
        pci_disable_device(pdev);
        return ret;
    }

serial_hsu_suspend() 和 serial_hsu_resume().
 

    #ifdef CONFIG_PM
    static int serial_hsu_suspend(struct pci_dev *pdev, pm_message_t state)
    {
        void *priv = pci_get_drvdata(pdev);
        struct uart_hsu_port *up;
        /* Make sure this is not the internal dma controller */
        if (priv && (pdev->device != 0x081E)) {
            up = priv;
            uart_suspend_port(&serial_hsu_reg, &up->port);
        }
        pci_save_state(pdev);
        pci_set_power_state(pdev, pci_choose_state(pdev, state));
            return 0;
    }
    static int serial_hsu_resume(struct pci_dev *pdev)
    {
        void *priv = pci_get_drvdata(pdev);
        struct uart_hsu_port *up;
        int ret;
        pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
        ret = pci_enable_device(pdev);
        if (ret)
            dev_warn(&pdev->dev,
                "HSU: can't re-enable device, try to continue\n");
        if (priv && (pdev->device != 0x081E)) {
            up = priv;
            uart_resume_port(&serial_hsu_reg, &up->port);
        }
        return 0;
    }
    #else
    #define serial_hsu_suspend    NULL
    #define serial_hsu_resume    NULL
    #endif
6 . serial_hsu_pops, 实现了 serial hsu 的各种串口操作。
    struct uart_ops serial_hsu_pops = {
        .tx_empty    = serial_hsu_tx_empty,
        .set_mctrl    = serial_hsu_set_mctrl,
        .get_mctrl    = serial_hsu_get_mctrl,
        .stop_tx    = serial_hsu_stop_tx,
        .start_tx    = serial_hsu_start_tx,
        .stop_rx    = serial_hsu_stop_rx,
        .enable_ms    = serial_hsu_enable_ms,
        .break_ctl    = serial_hsu_break_ctl,
        .startup    = serial_hsu_startup,
        .shutdown    = serial_hsu_shutdown,
        .set_termios    = serial_hsu_set_termios,
        .pm        = serial_hsu_pm,
        .type        = serial_hsu_type,
        .release_port    = serial_hsu_release_port,
        .request_port    = serial_hsu_request_port,
        .config_port    = serial_hsu_config_port,
        .verify_port    = serial_hsu_verify_port,
    };
7. serial_hsu_console 结构体, 实现了console功能,注意里面有个 serail_hsu_reg
    static struct console serial_hsu_console = {
        .name        = "ttyMFD",
        .write        = serial_hsu_console_write,
        .device        = uart_console_device,
        .setup        = serial_hsu_console_setup,
        .flags        = CON_PRINTBUFFER,
        .index        = 1,
        .data        = &serial_hsu_reg,       //串口 hsu 信息数据
    };
    #endif
static void serial_hsu_console_putchar(struct uart_port *port, int ch)

    {
        struct uart_hsu_port *up =
            container_of(port, struct uart_hsu_port, port);
        wait_for_xmitr(up);
        serial_out(up, UART_TX, ch);
    }
    /*
     * Print a string to the serial port trying not to disturb
     * any possible real use of the port...
     *
     *    The console_lock must be held when we get here.
     */
    static void
    serial_hsu_console_write(struct console *co, const char *s, unsigned int count)
    {
        struct uart_hsu_port *up = serial_hsu_ports[co->index];
        unsigned long flags;
        unsigned int ier;
        int locked = 1;
        local_irq_save(flags);
        if (up->port.sysrq)
            locked = 0;
        else if (oops_in_progress) {
            locked = spin_trylock(&up->port.lock);
        } else
            spin_lock(&up->port.lock);
        /* First save the IER then disable the interrupts */
        ier = serial_in(up, UART_IER);
        serial_out(up, UART_IER, 0);
        uart_console_write(&up->port, s, count, serial_hsu_console_putchar);
        /*
         * Finally, wait for transmitter to become empty
         * and restore the IER
         */
        wait_for_xmitr(up);
        serial_out(up, UART_IER, ier);
        if (locked)
            spin_unlock(&up->port.lock);
        local_irq_restore(flags);
    }
    static struct console serial_hsu_console;
    static int __init
    serial_hsu_console_setup(struct console *co, char *options)
    {
        struct uart_hsu_port *up;
        int baud = 115200;
        int bits = 8;
        int parity = 'n';
        int flow = 'n';
        int ret;
        if (co->index == -1 || co->index >= serial_hsu_reg.nr)
            co->index = 0;
        up = serial_hsu_ports[co->index];
        if (!up)
            return -ENODEV;
        if (options)
            uart_parse_options(options, &baud, &parity, &bits, &flow);
        ret = uart_set_options(&up->port, co, baud, parity, bits, flow);
        return ret;
    }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值