PCIE 是外围设备互连(Peripheral Component Interconnect Express)的简称,作为一种通用的总线接口标准,在目前的计算机系统中得到了非常广泛的应用。PCIE  总线支持 3 个独立的物理地址空间:存储器空间, IO 空间和配置空间。 每个PCIE 设备都有一个配置空间,配置空间采用 Id 寻址方法,用总线号,设备号,功能号和寄存器号来唯一标识一个配置空间。配置空间只能由 host 桥来访问。

<<Linux那些事儿之我是PCI>> 已经告诉我们如何从do_initcalls找到找到PCI 驱动的入口

postcore_initcall(pcibus_class_init);

postcore_initcall(pci_driver_init);

文件

函数

入口

内存位置

arch/i386/pci/acpi.c

pci_acpi_init

subsys_initcall

.initcall4.init

arch/i386/pci/common.c

pcibios_init

subsys_initcall

.initcall4.init

arch/i386/pci/i386.c

pcibios_assign_resources

fs_initcall

.initcall5.init

arch/i386/pci/legacy.c

pci_legacy_init

drivers/pci/pci-acpi.c

acpi_pci_init

arch_initcall

.initcall3.init

drivers/pci/pci- driver.c

pci_driver_init

postcore_initcall

.initcall2.init

drivers/pci/pci- sysfs.c

pci_sysfs_init

late_initcall

.initcall7.init

drivers/pci/pci.c

pci_init

device_initcall

.initcall6.init

drivers/pci/probe.c

pcibus_class_init

postcore_initcall

.initcall2.init

drivers/pci/proc.c

pci_proc_init

__initcall

.initcall6.init

arch/i386/pci/init.c

pci_access_init

arch_initcall

.initcall3.init

我们这里是海思3536 arm系统稍微修改一下:

文件

函数

入口

内存位置

drivers/pci/hipcie/pcie.c 

acpi_pci_init

subsys_initcall

.initcall4.init

drivers/pci/pci- driver.c

pci_driver_init

postcore_initcall

.initcall2.init

drivers/pci/pci- sysfs.c

pci_sysfs_init

late_initcall

.initcall7.init

drivers/pci/pci.c

pci_init

device_initcall

.initcall6.init

drivers/pci/probe.c

pcibus_class_init

postcore_initcall

.initcall2.init

drivers/pci/proc.c

pci_proc_init

__initcall

.initcall6.init 

上述函数注册了PCI class和总线驱动,总线级别的驱动早已经被那些技术大牛们开发好了,我们不用太关注其PCI总线驱动的实现细节,我们从hisi_pcie_init看看驱动程序是如何工作的。

#define PCIE_RC_DRV_NAME "hisi pcie root complex"
static struct resource hisi_pcie_resources[] = {
[0] = {
.start  = PCIE_DBI_BASE,
.end    = PCIE_DBI_BASE + __4KB__ - 1,
.flags  = IORESOURCE_REG,
}
};
static struct platform_driver hisi_pcie_platform_driver = {
.probe          = hisi_pcie_plat_driver_probe,
.remove         = hisi_pcie_plat_driver_remove,
.driver         = {
.owner  = THIS_MODULE,
.name   = PCIE_RC_DRV_NAME,
.bus    = &platform_bus_type,
.pm     = HISI_PCIE_PM_OPS
},
};
static struct platform_device hisi_pcie_platform_device = {
.name = PCIE_RC_DRV_NAME,
.id   = 0,
.dev = {
.platform_data  = NULL,
.dma_mask = &hipcie_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.release = hisi_pcie_platform_device_release,
},
.num_resources = ARRAY_SIZE(hisi_pcie_resources),
.resource = hisi_pcie_resources,
};
static int __init hisi_pcie_init(void)
{
int ret;
ret = platform_device_register(&hisi_pcie_platform_device);
if (ret)
goto err_device;
ret = platform_driver_register(&hisi_pcie_platform_driver);
if (ret)
goto err_driver;
if (pcie_init()) {
pcie_error("pcie sys init failed!");
goto err_init;
}
return 0;
err_init:
platform_driver_unregister(&hisi_pcie_platform_driver);
err_driver:
platform_device_unregister(&hisi_pcie_platform_device);
err_device:
return -1;
}

函数分析static int __init hisi_pcie_init(void)

1. 注册device和driver

ret = platform_device_register(&hisi_pcie_platform_device);  //注册设备

ret = platform_driver_register(&hisi_pcie_platform_driver);   //注册驱动

2.pcie_init();//hisi平台硬件寄存器相关基地址及寄存器配置;重点看这里哦!

static int __init pcie_init(void)
{
/*
* Scene: PCIe host(RC)<--->SWITCH<--->PCIe device(*)
*                            |
*                            |------->NULL SLOT
* PCIe will generate a DataAbort to ARM, when scaning NULL SLOT.
* Register hook to capture this exception and handle it.
*/
hook_fault_code(22, pcie_fault, 7, BUS_OBJERR,
"external abort on non-linefetch");
if (__arch_pcie_info_setup(pcie_info, &pcie_controllers_nr))
return -EIO;
if (__arch_pcie_sys_init(pcie_info))
goto pcie_init_err;
hipcie.nr_controllers = pcie_controllers_nr;
pr_err("Number of PCIe controllers: %d\n",
hipcie.nr_controllers);
pci_common_init(&hipcie);
return 0;
pcie_init_err:
__arch_pcie_info_release(pcie_info);
return -EIO;
}

1.__arch_pcie_info_setup(pcie_info, &pcie_controllers_nr)

2.__arch_pcie_sys_init(pcie_info)

3.pci_common_init(&hipcie);

我们一个个来看:

__arch_pcie_info_setup  完成重要的MEM/IO基地址映射

#define MISC_CTRL_BASE     0x12120000
#define PCIE_MEM_BASE       0x30000000
#define PCIE_EP_CONF_BASE   0x20000000
#define PCIE_DBI_BASE       0x1f000000
#define PERI_CRG_BASE       0x12040000
static int __arch_pcie_info_setup(struct pcie_info *info, int *controllers_nr)
{
unsigned int mem_size = CONFIG_PCIE0_DEVICES_MEM_SIZE;
unsigned int cfg_size = CONFIG_PCIE0_DEVICES_CONFIG_SIZE;
if ((mem_size > __256MB__) || (cfg_size > __256MB__)) {
pcie_error(
"Invalid parameter: pcie mem size[0x%x], pcie cfg size[0x%x]!",
mem_size, cfg_size);
return -EINVAL;
}
info->controller = 0;
/* RC configuration space */
info->conf_base_addr = (unsigned int)ioremap_nocache(PCIE_DBI_BASE,
__4KB__);
if (!info->conf_base_addr) {
pcie_error("Address mapping for RC dbi failed!");
return -EIO;
}
/* Configuration space for all EPs */
info->base_addr = (unsigned int)ioremap_nocache(PCIE_EP_CONF_BASE,
cfg_size);
if (!info->base_addr) {
iounmap((void *)info->conf_base_addr);
pcie_error("Address mapping for EPs cfg failed!");
return -EIO;
}
misc_ctrl_virt = ioremap_nocache(MISC_CTRL_BASE, __4KB__);
if (!misc_ctrl_virt) {
iounmap((void *)info->conf_base_addr);
iounmap((void *)info->base_addr);
pcie_error(
"Address mapping for misc control registers failed!");
return -EIO;
}
*controllers_nr = 1;
return 0;
}
__arch_pcie_sys_init(pcie_info) 按照data_sheet,对设备进行初始化

Reset=>PCIE RC work mode=>Enable clk=>Set PCIE controller class code to be PCI-PCI bridge device=>Enable controller

pci_common_init(&hipcie);

static struct hw_pci hipcie __initdata = {
.nr_controllers = 1,
.preinit    = pcie_preinit,
.swizzle    = pci_common_swizzle,
.setup      = pcie_setup,
.scan       = pcie_scan_bus,
.map_irq    = pcie_map_irq,
};
void pci_common_init(struct hw_pci *hw)
{
struct pci_sys_data *sys;
LIST_HEAD(head);
pci_add_flags(PCI_REASSIGN_ALL_RSRC);
if (hw->preinit)
hw->preinit();
pcibios_init_hw(hw, &head);
if (hw->postinit)
hw->postinit();
pci_fixup_irqs(pcibios_swizzle, pcibios_map_irq);
list_for_each_entry(sys, &head, node) {
struct pci_bus *bus = sys->bus;
if (!pci_has_flag(PCI_PROBE_ONLY)) {
/*
* Size the bridge windows.
*/
pci_bus_size_bridges(bus);
/*
* Assign resources.
*/
pci_bus_assign_resources(bus);
/*
* Enable bridges
*/
pci_enable_bridges(bus);
}
/*
* Tell drivers about devices found.
*/
pci_bus_add_devices(bus);
}
}

函数分析:

struct hw_pci 是关键,PCIE host驱动的开发,主要是填充该数据结构,把函数一个个实现,我们跟着代码来看一看

1. pcibios_init_hw(hw, &head);该函数初始化每一个controller,并且递归枚举它的子总线

1.1  ret = hw->setup(nr, sys);

1.2          ret = pcibios_init_resources(nr, sys);

1.3          sys->bus = hw->scan(nr, sys);

调用. setup即调用pcie_setup,这里关注两个函数:


ret = request_pcie_res(info->controller, sys);
=> ret = request_resource(&ioport_resource, io);
=> ret = request_resource(&iomem_resource, mem);

__arch_config_iatu_tbl(info, sys); 只有执行此config后,PCIE才能实现CFG_TYPE0 和CFG_TYPE1的配置事务访问(寄存器的具体配置请结合datasheet ATU地址转换)

调用.scan即调用pcie_scan_bus,它从主总线开始扫描总线上的PCI设备。一旦发现PCI-PCI桥,就初始化一条子总线,并且继续扫描子总线上的设备。这里注意

bus = pci_scan_root_bus(NULL, sys->busnr, &pcie_ops, sys, &sys->resources);

其中pcie_ops的定义:

static struct pci_ops pcie_ops = {
.read = pcie_read_conf,
.write = pcie_write_conf,
};

看到这里大家有点熟悉了没有PCIE设备驱动开发中常见的一组函数:

int pci_read_config_byte(struct pci_dev *pdev, int where, u8 *val);
int pci_read_config_word(struct pci_dev *pdev, int where, u8 *val);
int pci_read_config_dword(struct pci_dev *pdev, int where, u8 *val);
int pci_write_config_byte(struct pci_dev *pdev, int where, u8 *val);
int pci_write_config_word(struct pci_dev *pdev, int where, u8 *val);
int pci_write_config_dword(struct pci_dev *pdev, int where, u8 *val);

这组配置空间的读写函数,其实现就是pcie_ops,只有实现了该函数才能对PCIE设备读写,也只有实现了该函数pcie_scan_bus 才能完成总线上设备的扫描。

pci_scan_root_bus=>

pci_create_root_bus=>

pci_scan_child_bus(b);=>

pci_bus_add_devices(b); 完成扫描!!!

现在我们来看看pcie_ops 的read/write函数:


#define PCIE_CFG_BUS(busnr) ((busnr & 0xff) << 20)
#define PCIE_CFG_DEV(devfn) ((devfn & 0xff) << 12)
#define PCIE_CFG_REG(reg)   (reg & 0xffc)   /*set dword align*/
static inline unsigned int to_pcie_address(struct pci_bus *bus,
unsigned int devfn, int where)
{
struct pcie_info *info = bus_to_info(bus->number);
unsigned int address = 0;
if (!info) {
pcie_error(
"Cannot find corresponding controller for appointed device!");
BUG();
}
address = info->base_addr | PCIE_CFG_BUS(bus->number)
| PCIE_CFG_DEV(devfn) | PCIE_CFG_REG(where);
return address;
}
read:
addr = (void __iomem *)to_pcie_address(bus, devfn, where);
val = readl(addr);
write:
pcie_read_from_device(bus, devfn, where, 4, &org);
addr = (void __iomem *)to_pcie_address(bus, devfn, where);
if (size == 1) {
org &= (~(0xff << ((where & 0x3) << 3)));
org |= (value << ((where & 0x3) << 3));
} else if (size == 2) {
org &= (~(0xffff << ((where & 0x3) << 3)));
org |= (value << ((where & 0x3) << 3));
} else if (size == 4) {
org = value;
} else {
pcie_error("Unkown size(%d) for read ops", size);
BUG();
}
writel(org, addr);

配置事务读写就这样被实现了,从此我们可以轻松的访问PCIE配置空间:

pcie 驱动程序分析相关推荐

  1. PCI Express解析——系列文章【2】:PCIe原理分析之——PCI Express线路基础

    PCI Express解析--系列文章[2]:PCIe原理分析之--PCI Express线路基础 前文我们了解了一些概述的基本PCIe总线的简单知识点,下面本文从一些基本原理做一些分析 2 PCIe ...

  2. STM32-I2C总线驱动程序分析

    文章目录 I2C硬件电路原理图 I2C 简介 添加相应的文件并添加进工程文件 I2C驱动程序结构 I2C驱动程序分析 LM75A温度传感器电路原理图 LM75A温度传感器驱动程序分析 杨桃32学习笔记 ...

  3. android 电容屏(三):驱动调试之驱动程序分析篇

    平台信息: 内核:linux3.4.39 系统:android4.4  平台:S5P4418(cortex a9) 作者:瘋耔(欢迎转载,请注明作者) 欢迎指正错误,共同学习.共同进步!! 关注博主新 ...

  4. Microsoft宣布为Power BI提供AI模型构建器,关键驱动程序分析和Azure机器学习集成...

    微软的Power BI现在是一种正在大量结合人工智能(AI)的商业分析服务,它使用户无需编码经验或深厚的技术专长就能够创建报告,仪表板等.近日西雅图公司宣布推出几款新的AI功能,包括图像识别和文本分析 ...

  5. Linux NAND FLASH驱动程序分析(mini2440)

    Linux NAND FLASH驱动程序分析(mini2440) 一.Linux-MTD Subsystem介绍 FLASH在嵌入式系统中是必不可少的,它是bootloader.linux内核和文件系 ...

  6. linux网卡驱动程序分析

    linux网卡驱动程序分析 学习应该是一个先把问题简单化,在把问题复杂化的过程.一开始就着手处理复杂的问题,难免让人 有心惊胆颤,捉襟见肘的感觉.读Linux网卡驱动也是一样.那长长的源码夹杂着那些我 ...

  7. linux3.4.0 按键驱动程序分析(pandaboard omap4460)

    linux3.4.0 按键驱动程序分析(pandaboard omap4460) 在内核中,按键的驱动程序已经设计好了,要使自己板上的按键工作起来,需要做的是在相应的文件中添加硬件信息,然后对内核进行 ...

  8. Linux系统GIC驱动程序分析

    百问网技术交流群,百万嵌入式工程师聚集地: https://www.100ask.net/page/2248041 资料下载 coding无法使用浏览器打开,必须用git工具下载: git clone ...

  9. MTD 设备驱动 和 NAND Flash 驱动程序分析。

    硬件环境: 飞凌OK6410,256MB DDR,2GB NAND Flash.   NAND Flash 型号:K9G8G08U9A   .     分析源码:Linux 2.6.36.2 内核源码 ...

最新文章

  1. iOS开发UI篇—Date Picker和UITool Bar控件简单介绍
  2. es6 语法 (Proxy和Reflect 的对比)
  3. 102 二叉树层序遍历
  4. 一个方便使用的在线截图Web控件-WebImageMaker
  5. 补习系列(10)-springboot 之配置读取
  6. 联合、枚举和类型别名 - C++快速入门13
  7. 版本控制系统的演化、Git 分布式版本控制系统概述 及 工作流程
  8. 计算机组成原理同步测试,计算机组成原理白中英单元练习题
  9. WebService的知识总结(一)
  10. 为什么要写技术博客?
  11. 周记——20150817
  12. Android 控件数组
  13. MAXIMO语言切换,以及设置多语言
  14. npm install XXX 报错:error An unexpected error occurred:
  15. 传输层协议TCP和UDP的区别详解
  16. 深圳金融展 聚焦明朝万达数据防泄密
  17. 蓝牙产品做SRRC认证需要注意哪些事项?
  18. python 找到装饰器_[译] 12步轻松搞定python装饰器
  19. python处在哪个阶段_如何在学Python的过程中更好地成长技术
  20. 【金字塔Python量化学习笔记】01课:利用Python导出价格数据

热门文章

  1. DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATION error
  2. 【文献翻译】MDC-Checker:一种新的多域配置网络风险评估框架
  3. 大数相乘 (模板)
  4. 日常坚持记账,简单好用的记账本分享给你
  5. Python中Dataframe通过print输出多行时显示省略号
  6. c++中的类成员函数指针
  7. 【黑客攻防技术宝典】第4章 解析应用程序
  8. Linux 定时任务shell脚本定时移动服务器日志文件到指定目录下
  9. 中文核心期刊目录(2008年版).
  10. 公司企业兔年祝福元旦祝福贺卡邀请函模板!