基础知识
PCI/PCIE 与 CPU 连接的内存控制器一样,基于设备地址范围进行设备片选
从 CPU 发出地址 ->PCI/PCIE 控制器发出地址(经过转换)->PCI/PCIE 设备
关于PCI/PCIE,我们需要分配PCI/PCIE 控制器的地址空间,设置PCI/PCIE 设备的地址转换(offset),这些操作在插入PCI/PCIE 设备时会通过读取配置寄存器完成
PCI 是并行接口,PCIE 是串行接口(差分信号线)
PCI 地址线和数据线是复用的,通过 frame 引脚电平区分,frame 信号为低的话第一个时钟传输地址,后续传输数据,一开始的时候通过 IDSEL(与 AD 引脚连接) 引脚区分设备,选中设备的IDSEL 就可以配置设备了,设备的访问是通过桥来实现的

Type0 访问一般设备,Type1 访问桥后设备

一般设备访问配置寄存器步骤:
1.通过 IDSEL 选择设备
2.在 AD 上指定功能和寄存器
3.C/BE 分辨是读还是写
4.获取数据

访问桥后设备配置寄存器步骤:
1.通过 IDSEL 选择设备
- 在 AD 上指定功能和寄存器
3.C/BE 分辨是读还是写
4.获取信息,Header Type 区分是普通设备还是桥设备
5.分配 bus number
6.通过 type1 报文访问桥,填充 bus number 和设备编号(AD 引脚号)
7.中间桥转发 type0 报文

数据转发就是根据桥的范围来进行的,每个桥都知道自己及祖先桥和子孙桥号,通过判断范围来决定是否转发,这样 cpu 就能像访问普通内存一样读写这些 PCI 设备
当高速率时,并行引脚干扰严重,因此出现了串行引脚的 PCIe
可以看到使用差分信号线进行收发,这样可以抑制干扰,图中是一个 Lane(收发的四条线),也有可能有多个 Lane

PCIe 有事务层(封包,拆包),数据链路层(纠错)、物理层三层(发送)
PCIe 通过 root complex 管理地址,switch 转发,pex 连接 pci 设备


头部报文

这里 cfgrd0 表示访问一般设备,cfgrd1 表示访问桥之后的设备


通过总线号,设备号定位设备,定位过程是和 PCI 相似的,不过设备号是硬编码在设备内写死了,function number 是功能号(逻辑设备)


PCIe协议中有三种路由方式(基于ID的路由,基于地址的路由,隐式路由),这些路由在 Header Type 中就体现了,基于地址路由就是地址片选(发回来的数据包如果都不能处理就会流转到 rc 再向下流转),隐式路由一般是消息报文,目标在 Header Type 中就定了
PCI/PCIE 驱动(Rockchip)
首先需要 HOST 驱动(识别 pci_dev,pci_dev 可能是桥,桥下面也会有pci_dev),针对 EP 也就是pci_dev 我们需要提供驱动程序并向内核注册,注册之后会在 pci 总线上匹配,报文的配置是通过寄存器来进行的
host 驱动浅析
设备树
地址信息在设备树中体现了
pcie0: pcie@f8000000 {
compatible = "rockchip,rk3399-pcie";
#address-cells = <3>;
#size-cells = <2>;
aspm-no-l0s;
clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>,
<&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>;
clock-names = "aclk", "aclk-perf",
"hclk", "pm";
bus-range = <0x0 0x1f>;
max-link-speed = <1>;
linux,pci-domain = <0>;
msi-map = <0x0 &its 0x0 0x1000>;
interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH 0>,
<GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH 0>,
<GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH 0>;
interrupt-names = "sys", "legacy", "client";
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 7>;
interrupt-map = <0 0 0 1 &pcie0_intc 0>,
<0 0 0 2 &pcie0_intc 1>,
<0 0 0 3 &pcie0_intc 2>,
<0 0 0 4 &pcie0_intc 3>;
phys = <&pcie_phy>;
phy-names = "pcie-phy";
ranges = <0x83000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x1e00000
0x81000000 0x0 0xfbe00000 0x0 0xfbe00000 0x0 0x100000>;
reg = <0x0 0xf8000000 0x0 0x2000000>,
<0x0 0xfd000000 0x0 0x1000000>;
reg-names = "axi-base", "apb-base";
resets = <&cru SRST_PCIE_CORE>, <&cru SRST_PCIE_MGMT>,
<&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>,
<&cru SRST_PCIE_PM>, <&cru SRST_P_PCIE>,
<&cru SRST_A_PCIE>;
reset-names = "core", "mgmt", "mgmt-sticky", "pipe",
"pm", "pclk", "aclk";
status = "disabled";
pcie0_intc: interrupt-controller {
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <1>;
};
};
linux-orangepi-orange-pi-6.1-rk35xx\drivers\pci\controller\pcie-rockchip-host.c
static struct platform_driver rockchip_pcie_driver = {
.driver = {
.name = "rockchip-pcie",
.of_match_table = rockchip_pcie_of_match,
.pm = &rockchip_pcie_pm_ops,
},
.probe = rockchip_pcie_probe,
.remove = rockchip_pcie_remove,
};
module_platform_driver(rockchip_pcie_driver);
首先看 probe 函数
static int rockchip_pcie_probe(struct platform_device *pdev)
{
struct rockchip_pcie *rockchip;
struct device *dev = &pdev->dev;
struct pci_host_bridge *bridge;
int err;
if (!dev->of_node)
return -ENODEV;
bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rockchip));
if (!bridge)
return -ENOMEM;
rockchip = pci_host_bridge_priv(bridge);
rockchip->bridge = bridge;
platform_set_drvdata(pdev, rockchip);
rockchip->dev = dev;
rockchip->is_rc = true;
err = rockchip_pcie_parse_host_dt(rockchip);
if (err)
return err;
err = rockchip_pcie_enable_clocks(rockchip);
if (err)
return err;
err = rockchip_pcie_set_vpcie(rockchip);
if (err) {
dev_err(dev, "failed to set vpcie regulator\n");
goto err_set_vpcie;
}
err = rockchip_pcie_init_irq_domain(rockchip);
if (err < 0)
goto err_vpcie;
if (rockchip->deferred) {
err = sysfs_create_group(&pdev->dev.kobj, &pcie_attr_group);
if (err) {
dev_err(&pdev->dev, "SysFS group creation failed\n");
goto err_remove_irq_domain;
}
} else {
err = rockchip_pcie_really_probe(rockchip);
if (err) {
dev_err(&pdev->dev, "deferred probe failed\n");
goto err_deinit_port;
}
}
if (rockchip->dma_trx_enabled == 0)
return 0;
rockchip->dma_obj = rk_pcie_dma_obj_probe(dev);
if (IS_ERR(rockchip->dma_obj)) {
dev_err(dev, "failed to prepare dma object\n");
err = -EINVAL;
goto err_deinit_port;
}
if (rockchip->dma_obj) {
rockchip->dma_obj->start_dma_func = rk_pcie_start_dma_rk3399;
rockchip->dma_obj->config_dma_func = rk_pcie_config_dma_rk3399;
}
return 0;
err_deinit_port:
rockchip_pcie_deinit_phys(rockchip);
if (rockchip->deferred)
sysfs_remove_group(&pdev->dev.kobj, &pcie_attr_group);
err_remove_irq_domain:
irq_domain_remove(rockchip->irq_domain);
err_vpcie:
if (!IS_ERR(rockchip->vpcie12v))
regulator_disable(rockchip->vpcie12v);
if (!IS_ERR(rockchip->vpcie3v3))
regulator_disable(rockchip->vpcie3v3);
regulator_disable(rockchip->vpcie1v8);
regulator_disable(rockchip->vpcie0v9);
err_set_vpcie:
rockchip_pcie_disable_clocks(rockchip);
return err;
}
这里解析设备树
/**
* rockchip_pcie_parse_host_dt - Parse Device Tree
* @rockchip: PCIe port information
*
* Return: '0' on success and error value on failure
*/
static int rockchip_pcie_parse_host_dt(struct rockchip_pcie *rockchip)
{
struct device *dev = rockchip->dev;
int err;
err = rockchip_pcie_parse_dt(rockchip);
if (err)
return err;
rockchip->vpcie12v = devm_regulator_get_optional(dev, "vpcie12v");
if (IS_ERR(rockchip->vpcie12v)) {
if (PTR_ERR(rockchip->vpcie12v) != -ENODEV)
return PTR_ERR(rockchip->vpcie12v);
dev_info(dev, "no vpcie12v regulator found\n");
}
rockchip->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3");
if (IS_ERR(rockchip->vpcie3v3)) {
if (PTR_ERR(rockchip->vpcie3v3) != -ENODEV)
return PTR_ERR(rockchip->vpcie3v3);
dev_info(dev, "no vpcie3v3 regulator found\n");
}
rockchip->vpcie1v8 = devm_regulator_get(dev, "vpcie1v8");
if (IS_ERR(rockchip->vpcie1v8))
return PTR_ERR(rockchip->vpcie1v8);
rockchip->vpcie0v9 = devm_regulator_get(dev, "vpcie0v9");
if (IS_ERR(rockchip->vpcie0v9))
return PTR_ERR(rockchip->vpcie0v9);
return 0;
}
linux-orangepi-orange-pi-6.1-rk35xx\drivers\pci\controller\pcie-rockchip.c
int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip)
{
struct device *dev = rockchip->dev;
struct platform_device *pdev = to_platform_device(dev);
struct device_node *node = dev->of_node;
struct resource *regs;
int err;
if (rockchip->is_rc) {
regs = platform_get_resource_byname(pdev,
IORESOURCE_MEM,
"axi-base");
rockchip->reg_base = devm_pci_remap_cfg_resource(dev, regs);
if (IS_ERR(rockchip->reg_base))
return PTR_ERR(rockchip->reg_base);
} else {
rockchip->mem_res =
platform_get_resource_byname(pdev, IORESOURCE_MEM,
"mem-base");
if (!rockchip->mem_res)
return -EINVAL;
}
rockchip->apb_base =
devm_platform_ioremap_resource_byname(pdev, "apb-base");
if (IS_ERR(rockchip->apb_base))
return PTR_ERR(rockchip->apb_base);
err = rockchip_pcie_get_phys(rockchip);
if (err)
return err;
rockchip->lanes = 1;
err = of_property_read_u32(node, "num-lanes", &rockchip->lanes);
if (!err && (rockchip->lanes == 0 ||
rockchip->lanes == 3 ||
rockchip->lanes > 4)) {
dev_warn(dev, "invalid num-lanes, default to use one lane\n");
rockchip->lanes = 1;
}
rockchip->link_gen = of_pci_get_max_link_speed(node);
if (rockchip->link_gen < 0 || rockchip->link_gen > 2)
rockchip->link_gen = 2;
rockchip->core_rst = devm_reset_control_get_exclusive(dev, "core");
if (IS_ERR(rockchip->core_rst)) {
if (PTR_ERR(rockchip->core_rst) != -EPROBE_DEFER)
dev_err(dev, "missing core reset property in node\n");
return PTR_ERR(rockchip->core_rst);
}
rockchip->mgmt_rst = devm_reset_control_get_exclusive(dev, "mgmt");
if (IS_ERR(rockchip->mgmt_rst)) {
if (PTR_ERR(rockchip->mgmt_rst) != -EPROBE_DEFER)
dev_err(dev, "missing mgmt reset property in node\n");
return PTR_ERR(rockchip->mgmt_rst);
}
rockchip->mgmt_sticky_rst = devm_reset_control_get_exclusive(dev,
"mgmt-sticky");
if (IS_ERR(rockchip->mgmt_sticky_rst)) {
if (PTR_ERR(rockchip->mgmt_sticky_rst) != -EPROBE_DEFER)
dev_err(dev, "missing mgmt-sticky reset property in node\n");
return PTR_ERR(rockchip->mgmt_sticky_rst);
}
rockchip->pipe_rst = devm_reset_control_get_exclusive(dev, "pipe");
if (IS_ERR(rockchip->pipe_rst)) {
if (PTR_ERR(rockchip->pipe_rst) != -EPROBE_DEFER)
dev_err(dev, "missing pipe reset property in node\n");
return PTR_ERR(rockchip->pipe_rst);
}
rockchip->pm_rst = devm_reset_control_get_exclusive(dev, "pm");
if (IS_ERR(rockchip->pm_rst)) {
if (PTR_ERR(rockchip->pm_rst) != -EPROBE_DEFER)
dev_err(dev, "missing pm reset property in node\n");
return PTR_ERR(rockchip->pm_rst);
}
rockchip->pclk_rst = devm_reset_control_get_exclusive(dev, "pclk");
if (IS_ERR(rockchip->pclk_rst)) {
if (PTR_ERR(rockchip->pclk_rst) != -EPROBE_DEFER)
dev_err(dev, "missing pclk reset property in node\n");
return PTR_ERR(rockchip->pclk_rst);
}
rockchip->aclk_rst = devm_reset_control_get_exclusive(dev, "aclk");
if (IS_ERR(rockchip->aclk_rst)) {
if (PTR_ERR(rockchip->aclk_rst) != -EPROBE_DEFER)
dev_err(dev, "missing aclk reset property in node\n");
return PTR_ERR(rockchip->aclk_rst);
}
if (rockchip->is_rc) {
rockchip->ep_gpio = devm_gpiod_get_optional(dev, "ep",
GPIOD_OUT_HIGH);
if (IS_ERR(rockchip->ep_gpio))
return dev_err_probe(dev, PTR_ERR(rockchip->ep_gpio),
"failed to get ep GPIO\n");
}
rockchip->aclk_pcie = devm_clk_get(dev, "aclk");
if (IS_ERR(rockchip->aclk_pcie)) {
dev_err(dev, "aclk clock not found\n");
return PTR_ERR(rockchip->aclk_pcie);
}
rockchip->aclk_perf_pcie = devm_clk_get(dev, "aclk-perf");
if (IS_ERR(rockchip->aclk_perf_pcie)) {
dev_err(dev, "aclk_perf clock not found\n");
return PTR_ERR(rockchip->aclk_perf_pcie);
}
rockchip->hclk_pcie = devm_clk_get(dev, "hclk");
if (IS_ERR(rockchip->hclk_pcie)) {
dev_err(dev, "hclk clock not found\n");
return PTR_ERR(rockchip->hclk_pcie);
}
rockchip->clk_pcie_pm = devm_clk_get(dev, "pm");
if (IS_ERR(rockchip->clk_pcie_pm)) {
dev_err(dev, "pm clock not found\n");
return PTR_ERR(rockchip->clk_pcie_pm);
}
return 0;
}
EXPORT_SYMBOL_GPL(rockchip_pcie_parse_dt);
接下来看设备树解析和一些硬件操作完成后对于端口的初始化
static int rockchip_pcie_really_probe(struct rockchip_pcie *rockchip)
{
int err;
err = rockchip_pcie_host_init_port(rockchip);
if (err)
return err;
err = rockchip_pcie_setup_irq(rockchip);
if (err)
return err;
rockchip_pcie_enable_interrupts(rockchip);
err = rockchip_pcie_cfg_atu(rockchip);
if (err)
return err;
rockchip->bridge->sysdata = rockchip;
rockchip->bridge->ops = &rockchip_pcie_ops;
device_init_wakeup(rockchip->dev, true);
return pci_host_probe(rockchip->bridge);
}
static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip)
{
struct device *dev = rockchip->dev;
int err, i = MAX_LANE_NUM;
u32 status;
int timeouts = 500;
gpiod_set_value_cansleep(rockchip->ep_gpio, 0);
err = rockchip_pcie_init_port(rockchip);
if (err)
return err;
/* Fix the transmitted FTS count desired to exit from L0s. */
status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL_PLC1);
status = (status & ~PCIE_CORE_CTRL_PLC1_FTS_MASK) |
(PCIE_CORE_CTRL_PLC1_FTS_CNT << PCIE_CORE_CTRL_PLC1_FTS_SHIFT);
rockchip_pcie_write(rockchip, status, PCIE_CORE_CTRL_PLC1);
rockchip_pcie_set_power_limit(rockchip);
/* Set RC's clock architecture as common clock */
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS);
status |= PCI_EXP_LNKSTA_SLC << 16;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS);
/* Set RC's RCB to 128 */
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS);
status |= PCI_EXP_LNKCTL_RCB;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS);
/* Enable Gen1 training */
rockchip_pcie_write(rockchip, PCIE_CLIENT_LINK_TRAIN_ENABLE,
PCIE_CLIENT_CONFIG);
gpiod_set_value_cansleep(rockchip->ep_gpio, 1);
if (rockchip->wait_ep)
timeouts = 10000;
/* 500ms timeout value should be enough for Gen1/2 training */
err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_BASIC_STATUS1,
status, PCIE_LINK_UP(status), 20,
timeouts * USEC_PER_MSEC);
if (err) {
dev_err(dev, "PCIe link training gen1 timeout!\n");
goto err_power_off_phy;
}
err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_DEBUG_OUT_0,
status, PCIE_LINK_IS_L0(status), 20,
timeouts * USEC_PER_MSEC);
if (err) {
dev_err(dev, "LTSSM is not L0!\n");
return -ETIMEDOUT;
}
if (rockchip->link_gen == 2) {
/*
* Enable retrain for gen2. This should be configured only after
* gen1 finished.
*/
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS);
status |= PCI_EXP_LNKCTL_RL;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS);
err = readl_poll_timeout(rockchip->apb_base + PCIE_CORE_CTRL,
status, PCIE_LINK_IS_GEN2(status), 20,
500 * USEC_PER_MSEC);
if (err)
dev_dbg(dev, "PCIe link training gen2 timeout, fall back to gen1!\n");
}
/* Check the final link width from negotiated lane counter from MGMT */
status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL);
status = 0x1 << ((status & PCIE_CORE_PL_CONF_LANE_MASK) >>
PCIE_CORE_PL_CONF_LANE_SHIFT);
dev_dbg(dev, "current link width is x%d\n", status);
/* Power off unused lane(s) */
rockchip->lanes_map = rockchip_pcie_lane_map(rockchip);
for (i = 0; i < MAX_LANE_NUM; i++) {
if (!(rockchip->lanes_map & BIT(i))) {
dev_dbg(dev, "idling lane %d\n", i);
phy_power_off(rockchip->phys[i]);
}
}
/* disable ltssm */
if (rockchip->dma_trx_enabled)
rockchip_pcie_write(rockchip, PCIE_CLIENT_LINK_TRAIN_DISABLE,
PCIE_CLIENT_CONFIG);
rockchip_pcie_write(rockchip, ROCKCHIP_VENDOR_ID,
PCIE_CORE_CONFIG_VENDOR);
rockchip_pcie_write(rockchip,
PCI_CLASS_BRIDGE_PCI_NORMAL << 8,
PCIE_RC_CONFIG_RID_CCR);
/* Clear THP cap's next cap pointer to remove L1 substate cap */
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_THP_CAP);
status &= ~PCIE_RC_CONFIG_THP_CAP_NEXT_MASK;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_THP_CAP);
/* Clear L0s from RC's link cap */
if (of_property_read_bool(dev->of_node, "aspm-no-l0s")) {
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LINK_CAP);
status &= ~PCIE_RC_CONFIG_LINK_CAP_L0S;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LINK_CAP);
}
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_DCSR);
status &= ~PCIE_RC_CONFIG_DCSR_MPS_MASK;
status |= PCIE_RC_CONFIG_DCSR_MPS_256;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_DCSR);
return 0;
err_power_off_phy:
while (i--)
phy_power_off(rockchip->phys[i]);
i = MAX_LANE_NUM;
while (i--)
phy_exit(rockchip->phys[i]);
return err;
}
这里面对于的配置就相当于配置 TLP 头部了
int rockchip_pcie_init_port(struct rockchip_pcie *rockchip)
{
struct device *dev = rockchip->dev;
int err, i;
u32 regs;
err = reset_control_assert(rockchip->aclk_rst);
if (err) {
dev_err(dev, "assert aclk_rst err %d\n", err);
return err;
}
err = reset_control_assert(rockchip->pclk_rst);
if (err) {
dev_err(dev, "assert pclk_rst err %d\n", err);
return err;
}
err = reset_control_assert(rockchip->pm_rst);
if (err) {
dev_err(dev, "assert pm_rst err %d\n", err);
return err;
}
for (i = 0; i < MAX_LANE_NUM; i++) {
err = phy_init(rockchip->phys[i]);
if (err) {
dev_err(dev, "init phy%d err %d\n", i, err);
goto err_exit_phy;
}
}
err = reset_control_assert(rockchip->core_rst);
if (err) {
dev_err(dev, "assert core_rst err %d\n", err);
goto err_exit_phy;
}
err = reset_control_assert(rockchip->mgmt_rst);
if (err) {
dev_err(dev, "assert mgmt_rst err %d\n", err);
goto err_exit_phy;
}
err = reset_control_assert(rockchip->mgmt_sticky_rst);
if (err) {
dev_err(dev, "assert mgmt_sticky_rst err %d\n", err);
goto err_exit_phy;
}
err = reset_control_assert(rockchip->pipe_rst);
if (err) {
dev_err(dev, "assert pipe_rst err %d\n", err);
goto err_exit_phy;
}
udelay(10);
err = reset_control_deassert(rockchip->pm_rst);
if (err) {
dev_err(dev, "deassert pm_rst err %d\n", err);
goto err_exit_phy;
}
err = reset_control_deassert(rockchip->aclk_rst);
if (err) {
dev_err(dev, "deassert aclk_rst err %d\n", err);
goto err_exit_phy;
}
err = reset_control_deassert(rockchip->pclk_rst);
if (err) {
dev_err(dev, "deassert pclk_rst err %d\n", err);
goto err_exit_phy;
}
if (rockchip->link_gen == 2)
rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_2,
PCIE_CLIENT_CONFIG);
else
rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_1,
PCIE_CLIENT_CONFIG);
regs = PCIE_CLIENT_LINK_TRAIN_ENABLE | PCIE_CLIENT_ARI_ENABLE |
PCIE_CLIENT_CONF_LANE_NUM(rockchip->lanes);
if (rockchip->is_rc)
regs |= PCIE_CLIENT_CONF_ENABLE | PCIE_CLIENT_MODE_RC;
else
regs |= PCIE_CLIENT_CONF_DISABLE | PCIE_CLIENT_MODE_EP;
rockchip_pcie_write(rockchip, regs, PCIE_CLIENT_CONFIG);
for (i = 0; i < MAX_LANE_NUM; i++) {
err = phy_power_on(rockchip->phys[i]);
if (err) {
dev_err(dev, "power on phy%d err %d\n", i, err);
goto err_power_off_phy;
}
}
err = readx_poll_timeout(rockchip_pcie_read_addr,
PCIE_CLIENT_SIDE_BAND_STATUS,
regs, !(regs & PCIE_CLIENT_PHY_ST),
RK_PHY_PLL_LOCK_SLEEP_US,
RK_PHY_PLL_LOCK_TIMEOUT_US);
if (err) {
dev_err(dev, "PHY PLLs could not lock, %d\n", err);
goto err_power_off_phy;
}
/*
* Please don't reorder the deassert sequence of the following
* four reset pins.
*/
err = reset_control_deassert(rockchip->mgmt_sticky_rst);
if (err) {
dev_err(dev, "deassert mgmt_sticky_rst err %d\n", err);
goto err_power_off_phy;
}
err = reset_control_deassert(rockchip->core_rst);
if (err) {
dev_err(dev, "deassert core_rst err %d\n", err);
goto err_power_off_phy;
}
err = reset_control_deassert(rockchip->mgmt_rst);
if (err) {
dev_err(dev, "deassert mgmt_rst err %d\n", err);
goto err_power_off_phy;
}
err = reset_control_deassert(rockchip->pipe_rst);
if (err) {
dev_err(dev, "deassert pipe_rst err %d\n", err);
goto err_power_off_phy;
}
return 0;
err_power_off_phy:
while (i--)
phy_power_off(rockchip->phys[i]);
i = MAX_LANE_NUM;
err_exit_phy:
while (i--)
phy_exit(rockchip->phys[i]);
return err;
}
EXPORT_SYMBOL_GPL(rockchip_pcie_init_port);
在rockchip_pcie_really_probe 中调用了rockchip_pcie_cfg_atu
这个函数建立了 CPU/PCI地址空间的映射
static int rockchip_pcie_cfg_atu(struct rockchip_pcie *rockchip)
{
struct device *dev = rockchip->dev;
struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rockchip);
struct resource_entry *entry;
u64 pci_addr, size;
int offset;
int err;
int reg_no;
rockchip_pcie_cfg_configuration_accesses(rockchip,
AXI_WRAPPER_TYPE0_CFG);
entry = resource_list_first_type(&bridge->windows, IORESOURCE_MEM);
if (!entry)
return -ENODEV;
size = resource_size(entry->res);
pci_addr = entry->res->start - entry->offset;
rockchip->msg_bus_addr = pci_addr;
for (reg_no = 0; reg_no < (size >> 20); reg_no++) {
err = rockchip_pcie_prog_ob_atu(rockchip, reg_no + 1,
AXI_WRAPPER_MEM_WRITE,
20 - 1,
pci_addr + (reg_no << 20),
0);
if (err) {
dev_err(dev, "program RC mem outbound ATU failed\n");
return err;
}
}
/* Workaround for PCIe DMA transfer */
if (rockchip->dma_trx_enabled) {
rockchip_pcie_prog_ob_atu(rockchip, 1, AXI_WRAPPER_MEM_WRITE,
32 - 1, rockchip->mem_reserve_start, 0x0);
}
err = rockchip_pcie_prog_ib_atu(rockchip, 2, 32 - 1, 0x0, 0);
if (err) {
dev_err(dev, "program RC mem inbound ATU failed\n");
return err;
}
entry = resource_list_first_type(&bridge->windows, IORESOURCE_IO);
if (!entry)
return -ENODEV;
/* store the register number offset to program RC io outbound ATU */
offset = size >> 20;
size = resource_size(entry->res);
pci_addr = entry->res->start - entry->offset;
for (reg_no = 0; reg_no < (size >> 20); reg_no++) {
err = rockchip_pcie_prog_ob_atu(rockchip,
reg_no + 1 + offset,
AXI_WRAPPER_IO_WRITE,
20 - 1,
pci_addr + (reg_no << 20),
0);
if (err) {
dev_err(dev, "program RC io outbound ATU failed\n");
return err;
}
}
/* assign message regions */
rockchip_pcie_prog_ob_atu(rockchip, reg_no + 1 + offset,
AXI_WRAPPER_NOR_MSG,
20 - 1, 0, 0);
rockchip->msg_bus_addr += ((reg_no + offset) << 20);
rockchip->msg_region = devm_ioremap(dev, rockchip->msg_bus_addr, SZ_1M);
if (!rockchip->msg_region)
err = -ENOMEM;
return err;
}
在 probe 函数的调用链中会调用pci_scan_slot 扫描设备得到pci_dev
linux-orangepi-orange-pi-6.1-rk35xx\drivers\pci\probe.c
/**
* pci_scan_slot - Scan a PCI slot on a bus for devices
* @bus: PCI bus to scan
* @devfn: slot number to scan (must have zero function)
*
* Scan a PCI slot on the specified PCI bus for devices, adding
* discovered devices to the @bus->devices list. New devices
* will not have is_added set.
*
* Returns the number of new devices found.
*/
int pci_scan_slot(struct pci_bus *bus, int devfn)
{
struct pci_dev *dev;
int fn = 0, nr = 0;
if (only_one_child(bus) && (devfn > 0))
return 0; /* Already scanned the entire slot */
do {
dev = pci_scan_single_device(bus, devfn + fn);
if (dev) {
if (!pci_dev_is_added(dev))
nr++;
if (fn > 0)
dev->multifunction = 1;
} else if (fn == 0) {
/*
* Function 0 is required unless we are running on
* a hypervisor that passes through individual PCI
* functions.
*/
if (!hypervisor_isolated_pci_functions())
break;
}
fn = next_fn(bus, dev, fn);
} while (fn >= 0);
/* Only one slot has PCIe device */
if (bus->self && nr)
pcie_aspm_init_link_state(bus->self);
return nr;
}
EXPORT_SYMBOL(pci_scan_slot);
struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn)
{
struct pci_dev *dev;
dev = pci_get_slot(bus, devfn);
if (dev) {
pci_dev_put(dev);
return dev;
}
dev = pci_scan_device(bus, devfn);
if (!dev)
return NULL;
pci_device_add(dev, bus);
return dev;
}
/*
* Read the config data for a PCI device, sanity-check it,
* and fill in the dev structure.
*/
static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
{
struct pci_dev *dev;
u32 l;
if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000))
return NULL;
dev = pci_alloc_dev(bus);
if (!dev)
return NULL;
dev->devfn = devfn;
dev->vendor = l & 0xffff;
dev->device = (l >> 16) & 0xffff;
if (pci_setup_device(dev)) {
pci_bus_put(dev->bus);
kfree(dev);
return NULL;
}
return dev;
}