PCIE_MCC驱动阅读过程–HOST篇

文章目录

  • PCIE_MCC驱动阅读过程--HOST篇
    • hi35xx_dev_host.ko
      • init
      • probe
    • irq_map_host.ko
    • boot_device.ko
      • init
      • open/release/read/write/poll
      • ioctl
        • HI_GET_ALL_DEVICES
        • HI_PCIE_TRANSFER_DATA
          • init_ddr
            • move_pf_window_in
          • transfer_data
        • HI_START_TARGET_DEVICE
        • HI_RESET_TARGET_DEVICE
    • ./booter start_device
      • main函数
      • u-boot传输
    • pcit_dma_host.ko
      • init
      • open/release/read/write/poll
      • ioctl
    • mcc_drv_host.ko
    • mcc_usrdev_host.ko
      • init
      • open
      • read
      • write
      • ioctl
        • HI_MCC_IOC_ATTR_INIT
        • HI_MCC_IOC_CONNECT
        • HI_MCC_IOC_CHECK
          • 握手
            • 第零步握手
            • 第一步握手
          • 共享内存初始化
        • HI_MCC_IOC_GET_LOCAL_ID
        • HI_MCC_IOC_GET_REMOTE_ID

先看一下海思提供的 pcie_mcc代码里面的文档《主片引导从片启动方法.txt》,给出了几个模块的加载顺序,这里就先拿HOST端的加载顺序来看一下:

insmod hi35xx_dev_host.ko
insmod irq_map_host.ko
insmod boot_device.ko
./booter start_device
insmod pcit_dma_host.ko
insmod mcc_drv_host.ko
insmod mcc_usrdev_host.ko

从上面看,最底层的hi35xx_dev_host.ko,那么就先从这个看;一般直接与应用程序交互的应该是mcc_usrdev_host.ko。但由于海思好像把相关接口封装在了hi35xxvxxx_pciv.ko模块和libpciv.a库,源码不提供,所以就没有办法进行太多的分析,只能着重上面几个模块的分析。在实际看代码过程中并不是简单的从顶之下或者从底到上,都是看各个模块之间的关联关系。

hi35xx_dev_host.ko

init

模块注册部分如下,exit部分就是init部分的逆过程:

主要看一下init的过程:

pci_register_driver()注册驱动后,会在/sys/bus/pci/drivers/目录下看到对应名字的目录,名字就是PCI_HIDEV_MODULEpci_hidev_driver具体内容看下一节probe)。

看一下hidev_host_init()函数,就是针对于HOST端的特定函数赋值,相关函数后面用到了再进行细看。

看一下架构相关的pci_arch_init()函数,

看一下host_pcie_controller_init()函数,主要涉及硬件中断号的配置。0x12200000就是海思Hi3559AV100芯片的PCI Express控制器寄存器,包括做EP时的TYPE0以及做RC是的TYPE1的协议标准寄存器,以及内部其他的寄存器。

probe

回过头看pci_register_driver(&pci_hidev_driver)这一行代码,pci_hidev_driver的内容如下所示:

着重先看一下probe过程,这个probe被调用证明更底层的驱动或是硬件检测到了PCIe设备的接入并且驱动的pci_device_id匹配上了。因为只有PCIe做EP时寄存器描述才有BAR的概念,所以这个probe过程中获取的BAR资源应该都是指EP端的资源。

alloc_hi35xxdev()函数就是一个简单的申请内存并清零的一个过程:

mk_slot_index()函数是获取设备的总线号:

irq_map_host.ko

这个驱动就是针对上面的hi35xx_dev_host.ko初始化之后,获取PCIe的几个硬件中断号。

对应的设备树如下:143~146INT_A~INT_D的中断线,对应芯片数据手册的中断源175~178147是PCIE_EDMA的中断号,对应芯片数据手册的中断源17938对应的中断源是70,数据手册上注明是SOFTWARE,是专门用于软件的中断号?

boot_device.ko

init

模块的注册是注册一个杂项设备,设备名称是/dev/hisi_boot,具体的操作集是boot_usrdev_fops

pcie_arch_init()也是几个函数指针的赋值:

pcie_cfg_store():为两个全局变量申请内存,并读取对端设备的配置。

store_pci_context():读对端设备的相关配置信息。

open/release/read/write/poll

这几个函数都没有什么太多的内容,pool的函数也是空的,这里就不截图放下来了。

ioctl

这个驱动的ioctl()提供4个命令:

  • HI_GET_ALL_DEVICES : 获取所有设备
  • HI_PCIE_TRANSFER_DATA : 传输数据
  • HI_START_TARGET_DEVICE : 启动目标设备
  • HI_RESET_TARGET_DEVICE : 复位目标设备


HI_GET_ALL_DEVICES

这个命令调用的函数是pcie_boot_get_devices_info(),是一个简单的遍历过程。

HI_PCIE_TRANSFER_DATA

这个命令调用的函数是pcie_boot_transfer_data()pcie_arch_init()时将g_boot_opt.init_ddr初始化为init_ddr(),将g_boot_opt.transfer_data初始化为transfer_data().

init_ddr

init_ddr()函数如下所示:海思u-boot的前面32KB是一些初始化DDR的代码,寄存器信息以及加密验签的信息,这里如果是要调用DDR初始化,则u-boot的前面32KB发送到对端;否则知识简单地进行寄存器的初始化。

slave_ddr_init()函数如下:先写对端的一些寄存器(海思特有的一些初始化标记),然后将对端的片内RAM映射(Hi3559AV100的片内RAM空间地址从0x08000000开始的1MB)。将u-boot的前面32K拷贝到对面的片内RAM之后,对端的芯片就可以初始化DDR了(对端的Hi3559AV100芯片是设置的DDR启动模式,这时候再把完整的u-boot传到对面就可以跑u-boot了。)

slave_reg_write()slave_reg_read()函数如下:都是判断一下当前要写的寄存器地址是否已经经过move_pf_window_in映射进来了,如果当前要写的寄存器没有映射过,那么就将对端的寄存器映射BAR1上,然后再直接写映射出来的地址。(芯片的ATU会将读写操作自己转换成PCIe的TLP相应的事务。)

move_pf_window_in

看一下move_pf_window_in()函数:传入要映射的对端的的地址,再使能ATU地址转换。根据海思的手册意思,配置了ATU地址转换,那么访问对应的地址空间都会被ATU转换成对应的PCIe事务去处理。(目前的关键点是理解这个操作,彻底理解了之后阅读过程就轻松些了。)

pci_config_write()函数如下:读写对端的配置信息。

到内核的pci驱动的pci_bus_write_config_dword()函数:用自旋锁保护的读写操作宏。

这里的bus->ops->read/write应该是指向drivers/pci/hipcie/pcie.c里面的pcie_read_conf()pcie_write_conf()。根据总线号来判断要写对端设备还是写当前设备。

这里我们应该是要写入到对端的设备的,走pcie_write_to_device()

里面的pcie_read_from_device()函数如下:通过__to_pcie_address()函数转换后读对应偏移的值。

to_pcie_address()函数如下:info->base_addr是架构相关的PCIe初始化时通过ioremap_nocache映射的芯片PCIe控制器的配置空间。

假如是写入当前总线控制器,即ROOT BUS的,就没有了上面的to_pcie_address()函数,取而代之的是芯片的PCIe控制器的寄存器(注意是控制器的寄存器,而不是标准的PCIe配置寄存器。这片空间在初始化时通过ioremap_nocache()函数映射过了),pcie_write_to_dbi()函数:

pcie_read_from_dbi()函数:info->conf_base_addr

transfer_data

transfer_data()函数如下:这里的move_pf_window_in的序号是0,按照理解应该是映射目标地址到BAR0上,Hi3559AV100的BAR0有8MB大小,这里是用来传输数据。这里的目标地址,比如在DDR初始化时,就是DDR地址了。

HI_START_TARGET_DEVICE

这个命令调用的函数是pcie_boot_start_device,判断总线号后调用start_device()

start_device()函数就是一个写寄存器的过程,跟架构有关系,暂时就先不细看了。

HI_RESET_TARGET_DEVICE

这个命令调用的函数是pcie_boot_start_device,判断总线号后调用reset_device()

reset_device()函数就是一个写寄存器之后再将对端设备信息保存下来。

./booter start_device

main函数

先看一下main()函数:检验参数之后,ioctl()获取所有的设备,然后进行业务处理。



u-boot传输

u-boot的传输过程如下,特别的地方是增加了一个NEED_DDR_INIT标记。这部分代码可以回头看boot_device.ko的ioctl部分,至于其他的uImage的传输,除了少了一个DDR初始化的过程,基本一致的。

pcit_dma_host.ko

init

模块注册部分如下,exit部分就是init部分的逆过程:

主要看一下init的过程:

看DMA读写通道的初始过程:内部有两个DMA通道,调用trans_list_init()对内部的相关链表初始化后,再初始化了读写的自旋锁和信号量。

trans_list_init()初始化完后,最后加到free_list_head链表后。

dma_channel结构体如下:与内核DMA engine的不一样。

还有一个DMA中断相关的初始化:中断处理函数设置为host_dma_irq_handler(),然后申请中断资源。

看一下中断资源申请的,清除读写中断后,申请中断资源,再使能中断。

再回头看一下中断处理函数,调用__do_pcit_dma_trans()分别处理读写中断。

__do_pcit_dma_trans()处理的过程大概如下所示:根据读写方向,读取读写的中断状态,传输被打断则开启新的DMA传输,完成后将DMA传输相关的描述资源移动到free链表中,如果busy链表非空则继续DMA传输。具体的start_dma_task()->__start_dma_task()主要是一个写寄存器的过程,这里就不细看了,需要和寄存器手册对应着看就比较清晰了。


open/release/read/write/poll

pci_dma_miscdev结构体指向的fops操作集如下所示:

读写等操作函数基本没有什么内容,如下所示:

ioctl

相关的操作还是放在ioctl上,实现了读写操作,用pcit_create_task()函数来实现。

pcit_create_task()函数里面注释主要解释了一下DMA操作时的DMA地址情况,

可以对比一下手册上关于这个的描述:

__pcit_create_task()函数根据不同的方向使用不同的描述数据。

__do_create_task()函数:根据不同的类型选择对应的链表头,如果业务繁忙则退出;否则从空闲链表中取出一个,开启新的DMA传输(原来没有任何数据在传输)或者放到busy链表中(原来有数据传输,等终端触发后自动发起新的DMA传输)。



mcc_drv_host.ko

这一个模块只是单纯地提供一些接口,算是承前启后的作用,后面用到就进行分析一下。

mcc_usrdev_host.ko

先看一下mcc_usrdev_host.ko的代码,查看Makefile,可以看到这个模块是由下面两个文件编译而成的:

mcc_usrdev_host-objs := mcc_core/hios_mcc/hios_mcc_usrdev.o mcc_core/hios_mcc/hios_mcc.o

init

先看hios_mcc_usrdev.c,可以看到这个模块是注册了一个杂项设备。

这个杂项设备驱动提供了一些标准的字符设备的操作接口,open/close,read/write,ioctl/poll这些接口。

open

先看一下提供的的open()函数,可以看到函数很简单,就是把file->private_data指针置空了。

read

再看一下read()函数,这个函数就是查看一下链表里面是否有数据,如果有数据取出第一个数据后拷贝到用户空间,并没有明显看到与其他驱动的直接交互。


write

看一下write()函数,这里一开始的handle取值于file->private_data的过程就表明了在write()之前必须要对file->private_data赋值的过程,由于open()的过程是置空,那么必然是在其他接口进行了赋值。再看下面,申请空间拷贝了用户空间数据后,调用了hios_mcc_sendto_user()函数进行发送,这里就可以看到与其他文件的接口直接交互了。


ioctl

上面read/write过程中都有对file->private_data的取值过程,实际的赋值过程是在ioctl()这个接口里,提供了5个命令,看代码的情况应该是先INIT,再CONNECT,再执行其他的命令。

  • HI_MCC_IOC_ATTR_INIT:属性初始化
  • HI_MCC_IOC_CONNECT:连接对端PCIe
  • HI_MCC_IOC_CHECK:检查对端PCIe
  • HI_MCC_IOC_GET_LOCAL_ID:获取本地ID
  • HI_MCC_IOC_GET_REMOTE_ID:获取对端ID


HI_MCC_IOC_ATTR_INIT

ioctl()HI_MCC_IOC_ATTR_INIT调用hios_mcc_handle_attr_init()函数,这个属性初始化的过程就是一个简单的赋值过程,都赋值为一些非法的值。

HI_MCC_IOC_CONNECT

ioctl()HI_MCC_IOC_CONNECT调用hios_mcc_open()函数,里面有一个关于是否处于中断处理过程的判断,如果是处于中断处理过程中,申请内存需要GFP_ATOMIC原子申请,否则可能会休眠导致系统异常;hios_mcc_open()函数将用户态传入的attrtarget_idportpriority参数传入pci_vdd_open()函数,进行下一层处理。

再看一下pci_vdd_open()函数,一开始就对这个target_id进行入参判断,但ioctl()的INIT过程对这些参数赋值都是一个非法值,那么这个条件肯定不满足,那么就是应用层INIT之后会先对这个入参进行赋值,再调用CONNECT???(可能是先使用GET_REMOTE_ID之后再使用open就可以了???)。

HI_MCC_IOC_CHECK

ioctl()HI_MCC_IOC_CHECK调用hios_mcc_check_remote()函数:

调用的pci_vdd_check_remote()函数如下所示,获取到设备结构体后,根据当前是否是RC端来进行检查。

HOST端执行host_check_slv()进行检查,执行两步握手,设置共享内存为768K,并初始化共享内存。

握手
第零步握手

HOST 端:

使能本地读写DMA,映射对端寄存器到BAR空间。

SLAVE端:循环读取RC端写入的值,读到后检查握手位,共享内存在从片时,将共享内存地址写入寄存,置位握手位。

第一步握手

HOST端:等待从端写握手位,并读出共享内存的地址。

SLAVE端:直接返回。

共享内存初始化

将768K共享内存分成两半,一般用作发送消息,一半用来接收消息。

HI_MCC_IOC_GET_LOCAL_ID

ioctl()HI_MCC_IOC_GET_LOCAL_ID调用hios_mcc_getlocalid()函数:

调用的pci_vdd_remoteids()函数根据当前是否是HOST端(RC),返回0,如果当前是EP端,则读芯片寄存器返回总线号。

HI_MCC_IOC_GET_REMOTE_ID

ioctl()HI_MCC_IOC_GET_REMOTE_ID调用hios_mcc_getremoteids()函数:

调用的pci_vdd_remoteids()函数根据当前是否是HOST端(RC),返回SLAVE端(EP)的总线号,如果当前是EP端,则返回ID为0,表明对端是RC。

PCIE_MCC驱动阅读过程--HOST篇相关推荐

  1. Linux SD卡驱动开发(二) —— SD 卡驱动分析HOST篇

    回顾一下前面的知识,MMC 子系统范围三个部分: HOST 部分是针对不同主机的驱动程序,这一部是驱动程序工程师需要根据自己的特点平台来完成的. CORE 部分: 这是整个MMC 的核心存,这部分完成 ...

  2. 深度学习论文阅读目标检测篇(三):Faster R-CNN《 Towards Real-Time Object Detection with Region Proposal Networks》

    深度学习论文阅读目标检测篇(三):Faster R-CNN< Towards Real-Time Object Detection with Region Proposal Networks&g ...

  3. 【VideoQA最新论文阅读】第一篇视频问答综述Video Question Answering: a Survey of Models and Datasets

    Video Question Answering: a Survey of Models and Datasets 长文预警!!! p.s.此篇文章于2021年1月25日新鲜出炉,在Springer需 ...

  4. 深度学习论文阅读目标检测篇(二):Fast R-CNN《Fast R-CNN》

    深度学习论文阅读目标检测篇(二):Fast R-CNN<Fast R-CNN> Abstract 摘要 1. Introduction 引言 1.1 RCNN and SPPnet 1.2 ...

  5. 领域驱动设计-原理心得篇

    "最初我给本文起的标题是<领域驱动设计-理论入门篇>,但是文中掺杂了太多的个人理解,入门篇就显得太官方了,为了避免错误的理解把读者带偏,所以改成<领域驱动设计-理论心得篇& ...

  6. WINCE6.0 + S3C2443的启动过程---nboot篇

    WINCE6.0 + S3C2443的启动过程---nboot篇 我们启动WINCE6.0的方式是:nboot+eboot+NK.那么是如何启动WINCE6.0系统映像的呢?首先是nboot把eboo ...

  7. linux下I2C驱动发送IO时序,Linux I2C 驱动阅读的碰到的一些网上没有提到的东西

    # re: Linux I2C 驱动阅读的碰到的一些网上没有提到的东西  回复  更多评论 2009-04-11 13:39 by 初学都 楼主,你好.我看了你那篇<Linux I2C核心.总线 ...

  8. 鸿蒙关键技术研究,鸿蒙内核源码分析(静态链接篇) | 完整小项目看透静态链接过程 | 百篇博客分析HarmonyOS源码 | v54.02...

    百篇博客系列篇.本篇为: 下图是一个可执行文件编译,链接的过程. 本篇将通过一个完整的小工程来阐述ELF编译,链接过程,并分析.o和bin文件中各区,符号表之间的关系.从一个崭新的视角去看中间过程,阅 ...

  9. 深度学习论文阅读目标检测篇(四)中英文对照版:YOLOv1《 You Only Look Once: Unified, Real-Time Object Detection》

    深度学习论文阅读目标检测篇(四)中英文对照版:YOLOv1< You Only Look Once: Unified, Real-Time Object Detection> Abstra ...

最新文章

  1. 2020年最新全球大学学术排名出炉
  2. Excel中的VBA宏:每次划款前从总名册中同步用户数据到当前页
  3. boot入门思想 spring_SpringBoot快速入门
  4. Hybris Commerce Product字段名列表
  5. 一个亿的融资在一家芯片初创公司可以烧多久?
  6. extjs tree下拉列表_使用ztree来代替Extjs的下拉树
  7. C++中的重难点看这一篇就够了
  8. 可能是最全面的 Java G1学习笔记
  9. Map 集合循环、遍历的 四 种方式
  10. python编程求极限_Sympy笔记一
  11. MySQL 优化---索引实战(三)
  12. php源码网站安装教程,php源码安装教程(php源码网站搭建方法和过程)
  13. 使用Xamarin实现跨平台移动应用开发(转载)
  14. turtle库基本介绍
  15. 奇数阶魔方阵算法分析
  16. 自动秘钥密码(Autokey)
  17. 排行前1000的docker容器中,20%存在root帐户配置错误
  18. pcb过孔漏铜_过孔露铜改善评估报告
  19. ubuntu版本系统升级
  20. 旧物回收软件都需要哪些功能?

热门文章

  1. CTFHub | PHPINFO
  2. 30岁985大学计算机硕士,985院校硕士的心酸,今年已经30岁了,却还拿着不到5000块的工资...
  3. VC++调节显示器的亮度SetDeviceGammaRamp
  4. 考c语言是什么,C语言是计算机二级吗考什么?考了有什么用
  5. 8个很有用的PHP安全函数,你知道几个?
  6. 流程图、思维导图、网络拓扑图、组织结构图——ProcessOn在线工具及增加文件数方法
  7. [渝粤教育] 西南科技大学 信息法律法规 在线考试复习资料2021版(2)
  8. 中国人民大学计算机考研资料汇总
  9. Python ---(十九)Tkinter窗口组件:Toplevel
  10. Intellij热部署插件Jrebel安装详解(2020新版或以前旧版)