rk3588 pcie驱动分析之三 (驱动probe流程) 本文开始讲述pcie 驱动probe流程,其中pcie设备枚举以及设备BAR空间资源分配等内容会单独为几个章节进行描述,当前章节不会对其进行描述。1、驱动probe流程pcie控制器设备树配置参考设备树部分内容说明,这里不再赘述。设备树兼容性配置:compatible rockchip,rk3588-pcie, snps,dw-pcie;1.1、rk3588 pcie平台驱动注册static const struct rk_pcie_of_data rk_pcie_rc_of_data { .mode RK_PCIE_RC_TYPE, }; static const struct rk_pcie_of_data rk_pcie_ep_of_data { .mode RK_PCIE_EP_TYPE, }; static const struct of_device_id rk_pcie_of_match[] { { .compatible rockchip,rk1808-pcie, .data rk_pcie_rc_of_data, }, { .compatible rockchip,rk1808-pcie-ep, .data rk_pcie_ep_of_data, }, ... { /*匹配rk3588 pcie rc模式*/ .compatible rockchip,rk3588-pcie, .data rk_pcie_rc_of_data, }, { .compatible rockchip,rk3588-pcie-ep, .data rk_pcie_ep_of_data, }, {}, }; MODULE_DEVICE_TABLE(of, rk_pcie_of_match/*数组*/); /*平台驱动注册*/ static struct platform_driver rk_plat_pcie_driver { .driver { .name rk-pcie, .of_match_table rk_pcie_of_match, .suppress_bind_attrs true, .pm rockchip_dw_pcie_pm_ops, }, .probe rk_pcie_probe, }; module_platform_driver(rk_plat_pcie_driver); /*平台驱动*/1.2、probe函数流程创建内核任务来进行PCIE RC 初始化以及PCIe设备的枚举。rootUbuntu:~# zcat /proc/config.gz | grep CONFIG_PCIE_RK_THREADED_INITCONFIG_PCIE_RK_THREADED_INITystatic int rk_pcie_probe(struct platform_device *pdev) { if (IS_ENABLED(CONFIG_PCIE_RK_THREADED_INIT)) { struct task_struct *tsk; /*创建内核任务来进行pcie的初始化以及枚举操作*/ tsk kthread_run(rk_pcie_really_probe, pdev, rk-pcie); if (IS_ERR(tsk)) { dev_err(pdev-dev, start rk-pcie thread failed\n); return PTR_ERR(tsk); } return 0; } return rk_pcie_really_probe(pdev); } 为了不失整体性,先将rk_pcie_really_probe函数整体注释如下,依次描述其功能作用。 static int rk_pcie_really_probe(void *p) { struct platform_device *pdev p; struct device *dev pdev-dev; struct rk_pcie *rk_pcie; struct dw_pcie *pci; int ret; const struct of_device_id *match; const struct rk_pcie_of_data *data; enum rk_pcie_device_mode mode; struct device_node *np pdev-dev.of_node; u32 val 0; int irq; /*兼容性检查*/ match of_match_device(rk_pcie_of_match, dev); if (!match) { ret -EINVAL; goto release_driver; } data (struct rk_pcie_of_data *)match-data; mode (enum rk_pcie_device_mode)data-mode; /*RK_PCIE_RC_TYPE*/ /*分配 struct rk_pcie数据结构空间*/ rk_pcie devm_kzalloc(dev, sizeof(*rk_pcie), GFP_KERNEL); if (!rk_pcie) { ret -ENOMEM; goto release_driver; } /*分配 struct dw_pcie数据结构空间*/ pci devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); if (!pci) { ret -ENOMEM; goto release_driver; } /*dw_pcie-dev指向设备树节点创建的设备dev*/ pci-dev dev; /*平台设备pcie*/ pci-ops dw_pcie_ops; /*dw_pcie-ops的指向dw_pcie_ops*/ rk_pcie-msi_vector_num data-msi_vector_num; /*0*/ rk_pcie-mode mode; /*RK_PCIE_RC_TYPE*/ rk_pcie-pci pci; /*指向struct dw_pcie*/ if (of_device_is_compatible(np, rockchip,rk1808-pcie) || of_device_is_compatible(np, rockchip,rk1808-pcie-ep)) rk_pcie-is_rk1808 true; else rk_pcie-is_rk1808 false; /* PHY_MODE_PCIE_BIFURCATION是一个在Linux内核中定义的宏,它的核心作用是作为一个模式标志, 用于配置PCIe PHY(物理层)工作在分叉(Bifurcation)模式下。 简单来说,这个模式允许将一条高带宽的PCIe链路(例如x4或x8)拆分成多条独立的,带宽较低的链路(例如x2或x1) */ if (device_property_read_bool(dev, rockchip,bifurcation)) rk_pcie-bifurcation true; /* 获取pcie设备树中配置的dbi,apb,复位以及设备在位管脚 */ ret rk_pcie_resource_get(pdev, rk_pcie); if (ret) { dev_err(dev, resource init failed\n); goto release_driver; } /*pcie设备是否在位检测管脚*/ if (!IS_ERR_OR_NULL(rk_pcie-prsnt_gpio)) { /*在位管脚已配置*/ if (!gpiod_get_value(rk_pcie-prsnt_gpio)) { /*设备不在位(获取的值为0),无效则报错*/ ret -ENODEV; goto release_driver; } } rk_pcie-supports_clkreq device_property_read_bool(dev, supports-clkreq); retry_regulator: /* DONT MOVE ME: must be enable before phy init */ rk_pcie-vpcie3v3 devm_regulator_get_optional(dev, vpcie3v3); if (IS_ERR(rk_pcie-vpcie3v3)) { if (PTR_ERR(rk_pcie-vpcie3v3) ! -ENODEV) { /*vpcie3v3节点存在,则多次尝试将其找到*/ if (IS_ENABLED(CONFIG_PCIE_RK_THREADED_INIT)) { /* Deferred but in threaded context for most 10s */ msleep(20); if (val 500) goto retry_regulator; } ret PTR_ERR(rk_pcie-vpcie3v3); goto release_driver; } dev_info(dev, no vpcie3v3 regulator found\n); } /*使能vpcie3v3 regulator*/ ret rk_pcie_enable_power(rk_pcie); if (ret) goto release_driver; /*初始化pcie phy以及电源*/ ret rk_pcie_phy_init(rk_pcie); if (ret) { dev_err(dev, phy init failed\n); goto disable_vpcie3v3; } /* devm_reset_control_array_get_exclusive处理那些需要控制多条复位线的设备。 当一个设备在设备树中定义了多个resets属性时使用这个函数可以一次性获取所有复位控制句柄,并将它们作为一个整体来操作。 当前设备树中多个复位配置如下: 复位线列表,顺序与reset-names对应 resets cru SRST_PCIE0_POWER_UP, cru SRST_P_PCIE0; reset-names pcie, periph; */ rk_pcie-rsts devm_reset_control_array_get_exclusive(dev); if (IS_ERR(rk_pcie-rsts)) { ret PTR_ERR(rk_pcie-rsts); dev_err(dev, failed to get reset lines\n); goto disable_phy; } /*解复位*/ reset_control_deassert(rk_pcie-rsts); /*挂载sys irq中断处理函数*/ ret rk_pcie_request_sys_irq(rk_pcie, pdev); if (ret) { dev_err(dev, pcie irq init failed\n); goto disable_phy; } /*pdev-dev-driver_data rk_pcie 平台设备的dev-driver_data指向rk_pcie数据结构*/ platform_set_drvdata(pdev, rk_pcie); /*初始化时钟*/ ret rk_pcie_clk_init(rk_pcie); if (ret) { dev_err(dev, clock init failed\n); goto disable_phy; } /*dbi部分只读寄存器开启读写使能,后面会关闭该功能*/ dw_pcie_dbi_ro_wr_en(pci); if (rk_pcie-is_rk1808) { ret rk1808_pcie_fixup(rk_pcie, np); if (ret) goto deinit_clk; } else { /*加速链路训练*/ rk_pcie_fast_link_setup(rk_pcie); } /* Legacy interrupt is optional */ /*初始化pcie legacy中断*/ ret rk_pcie_init_irq_domain(rk_pcie); if (!ret) { /*成功*/ /*legacy中断*/ irq platform_get_irq_byname(pdev, legacy); if (irq 0) { /* 原子性地设置链式(chained)中断的处理函数及其私有数据 legacy INTAD的中断处理函数是由PCIe设备驱动通过request_irq()注册的 rk_pcie_legacy_int_handler由触发legacy(GIC_SPI 260)时调用 */ irq_set_chained_handler_and_data(irq, rk_pcie_legacy_int_handler/*级联处理函数*/,rk_pcie); /* Unmask all legacy interrupt from INTA~INTD */ rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_MASK_LEGACY,UNMASK_ALL_LEGACY_INT/*将所有legacy中断屏蔽掉*/); } else { dev_info(dev, missing legacy IRQ resource\n); } } /* Set PCIe mode,移除参考时钟,设置为RC模式*/ rk_pcie_set_mode(rk_pcie); /* Force into loopback master mode */ /*用于PCIe PHY(物理层)环回测试模式*/ if (device_property_read_bool(dev, rockchip,lpbk-master)) { val dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL); val | PORT_LINK_LPBK_ENABLE; dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val); rk_pcie-is_signal_test true; } /* Force into compliance mode */ /* 将PCIe链路置于合规性模式(Compliance Mode),这是一种PCIe规范定义的测试模式 在合规性模式下,PCIe设备会发送特定的,符合规范要求的测试码型(Test Patterns),而非正常的数据包 */ if (device_property_read_bool(dev, rockchip,compliance-mode)) { val dw_pcie_readl_dbi(pci, PCIE_CAP_LINK_CONTROL2_LINK_STATUS); val | BIT(4); dw_pcie_writel_dbi(pci, PCIE_CAP_LINK_CONTROL2_LINK_STATUS, val); rk_pcie-is_signal_test true; } /* Skip waiting for training to pass in system PM routine */ if (device_property_read_bool(dev, rockchip,skip-scan-in-resume)) rk_pcie-skip_scan_in_resume true; /*创建工作队列*/ rk_pcie-hot_rst_wq create_singlethread_workqueue(rk_pcie_hot_rst_wq); if (!rk_pcie-hot_rst_wq) { dev_err(dev, failed to create hot_rst workqueue\n); ret -ENOMEM; goto remove_irq_domain; } /*初始化工作*/ INIT_WORK(rk_pcie-hot_rst_work, rk_pcie_hot_rst_work); switch (rk_pcie-mode) { case RK_PCIE_RC_TYPE: /*RC模式*/ /*host_bridge,bus总线的创建以及pcie设备的枚举,bar空间资源的分配等等管理工作*/ ret rk_add_pcie_port(rk_pcie, pdev); break; case RK_PCIE_EP_TYPE: ret rk_pcie_add_ep(rk_pcie); break; } if (rk_pcie-is_signal_test true) return 0; if (ret) goto remove_rst_wq; /*PCIe初始化DMA*/ ret rk_pcie_init_dma_trx(rk_pcie); if (ret) { dev_err(dev, failed to add dma extension\n); goto remove_rst_wq; } /*dma操作函数*/ if (rk_pcie-dma_obj) { rk_pcie-dma_obj-start_dma_func rk_pcie_start_dma_dwc; rk_pcie-dma_obj-config_dma_func rk_pcie_config_dma_dwc; } if (rk_pcie-is_rk1808) { /* hold link reset grant after link-up */ ret rk_pcie_reset_grant_ctrl(rk_pcie, false); if (ret) goto remove_rst_wq; } /*关闭dbi针对只读寄存器进行读写操作*/ dw_pcie_dbi_ro_wr_dis(pci); device_init_wakeup(dev, true); /* Enable async system PM for multiports SoC */ device_enable_async_suspend(dev); if (IS_ENABLED(CONFIG_DEBUG_FS)) { ret rockchip_pcie_debugfs_init(rk_pcie); if (ret 0) dev_err(dev, failed to setup debugfs: %d\n, ret); /* Enable RASDES Error event by default */ val dw_pcie_find_ext_capability(rk_pcie-pci, PCI_EXT_CAP_ID_VNDR); if (!val) { dev_err(dev, Not able to find RASDES CAP!\n); return 0; } dw_pcie_writel_dbi(rk_pcie-pci, val 8, 0x1c); dw_pcie_writel_dbi(rk_pcie-pci, val 8, 0x3); } return 0; remove_rst_wq: destroy_workqueue(rk_pcie-hot_rst_wq); remove_irq_domain: if (rk_pcie-irq_domain) irq_domain_remove(rk_pcie-irq_domain); disable_phy: phy_power_off(rk_pcie-phy); phy_exit(rk_pcie-phy); deinit_clk: clk_bulk_disable_unprepare(rk_pcie-clk_cnt, rk_pcie-clks); disable_vpcie3v3: rk_pcie_disable_power(rk_pcie); release_driver: if (IS_ENABLED(CONFIG_PCIE_RK_THREADED_INIT)) device_release_driver(dev); return ret; }1.2.1、rk_pcie_resource_get函数解析drivers/pci/controller/dwc/pcie-dw-rockchip.c enum rk_pcie_device_mode { RK_PCIE_EP_TYPE, RK_PCIE_RC_TYPE, }; pcie控制器工作模式枚举类型:RC和EP模式 drivers/pci/controller/dwc/pcie-designware.h struct dw_pcie_ops { u64 (*cpu_addr_fixup)(struct dw_pcie *pcie, u64 cpu_addr); u32 (*read_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg, size_t size); void (*write_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg, size_t size, u32 val); void (*write_dbi2)(struct dw_pcie *pcie, void __iomem *base, u32 reg, size_t size, u32 val); int (*link_up)(struct dw_pcie *pcie); int (*start_link)(struct dw_pcie *pcie); void (*stop_link)(struct dw_pcie *pcie); }; rk_pcie-opsdw_pcie_ops; static const struct dw_pcie_ops dw_pcie_ops { .start_link rk_pcie_establish_link, .link_up rk_pcie_link_up, }; 上述ops为pcie控制器操作dbi寄存器以及link检查相关函数实现,当前只实现可link up检查相关的函数。 include/linux/device.h static inline void *dev_get_drvdata(const struct device *dev) { return dev-driver_data; } static inline void dev_set_drvdata(struct device *dev, void *data) { dev-driver_data data; } rk_pcie_resource_get函数用于获取pcie设备树中配置的dbi,apb,复位以及设备在位管脚 static int rk_pcie_resource_get(struct platform_device *pdev,struct rk_pcie *rk_pcie) { struct resource *dbi_base; struct resource *apb_base; /*pcie-dbi属性获取*/ dbi_base platform_get_resource_byname(pdev, IORESOURCE_MEM, pcie-dbi); if (!dbi_base) { dev_err(pdev-dev, get pcie-dbi failed\n); return -ENODEV; } /*dbi地址空间映射*/ rk_pcie-dbi_base devm_ioremap_resource(pdev-dev, dbi_base); if (IS_ERR(rk_pcie-dbi_base)) return PTR_ERR(rk_pcie-dbi_base); /*dw_pcie-dbi_base和rk_pcie-dbi_base指向相同位置*/ rk_pcie-pci-dbi_base rk_pcie-dbi_base; /*pcie-apb属性获取*/ apb_base platform_get_resource_byname(pdev, IORESOURCE_MEM, pcie-apb); if (!apb_base) { dev_err(pdev-dev, get pcie-apb failed\n); return -ENODEV; } /*apb地址空间映射*/ rk_pcie-apb_base devm_ioremap_resource(pdev-dev, apb_base); if (IS_ERR(rk_pcie-apb_base)) return PTR_ERR(rk_pcie-apb_base); /* * Rest the device before enabling power because some of the * platforms may use external refclk input with the some power * rail connect to 100MHz OSC chip. So once the power is up for * the slot and the refclk is available, which isnt quite follow * the spec. We should make sure it is in reset state before * everthings ready. */ /*复位管脚(必须存在)*/ rk_pcie-rst_gpio devm_gpiod_get_optional(pdev-dev, reset, GPIOD_OUT_LOW); if (IS_ERR(rk_pcie-rst_gpio)) { dev_err(pdev-dev, invalid reset-gpios property in node\n); /*如果无复位管脚则返回错误*/ return PTR_ERR(rk_pcie-rst_gpio); } /*复位释放前的延时时间*/ if (device_property_read_u32(pdev-dev, rockchip,perst-inactive-ms, rk_pcie-perst_inactive_ms)) rk_pcie-perst_inactive_ms 200; /*设备在位检测管脚(非必须)*/ rk_pcie-prsnt_gpio devm_gpiod_get_optional(pdev-dev, prsnt, GPIOD_IN); if (IS_ERR_OR_NULL(rk_pcie-prsnt_gpio)) dev_info(pdev-dev, invalid prsnt-gpios property in node\n); return 0; }1.2.2、注册sys中断处理函数在rk3588 pcie设备树中关于中断部分配置如下:pcie3x4: pciefe150000 { compatible rockchip,rk3588-pcie, snps,dw-pcie/*Synopsys DesignWare PCIe IP 核心*/; ... interrupts GIC_SPI 263 IRQ_TYPE_LEVEL_HIGH, GIC_SPI 262 IRQ_TYPE_LEVEL_HIGH, GIC_SPI 261 IRQ_TYPE_LEVEL_HIGH, GIC_SPI 260 IRQ_TYPE_LEVEL_HIGH, GIC_SPI 259 IRQ_TYPE_LEVEL_HIGH; interrupt-names sys, pmc, msg, legacy, err; ... }; drivers/pci/controller/rockchip-pcie-dma.h DMA读/写中断状态以及中断状态清除寄存器定义如下: /** * The Interrupt Status Register for read and write. */ union int_status { struct { /*中断读/写完成状态,DMA读/写通道完成*/ u32 donesta :8; u32 rsvd0 :8; /*异常中断状态,DMA读/写通道检测到错误*/ u32 abortsta :8; u32 rsvd1 :8; }; u32 asdword; }; /** * The Interrupt Clear Register for read and write. */ union int_clear { struct { /*DMA 读/写完成中断清除,写1清除*/ u32 doneclr :8; u32 rsvd0 :8; /*DMA 读/写异常中断清除,写1清除*/ u32 abortclr :8; u32 rsvd1 :8; }; u32 asdword; };/*系统sys中断处理函数注册*/ static int rk_pcie_request_sys_irq(struct rk_pcie *rk_pcie, struct platform_device *pdev) { int irq; int ret; /*获取sys对应的irq*/ irq platform_get_irq_byname(pdev, sys); if (irq 0) { dev_err(rk_pcie-pci-dev, missing sys IRQ resource\n); return -EINVAL; } /*注册中断*/ ret devm_request_irq(rk_pcie-pci-dev, irq, rk_pcie_sys_irq_handler, IRQF_SHARED, pcie-sys, rk_pcie); if (ret) { dev_err(rk_pcie-pci-dev, failed to request PCIe subsystem IRQ\n); return ret; } return 0; } sys irq中断处理函数 static irqreturn_t rk_pcie_sys_irq_handler(int irq, void *arg) { struct rk_pcie *rk_pcie arg; u32 chn; union int_status status; union int_clear clears; u32 reg; /*获取DMA写寄存器状态,清除对应通道异常和完成标志(如果存在的话)*/ status.asdword dw_pcie_readl_dbi(rk_pcie-pci, PCIE_DMA_OFFSET PCIE_DMA_WR_INT_STATUS); for (chn 0; chn PCIE_DMA_CHANEL_MAX_NUM/*2*/; chn) { if (status.donesta/*DMA写完成*/ BIT(chn)) { clears.doneclr 0x1 chn; dw_pcie_writel_dbi(rk_pcie-pci, PCIE_DMA_OFFSET PCIE_DMA_WR_INT_CLEAR, clears.asdword); if (rk_pcie-dma_obj rk_pcie-dma_obj-cb) /*DMA写中断完成,则调用dma_obj的回调函数,注意方向参数*/ rk_pcie-dma_obj-cb(rk_pcie-dma_obj, chn, DMA_TO_BUS); } if (status.abortsta/*DMA写错误*/ BIT(chn)) { dev_err(rk_pcie-pci-dev, %s, abort\n, __func__); clears.abortclr 0x1 chn; dw_pcie_writel_dbi(rk_pcie-pci, PCIE_DMA_OFFSET PCIE_DMA_WR_INT_CLEAR, clears.asdword); } } /*获取DMA读寄存器状态,清除对应通道异常和完成标志(如果存在的话)*/ status.asdword dw_pcie_readl_dbi(rk_pcie-pci, PCIE_DMA_OFFSET PCIE_DMA_RD_INT_STATUS); for (chn 0; chn PCIE_DMA_CHANEL_MAX_NUM; chn) { if (status.donesta/*DMA读完成标志*/ BIT(chn)) { clears.doneclr 0x1 chn; dw_pcie_writel_dbi(rk_pcie-pci, PCIE_DMA_OFFSET PCIE_DMA_RD_INT_CLEAR, clears.asdword); if (rk_pcie-dma_obj rk_pcie-dma_obj-cb) /*DMA读中断完成,则调用dma_obj的回调函数,注意方向参数*/ rk_pcie-dma_obj-cb(rk_pcie-dma_obj, chn, DMA_FROM_BUS); } if (status.abortsta/*DMA读异常标志*/ BIT(chn)) { dev_err(rk_pcie-pci-dev, %s, abort\n, __func__); clears.abortclr 0x1 chn; dw_pcie_writel_dbi(rk_pcie-pci, PCIE_DMA_OFFSET PCIE_DMA_RD_INT_CLEAR, clears.asdword); } } /* link_req_rst_not_int是PCIe控制器中一个状态/中断位,全称为Link Request Reset Notify Interrupt。 它的核心作用是当PCIe链路发生Hot Reset(热复位)或Link-Down Reset(链路断开复位)时,控制器通过置位该位向主机软件发出一个早期预警。 */ reg rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_INTR_STATUS_MISC); if (reg BIT(2)) /*link_req_rst_not_int*/ /*检测当PCIe链路发生Hot Reset(热复位)或Link-Down Reset(链路断开复位)时,将host rest任务挂载到工作队列上*/ queue_work(rk_pcie-hot_rst_wq, rk_pcie-hot_rst_work); /*清除中断状态*/ rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_STATUS_MISC, reg); return IRQ_HANDLED; } [rootOpenEuler ~]# zcat /proc/config.gz | grep CONFIG_ROCKCHIP_PCIE_DMA_OBJ # CONFIG_ROCKCHIP_PCIE_DMA_OBJ is not set 由于当前rk3588 内核中并没有启用pcie dma相关配置,所以pcie dma操作并没有启用,在后面会独立出一个章节来讲述pcie dma操作。 static void rk_pcie_fast_link_setup(struct rk_pcie *rk_pcie) { u32 val; /* LTSSM EN ctrl mode */ val rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_HOT_RESET_CTRL/*0x180*/); /* 加速链路训练过程,在系统启动或PCIe链路从复位恢复时,这些配置可以减少不必要的等待时间, 使设备更快地进入L0状态,从而缩短整体初始化时间 */ val | (PCIE_LTSSM_ENABLE_ENHANCE | PCIE_LTSSM_APP_DLY2_EN) | ((PCIE_LTSSM_APP_DLY2_EN/*enable delaying the link training after Hot Reset*/ | PCIE_LTSSM_ENABLE_ENHANCE/*enable ltssm_enbale enhance mode.*/) 16); rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_HOT_RESET_CTRL, val); } 上述函数配置在hot reset后使能延迟链路训练以及链路训练状态状态机增强模式以加强链路link。1.2.3、初始化pcie legacy(INTx)中断PCIe对传统PCI Legacy中断(INTx)进行了消息化改造,通过消息TLP(Transaction Layer Packet)来模拟物理中断引脚,而非使用专用信号线。当前rk3588 pcie设备树中关于pcie legacy中断配置如下:pcie3x4: pciefe150000 { compatible rockchip,rk3588-pcie, snps,dw-pcie/*Synopsys DesignWare PCIe IP 核心*/; #address-cells 3; #size-cells 2; ... interrupts GIC_SPI 263 IRQ_TYPE_LEVEL_HIGH, GIC_SPI 262 IRQ_TYPE_LEVEL_HIGH, GIC_SPI 261 IRQ_TYPE_LEVEL_HIGH, GIC_SPI 260 IRQ_TYPE_LEVEL_HIGH, GIC_SPI 259 IRQ_TYPE_LEVEL_HIGH; interrupt-names sys, pmc, msg, legacy, err; #interrupt-cells 1; interrupt-map-mask 0 0 0 7; interrupt-map 0 0 0 1 pcie3x4_intc 0, 0 0 0 2 pcie3x4_intc 1, 0 0 0 3 pcie3x4_intc 2, 0 0 0 4 pcie3x4_intc 3; ... pcie3x4_intc: legacy-interrupt-controller { interrupt-controller; #address-cells 0; #interrupt-cells 1; interrupt-parent gic; interrupts GIC_SPI 260 IRQ_TYPE_EDGE_RISING; }; };上述设备树配置描述了RK3588 PCIe控制器如何处理传统PCI中断(INTx)。核心机制是将下游PCIe设备的INTA#~INTD#中断引脚,通过interrupt-map映射到一个虚拟的legacy中断控制器(pcie3x4_intc),再由该控制器聚合为一个GIC中断上报给CPU。/*初始化pcie legacy中断*/ static int rk_pcie_init_irq_domain(struct rk_pcie *rockchip) { struct device *dev rockchip-pci-dev; /* pcie设备树子结点 pcie3x4_intc: legacy-interrupt-controller { interrupt-controller; #address-cells 0; #interrupt-cells 1; interrupt-parent gic; interrupts GIC_SPI 260 IRQ_TYPE_EDGE_RISING; }; */ struct device_node *intc of_get_next_child(dev-of_node, NULL); if (!intc) { dev_err(dev, missing child interrupt-controller node\n); return -EINVAL; } raw_spin_lock_init(rockchip-intx_lock); /* 用于创建和注册一个基于线性映射的irq_domain 建立一个固定大小的映射表用于将中断控制器本地的硬件中断号(hwirq)快速映射到Linux全局虚拟中断号(virq) */ rockchip-irq_domain irq_domain_add_linear(intc, PCI_NUM_INTX/*4*/, intx_domain_ops, rockchip); if (!rockchip-irq_domain) { dev_err(dev, failed to get a INTx IRQ domain\n); return -EINVAL; } return 0; } pcie legacy中断控制操作函数,开关,屏蔽中断等基本操作 static struct irq_chip rk_pcie_legacy_irq_chip { .name rk-pcie-legacy-int, .irq_enable rk_pcie_legacy_irq_unmask, .irq_disable rk_pcie_legacy_irq_mask, .irq_mask rk_pcie_legacy_irq_mask, .irq_unmask rk_pcie_legacy_irq_unmask, .flags IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, }; static int rk_pcie_intx_map(struct irq_domain *domain, unsigned int irq, irq_hw_number_t hwirq) { irq_set_chip_and_handler(irq, rk_pcie_legacy_irq_chip, handle_simple_irq); irq_set_chip_data(irq, domain-host_data); return 0; } static const struct irq_domain_ops intx_domain_ops { .map rk_pcie_intx_map, }; static void rk_pcie_legacy_int_handler(struct irq_desc *desc) { struct irq_chip *chip irq_desc_get_chip(desc); struct rk_pcie *rockchip irq_desc_get_handler_data(desc); struct device *dev rockchip-pci-dev; u32 reg; u32 hwirq; u32 virq; chained_irq_enter(chip, desc); /*pcie legacy中断状态*/ reg rk_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_LEGACY); reg reg 0xf; /*获取legacy中断状态寄存器值*/ while (reg) { /*接收到Assert_INTX消息,触发中断*/ hwirq ffs(reg) - 1; reg ~BIT(hwirq); /*找到对应的虚拟中断号*/ virq irq_find_mapping(rockchip-irq_domain, hwirq/*硬件中断号*/); if (virq) generic_handle_irq(virq); /*通用中断处理函数*/ else dev_err(dev, unexpected IRQ, INT%d\n, hwirq); } chained_irq_exit(chip, desc); } /* * Entry/exit functions for chained handlers where the primary IRQ chip * may implement either fasteoi or level-trigger flow control. */ static inline void chained_irq_enter(struct irq_chip *chip, struct irq_desc *desc) { /* FastEOI controllers require no action on entry. */ if (chip-irq_eoi) return; if (chip-irq_mask_ack) { chip-irq_mask_ack(desc-irq_data); } else { /*irq_mask函数必须实现*/ chip-irq_mask(desc-irq_data); if (chip-irq_ack) chip-irq_ack(desc-irq_data); } } static inline void chained_irq_exit(struct irq_chip *chip, struct irq_desc *desc) { if (chip-irq_eoi) chip-irq_eoi(desc-irq_data); else /*irq_unmask函数必须实现*/ chip-irq_unmask(desc-irq_data); }1.2.4、HOT RESET 工作队列创建/*热插拔现设置命令和状态寄存器,再等待pcie ltssm状态为0*/ static void rk_pcie_hot_rst_work(struct work_struct *work) { struct rk_pcie *rk_pcie container_of(work, struct rk_pcie, hot_rst_work); u32 val, status; int ret; /* Setup command register */ /*设置配置空间Command and Status Register*/ val dw_pcie_readl_dbi(rk_pcie-pci, PCI_COMMAND); val 0xffff0000; val | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_SERR; dw_pcie_writel_dbi(rk_pcie-pci, PCI_COMMAND, val); /* 使能延迟训练在热复位后,enable delaying the link training after Hot Reset */ if (rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_HOT_RESET_CTRL) PCIE_LTSSM_APP_DLY2_EN) { /* 如果ltssm状态为0条件满足则跳出等待,否则最大等待10ms */ ret readl_poll_timeout(rk_pcie-apb_base PCIE_CLIENT_LTSSM_STATUS, status, ((status 0x3F) 0/*S_DETECT_QUIET*/), 100/*单次等待时间,单位us*/, RK_PCIE_HOTRESET_TMOUT_US/*10ms超时时间*/); if (ret) dev_err(rk_pcie-pci-dev, wait for detect quiet failed!\n); /*写延时完成标志位,即关闭延时,开启链路训练??*/ rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_HOT_RESET_CTRL, (PCIE_LTSSM_APP_DLY2_DONE) | ((PCIE_LTSSM_APP_DLY2_DONE) 16)); } } readl_poll_timeout在超时时间内获取addr处的值val,然后判断条件cond是否成立,成立则返回0,否则返回-ETIMEDOUT 超時 #define readl_poll_timeout(addr, val, cond/*判断条件*/, delay_us/*单次操作延时*/, timeout_us/*整体超时时间*/) \ readx_poll_timeout(readl, addr, val, cond, delay_us, timeout_us) /** * readx_poll_timeout - Periodically poll an address until a condition is met or a timeout occurs * op: accessor function (takes addr as its only argument) * addr: Address to poll * val: Variable to read the value into * cond: Break condition (usually involving val) * sleep_us: Maximum time to sleep between reads in us (0 * tight-loops). Should be less than ~20ms since usleep_range * is used (see Documentation/timers/timers-howto.rst). * timeout_us: Timeout in us, 0 means never timeout * * Returns 0 on success and -ETIMEDOUT upon a timeout. In either * case, the last read value at addr is stored in val. Must not * be called from atomic context if sleep_us or timeout_us are used. * * When available, youll probably want to use one of the specialized * macros defined below rather than this macro directly. */ #define readx_poll_timeout(op, addr, val, cond, sleep_us, timeout_us) \ read_poll_timeout(op, val, cond, sleep_us, timeout_us, false, addr) #define read_poll_timeout(op, val, cond, sleep_us, timeout_us, \ sleep_before_read, args...) \ ({ \ u64 __timeout_us (timeout_us); \ unsigned long __sleep_us (sleep_us); \ ktime_t __timeout ktime_add_us(ktime_get(), __timeout_us); \ might_sleep_if((__sleep_us) ! 0); \ if (sleep_before_read __sleep_us) \ usleep_range((__sleep_us 2) 1, __sleep_us); \ /*先睡眠一半时间*/ for (;;) { \ (val) op(args); \ if (cond) \ /*条件成立则break*/ break; \ if (__timeout_us \ ktime_compare(ktime_get(), __timeout) 0) { \ (val) op(args); \ break; \ } \ if (__sleep_us) \ usleep_range((__sleep_us 2) 1, __sleep_us); \ } \ (cond) ? 0 : -ETIMEDOUT; \ })Q:为什么必须等待LTSSM状态变为0(即为S_DETECT_QUIET状态)A:等待LTSSM状态变为0(即S_DETECT_QUIET)是热复位流程中一个关键的同步步骤,其根本目的是确保链路复位彻底完成,为重新训练做好准备。Detect.Quiet是链路复位后的起点,进入该状态意味着链路已经完全复位,所有之前的训练状态都被清除,准备从零开始重新训练。关于host_bridge,bus的创建以及pcie设备的枚举和设备bar空间资源的分配将在后续内容中描述。