pcie 对rc操作的ops

rc 的全程是root compose,一般有host bridage + host bus + 几个 host port组成,是最靠近cpu的pci device。对rc的操作有一个专门的ops,每家的都不一样。这个ops 一般实在pci_mcfg_match_quirks 中根据bios传递过来的mcfg_oem_id/mcfg_oem_table_id/mcfg_oem_revision 来决定。
kernel中所支持的ops都在mcfg_quirks中
static struct mcfg_fixup mcfg_quirks[] = {
/*    { OEM_ID, OEM_TABLE_ID, REV, DOMAIN, BUS_RANGE, cfgres, ops }, */


    { "QCOM  ", "QDF2432 ", 1, 0, MCFG_BUS_ANY, &pci_32b_ops },
    { "QCOM  ", "QDF2432 ", 1, 1, MCFG_BUS_ANY, &pci_32b_ops },
    { "QCOM  ", "QDF2432 ", 1, 2, MCFG_BUS_ANY, &pci_32b_ops },
    { "QCOM  ", "QDF2432 ", 1, 3, MCFG_BUS_ANY, &pci_32b_ops },
    { "QCOM  ", "QDF2432 ", 1, 4, MCFG_BUS_ANY, &pci_32b_ops },
    { "QCOM  ", "QDF2432 ", 1, 5, MCFG_BUS_ANY, &pci_32b_ops },
    { "QCOM  ", "QDF2432 ", 1, 6, MCFG_BUS_ANY, &pci_32b_ops },
    { "QCOM  ", "QDF2432 ", 1, 7, MCFG_BUS_ANY, &pci_32b_ops },
#ifdef CONFIG_PCI_HISI_ACPI
    #define PCI_ACPI_QUIRK_QUAD_DOM(table_id, seg, ops) \
    { "HISI  ", table_id, 0, seg + 0, MCFG_BUS_ANY, ops }, \
    { "HISI  ", table_id, 0, seg + 1, MCFG_BUS_ANY, ops }, \
    { "HISI  ", table_id, 0, seg + 2, MCFG_BUS_ANY, ops }, \
    { "HISI  ", table_id, 0, seg + 3, MCFG_BUS_ANY, ops }
    PCI_ACPI_QUIRK_QUAD_DOM("HIP05   ", 0, &hisi_pcie_ops),
    PCI_ACPI_QUIRK_QUAD_DOM("HIP06   ", 0, &hisi_pcie_ops),
    PCI_ACPI_QUIRK_QUAD_DOM("HIP07   ", 0, &hisi_pcie_ops),
    PCI_ACPI_QUIRK_QUAD_DOM("HIP07   ", 4, &hisi_pcie_ops),
    PCI_ACPI_QUIRK_QUAD_DOM("HIP07   ", 8, &hisi_pcie_ops),
    PCI_ACPI_QUIRK_QUAD_DOM("HIP07   ", 12, &hisi_pcie_ops),
#endif

};

如果rc不需要特殊的设定,就可以用kernel提供的pci_generic_ecam_ops,这时候在pci_mcfg_match_quirks 中不对已经在pci_mcfg_lookup中赋值res和ops 进行override.

以hisi的ops为例,其定义在pci/host/pcie-hisi-acpi.c 中.
struct pci_ecam_ops hisi_pcie_ops = {
    .bus_shift    = 20,
    .init         =  hisi_pcie_init,
    .pci_ops      = {
        .map_bus    = hisi_pcie_map_bus,
        .read       = hisi_pcie_acpi_rd_conf,
        .write      = hisi_pcie_acpi_wr_conf,
    }
};

在hisi_pcie_init->hisi_pcie_rc_addr_get
static int hisi_pcie_rc_addr_get(struct acpi_device *adev,
                 void __iomem **addr)
{
    struct acpi_device *child_adev;
    struct list_head list;
    struct resource *res;
    struct resource_entry *entry;
    unsigned long flags;
    int ret;

    list_for_each_entry(child_adev, &adev->children, node) {
        ret = acpi_match_device_ids(child_adev, hisi_pcie_rc_res_ids);
        if (ret)
            continue;

        INIT_LIST_HEAD(&list);
        flags = IORESOURCE_MEM;
        ret = acpi_dev_get_resources(child_adev, &list,
                         acpi_dev_filter_resource_type_cb,
                         (void *)flags);
        if (ret < 0) {
            dev_err(&child_adev->dev,
                "failed to parse _CRS method, error code %d\n",
                ret);
            return ret;
        } else if (ret == 0) {
            dev_err(&child_adev->dev,
                "no IO and memory resources present in _CRS\n");
            return -EINVAL;
        }

        entry = list_first_entry(&list, struct resource_entry, node);
        res = entry->res;
        *addr = devm_ioremap(&child_adev->dev,
                     res->start, resource_size(res));
        acpi_dev_free_resource_list(&list);
        if (IS_ERR(*addr)) {
            dev_err(&child_adev->dev, "error with ioremap\n");
            return -ENOMEM;
        }

        return 0;
    }

    return -EINVAL;
}
主要对bios中定义的resource 进行ioremap,即将物理地址转化成虚拟地址,这样就可以通过读写内存的方式对rc进行操作.bios 中一般定义的rc 如下:
static const struct acpi_device_id hisi_pcie_rc_res_ids[] = {
    {"HISI0081", 0},
    {"", 0},
};
因此我们会匹配HISI0081
 * Retrieve RC base and size from sub-device under the RC
 * Device (RES1)
 * {
 *    Name (_HID, "HISI0081")
 *    Name (_CID, "PNP0C02")
 *    Name (_CRS, ResourceTemplate (){
 *        Memory32Fixed (ReadWrite, 0xb0080000, 0x10000)
 *    })
 * }
在hisi_pcie_acpi_rd_conf 和 hisi_pcie_acpi_wr_conf 函数总都会调用map函数来根据devfn 得到要操作的地址
static int hisi_pcie_acpi_rd_conf(struct pci_bus *bus, u32 devfn, int where,
                  int size, u32 *val)
{
    struct pci_config_window *cfg = bus->sysdata;
    int dev = PCI_SLOT(devfn);

    if (bus->number == cfg->busr.start) {
        /* access only one slot on each root port */
        if (dev > 0)
            return PCIBIOS_DEVICE_NOT_FOUND;
        else
            return pci_generic_config_read32(bus, devfn, where,
                             size, val);
    }

    return pci_generic_config_read(bus, devfn, where, size, val);
}
pci_generic_config_read32 中调用bus->ops->map_bus 就是要得到当前devfn 对应的地址.
int pci_generic_config_read32(struct pci_bus *bus, unsigned int devfn,
                  int where, int size, u32 *val)
{
    void __iomem *addr;

    addr = bus->ops->map_bus(bus, devfn, where & ~0x3);
    if (!addr) {
        *val = ~0;
        return PCIBIOS_DEVICE_NOT_FOUND;
    }

    *val = readl(addr);

    if (size <= 2)
        *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);

    return PCIBIOS_SUCCESSFUL;
}

hisi_pcie_map_bus
static void __iomem *hisi_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
                       int where)
{
    struct pci_config_window *cfg = bus->sysdata;
    void __iomem *reg_base = cfg->priv;

    if (bus->number == cfg->busr.start)
        return reg_base + where;
    else
        return pci_ecam_map_bus(bus, devfn, where);
}
在hisi_pcie_map_bus 中如果走else的话,就会调用针对ecam 的resource进行计算得到地址.ecam 之前已将讲过了
void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn,
                   int where)
{
    struct pci_config_window *cfg = bus->sysdata;
    unsigned int devfn_shift = cfg->ops->bus_shift - 8;
    unsigned int busn = bus->number;
    void __iomem *base;

    if (busn < cfg->busr.start || busn > cfg->busr.end)
        return NULL;

    busn -= cfg->busr.start;
    if (per_bus_mapping)
        base = cfg->winp[busn];
    else
        base = cfg->win + (busn << cfg->ops->bus_shift);
    return base + (devfn << devfn_shift) + where;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值