pcie 驱动程序分析

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看看驱动程序是如何工作的。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 
          
#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;
}
 来自CODE的代码片
snippet_file_0.txt

函数分析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平台硬件寄存器相关基地址及寄存器配置;重点看这里哦!

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 
           
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;
}
 来自CODE的代码片
snippet_file_0.txt

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基地址映射

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 
           
#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;
}
 来自CODE的代码片
snippet_file_0.txt
__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);

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 
            
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);
}
}
 来自CODE的代码片
snippet_file_0.txt

函数分析:

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函数:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 
           
#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);
 来自CODE的代码片
snippet_file_0.txt
 


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



猜你喜欢

转载自blog.csdn.net/ds1130071727/article/details/80197704