海思linux运维 面试题,【EALinux硬件面试题】面试问题:pcie 驱动… - 看准网

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

<> 已经告诉我们如何从do_initcalls找到找到PCI 驱动的入口

postcore_initcall(pcibus_class_init);

postcore_initcall(pci_driver_init);

文件函数入口内存位置

arch/i386/pci/acpi.cpci_acpi_initsubsys_initcall.initcall4.init

arch/i386/pci/common.cpcibios_initsubsys_initcall.initcall4.init

arch/i386/pci/i386.cpcibios_assign_resourcesfs_initcall.initcall5.init

arch/i386/pci/legacy.cpci_legacy_init

drivers/pci/pci-acpi.cacpi_pci_initarch_initcall.initcall3.init

drivers/pci/pci- driver.cpci_driver_initpostcore_initcall.initcall2.init

drivers/pci/pci- sysfs.cpci_sysfs_initlate_initcall.initcall7.init

drivers/pci/pci.cpci_initdevice_initcall.initcall6.init

drivers/pci/probe.cpcibus_class_initpostcore_initcall.initcall2.init

drivers/pci/proc.cpci_proc_init__initcall.initcall6.init

arch/i386/pci/init.cpci_access_initarch_initcall.initcall3.init

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

文件函数入口内存位置

drivers/pci/hipcie/pcie.cacpi_pci_initsubsys_initcall.initcall4.init

drivers/pci/pci- driver.cpci_driver_initpostcore_initcall.initcall2.init

drivers/pci/pci- sysfs.cpci_sysfs_initlate_initcall.initcall7.init

drivers/pci/pci.cpci_initdevice_initcall.initcall6.init

drivers/pci/probe.cpcibus_class_initpostcore_initcall.initcall2.init

drivers/pci/proc.cpci_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;}

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

static int __init pcie_init(void){ /* * Scene: PCIe host(RC)SWITCHPCIe 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基地址映射

#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 0x12040000static 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);

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);intpci_write_config_byte(structpci_dev*pdev,intwhere, u8*val);intpci_write_config_word(structpci_dev*pdev,intwhere, u8*val);intpci_write_config_dword(structpci_dev*pdev,intwhere, 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);

来自CODE的代码片snippet_file_0.txt

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值