static void pci_msi_teardown_msi_irqs(struct pci_dev *dev) { struct irq_domain *domain; domain = dev_get_msi_domain(&dev->dev); if (domain && irq_domain_is_hierarchy(domain)) msi_domain_free_irqs(domain, &dev->dev); else arch_teardown_msi_irqs(dev); }
static struct irq_domain *pci_msi_get_domain(struct pci_dev *dev) { struct irq_domain *domain; domain = dev_get_msi_domain(&dev->dev); if (domain) return domain; return arch_get_pci_msi_domain(dev); }
static int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) { struct irq_domain *domain; domain = dev_get_msi_domain(&dev->dev); if (domain && irq_domain_is_hierarchy(domain)) return msi_domain_alloc_irqs(domain, &dev->dev, nvec); return arch_setup_msi_irqs(dev, nvec, type); }
/** * dprc_remove - callback invoked when a DPRC is being unbound from this driver * * @mc_dev: Pointer to fsl-mc device representing the DPRC * * It removes the DPRC's child objects from Linux (not from the MC) and * closes the DPRC device in the MC. * It tears down the interrupts that were configured for the DPRC device. * It destroys the interrupt pool associated with this MC bus. */ static int dprc_remove(struct fsl_mc_device *mc_dev) { int error; struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0)) return -EINVAL; if (WARN_ON(!mc_dev->mc_io)) return -EINVAL; if (WARN_ON(!mc_bus->irq_resources)) return -EINVAL; if (dev_get_msi_domain(&mc_dev->dev)) dprc_teardown_irq(mc_dev); device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove); if (dev_get_msi_domain(&mc_dev->dev)) { fsl_mc_cleanup_irq_pool(mc_bus); dev_set_msi_domain(&mc_dev->dev, NULL); } fsl_mc_cleanup_all_resource_pools(mc_dev); error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); if (error < 0) dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error); if (!fsl_mc_is_root_dprc(&mc_dev->dev)) { fsl_destroy_mc_io(mc_dev->mc_io); mc_dev->mc_io = NULL; } dev_info(&mc_dev->dev, "DPRC device unbound from driver"); return 0; }
/** * dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state * * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object * * Scans the physical DPRC and synchronizes the state of the Linux * bus driver with the actual state of the MC by adding and removing * devices as appropriate. */ int dprc_scan_container(struct fsl_mc_device *mc_bus_dev) { int error; unsigned int irq_count; struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); fsl_mc_init_all_resource_pools(mc_bus_dev); /* * Discover objects in the DPRC: */ mutex_lock(&mc_bus->scan_mutex); error = dprc_scan_objects(mc_bus_dev, &irq_count); mutex_unlock(&mc_bus->scan_mutex); if (error < 0) goto error; if (dev_get_msi_domain(&mc_bus_dev->dev) && !mc_bus->irq_resources) { if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) { dev_warn(&mc_bus_dev->dev, "IRQs needed (%u) exceed IRQs preallocated (%u)\n", irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); } error = fsl_mc_populate_irq_pool( mc_bus, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); if (error < 0) goto error; } return 0; error: fsl_mc_cleanup_all_resource_pools(mc_bus_dev); return error; }
/** * dprc_probe - callback invoked when a DPRC is being bound to this driver * * @mc_dev: Pointer to fsl-mc device representing a DPRC * * It opens the physical DPRC in the MC. * It scans the DPRC to discover the MC objects contained in it. * It creates the interrupt pool for the MC bus associated with the DPRC. * It configures the interrupts for the DPRC device itself. */ static int dprc_probe(struct fsl_mc_device *mc_dev) { int error; size_t region_size; struct device *parent_dev = mc_dev->dev.parent; struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); bool mc_io_created = false; bool msi_domain_set = false; if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0)) return -EINVAL; if (WARN_ON(dev_get_msi_domain(&mc_dev->dev))) return -EINVAL; if (!mc_dev->mc_io) { /* * This is a child DPRC: */ if (WARN_ON(!dev_is_fsl_mc(parent_dev))) return -EINVAL; if (WARN_ON(mc_dev->obj_desc.region_count == 0)) return -EINVAL; region_size = mc_dev->regions[0].end - mc_dev->regions[0].start + 1; error = fsl_create_mc_io(&mc_dev->dev, mc_dev->regions[0].start, region_size, NULL, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, &mc_dev->mc_io); if (error < 0) return error; mc_io_created = true; /* * Inherit parent MSI domain: */ dev_set_msi_domain(&mc_dev->dev, dev_get_msi_domain(parent_dev)); msi_domain_set = true; } else { /* * This is a root DPRC */ struct irq_domain *mc_msi_domain; if (WARN_ON(dev_is_fsl_mc(parent_dev))) return -EINVAL; error = fsl_mc_find_msi_domain(parent_dev, &mc_msi_domain); if (error < 0) { dev_warn(&mc_dev->dev, "WARNING: MC bus without interrupt support\n"); } else { dev_set_msi_domain(&mc_dev->dev, mc_msi_domain); msi_domain_set = true; } } error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, &mc_dev->mc_handle); if (error < 0) { dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error); goto error_cleanup_msi_domain; } error = dprc_get_attributes(mc_dev->mc_io, 0, mc_dev->mc_handle, &mc_bus->dprc_attr); if (error < 0) { dev_err(&mc_dev->dev, "dprc_get_attributes() failed: %d\n", error); goto error_cleanup_open; } if (mc_bus->dprc_attr.version.major < DPRC_MIN_VER_MAJOR || (mc_bus->dprc_attr.version.major == DPRC_MIN_VER_MAJOR && mc_bus->dprc_attr.version.minor < DPRC_MIN_VER_MINOR)) { dev_err(&mc_dev->dev, "ERROR: DPRC version %d.%d not supported\n", mc_bus->dprc_attr.version.major, mc_bus->dprc_attr.version.minor); error = -ENOTSUPP; goto error_cleanup_open; } mutex_init(&mc_bus->scan_mutex); /* * Discover MC objects in DPRC object: */ error = dprc_scan_container(mc_dev); if (error < 0) goto error_cleanup_open; /* * Configure interrupt for the DPRC object associated with this MC bus: */ error = dprc_setup_irq(mc_dev); if (error < 0) goto error_cleanup_open; dev_info(&mc_dev->dev, "DPRC device bound to driver"); return 0; error_cleanup_open: (void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); error_cleanup_msi_domain: if (msi_domain_set) dev_set_msi_domain(&mc_dev->dev, NULL); if (mc_io_created) { fsl_destroy_mc_io(mc_dev->mc_io); mc_dev->mc_io = NULL; } return error; }
/** * Add a newly discovered MC object device to be visible in Linux */ int fsl_mc_device_add(struct dprc_obj_desc *obj_desc, struct fsl_mc_io *mc_io, struct device *parent_dev, struct fsl_mc_device **new_mc_dev) { int error; struct fsl_mc_device *mc_dev = NULL; struct fsl_mc_bus *mc_bus = NULL; struct fsl_mc_device *parent_mc_dev; if (parent_dev->bus == &fsl_mc_bus_type) parent_mc_dev = to_fsl_mc_device(parent_dev); else parent_mc_dev = NULL; if (strcmp(obj_desc->type, "dprc") == 0) { /* * Allocate an MC bus device object: */ mc_bus = devm_kzalloc(parent_dev, sizeof(*mc_bus), GFP_KERNEL); if (!mc_bus) return -ENOMEM; mc_dev = &mc_bus->mc_dev; } else { /* * Allocate a regular fsl_mc_device object: */ mc_dev = kmem_cache_zalloc(mc_dev_cache, GFP_KERNEL); if (!mc_dev) return -ENOMEM; } mc_dev->obj_desc = *obj_desc; mc_dev->mc_io = mc_io; device_initialize(&mc_dev->dev); mc_dev->dev.parent = parent_dev; mc_dev->dev.bus = &fsl_mc_bus_type; dev_set_name(&mc_dev->dev, "%s.%d", obj_desc->type, obj_desc->id); if (strcmp(obj_desc->type, "dprc") == 0) { struct fsl_mc_io *mc_io2; mc_dev->flags |= FSL_MC_IS_DPRC; /* * To get the DPRC's ICID, we need to open the DPRC * in get_dprc_icid(). For child DPRCs, we do so using the * parent DPRC's MC portal instead of the child DPRC's MC * portal, in case the child DPRC is already opened with * its own portal (e.g., the DPRC used by AIOP). * * NOTE: There cannot be more than one active open for a * given MC object, using the same MC portal. */ if (parent_mc_dev) { /* * device being added is a child DPRC device */ mc_io2 = parent_mc_dev->mc_io; } else { /* * device being added is the root DPRC device */ if (WARN_ON(!mc_io)) { error = -EINVAL; goto error_cleanup_dev; } mc_io2 = mc_io; atomic_inc(&root_dprc_count); } error = get_dprc_icid(mc_io2, obj_desc->id, &mc_dev->icid); if (error < 0) goto error_cleanup_dev; } else { /* * A non-DPRC MC object device has to be a child of another * MC object (specifically a DPRC object) */ mc_dev->icid = parent_mc_dev->icid; mc_dev->dma_mask = FSL_MC_DEFAULT_DMA_MASK; mc_dev->dev.dma_mask = &mc_dev->dma_mask; dev_set_msi_domain(&mc_dev->dev, dev_get_msi_domain(&parent_mc_dev->dev)); } /* * Get MMIO regions for the device from the MC: * * NOTE: the root DPRC is a special case as its MMIO region is * obtained from the device tree */ if (parent_mc_dev && obj_desc->region_count != 0) { error = fsl_mc_device_get_mmio_regions(mc_dev, parent_mc_dev); if (error < 0) goto error_cleanup_dev; } /* * The device-specific probe callback will get invoked by device_add() */ error = device_add(&mc_dev->dev); if (error < 0) { dev_err(parent_dev, "device_add() failed for device %s: %d\n", dev_name(&mc_dev->dev), error); goto error_cleanup_dev; } (void)get_device(&mc_dev->dev); dev_dbg(parent_dev, "Added MC object device %s\n", dev_name(&mc_dev->dev)); *new_mc_dev = mc_dev; return 0; error_cleanup_dev: kfree(mc_dev->regions); if (mc_bus) devm_kfree(parent_dev, mc_bus); else kmem_cache_free(mc_dev_cache, mc_dev); return error; }