/** * fsl_mc_device_remove - Remove a MC object device from being visible to * Linux * * @mc_dev: Pointer to a MC object device object */ void fsl_mc_device_remove(struct fsl_mc_device *mc_dev) { struct fsl_mc_bus *mc_bus = NULL; kfree(mc_dev->regions); /* * The device-specific remove callback will get invoked by device_del() */ device_del(&mc_dev->dev); put_device(&mc_dev->dev); if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) { mc_bus = to_fsl_mc_bus(mc_dev); if (mc_dev->mc_io) { fsl_destroy_mc_io(mc_dev->mc_io); mc_dev->mc_io = NULL; } if (fsl_mc_is_root_dprc(&mc_dev->dev)) { if (atomic_read(&root_dprc_count) > 0) atomic_dec(&root_dprc_count); else WARN_ON(1); } } if (mc_bus) devm_kfree(mc_dev->dev.parent, mc_bus); else kmem_cache_free(mc_dev_cache, mc_dev); }
/** * Creates an MC I/O object * * @dev: device to be associated with the MC I/O object * @mc_portal_phys_addr: physical address of the MC portal to use * @mc_portal_size: size in bytes of the MC portal * @dpmcp-dev: Pointer to the DPMCP object associated with this MC I/O * object or NULL if none. * @flags: flags for the new MC I/O object * @new_mc_io: Area to return pointer to newly created MC I/O object * * Returns '0' on Success; Error code otherwise. */ int __must_check fsl_create_mc_io(struct device *dev, phys_addr_t mc_portal_phys_addr, u32 mc_portal_size, struct fsl_mc_device *dpmcp_dev, u32 flags, struct fsl_mc_io **new_mc_io) { int error; struct fsl_mc_io *mc_io; void __iomem *mc_portal_virt_addr; struct resource *res; mc_io = devm_kzalloc(dev, sizeof(*mc_io), GFP_KERNEL); if (!mc_io) return -ENOMEM; mc_io->dev = dev; mc_io->flags = flags; mc_io->portal_phys_addr = mc_portal_phys_addr; mc_io->portal_size = mc_portal_size; if (flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) spin_lock_init(&mc_io->spinlock); else mutex_init(&mc_io->mutex); res = devm_request_mem_region(dev, mc_portal_phys_addr, mc_portal_size, "mc_portal"); if (!res) { dev_err(dev, "devm_request_mem_region failed for MC portal %#llx\n", mc_portal_phys_addr); return -EBUSY; } mc_portal_virt_addr = devm_ioremap_nocache(dev, mc_portal_phys_addr, mc_portal_size); if (!mc_portal_virt_addr) { dev_err(dev, "devm_ioremap_nocache failed for MC portal %#llx\n", mc_portal_phys_addr); return -ENXIO; } mc_io->portal_virt_addr = mc_portal_virt_addr; if (dpmcp_dev) { error = fsl_mc_io_set_dpmcp(mc_io, dpmcp_dev); if (error < 0) goto error_destroy_mc_io; } *new_mc_io = mc_io; return 0; error_destroy_mc_io: fsl_destroy_mc_io(mc_io); return error; }
/** * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals * of a given MC bus * * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free */ void fsl_mc_portal_free(struct fsl_mc_io *mc_io) { struct fsl_mc_resource *resource; resource = mc_io->resource; if (WARN_ON(resource->type != FSL_MC_POOL_DPMCP)) return; if (WARN_ON(!resource->data)) return; fsl_destroy_mc_io(mc_io); fsl_mc_resource_free(resource); }
/** * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals * of a given MC bus * * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free */ void fsl_mc_portal_free(struct fsl_mc_io *mc_io) { struct fsl_mc_device *dpmcp_dev; struct fsl_mc_resource *resource; /* * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed * to have a DPMCP object associated with. */ dpmcp_dev = mc_io->dpmcp_dev; if (WARN_ON(!dpmcp_dev)) return; resource = dpmcp_dev->resource; if (WARN_ON(!resource || resource->type != FSL_MC_POOL_DPMCP)) return; if (WARN_ON(resource->data != dpmcp_dev)) return; fsl_destroy_mc_io(mc_io); fsl_mc_resource_free(resource); }
/** * 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_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; }
/** * fsl_mc_bus_probe - callback invoked when the root MC bus is being * added */ static int fsl_mc_bus_probe(struct platform_device *pdev) { struct dprc_obj_desc obj_desc; int error; struct fsl_mc *mc; struct fsl_mc_device *mc_bus_dev = NULL; struct fsl_mc_io *mc_io = NULL; int container_id; phys_addr_t mc_portal_phys_addr; u32 mc_portal_size; struct mc_version mc_version; struct resource res; dev_info(&pdev->dev, "Root MC bus device probed"); mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); if (!mc) return -ENOMEM; platform_set_drvdata(pdev, mc); /* * Get physical address of MC portal for the root DPRC: */ error = of_address_to_resource(pdev->dev.of_node, 0, &res); if (error < 0) { dev_err(&pdev->dev, "of_address_to_resource() failed for %s\n", pdev->dev.of_node->full_name); return error; } mc_portal_phys_addr = res.start; mc_portal_size = resource_size(&res); error = fsl_create_mc_io(&pdev->dev, mc_portal_phys_addr, mc_portal_size, NULL, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, &mc_io); if (error < 0) return error; error = mc_get_version(mc_io, 0, &mc_version); if (error != 0) { dev_err(&pdev->dev, "mc_get_version() failed with error %d\n", error); goto error_cleanup_mc_io; } dev_info(&pdev->dev, "Freescale Management Complex Firmware version: %u.%u.%u\n", mc_version.major, mc_version.minor, mc_version.revision); if (mc_version.major < MC_VER_MAJOR) { dev_err(&pdev->dev, "ERROR: MC firmware version not supported by driver (driver version: %u.%u)\n", MC_VER_MAJOR, MC_VER_MINOR); error = -ENOTSUPP; goto error_cleanup_mc_io; } if (mc_version.major > MC_VER_MAJOR) { dev_warn(&pdev->dev, "WARNING: driver may not support newer MC firmware features (driver version: %u.%u)\n", MC_VER_MAJOR, MC_VER_MINOR); } error = get_mc_addr_translation_ranges(&pdev->dev, &mc->translation_ranges, &mc->num_translation_ranges); if (error < 0) goto error_cleanup_mc_io; error = dpmng_get_container_id(mc_io, 0, &container_id); if (error < 0) { dev_err(&pdev->dev, "dpmng_get_container_id() failed: %d\n", error); goto error_cleanup_mc_io; } obj_desc.vendor = FSL_MC_VENDOR_FREESCALE; strcpy(obj_desc.type, "dprc"); obj_desc.id = container_id; obj_desc.ver_major = DPRC_VER_MAJOR; obj_desc.ver_minor = DPRC_VER_MINOR; obj_desc.irq_count = 1; obj_desc.region_count = 0; error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, &mc_bus_dev); if (error < 0) goto error_cleanup_mc_io; mc->root_mc_bus_dev = mc_bus_dev; return 0; error_cleanup_mc_io: fsl_destroy_mc_io(mc_io); return error; }