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;
}
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;
}