pcie ecam --- Linux kernel 实现欣赏

在这里插入图片描述

请根据我的代码帮我分析设备树信息 pcie@d8900000 { compatible = "hisilicon,udrv-pcie-ecam-dt"; reg = <0x0b 0x00 0x00 0x400000 0x00 0xd8900000 0x00 0x80000 0x00 0x700000 0x00 0x00>; reg-names = "config\0dbi\0msi"; bus-range = <0x00 0x03>; ranges = <0x82000000 0x00 0x00 0x0b 0x800000 0x00 0x1f800000>; #address-cells = <0x03>; #size-cells = <0x02>; device_type = "pci"; dma-coherent; msi-trans-type = <0x01>; interrupts = <0x00 0x1f7 0x04>; #interrupt-cells = <0x01>; interrupt-map-mask = <0xf800 0x00 0x00 0x07>; interrupt-map = <0x00 0x00 0x00 0x01 0x01 0x00 0x1f9 0x04 0x00 0x00 0x00 0x02 0x01 0x00 0x1fa 0x04 0x00 0x00 0x00 0x03 0x01 0x00 0x1fb 0x04 0x00 0x00 0x00 0x04 0x01 0x00 0x1fc 0x04>; linux,pci-domain = <0x00>; core-version = <0x00>; clocks_num = <0x02>; clocks = <0x09 0x00 0x09 0x01>; clock-names = "CLK_PCIE_CLK0\0CLK_PCIE_CLK1"; resets_num = <0x01>; resets = <0x09 0x00>; reset-names = "RST_PCIE_RST0"; status = "okay"; port@0 { lanes-nums = <0x01>; max-lanes = <0x01>; max-speed = <0x03>; port-id = <0x00>; lport-id = <0x00>; core-id = <0x00>; port-type = <0x01>; target-speed = <0x01>; probe = <0x01>; 中的reg = <0x0b 0x00 0x00 0x400000 0x00 0xd8900000 0x00 0x80000 0x00 0x700000 0x00 0x00>;是在代码中哪部分解析的,接着按照 依次表示配置空间、寄存器、msi的基地址及大小,配置格式为linux通用的reg格式,64位。<addr_h addr_l size_h size_l>, <addr_h addr_l size_h size_l>, <addr_h addr_l size_h size_l> addr_h: 寄存器空间基址高32bit addr_l: 寄存器空间基址低32bit size_h: 寄存器空间大小高32bit size_l: 寄存器空间大小低32bit 来给我解释这几个数值是什么意思 另外帮我分析原本单板接pcie siwtch设备后,挂在switch上的pcie网卡设备lspci只能识别一个(实际挂载不止一个), 为什么将reg = <0x0b 0x00 0x00 0x400000 0x00 0xd8900000 0x00 0x80000 0x00 0x700000 0x00 0x00>;中的0x400000改成0x800000,以及bus-range = <0x00 0x03>中的0x3改大之后 lspci就能识别剩下的挂载的pcie设备了呢 代码部分: #include <linux/module.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/of_address.h> #include <linux/of_pci.h> #include <linux/platform_device.h> #include <linux/of_device.h> #include <linux/pci.h> #include <linux/pci-ecam.h> #include <linux/kallsyms.h> #include <linux/err.h> #include <linux/irqchip/chained_irq.h> #include <linux/irq.h> #include <linux/msi.h> #include <linux/fwnode.h> #include <linux/acpi.h> #include <linux/idr.h> #include <linux/version.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) #include <linux/reset-controller.h> #include <linux/reset.h> #else #include "../subctrl/include.linux/reset-controller.h" #include "../subctrl/include.linux/reset.h" #endif #include <linux/clk.h> #include <linux/version.h> #include "pcie_udrv.h" #define MSI_ADDR_DEST_ID_SHIFT 4 #define MSI_ADDR_HEADER 0xfee00000 #define MSI_ADDR_DEST_ID_MASK 0xfff0000f #define MSI_ADDR_DEST_ID_CPU(cpu) ((cpu) << MSI_ADDR_DEST_ID_SHIFT) LIST_HEAD(pcie_host_list); static int udrv_pcie_host_ep_init(struct platform_device *pdev); #define copy_resource(dst, src, name_) do { \ (dst)->start = (src)->start; \ (dst)->end = (src)->end; \ (dst)->flags = (src)->flags; \ (dst)->name = name_; \ } while (0) #define UDRV_MAX_PCIE_HOST_NUM 16 #define PCIE_DP_PORT 2 #define PCIE_UP_PORT 3 #define PCIE_MAX_FMEA_DEV_NUM 12 DEFINE_IDR(pcie_idr); static void udrv_pcie_msi_mask_irq(struct irq_data *data) { pci_msi_mask_irq(data); irq_chip_mask_parent(data); } static void udrv_pcie_msi_unmask_irq(struct irq_data *data) { pci_msi_unmask_irq(data); irq_chip_unmask_parent(data); } static struct irq_chip udrv_pcie_msi_irq_chip = { .name = "udrv_msi", .irq_mask = udrv_pcie_msi_mask_irq, .irq_unmask = udrv_pcie_msi_unmask_irq, }; static struct msi_domain_info udrv_pcie_msi_domain_info = { .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), .chip = &udrv_pcie_msi_irq_chip, }; static void udrv_pcie_handle_msi_irq(struct udrv_pcie_host *pcie) { uint32_t status = 0; unsigned long bit_pos = 0, status_u64 = 0; uint32_t irq; handle pf_handle = pcie->host_info.pf_handle; uint32_t ret = ndrv_pcie_host_get_msi_status(pf_handle, &status); if ((ret != 0) || (status == 0)) { return; } status_u64 = (unsigned long)status; (void)ndrv_pcie_host_msi_mask_all(pf_handle, 0xFFFFFFFF); while ((bit_pos = find_next_bit(&status_u64, UDRV_MAX_MSI_IRQS, bit_pos)) != UDRV_MAX_MSI_IRQS) { irq = irq_find_mapping(pcie->msi.irq_domain, bit_pos); (void)ndrv_pcie_host_msi_clear(pf_handle, (u32)bit_pos); generic_handle_irq(irq); bit_pos++; } (void)ndrv_pcie_host_msi_mask_all(pf_handle, 0); } static void udrv_pcie_handle_chained_msi_irq(struct irq_desc *desc) { struct udrv_pcie_host *pcie = NULL; struct irq_chip *chip = irq_desc_get_chip(desc); chained_irq_enter(chip, desc); pcie = irq_desc_get_handler_data(desc); udrv_pcie_handle_msi_irq(pcie); chained_irq_exit(chip, desc); } static void udrv_pcie_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) { struct udrv_pcie_host *pcie = irq_data_get_irq_chip_data(data); struct udrv_pcie_host_msi_info *msi = &pcie->msi; msg->address_lo = lower_32_bits(msi->msi_addr); msg->address_hi = upper_32_bits(msi->msi_addr); msg->data = (u32)data->hwirq; dev_info(pcie->dev, "[iWare][Info] msi#%d address_hi %#x address_lo %#x\n", (int)data->hwirq, msg->address_hi, msg->address_lo); } static int udrv_pcie_msi_set_affinity(struct irq_data *irq_data, const struct cpumask *mask, bool force) { struct udrv_pcie_host *pcie; struct msi_desc *desc; struct msi_msg msg; int cpu; /* 检查 irq_data 和 mask 是否为 NULL */ if (!irq_data || !mask) { return -EINVAL; } /* 获取 irq_data 中的数据 */ pcie = irq_data_get_irq_chip_data(irq_data); desc = irq_data_get_msi_desc(irq_data); /* 检查 pcie 和 desc 是否为 NULL */ if (!pcie || !desc) { return -EINVAL; } msg = desc->msg; /* 1. 获取有效的目标 CPU */ cpu = cpumask_first_and(mask, cpu_online_mask); if (cpu >= nr_cpu_ids) { return -EINVAL; } /* 2. 更新 MSI 消息目标 ID */ msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; msg.address_lo |= MSI_ADDR_DEST_ID_CPU(cpu); /* 3. 写入新的 MSI 消息 */ pci_write_msi_msg(irq_data->irq, &msg); /* 4. 更新父中断控制器 */ if (irq_data->parent_data && irq_data->parent_data->chip && irq_data->parent_data->chip->irq_set_affinity) { return irq_data->parent_data->chip->irq_set_affinity(irq_data->parent_data, mask, force); } return IRQ_SET_MASK_OK_DONE; } static void udrv_pcie_bottm_mask(struct irq_data *data) { struct udrv_pcie_host *pcie = irq_data_get_irq_chip_data(data); struct udrv_pcie_host_msi_info *msi = &pcie->msi; handle pf_handle = pcie->host_info.pf_handle; unsigned long flags; raw_spin_lock_irqsave(&msi->lock, flags); ndrv_pcie_host_msi_mask_one(pf_handle, (u32)data->hwirq, 1); raw_spin_unlock_irqrestore(&msi->lock, flags); } static void udrv_pcie_bottm_unmask(struct irq_data *data) { struct udrv_pcie_host *pcie = irq_data_get_irq_chip_data(data); struct udrv_pcie_host_msi_info *msi = &pcie->msi; handle pf_handle = pcie->host_info.pf_handle; unsigned long flags; raw_spin_lock_irqsave(&msi->lock, flags); ndrv_pcie_host_msi_mask_one(pf_handle, (u32)data->hwirq, 0); raw_spin_unlock_irqrestore(&msi->lock, flags); } static struct irq_chip udrv_pcie_msi_bottom_irq_chip = { .name = "udrv_bottom_msi", .irq_compose_msi_msg = udrv_pcie_compose_msi_msg, .irq_set_affinity = udrv_pcie_msi_set_affinity, .irq_mask = udrv_pcie_bottm_mask, .irq_unmask = udrv_pcie_bottm_unmask, }; static int dw_pcie_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int num_irqs, void *args) { unsigned long flags; struct udrv_pcie_host *pcie = domain->host_data; struct udrv_pcie_host_msi_info *msi = &pcie->msi; int bit; uint32_t i; raw_spin_lock_irqsave(&msi->lock, flags); bit = bitmap_find_free_region(msi->msi_irq_in_use_bits, UDRV_MAX_MSI_IRQS, order_base_2(num_irqs)); raw_spin_unlock_irqrestore(&msi->lock, flags); if (bit < 0) { return -ENOSPC; } for (i = 0; i < num_irqs; i++) { irq_domain_set_info(domain, virq + i, bit + i, &udrv_pcie_msi_bottom_irq_chip, pcie, handle_level_irq, NULL, NULL); } return 0; } static void udrv_pcie_irq_domain_free(struct irq_domain *domain, unsigned int virq, unsigned int num_irqs) { unsigned long flags; struct udrv_pcie_host *pcie = domain->host_data; struct udrv_pcie_host_msi_info *msi = &pcie->msi; struct irq_data *data = irq_domain_get_irq_data(domain, virq); uint32_t i; raw_spin_lock_irqsave(&msi->lock, flags); bitmap_release_region(msi->msi_irq_in_use_bits, (u32)data->hwirq, order_base_2(num_irqs)); raw_spin_unlock_irqrestore(&msi->lock, flags); for (i = 0; i < num_irqs; i++) { data = irq_domain_get_irq_data(domain, virq + i); irq_domain_reset_irq_data(data); } } static const struct irq_domain_ops udrv_pcie_msi_domain_ops = { .alloc = dw_pcie_irq_domain_alloc, .free = udrv_pcie_irq_domain_free, }; static void udrv_pcie_remove_msi(struct udrv_pcie_host *pcie) { handle pf_handle = pcie->host_info.pf_handle; struct udrv_pcie_host_msi_info *msi = &pcie->msi; (void)ndrv_pcie_host_msi_mask_all(pf_handle, 0xFFFFFFFF); if (msi->msi_trans_type == MSI_ITS) { return; } irq_set_chained_handler((u32)msi->irq, NULL); irq_set_handler_data((u32)msi->irq, NULL); irq_domain_remove(msi->msi_domain); irq_domain_remove(msi->irq_domain); } static int udrv_pcie_allocate_msi_domains(struct udrv_pcie_host *pcie) { struct udrv_pcie_host_msi_info *msi = &pcie->msi; struct fwnode_handle *fwnode = of_node_to_fwnode(pcie->dev->of_node); msi->irq_domain = irq_domain_create_linear(fwnode, UDRV_MAX_MSI_IRQS, &udrv_pcie_msi_domain_ops, pcie); if (!msi->irq_domain) { dev_err(pcie->dev, "[iWare][Error] irq_domain_create_linear fail\n"); return -ENOMEM; } msi->msi_domain = pci_msi_create_irq_domain(fwnode, &udrv_pcie_msi_domain_info, msi->irq_domain); if (!msi->msi_domain) { irq_domain_remove(msi->irq_domain); dev_err(pcie->dev, "[iWare][Error] pci_msi_create_irq_domain fail\n"); return -ENOMEM; } return 0; } static int udrv_pcie_get_msi_info_from_dt(struct udrv_pcie_host *pcie) { struct device *dev = pcie->dev; struct udrv_pcie_host_msi_info *msi = &pcie->msi; struct resource *res = NULL; struct platform_device *pdev = to_platform_device(dev); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "msi"); if (!res) { dev_err(dev, "[iWare][Error] get msi address fail\n"); return -EINVAL; } if (of_property_read_u32(dev->of_node, "msi-trans-type", (u32 *)(uintptr_t)(&msi->msi_trans_type)) != 0) { msi->msi_trans_type = MSI_ITS; } msi->msi_addr = res->start; if (msi->msi_trans_type == MSI_ITS) { return 0; } msi->irq = platform_get_irq(pdev, 0); if (msi->irq <= 0) { dev_info(dev, "[iWare][Info] no msi irq,now jump\n"); return 0; } return 0; } static void udrv_pcie_free_atu_info(struct udrv_pcie_host *pcie) { struct list_head *head = &pcie->host_info.atu_info.entry; struct udrv_pcie_atu_info *tmp = NULL; struct udrv_pcie_atu_info *pos = NULL; list_for_each_entry_safe(pos, tmp, head, entry) { list_del(&pos->entry); kfree(pos); } } static void udrv_pcie_free_port_info(struct udrv_pcie_host *pcie) { struct list_head *head = &pcie->port_info.entry; struct udrv_pcie_port_info *tmp = NULL; struct udrv_pcie_port_info *pos = NULL; list_for_each_entry_safe(pos, tmp, head, entry) { if (pos->idr >= 0) { idr_remove(&pcie_idr, pos->idr); } list_del(&pos->entry); kfree(pos); } } static int udrv_pcie_get_atu_info_from_dt(struct udrv_pcie_host *pcie) { struct device *dev = pcie->dev; struct udrv_pcie_atu_info *atu_info = NULL; struct list_head *head = &pcie->host_info.atu_info.entry; struct of_pci_range_parser parser; struct of_pci_range range; int ret = of_pci_range_parser_init(&parser, dev->of_node); if (ret != 0) { dev_err(dev, "[iWare][Error] parser range failed\n"); goto err_range; } for_each_of_pci_range(&parser, &range) { if (((range.flags & IORESOURCE_TYPE_BITS) != IORESOURCE_IO) && ((range.flags & IORESOURCE_TYPE_BITS) != IORESOURCE_MEM)) { continue; } atu_info = kzalloc(sizeof(struct udrv_pcie_atu_info), GFP_KERNEL); if (atu_info == NULL) { ret = -ENOMEM; goto fail; } atu_info->cpu_addr = range.cpu_addr; atu_info->pcie_addr = range.pci_addr; atu_info->size = range.size; atu_info->atu_mode = ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM) ? 0 : 1; list_add_tail(&atu_info->entry, head); } return 0; fail: udrv_pcie_free_atu_info(pcie); err_range: ndrv_pcie_close_pf(pcie->host_info.pf_handle, pcie->host_info.core_version); return ret; } static int udrv_pcie_get_host_property_info_from_dt(struct device *dev, struct udrv_pcie_host_info *host_info) { if (of_property_read_u32(dev->of_node, "type-support", (u32 *)(uintptr_t)(&host_info->type_support_mask)) != 0) { dev_info(dev, "[iWare] no declare type support, default rp mode\n"); set_bit(NDRV_PCIE_RP_MODE, &(host_info->type_support_mask)); } if (of_property_read_u32(dev->of_node, "clocks_num", &host_info->clk_num) != 0) { dev_err(dev, "[iWare][Error] Faild read clk_num\n"); return -EINVAL; } if (of_property_read_u32(dev->of_node, "resets_num", &host_info->rst_num) != 0) { dev_err(dev, "[iWare][Error] Faild read rsts_num\n"); return -EINVAL; } if (of_property_read_u32(dev->of_node, "core-version", &host_info->core_version) != 0) { dev_err(dev, "[iWare][Error] Faild to read core version\n"); return -EINVAL; } if (of_property_read_u32(dev->of_node, "rc_mode", (u32 *)(uintptr_t)(&host_info->rc_mode)) != 0) { host_info->rc_mode = NDRV_PCIE_RC_NORMAL; } return 0; } static int udrv_pcie_get_host_info_from_dt(struct udrv_pcie_host *pcie) { struct device *dev = pcie->dev; struct platform_device *pdev = to_platform_device(dev); struct udrv_pcie_host_info *host_info = &pcie->host_info; struct resource *apb_res = NULL; struct io_region region; int ret; if (of_property_read_u32(dev->of_node, "linux,pci-domain", &host_info->host_id) != 0) { dev_err(dev, "[iWare][Error] Faild read pci-domain\n"); return -EINVAL; } if (host_info->host_id >= UDRV_MAX_PCIE_HOST_NUM) { dev_err(dev, "[iWare][Error] Invalid domain nr = 0x%x\n", host_info->host_id); return -EINVAL; } ret = udrv_pcie_get_host_property_info_from_dt(dev, host_info); if (ret != 0) { return ret; } apb_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); if (apb_res == NULL) { dev_err(dev, "[iWare][Error] Faild to get dbi address\n"); return -EINVAL; } host_info->apb_base = devm_pci_remap_cfgspace(dev, apb_res->start, resource_size(apb_res)); host_info->apb_paddr = apb_res->start; host_info->apb_size = (u32)resource_size(apb_res); if (IS_ERR(host_info->apb_base)) { dev_err(dev, "[iWare][Error] Faild to remap apb_base\n"); return -EINVAL; } region.io_base = host_info->apb_base; region.io_size = host_info->apb_size; host_info->pf_handle = ndrv_pcie_open_pf(&region, host_info->core_version, host_info->rc_mode); if (host_info->pf_handle == NULL) { dev_err(pcie->dev, "[iWare][Error] ndrv pcie_open_pf fail\n"); return -EINVAL; } return udrv_pcie_get_atu_info_from_dt(pcie); } static int udrv_pcie_host_init(struct udrv_pcie_host *pcie) { u32 ret; u32 atu_id = 0; struct ndrv_pcie_ecam_cfg_info ecam_cfg; struct ndrv_pcie_atu_cfg_info atu_cfg; struct udrv_pcie_host_info *host_info = &pcie->host_info; struct udrv_pcie_atu_info *pos = NULL; struct list_head *head = &pcie->host_info.atu_info.entry; /* 仅支持RP的host,需要初始化TX atu和ecam */ if (test_bit(NDRV_PCIE_RP_MODE, &(host_info->type_support_mask)) != 0) { list_for_each_entry(pos, head, entry) { atu_cfg.tx_src_base_addr = pos->cpu_addr; atu_cfg.tx_dst_base_addr = pos->pcie_addr; atu_cfg.tx_region_size = pos->size; atu_cfg.atu_mode = pos->atu_mode; ret = ndrv_pcie_host_ap_atu_init(host_info->pf_handle, &atu_cfg, atu_id); ++atu_id; if (ret != 0) { pr_err("[iWare][Error] init atu:0x%x failed, ret=%u\n", atu_id, ret); return -EINVAL; } } ecam_cfg.ecam_base_addr_l = (u32)(host_info->ecam_res.start); ecam_cfg.ecam_base_addr_h = (u32)(host_info->ecam_res.start >> 32); /* 高32bit */ ecam_cfg.ecam_base_size = (u32)resource_size(&host_info->ecam_res); ecam_cfg.ecam_start_bus_no = (u32)host_info->bus_res.start; ret = ndrv_pcie_host_ap_ecam_init(host_info->pf_handle, &ecam_cfg); if (ret != 0) { pr_err("[iWare][Error] init ap ecam failed, ret=%u\n", ret); return -EINVAL; } } ret = ndrv_pcie_host_ap_enable(host_info->pf_handle, 1); if (ret != 0) { pr_err("[iWare][Error] ap enable failed, ret=%u\n", ret); return -EINVAL; } return 0; } #ifdef CONFIG_UDRV_FMEA static int pcie_fmea_init(struct udrv_pcie_host *pcie) { int ret; struct fmea_dev_info dev_info; const struct ndrv_fmea_item_info *item_table; struct ndrv_pcie_fmea_open_cfg cfg; dev_info.dev = pcie->dev; dev_info.pdev_id = pcie->host_info.host_id; dev_info.name = FMEA_MODULE_NAME(pcie); cfg.paddr = pcie->host_info.apb_paddr; item_table = ndrv_pcie_pf_get_fmea_table(pcie->host_info.pf_handle, &cfg); if (item_table == NULL) { dev_err(pcie->dev, "[iWare][Error] pcie get_fmea_table fail\n"); return -EINVAL; } ret = kdrv_fmea_iomm_unit_list_init(&dev_info, item_table, &pcie->iomm_info); if (ret != 0) { dev_err(pcie->dev, "[iWare][Error] kdrv fmea_iomm_unit_list_init fail\n"); return -EINVAL; } return 0; } static void pcie_fmea_deinit(struct udrv_pcie_host *pcie) { kdrv_fmea_iomm_unit_list_deinit(&pcie->iomm_info); } int kdrv_pcie_fmea_entry(u32 pcie_id, u32 group_id, u64 *err_info, u32 *alm_flg, char *buf, u32 size) { struct udrv_pcie_host *pcie = NULL; struct fmea_iomm_unit *pcie_fmobj = NULL; pcie = hisi_pcie_get_by_host_id(pcie_id); if (pcie == NULL) { pr_err("[iWare][Error] [pcie fmea entry], get pcie failed\n"); return -ENODEV; } if (group_id >= pcie->iomm_info.group_num) { return -ERANGE; } pcie_fmobj = &pcie->iomm_info.iomms[group_id]; if (!pcie_fmobj) { return -ENODEV; } return kdrv_fmea_entry(pcie_fmobj, err_info, alm_flg, buf, size); } EXPORT_SYMBOL(kdrv_pcie_fmea_entry); int kdrv_pcie_get_fmea_group_num_(struct udrv_pcie_host *pcie, uint32_t *num) { if (pcie == NULL || num == NULL) { return -EINVAL; } *num = pcie->iomm_info.group_num; return 0; } int kdrv_pcie_get_fmea_dev_num_(struct udrv_pcie_host *pcie, uint32_t *num) { if (pcie == NULL || num == NULL) { return -EINVAL; } *num = PCIE_MAX_FMEA_DEV_NUM; return 0; } #endif static int udrv_pcie_host_ecam_init(struct pci_config_window *cfg_w) { int ret; struct platform_device *pdev = to_platform_device(cfg_w->parent); struct udrv_pcie_host *pcie = platform_get_drvdata(pdev); struct udrv_pcie_host_info *host_info = &pcie->host_info; struct resource *cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); if (!cfg_res) { pr_err("[iWare]cfg_res is null\n"); return -EINVAL; } copy_resource(&host_info->ecam_res, cfg_res, "config"); copy_resource(&host_info->bus_res, &cfg_w->busr, "bus"); #ifdef CONFIG_UDRV_DEBUG udrv_pcie_show_host_info(pcie); #endif ret = pcie->ops->port_ops->init(pcie); if (ret != 0) { pr_err("[iWare][Error] port init failed,ret=%d\n", ret); return ret; } ret = pcie->ops->host_ops->init(pcie); if (ret != 0) { pr_err("[iWare][Error] host init failed,ret=%d\n", ret); return ret; } ret = pcie->ops->msi_ops->init(pcie); if (ret != 0) { pr_err("[iWare][Error] msi init failed,ret=%d\n", ret); } return ret; } /* host仅支持ep场景,仅初始化芯片 */ static int udrv_pcie_host_ep_init(struct platform_device *pdev) { int ret; struct udrv_pcie_host *pcie = platform_get_drvdata(pdev); #ifdef CONFIG_UDRV_DEBUG udrv_pcie_show_host_info(pcie); #endif ret = pcie->ops->port_ops->init(pcie); if (ret != 0) { return ret; } ret = pcie->ops->host_ops->init(pcie); if (ret != 0) { return ret; } ret = pcie->ops->msi_ops->init(pcie); return ret; } static int udrv_pcie_get_switch_info(struct device *dev, struct device_node *child, struct udrv_pcie_port_info *port_info) { if (of_property_read_u32(child, "switch-core-id", &port_info->switch_core_id) != 0) { dev_err(dev, "[iWare][Error] Faild to read switch-core-id\n"); return -EINVAL; } if (of_property_read_u32(child, "core-0-1-dp-bitmap", &port_info->core_0_1_dp_bitmap) != 0) { dev_err(dev, "[iWare][Error] Faild to read core-0-1-dp-bitmap\n"); return -EINVAL; } if (of_property_read_u32(child, "core-2-3-dp-bitmap", &port_info->core_2_3_dp_bitmap) != 0) { dev_err(dev, "[iWare][Error] Faild to read core-2-3-dp-bitmap\n"); return -EINVAL; } if (of_property_read_u32(child, "up-core-id", &port_info->up_core_id) != 0) { dev_err(dev, "[iWare][Error] Faild to read up-core-id\n"); return -EINVAL; } return 0; } static int udrv_pcie_get_port_info_from_dts(struct device *dev, struct device_node *child, struct udrv_pcie_port_info *port_info) { if (of_property_read_u32(child, "port-type", &port_info->port_type) != 0) { dev_err(dev, "[iWare][Error] Faild to read port-type\n"); return -EINVAL; } if (port_info->port_type == NDRV_PCIE_EP_MODE) { if (of_property_read_u64(child, "ep-addr", &port_info->ep_addr) != 0) { dev_err(dev, "[iWare][Error] Faild to read ep_addr\n"); return -EINVAL; } if (of_property_read_u32(child, "ep-size", &port_info->ep_size) != 0) { dev_err(dev, "[iWare][Error] Faild to read ep_size\n"); return -EINVAL; } } if (of_property_read_u32(child, "port-id", &port_info->port_id) != 0) { dev_err(dev, "[iWare][Error] Faild to read port-id\n"); return -EINVAL; } if (of_property_read_u32(child, "lport-id", &port_info->lport_id) != 0) { dev_err(dev, "[iWare][Error] Faild to read lport-id\n"); return -EINVAL; } if (of_property_read_u32(child, "pri_bus", &port_info->pri_bus) != 0) { port_info->pri_bus = 0; } if (of_property_read_u32(child, "sec_bus", &port_info->sec_bus) != 0) { port_info->sec_bus = 0; } if (of_property_read_u32(child, "sub_bus", &port_info->sub_bus) != 0) { port_info->sub_bus = 0; } return 0; } static int udrv_pcie_get_port_info_child_dt(struct device *dev, struct device_node *child, struct udrv_pcie_port_info *port_info) { int ret; ret = udrv_pcie_get_port_info_from_dts(dev, child, port_info); if (ret != 0) { return ret; } if (of_property_read_u32(child, "lanes-nums", &port_info->lane_num) != 0) { dev_err(dev, "[iWare][Error] Faild to read lanes\n"); return -EINVAL; } if (of_property_read_u32(child, "max-lanes", &port_info->max_lanes) != 0) { port_info->max_lanes = port_info->lane_num; } if (of_property_read_u32(child, "max-speed", &port_info->max_speed) != 0) { dev_err(dev, "[iWare][Error] Faild to read max-speed\n"); return -EINVAL; } if (of_property_read_u32(child, "target-speed", &port_info->target_speed) != 0) { port_info->target_speed = port_info->max_speed; } if (of_property_read_u32(child, "payload", &port_info->payload) != 0) { port_info->payload = NDRV_PCIE_PAYLOAD_128B; } if (of_property_read_u32(child, "read_req", &port_info->read_req) != 0) { port_info->read_req = NDRV_PCIE_PAYLOAD_512B; } if (of_property_read_u32(child, "pcs_clk", &port_info->pcs_clk) != 0) { port_info->pcs_clk = NDRV_PCIE_PCS_CLK_100M; } if (of_property_read_u32(child, "core-id", &port_info->core_id) != 0) { port_info->core_id = 0; } if (of_property_read_u32(child, "probe", &port_info->is_probe) != 0) { port_info->is_probe = 0; } if (of_property_read_u32(child, "aer_en", &port_info->aer_en) != 0) { port_info->aer_en = 0; } if (port_info->port_type == PCIE_DP_PORT || port_info->port_type == PCIE_UP_PORT) { return udrv_pcie_get_switch_info(dev, child, port_info); } return 0; } static int udrv_pcie_get_port_info_from_dt(struct udrv_pcie_host *pcie) { struct device *dev = pcie->dev; struct device_node *child = NULL; struct udrv_pcie_port_info *port_info = NULL; struct list_head *head = &pcie->port_info.entry; int ret; for_each_child_of_node(dev->of_node, child) { port_info = kzalloc(sizeof(struct udrv_pcie_port_info), GFP_KERNEL); if (!port_info) { ret = -ENOMEM; goto get_port_fail; } list_add_tail(&port_info->entry, head); ret = udrv_pcie_get_port_info_child_dt(dev, child, port_info); if (ret != 0) { dev_err(dev, "[iWare][Error] get child dt failed,ret:%d\n", ret); goto get_port_fail; } port_info->idr = (u32)idr_alloc(&pcie_idr, pcie, (int)port_info->lport_id, (int)port_info->lport_id + 1, GFP_KERNEL); if ((port_info->idr < 0) || (port_info->idr != port_info->lport_id)) { dev_err(dev, "[iWare][Error] idr_alloc fail, port_id:%d, idr:%d\n", port_info->port_id, port_info->idr); ret = -ENOSPC; goto get_port_fail; } } return 0; get_port_fail: udrv_pcie_free_port_info(pcie); return ret; } static int udrv_pcie_subctrl_dereset(struct udrv_pcie_host *pcie) { struct device *dev = pcie->dev; struct reset_control *rst = NULL; struct clk *clk = NULL; struct device_node *node = dev->of_node; int ret, i; u32 clk_num = pcie->host_info.clk_num; u32 rst_num = pcie->host_info.rst_num; for (i = 0; i < (int)rst_num; i++) { rst = of_reset_control_get_by_index(node, i); if (IS_ERR(rst)) { dev_err(dev, "[iWare][Error] [udrv pcie_subctrl_dereset] get rst failed\n"); return -EFAULT; } ret = reset_control_deassert(rst); if (ret != 0) { dev_err(dev, "[iWare][Error] [udrv pcie_subctrl_dereset] soft rst failed, ret=%d\n", ret); return ret; } reset_control_put(rst); } for (i = 0; i < (int)clk_num; i++) { clk = of_clk_get(node, i); if (IS_ERR(clk)) { dev_err(dev, "[iWare][Error] [udrv pcie_subctrl_dereset] get clk failed\n"); return -EFAULT; } ret = clk_prepare_enable(clk); if (ret != 0) { dev_err(dev, "[iWare][Error] [udrv pcie_subctrl_dereset] open clk failed, ret=%d\n", ret); return ret; } clk_put(clk); } return 0; } static void udrv_pcie_convert_port_cfg(struct ndrv_pcie_port_cfg_info *cfg, struct udrv_pcie_port_info *pos) { cfg->phy_port_id = pos->port_id; cfg->mode = pos->port_type; cfg->ep_addr = pos->ep_addr; cfg->ep_size = pos->ep_size; cfg->lane_num = pos->lane_num; cfg->target_speed = pos->target_speed; cfg->max_lane = pos->max_lanes; cfg->max_speed = pos->max_speed; cfg->core_id = pos->core_id; cfg->payload = pos->payload; cfg->read_req = pos->read_req; cfg->pcs_clk = pos->pcs_clk; cfg->switch_info.switch_core_id = pos->switch_core_id; cfg->switch_info.core0_1_dp_bitmap = pos->core_0_1_dp_bitmap; cfg->switch_info.core2_3_dp_bitmap = pos->core_2_3_dp_bitmap; cfg->switch_info.up_core_id = pos->up_core_id; cfg->pri_bus = pos->pri_bus; cfg->sec_bus = pos->sec_bus; cfg->sub_bus = pos->sub_bus; cfg->aer_en = pos->aer_en; } static int udrv_pcie_port_init(struct udrv_pcie_host *pcie) { u32 u_ret; int ret; struct udrv_pcie_host_info *host_info = &pcie->host_info; struct udrv_pcie_port_info *pos = NULL; struct list_head *head = &pcie->port_info.entry; struct ndrv_pcie_port_cfg_info cfg; /* 打开时钟和解复位 */ ret = udrv_pcie_subctrl_dereset(pcie); if (ret != 0) { dev_err(pcie->dev, "[iWare][Error] host_id = %u, subctrl failed, ret = 0x%x\n", host_info->host_id, ret); return ret; } list_for_each_entry(pos, head, entry) { udrv_pcie_convert_port_cfg(&cfg, pos); u_ret = ndrv_pcie_host_port_init(host_info->pf_handle, &cfg); if (u_ret != 0) { dev_err(pcie->dev, "[iWare][Error] port_init fail, host_id = %u, ret = 0x%x\n", host_info->host_id, u_ret); return -EINVAL; } if (pos->is_probe != 0) { u_ret = ndrv_pcie_set_port_enable(host_info->pf_handle, pos->core_id, pos->port_id, 1); if (u_ret != 0) { return -EINVAL; } } } return 0; } static int udrv_pcie_msi_init(struct udrv_pcie_host *pcie) { struct udrv_pcie_host_msi_info *msi = &pcie->msi; handle pf_handle = pcie->host_info.pf_handle; u32 u_ret; int ret; u_ret = ndrv_pcie_host_set_msi_addr(pf_handle, msi->msi_addr); if (u_ret != 0) { return -EINVAL; } u_ret = ndrv_pcie_host_set_msi_enable(pf_handle, msi->msi_trans_type); if (u_ret != 0) { return -EINVAL; } if (msi->msi_trans_type == MSI_ITS) { return 0; } raw_spin_lock_init(&msi->lock); ret = udrv_pcie_allocate_msi_domains(pcie); if (ret != 0) { dev_err(pcie->dev, "[iWare][Error] allocate_msi_domains fail, ret = %d\n", ret); return ret; } irq_set_chained_handler_and_data((u32)msi->irq, udrv_pcie_handle_chained_msi_irq, pcie); return 0; } static void __iomem *udrv_pcie_ecam_map_bus(struct pci_bus *bus, unsigned int devfn, int where) { u32 bus_no = bus->number; void __iomem *base = NULL; struct pci_config_window *config = bus->sysdata; u32 devfn_shift = config->ops->bus_shift - 8; // dev + func = 5 + 3 = 8 if ((bus_no < config->busr.start) || (bus_no > config->busr.end)) { return NULL; } bus_no -= (u32)config->busr.start; base = config->win + (bus_no << config->ops->bus_shift); return base + (devfn << devfn_shift) + where; } static int udrv_pcie_host_common_probe(struct platform_device *pdev, struct pci_ecam_ops *ops) { #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) return pci_host_common_probe(pdev, ops); #else struct udrv_pcie_host *pcie = platform_get_drvdata(pdev); int ret; ret = pci_host_common_probe(pdev); // 5.10调用完内核的probe之后drvdata会被设成bridge,需要设回来,同时保存bridge以便remove时使用 pcie->bridge = platform_get_drvdata(pdev); platform_set_drvdata(pdev, pcie); return ret; #endif } static int udrv_pcie_host_common_remove(struct platform_device *pdev) { #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) return 0; // 早期版本的内核没有提供remove接口 #else struct udrv_pcie_host *pcie = platform_get_drvdata(pdev); // 调用内核的remove之前需要将drvdata设成框架要求的bridge platform_set_drvdata(pdev, pcie->bridge); return pci_host_common_remove(pdev); #endif } static int udrv_pcie_host_probe_for_dt(struct udrv_pcie_host *pcie) { struct platform_device *pdev = to_platform_device(pcie->dev); struct udrv_pcie_host_info *host_info = &pcie->host_info; /* host支持RC模式时,需要通过内核接口触发probe */ if (test_bit(NDRV_PCIE_RP_MODE, &(host_info->type_support_mask)) != 0) { return udrv_pcie_host_common_probe(pdev, &pcie->ops->host_ops->ecam_ops); } else { /* 不支持rc的host(ep or switch),不调用内核接口创建host,直接初始化芯片 */ return udrv_pcie_host_ep_init(pdev); } } static struct udrv_pcie_host_ops g_pcie_host_ops_for_dt = { .ecam_ops = { .bus_shift = 20, /* 20bus的起始bit */ .init = udrv_pcie_host_ecam_init, .pci_ops = { .map_bus = udrv_pcie_ecam_map_bus, .read = pci_generic_config_read, .write = pci_generic_config_write, } }, .probe = udrv_pcie_host_probe_for_dt, .get_info = udrv_pcie_get_host_info_from_dt, .init = udrv_pcie_host_init, }; static struct udrv_pcie_port_ops g_pcie_port_ops_for_dt = { .get_info = udrv_pcie_get_port_info_from_dt, .init = udrv_pcie_port_init, }; static struct udrv_pcie_msi_ops g_pcie_msi_ops_for_dt = { .get_info = udrv_pcie_get_msi_info_from_dt, .init = udrv_pcie_msi_init, }; static const struct udrv_pcie_ops g_pcie_ops_for_dt = { .port_ops = &g_pcie_port_ops_for_dt, .host_ops = &g_pcie_host_ops_for_dt, .msi_ops = &g_pcie_msi_ops_for_dt, }; static const struct of_device_id g_udrv_pcie_of_match[] = { { .compatible = "hisilicon,udrv-pcie-ecam-dt", .data = &g_pcie_host_ops_for_dt.ecam_ops, }, {}, }; MODULE_DEVICE_TABLE(of, g_udrv_pcie_of_match); static const struct udrv_pcie_ops *udrv_pcie_get_host_ops(struct device *dev) { return &g_pcie_ops_for_dt; } static int udrv_pcie_probe(struct platform_device *pdev) { int ret = 0; struct device *dev = &pdev->dev; struct udrv_pcie_host *pcie = NULL; const struct udrv_pcie_ops *ops = udrv_pcie_get_host_ops(dev); if (!ops) { dev_err(dev, "[iWare][Error] get ops fail\n"); return -EINVAL; } pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) { dev_err(dev, "[iWare][Error] devm_kzalloc fail\n"); return -ENOMEM; } INIT_LIST_HEAD(&pcie->host_info.atu_info.entry); INIT_LIST_HEAD(&pcie->port_info.entry); pcie->ops = ops; pcie->dev = dev; platform_set_drvdata(pdev, pcie); list_add_tail(&pcie->node, &pcie_host_list); if ((ops->host_ops) && (ops->host_ops->get_info(pcie) != 0)) { pr_err("[iWare][Error] get host dts info failed \n"); ret = -EINVAL; goto err_host; } if ((ops->port_ops) && (ops->port_ops->get_info(pcie) != 0)) { pr_err("[iWare][Error] get port dts info failed \n"); ret = -EINVAL; goto err_port; } if ((ops->msi_ops) && (ops->msi_ops->get_info(pcie) != 0)) { pr_err("[iWare][Error] get msi dts info failed \n"); ret = -EINVAL; goto err_msi; } if ((ops->host_ops) && (ops->host_ops->probe(pcie) != 0)) { pr_err("[iWare][Error] pcie probe failed \n"); ret = -EINVAL; goto err_probe; } #ifdef CONFIG_UDRV_FMEA ret = pcie_fmea_init(pcie); if (ret != 0) { pr_err("[iWare][Error] fmea init failed,ret=%d\n", ret); goto err_fmea; } #endif #ifdef CONFIG_UDRV_KDRV_INFECTED ret = kdrv_pcie_probe_infected_callback(pcie); if (ret != 0) { pr_err("[iWare][Error] kdrv_pcie_probe_infected_callback fail %d\n", ret); goto err_infected_callback; } #endif return 0; #ifdef CONFIG_UDRV_KDRV_INFECTED err_infected_callback: #endif #ifdef CONFIG_UDRV_FMEA pcie_fmea_deinit(pcie); err_fmea: #endif /* 调用框架和probe对等的remove */ if (test_bit(NDRV_PCIE_RP_MODE, &(pcie->host_info.type_support_mask)) != 0) { (void)udrv_pcie_host_common_remove(pdev); } udrv_pcie_remove_msi(pcie); err_probe: err_msi: udrv_pcie_free_port_info(pcie); err_port: udrv_pcie_free_atu_info(pcie); ndrv_pcie_close_pf(pcie->host_info.pf_handle, pcie->host_info.core_version); err_host: list_del(&pcie->node); return ret; } static int udrv_pcie_remove(struct platform_device *pdev) { struct udrv_pcie_host *pcie = platform_get_drvdata(pdev); struct udrv_pcie_host_info *host_info = &pcie->host_info; #ifdef CONFIG_UDRV_KDRV_INFECTED kdrv_pcie_remove_infected_callback(pcie); #endif #ifdef CONFIG_UDRV_FMEA pcie_fmea_deinit(pcie); #endif /* 调用框架和probe对等的remove */ if (test_bit(NDRV_PCIE_RP_MODE, &(host_info->type_support_mask)) != 0) { (void)udrv_pcie_host_common_remove(pdev); } udrv_pcie_remove_msi(pcie); udrv_pcie_free_port_info(pcie); udrv_pcie_free_atu_info(pcie); ndrv_pcie_close_pf(pcie->host_info.pf_handle, pcie->host_info.core_version); list_del(&pcie->node); return 0; } static struct platform_driver g_udrv_pcie_dt_driver = { .probe = udrv_pcie_probe, .remove = udrv_pcie_remove, .driver = { .name = "udrv-pcie", .of_match_table = g_udrv_pcie_of_match, }, }; struct platform_driver *udrv_get_pcie_dt_driver(void) { return &g_udrv_pcie_dt_driver; } static struct platform_driver * const drivers[] = { &g_udrv_pcie_dt_driver, }; int find_pcie_host_by_id(struct device *dev, void *data) { struct pcie_find_data *pcie_search_info = (struct pcie_find_data *)data; struct udrv_pcie_host *pcie = dev_get_drvdata(dev); if (pcie->host_info.host_id == pcie_search_info->host_id) { pcie_search_info->pcie = pcie; return 1; // 找到了退出遍历dev } return 0; } struct udrv_pcie_host *hisi_pcie_get_by_host_id(uint32_t host_id) { int ret; struct pcie_find_data pcie_search_info = { 0 }; pcie_search_info.host_id = host_id; ret = driver_for_each_device(&g_udrv_pcie_dt_driver.driver, NULL, &pcie_search_info, find_pcie_host_by_id); if (pcie_search_info.pcie == NULL) { // 找没找到直接判断返回的hipcie,不依赖driver_for_each_device返回值 pr_err("[iWare][Error] find pcie fail: host_id = %u\n", host_id); return NULL; } return pcie_search_info.pcie; } static int __init udrv_pcie_init(void) { return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); } static void __exit udrv_pcie_exit(void) { platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); } module_init(udrv_pcie_init); module_exit(udrv_pcie_exit); MODULE_LICENSE("Dual BSD/GPL");
最新发布
10-24
<think>分析设备树中的reg属性:reg = <0x0b 0x00 0x00 0x400000 0x00 0xd8900000 0x00 0x80000 0x00 0x700000 0x00 0x00>; 根据设备树绑定规范,该reg属性定义了三个地址区域(每个区域由4个32位数值组成,分别是addr_h, addr_l, size_h, size_l): 1. 配置空间(config):0x0b 0x00 0x00 0x400000 -> 地址高32位=0x0b000000,地址低32位=0x00000000,大小高32位=0x00000000,大小低32位=0x400000 -> 所以配置空间基地址为0x0b00000000000000,大小为0x400000(4MB)。 2. 寄存器空间(dbi):0x00 0xd8900000 0x00 0x80000 -> 地址高32位=0x00000000,地址低32位=0xd8900000,大小高32位=0x00000000,大小低32位=0x80000 -> 所以寄存器空间基地址为0xd8900000,大小为0x80000(512KB)。 3. MSI空间(msi):0x00 0x700000 0x00 0x00 -> 地址高32位=0x00000000,地址低32位=0x00700000,大小高32位=0x00000000,大小低32位=0x00000000 -> 这里大小为0,但设备树中通常不会为0,可能是个错误?不过根据代码,MSI空间是通过另一个资源获取的(platform_get_resource_byname(pdev, IORESOURCE_MEM, "msi"))。 在代码中解析reg属性的位置: 在函数`udrv_pcie_get_host_info_from_dt`中,分别通过`platform_get_resource_byname`获取名为"config"和"dbi"的资源。而对于MSI,则是通过名为"msi"的资源获取(在`udrv_pcie_get_msi_info_from_dt`函数中)。所以reg属性中的第三个区域(MSI)在代码中并没有直接使用,因为它是通过另一个资源获取的。 关于第二个问题:为什么将reg中的0x400000改成0x800000,以及bus-range的0x3改大后,就能识别更多的设备? 原因分析: 1. reg属性中的第一个区域是配置空间(ECAM),大小为0x400000(4MB)。在PCIe中,每个总线(bus)需要1MB的配置空间(因为每个总线有256个设备,每个设备有4K配置空间,所以256*4K=1MB)。原始大小为4MB,因此最多支持4个总线(bus 0~3)。而bus-range属性指定了总线号范围为0到3(bus-range=<0x00 0x03>)。 2. 当挂载了PCIe switch后,下游会扩展出多个总线,如果总线号超过了3(比如4、5等),那么就无法被映射到配置空间,因为配置空间的大小只覆盖了4个总线(4MB)。 3. 将配置空间大小从0x400000(4MB)改为0x800000(8MB)后,配置空间可以覆盖8个总线(8MB/1MB=8),同时将bus-range的最大总线号从3改大(比如改成7),这样系统就可以访问到更多总线上的设备。 在代码中,配置空间的大小由设备树中的reg属性指定,并在`udrv_pcie_host_ecam_init`函数中通过`platform_get_resource_byname(pdev, IORESOURCE_MEM, "config")`获取,然后赋值给`host_info->ecam_res`,并在初始化ECAM时使用(`ecam_cfg.ecam_base_size`)。该大小被传递给底层驱动(`ndrv_pcie_host_ap_ecam_init`)用于设置ECAM窗口的大小。 因此,修改设备树中配置空间的大小和总线范围后,PCIe控制器就能支持更多总线的配置空间访问,从而识别更多设备。 代码中相关部分: 在`udrv_pcie_host_ecam_init`函数中: struct resource *cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); copy_resource(&host_info->ecam_res, cfg_res, "config"); 在`udrv_pcie_host_init`函数中: ecam_cfg.ecam_base_size = (u32)resource_size(&host_info->ecam_res); 然后调用`ndrv_pcie_host_ap_ecam_init(host_info->pf_handle, &ecam_cfg);` 这样底层驱动就知道ECAM窗口的大小了。 另外,bus-range在设备树中被解析到`cfg_w->busr`(在通用PCI主机驱动中),然后赋值给`host_info->bus_res`(在`udrv_pcie_host_ecam_init`中)。在初始化ECAM时,起始总线号(ecam_cfg.ecam_start_bus_no)被设置为`host_info->bus_res.start`。 因此,当bus-range范围变大,并且ECAM空间足够大时,就可以支持更多的总线。 结论: 修改设备树中配置空间的大小(从4MB改为8MB)和总线范围(从0-3改为0-7)后,PCIe控制器能够支持最多8个总线,因此可以识别连接到PCIe switch下游的更多设备。 </think>### 设备树 `reg` 属性解析 设备树中的 `reg = <0x0b 0x00 0x00 0x400000 0x00 0xd8900000 0x00 0x80000 0x00 0x700000 0x00 0x00>` 遵循 Linux 通用格式,每 4 个字段描述一个区域(地址高、地址低、大小高、大小低)。这里包含三个区域: 1. **配置空间 (config)** - **基地址**:高 32 位 `0x0b` + 低 32 位 `0x00` = `0x0b00000000` - **大小**:高 32 位 `0x00` + 低 32 位 `0x400000` = **4 MB** - 用途:PCIe ECAM 配置空间(每个 Bus 需 1 MB)。 2. **寄存器空间 (dbi)** - **基地址**:高 32 位 `0x00` + 低 32 位 `0xd8900000` = `0xd8900000` - **大小**:高 32 位 `0x00` + 低 32 位 `0x80000` = **512 KB** - 用途:PCIe 控制器寄存器(如 DBI 空间)。 3. **MSI 空间 (msi)** - **基地址**:高 32 位 `0x00` + 低 32 位 `0x700000` = `0x700000` - **大小**:高 32 位 `0x00` + 低 32 位 `0x00` = **0 KB**(无效,实际通过独立资源获取) - 注意:实际 MSI 地址通过 `platform_get_resource_byname(pdev, IORESOURCE_MEM, "msi")` 获取。 --- ### 代码解析位置 在 `udrv_pcie_get_host_info_from_dt()` 函数中解析: ```c // 获取 config 空间资源 struct resource *cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); copy_resource(&host_info->ecam_res, cfg_res, "config"); // 获取 dbi 寄存器空间资源 struct resource *apb_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); host_info->apb_base = devm_pci_remap_cfgspace(dev, apb_res->start, resource_size(apb_res)); // 获取 msi 空间资源(在 udrv_pcie_get_msi_info_from_dt()) struct resource *res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "msi"); msi->msi_addr = res->start; ``` --- ### PCIe Switch 设备识别问题分析 #### 原始问题 单板挂载 PCIe Switch 后,`lspci` 只能识别一个设备(实际有多个),原因如下: 1. **ECAM 空间不足**: - 原始配置空间大小 `0x400000`(4 MB),每个 PCIe Bus 需要 1 MB 空间。 - `bus-range = <0x00 0x03>` 允许总线号 0~3(共 4 条 Bus),但 4 MB 仅支持 **4 个 Bus**。 - Switch 下游设备占用额外 Bus,超出 4 Bus 限制。 2. **Bus 范围限制**: - `bus-range` 最大值 `0x03` 仅允许总线号 0~3,无法分配更高总线号。 #### 解决方案 修改设备树两处: 1. **扩大 ECAM 空间**: `reg` 中 `0x400000` → `0x800000`(8 MB),可支持 **8 个 Bus**。 2. **扩展 Bus 范围**: `bus-range = <0x00 0x07>`(允许总线号 0~7)。 #### 底层原理 在 `udrv_pcie_host_init()` 中初始化 ECAM: ```c ecam_cfg.ecam_base_size = (u32)resource_size(&host_info->ecam_res); // 8 MB ecam_cfg.ecam_start_bus_no = (u32)host_info->bus_res.start; // 0 ndrv_pcie_host_ap_ecam_init(host_info->pf_handle, &ecam_cfg); ``` - 8 MB 空间可映射 8 个 Bus(Bus 0~7)。 - PCIe 内核遍历 `bus-range` 时能发现下游 Switch 的所有设备。 --- ### 总结 - **ECAM 大小**:必须 ≥ (`总线数量 × 1 MB`)。 - **Bus 范围**:需覆盖实际总线号。 修改后,PCIe 控制器能正确枚举 Switch 下游的所有设备。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值