@@ -69,6 +69,9 @@ struct pci_host_bridge {
struct pci_config_window* cfg; /* Pointer to the bridge config window */
const struct pci_ops *ops;
void *priv; /* Private data of the bridge. */
+ /* Child bus */
+ struct pci_config_window* child_cfg;
+ const struct pci_ops *child_ops;
};
struct pci_ops {
@@ -97,15 +100,20 @@ struct pci_ecam_ops {
/* Default ECAM ops */
extern const struct pci_ecam_ops pci_generic_ecam_ops;
-struct pci_host_bridge *pci_host_common_probe(struct dt_device_node *dev,
- const struct pci_ecam_ops *ops,
- size_t priv_sz);
+struct pci_host_bridge *
+pci_host_common_probe(struct dt_device_node *dev,
+ const struct pci_ecam_ops *ops,
+ const struct pci_ecam_ops *child_ops,
+ size_t priv_sz);
int pci_generic_config_read(struct pci_host_bridge *bridge, pci_sbdf_t sbdf,
uint32_t reg, uint32_t len, uint32_t *value);
int pci_generic_config_write(struct pci_host_bridge *bridge, pci_sbdf_t sbdf,
uint32_t reg, uint32_t len, uint32_t value);
void __iomem *pci_ecam_map_bus(struct pci_host_bridge *bridge,
pci_sbdf_t sbdf, uint32_t where);
+void __iomem *pci_ecam_map_bus_generic(const struct pci_config_window *cfg,
+ const struct pci_ecam_ops *ops,
+ pci_sbdf_t sbdf, uint32_t where);
bool pci_ecam_need_p2m_hwdom_mapping(struct domain *d,
struct pci_host_bridge *bridge,
uint64_t addr);
@@ -20,12 +20,10 @@
/*
* Function to implement the pci_ops->map_bus method.
*/
-void __iomem *pci_ecam_map_bus(struct pci_host_bridge *bridge,
- pci_sbdf_t sbdf, uint32_t where)
+void __iomem *pci_ecam_map_bus_generic(const struct pci_config_window *cfg,
+ const struct pci_ecam_ops *ops,
+ pci_sbdf_t sbdf, uint32_t where)
{
- const struct pci_config_window *cfg = bridge->cfg;
- const struct pci_ecam_ops *ops =
- container_of(bridge->ops, const struct pci_ecam_ops, pci_ops);
unsigned int devfn_shift = ops->bus_shift - 8;
void __iomem *base;
unsigned int busn = sbdf.bus;
@@ -39,6 +37,15 @@ void __iomem *pci_ecam_map_bus(struct pci_host_bridge *bridge,
return base + (sbdf.devfn << devfn_shift) + where;
}
+void __iomem *pci_ecam_map_bus(struct pci_host_bridge *bridge,
+ pci_sbdf_t sbdf, uint32_t where)
+{
+ const struct pci_ecam_ops *ops =
+ container_of(bridge->ops, const struct pci_ecam_ops, pci_ops);
+
+ return pci_ecam_map_bus_generic(bridge->cfg, ops, sbdf, where);
+}
+
bool __init pci_ecam_need_p2m_hwdom_mapping(struct domain *d,
struct pci_host_bridge *bridge,
uint64_t addr)
@@ -18,10 +18,31 @@
#define INVALID_VALUE (~0U)
#define PCI_ERR_VALUE(len) GENMASK(0, len * 8)
+static const struct pci_ops *get_ops(struct pci_host_bridge *bridge,
+ pci_sbdf_t sbdf)
+{
+ if ( bridge->child_ops )
+ {
+ struct pci_config_window* cfg = bridge->child_cfg;
+
+ if ( (sbdf.bus >= cfg->busn_start) && (sbdf.bus <= cfg->busn_end) )
+ return bridge->child_ops;
+ }
+ return bridge->ops;
+}
+
+static void __iomem *map_bus(struct pci_host_bridge *bridge, pci_sbdf_t sbdf,
+ uint32_t reg)
+{
+ const struct pci_ops *ops = get_ops(bridge, sbdf);
+
+ return ops->map_bus(bridge, sbdf, reg);
+}
+
int pci_generic_config_read(struct pci_host_bridge *bridge, pci_sbdf_t sbdf,
uint32_t reg, uint32_t len, uint32_t *value)
{
- void __iomem *addr = bridge->ops->map_bus(bridge, sbdf, reg);
+ void __iomem *addr = map_bus(bridge, sbdf, reg);
if ( !addr )
{
@@ -50,7 +71,7 @@ int pci_generic_config_read(struct pci_host_bridge *bridge, pci_sbdf_t sbdf,
int pci_generic_config_write(struct pci_host_bridge *bridge, pci_sbdf_t sbdf,
uint32_t reg, uint32_t len, uint32_t value)
{
- void __iomem *addr = bridge->ops->map_bus(bridge, sbdf, reg);
+ void __iomem *addr = map_bus(bridge, sbdf, reg);
if ( !addr )
return -ENODEV;
@@ -78,14 +99,16 @@ static uint32_t pci_config_read(pci_sbdf_t sbdf, unsigned int reg,
{
uint32_t val = PCI_ERR_VALUE(len);
struct pci_host_bridge *bridge = pci_find_host_bridge(sbdf.seg, sbdf.bus);
+ const struct pci_ops *ops;
if ( unlikely(!bridge) )
return val;
- if ( unlikely(!bridge->ops->read) )
+ ops = get_ops(bridge, sbdf);
+ if ( unlikely(!ops->read) )
return val;
- bridge->ops->read(bridge, sbdf, reg, len, &val);
+ ops->read(bridge, sbdf, reg, len, &val);
return val;
}
@@ -94,14 +117,16 @@ static void pci_config_write(pci_sbdf_t sbdf, unsigned int reg,
unsigned int len, uint32_t val)
{
struct pci_host_bridge *bridge = pci_find_host_bridge(sbdf.seg, sbdf.bus);
+ const struct pci_ops *ops;
if ( unlikely(!bridge) )
return;
- if ( unlikely(!bridge->ops->write) )
+ ops = get_ops(bridge, sbdf);
+ if ( unlikely(!ops->write) )
return;
- bridge->ops->write(bridge, sbdf, reg, len, val);
+ ops->write(bridge, sbdf, reg, len, val);
}
/*
@@ -57,17 +57,12 @@ static void pci_ecam_free(struct pci_config_window *cfg)
xfree(cfg);
}
-static struct pci_config_window * __init
-gen_pci_init(struct dt_device_node *dev, const struct pci_ecam_ops *ops)
+static void __init gen_pci_init_bus_range(struct dt_device_node *dev,
+ struct pci_host_bridge *bridge,
+ struct pci_config_window *cfg)
{
- int err, cfg_reg_idx;
u32 bus_range[2];
- paddr_t addr, size;
- struct pci_config_window *cfg;
-
- cfg = xzalloc(struct pci_config_window);
- if ( !cfg )
- return NULL;
+ int err;
err = dt_property_read_u32_array(dev, "bus-range", bus_range,
ARRAY_SIZE(bus_range));
@@ -82,6 +77,36 @@ gen_pci_init(struct dt_device_node *dev, const struct pci_ecam_ops *ops)
if ( cfg->busn_end > cfg->busn_start + 0xff )
cfg->busn_end = cfg->busn_start + 0xff;
}
+}
+
+static void __init gen_pci_init_bus_range_child(struct dt_device_node *dev,
+ struct pci_host_bridge *bridge,
+ struct pci_config_window *cfg)
+{
+ cfg->busn_start = bridge->cfg->busn_start + 1;
+ cfg->busn_end = bridge->cfg->busn_end;
+ bridge->cfg->busn_end = bridge->cfg->busn_start;
+
+ printk(XENLOG_INFO "Root bus end updated: [bus %x-%x]\n",
+ bridge->cfg->busn_start, bridge->cfg->busn_end);
+}
+
+static struct pci_config_window * __init
+gen_pci_init(struct dt_device_node *dev, struct pci_host_bridge *bridge,
+ const struct pci_ecam_ops *ops,
+ void (*init_bus_range)(struct dt_device_node *dev,
+ struct pci_host_bridge *bridge,
+ struct pci_config_window *cfg))
+{
+ int err, cfg_reg_idx;
+ paddr_t addr, size;
+ struct pci_config_window *cfg;
+
+ cfg = xzalloc(struct pci_config_window);
+ if ( !cfg )
+ return NULL;
+
+ init_bus_range(dev, bridge, cfg);
if ( ops->cfg_reg_index )
{
@@ -208,9 +233,11 @@ static int pci_bus_find_domain_nr(struct dt_device_node *dev)
return domain;
}
-struct pci_host_bridge *pci_host_common_probe(struct dt_device_node *dev,
- const struct pci_ecam_ops *ops,
- size_t priv_sz)
+struct pci_host_bridge *
+pci_host_common_probe(struct dt_device_node *dev,
+ const struct pci_ecam_ops *ops,
+ const struct pci_ecam_ops *child_ops,
+ size_t priv_sz)
{
struct pci_host_bridge *bridge;
struct pci_config_window *cfg;
@@ -225,7 +252,7 @@ struct pci_host_bridge *pci_host_common_probe(struct dt_device_node *dev,
return ERR_PTR(-ENOMEM);
/* Parse and map our Configuration Space windows */
- cfg = gen_pci_init(dev, ops);
+ cfg = gen_pci_init(dev, bridge, ops, gen_pci_init_bus_range);
if ( !cfg )
{
err = -ENOMEM;
@@ -245,6 +272,21 @@ struct pci_host_bridge *pci_host_common_probe(struct dt_device_node *dev,
bridge->segment = domain;
+ if ( child_ops )
+ {
+ /* Parse and map child's Configuration Space windows */
+ cfg = gen_pci_init(dev, bridge, child_ops,
+ gen_pci_init_bus_range_child);
+ if ( !cfg )
+ {
+ err = -ENOMEM;
+ goto err_child;
+ }
+
+ bridge->child_cfg = cfg;
+ bridge->child_ops = &child_ops->pci_ops;
+ }
+
if ( priv_sz )
{
bridge->priv = xzalloc_bytes(priv_sz);
@@ -262,6 +304,9 @@ struct pci_host_bridge *pci_host_common_probe(struct dt_device_node *dev,
err_priv:
xfree(bridge->priv);
+err_child:
+ xfree(bridge->cfg);
+
err_exit:
xfree(bridge);
@@ -296,9 +341,11 @@ struct pci_host_bridge *pci_find_host_bridge(uint16_t segment, uint8_t bus)
{
if ( bridge->segment != segment )
continue;
- if ( (bus < bridge->cfg->busn_start) || (bus > bridge->cfg->busn_end) )
- continue;
- return bridge;
+ if ( bridge->child_cfg && (bus >= bridge->child_cfg->busn_start) &&
+ (bus <= bridge->child_cfg->busn_end) )
+ return bridge;
+ if ( (bus >= bridge->cfg->busn_start) && (bus <= bridge->cfg->busn_end) )
+ return bridge;
}
return NULL;
@@ -364,6 +411,7 @@ int __init pci_host_bridge_mappings(struct domain *d)
{
const struct dt_device_node *dev = bridge->dt_node;
unsigned int i;
+ bool need_mapping;
for ( i = 0; i < dt_number_of_address(dev); i++ )
{
@@ -379,7 +427,11 @@ int __init pci_host_bridge_mappings(struct domain *d)
return err;
}
- if ( bridge->ops->need_p2m_hwdom_mapping(d, bridge, addr) )
+ need_mapping = bridge->ops->need_p2m_hwdom_mapping(d, bridge, addr);
+ if ( need_mapping && bridge->child_ops )
+ need_mapping = bridge->child_ops->
+ need_p2m_hwdom_mapping(d, bridge, addr);
+ if ( need_mapping )
{
err = map_range_to_domain(dev, addr, size, &mr_data);
if ( err )
@@ -29,7 +29,7 @@ static const struct dt_device_match __initconstrel gen_pci_dt_match[] =
static int __init pci_host_generic_probe(struct dt_device_node *dev,
const void *data)
{
- return PTR_RET(pci_host_common_probe(dev, &pci_generic_ecam_ops, 0));
+ return PTR_RET(pci_host_common_probe(dev, &pci_generic_ecam_ops, NULL, 0));
}
DT_DEVICE_START(pci_gen, "PCI HOST GENERIC", DEVICE_PCI_HOSTBRIDGE)
@@ -47,7 +47,7 @@ static const struct dt_device_match __initconstrel nwl_pcie_dt_match[] =
static int __init pci_host_generic_probe(struct dt_device_node *dev,
const void *data)
{
- return PTR_RET(pci_host_common_probe(dev, &nwl_pcie_ops, 0));
+ return PTR_RET(pci_host_common_probe(dev, &nwl_pcie_ops, NULL, 0));
}
DT_DEVICE_START(pci_gen, "PCI HOST ZYNQMP", DEVICE_PCI_HOSTBRIDGE)
@@ -8,15 +8,17 @@
#include <asm/mmio.h>
static pci_sbdf_t vpci_sbdf_from_gpa(const struct pci_host_bridge *bridge,
- paddr_t gpa)
+ paddr_t gpa, bool use_root)
{
pci_sbdf_t sbdf;
if ( bridge )
{
- sbdf.sbdf = VPCI_ECAM_BDF(gpa - bridge->cfg->phys_addr);
+ const struct pci_config_window *cfg = use_root ? bridge->cfg :
+ bridge->child_cfg;
+ sbdf.sbdf = VPCI_ECAM_BDF(gpa - cfg->phys_addr);
sbdf.seg = bridge->segment;
- sbdf.bus += bridge->cfg->busn_start;
+ sbdf.bus += cfg->busn_start;
}
else
sbdf.sbdf = VPCI_ECAM_BDF(gpa - GUEST_VPCI_ECAM_BASE);
@@ -24,11 +26,9 @@ static pci_sbdf_t vpci_sbdf_from_gpa(const struct pci_host_bridge *bridge,
return sbdf;
}
-static int vpci_mmio_read(struct vcpu *v, mmio_info_t *info,
- register_t *r, void *p)
+static int vpci_mmio_read(struct vcpu *v, mmio_info_t *info, register_t *r,
+ pci_sbdf_t sbdf)
{
- struct pci_host_bridge *bridge = p;
- pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa);
const unsigned int access_size = (1U << info->dabt.size) * 8;
const register_t invalid = GENMASK_ULL(access_size - 1, 0);
/* data is needed to prevent a pointer cast on 32bit */
@@ -46,31 +46,86 @@ static int vpci_mmio_read(struct vcpu *v, mmio_info_t *info,
return 0;
}
-static int vpci_mmio_write(struct vcpu *v, mmio_info_t *info,
- register_t r, void *p)
+static int vpci_mmio_read_root(struct vcpu *v, mmio_info_t *info,
+ register_t *r, void *p)
+{
+ struct pci_host_bridge *bridge = p;
+ pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa, true);
+
+ return vpci_mmio_read(v, info, r, sbdf);
+}
+
+static int vpci_mmio_read_child(struct vcpu *v, mmio_info_t *info,
+ register_t *r, void *p)
{
struct pci_host_bridge *bridge = p;
- pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa);
+ pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa, false);
+ return vpci_mmio_read(v, info, r, sbdf);
+}
+
+static int vpci_mmio_write(struct vcpu *v, mmio_info_t *info,
+ register_t r, pci_sbdf_t sbdf)
+{
return vpci_ecam_write(sbdf, ECAM_REG_OFFSET(info->gpa),
1U << info->dabt.size, r);
}
+static int vpci_mmio_write_root(struct vcpu *v, mmio_info_t *info,
+ register_t r, void *p)
+{
+ struct pci_host_bridge *bridge = p;
+ pci_sbdf_t sbdf;
+
+ if ( !vpci_sbdf_from_gpa(v->domain, bridge, info->gpa,
+ true, &sbdf) )
+ return 0;
+
+ return vpci_mmio_write(v, info, r, sbdf);
+}
+
+static int vpci_mmio_write_child(struct vcpu *v, mmio_info_t *info,
+ register_t r, void *p)
+{
+ struct pci_host_bridge *bridge = p;
+ pci_sbdf_t sbdf;
+
+ if ( !vpci_sbdf_from_gpa(v->domain, bridge, info->gpa,
+ false, &sbdf) )
+ return 0;
+
+ return vpci_mmio_write(v, info, r, sbdf);
+}
+
static const struct mmio_handler_ops vpci_mmio_handler = {
- .read = vpci_mmio_read,
- .write = vpci_mmio_write,
+ .read = vpci_mmio_read_root,
+ .write = vpci_mmio_write_root,
+};
+
+static const struct mmio_handler_ops vpci_mmio_handler_child = {
+ .read = vpci_mmio_read_child,
+ .write = vpci_mmio_write_child,
};
static int vpci_setup_mmio_handler_cb(struct domain *d,
struct pci_host_bridge *bridge)
{
struct pci_config_window *cfg = bridge->cfg;
+ int count = 1;
register_mmio_handler(d, &vpci_mmio_handler,
cfg->phys_addr, cfg->size, bridge);
- /* We have registered a single MMIO handler. */
- return 1;
+ if ( bridge->child_ops )
+ {
+ struct pci_config_window *cfg = bridge->child_cfg;
+
+ register_mmio_handler(d, &vpci_mmio_handler_child,
+ cfg->phys_addr, cfg->size, bridge);
+ count++;
+ }
+
+ return count;
}
int domain_vpci_init(struct domain *d)
@@ -101,8 +156,12 @@ int domain_vpci_init(struct domain *d)
static int vpci_get_num_handlers_cb(struct domain *d,
struct pci_host_bridge *bridge)
{
- /* Each bridge has a single MMIO handler for the configuration space. */
- return 1;
+ int count = 1;
+
+ if ( bridge->child_cfg )
+ count++;
+
+ return count;
}
unsigned int domain_vpci_get_num_mmio_handlers(struct domain *d)