美文网首页
lpc_sich.c源码解析

lpc_sich.c源码解析

作者: 布枝盗 | 来源:发表于2021-12-27 16:21 被阅读0次

学习Linux源码要先从大方向去把握,掌握好代码的整体框架,设计思想以后再去探究细节上的东西。这样可以让整个学习事半功倍,有效的提升学习的效率。分析这个代码我们也采用这样的方式,先从大方向入手,了解了整个脉络了以后,再针对细枝末节一个个解析。

static struct pci_driver lpc_sich_driver = {
    .name       = "lpc_sich",
    .id_table   = lpc_sich_ids,
    .probe      = lpc_sich_probe,
    .remove     = lpc_sich_remove,
};

首先是驱动结构体,大体上所有驱动都需要这么一个结构体,module_pci_driver会将这个驱动注册进内核。
name就是这个驱动的名称,用一个字符串表示;id_table是使用到这个驱动的设备列表;
probe很重要,是匹配设备和驱动的关键,没有它设备无法正常工作起来; remove则是在删除驱动时用的上的东西。

// id_table代码
static DEFINE_PCI_DEVICE_TABLE(lpc_sich_ids) = {
          // PCI_VENDOR_ID_JN  厂商号
        // PCI_DEVICE_ID_SICH_LPC 设备ID,本文件是配置的lpc驱动
    { PCI_DEVICE(PCI_VENDOR_ID_JN, PCI_DEVICE_ID_SICH_LPC) },
    { 0, }
};

接着解析lpc_sich_probe,即probe函数。 probe函数在设备驱动注册最后收尾工作,当设备的device 和其对应的driver 在总线上完成配对之后,系统就调用函数以及其他相关工作。
文件作者在这里使用了一个自定义的结构体

struct lpc_sich_adapter {
    void __iomem    *hst_regs;      //  驱动的基地址
    struct device   *dev;              // 设备信息
    int irq;                                  // 驱动
    struct irq_chip_generic *gc;  //  中断片信息
    unsigned features;                //  使用到的技术
};

注册过程开始前首先就为结构体分配内存
lpc_adapter = kzalloc(sizeof(*lpc_adapter), GFP_KERNEL);
lpc_sich_adapter内存分配成功后,调用pci_enable_device(pdev)使能设备。
调用pci_request_selected_regions(pdev, 1, KBUILD_MODNAME)为设备申请内存.

// 为lpc_adaper变量赋值
lpc_adapter->dev = &pdev->dev;  
    lpc_adapter->hst_regs = pci_iomap(pdev, LPC_HST_BAR, 0);  // hst是High-Speed Transfer
    lpc_adapter->features = 0;
// 设置主设备
pci_set_master(pdev);
// todo
set_bit(LPC_USE_MSI, &lpc_adapter->features);  
ret = pci_enable_msi(pdev);

irq_chip_generic结构体是用来描述驱动中断的结构体,该结构体需要进行很多设置,最主要的是ct属性,也就是chip_type。

c = irq_alloc_generic_chip("LPC_SICH", num_ct, irq_base, 
            lpc_adapter->hst_regs,
            handle_level_irq);

    ct = gc->chip_types;
    ct->regs.mask = LPC_IRQ_MASK;
    ct->regs.ack = LPC_IRQ;
    ct->chip.irq_mask = irq_gc_mask_set_bit;
    ct->chip.irq_unmask = irq_gc_mask_clr_bit;
    ct->chip.irq_ack = irq_gc_ack_set_bit;
    irq_setup_generic_chip(gc, IRQ_MSK(LPC_NR_IRQS), 0, 0, IRQ_NOPROBE | IRQ_LEVEL);

后续会调用一些函数对细节上进行一些设置,irq_set_handler_data用来设置中断,
lpc_enable自定义函数,通过修改寄存器的方式使能设备
lpc_mem_flash_init/lpc_fw_flash_init 这两个函数初始化内存资源,因为是通过pci进行连接的,所以直接走的总线。

irq_set_handler_data(lpc_adapter->irq, lpc_adapter);
    irq_set_chained_handler(lpc_adapter->irq, (irq_flow_handler_t) lpc_irq_handler_mfd);

    pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
    pci_command |= PCI_COMMAND_IO; 
    pci_write_config_word(pdev, PCI_COMMAND, pci_command);

    pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
    dev_info(&pdev->dev, "pci command = %#x\n", pci_command);

    lpc_enable(lpc_adapter);

    lpc_mem_flash_init(pdev,lpc_adapter);
    lpc_fw_flash_init(pdev,lpc_adapter);

下面这段话需要注意下,因为lpc也属于mfd设备(mutilfunction device), 因此需要调用mfd_add_devices

            lpc_sich_cells, ARRAY_SIZE(lpc_sich_cells), NULL,
            0, NULL);

后续再使能设备中断, 将pdev写入适配器,就基本上完成了probe函数

lpc_enable_irqs(lpc_adapter);

    pci_set_drvdata(pdev, lpc_adapter);

一路分析下来, 实际上整个文件都是在围绕着probe这个点做文章,在probe中需要对各个结构体做大量设置,以及调用若干函数,使得设备和驱动在总线上做匹配, 这些都需再以后做大量的积累。只有多去学习,理解,多去写,才能做到看到驱动也不发慌。

相关文章

网友评论

      本文标题:lpc_sich.c源码解析

      本文链接:https://www.haomeiwen.com/subject/mrhyqrtx.html