/** * 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); }
/** * fsl_mc_bus_match - device to driver matching callback * @dev: the MC object device structure to match against * @drv: the device driver to search for matching MC object device id * structures * * Returns 1 on success, 0 otherwise. */ static int fsl_mc_bus_match(struct device *dev, struct device_driver *drv) { const struct fsl_mc_device_match_id *id; struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv); bool found = false; bool major_version_mismatch = false; bool minor_version_mismatch = false; if (WARN_ON(!fsl_mc_bus_exists())) goto out; if (!mc_drv->match_id_table) goto out; /* * If the object is not 'plugged' don't match. * Only exception is the root DPRC, which is a special case. */ if ((mc_dev->obj_desc.state & DPRC_OBJ_STATE_PLUGGED) == 0 && !fsl_mc_is_root_dprc(&mc_dev->dev)) goto out; /* * Traverse the match_id table of the given driver, trying to find * a matching for the given MC object device. */ for (id = mc_drv->match_id_table; id->vendor != 0x0; id++) { if (id->vendor == mc_dev->obj_desc.vendor && strcmp(id->obj_type, mc_dev->obj_desc.type) == 0) { if (id->ver_major == mc_dev->obj_desc.ver_major) { found = true; if (id->ver_minor != mc_dev->obj_desc.ver_minor) minor_version_mismatch = true; } else { major_version_mismatch = true; } break; } } if (major_version_mismatch) { dev_warn(dev, "Major version mismatch: driver version %u.%u, MC object version %u.%u\n", id->ver_major, id->ver_minor, mc_dev->obj_desc.ver_major, mc_dev->obj_desc.ver_minor); } else if (minor_version_mismatch) { dev_warn(dev, "Minor version mismatch: driver version %u.%u, MC object version %u.%u\n", id->ver_major, id->ver_minor, mc_dev->obj_desc.ver_major, mc_dev->obj_desc.ver_minor); } out: dev_dbg(dev, "%smatched\n", found ? "" : "not "); return found; }
/** * fsl_mc_bus_remove - callback invoked when the root MC bus is being * removed */ static int fsl_mc_bus_remove(struct platform_device *pdev) { struct fsl_mc *mc = platform_get_drvdata(pdev); if (WARN_ON(!fsl_mc_is_root_dprc(&mc->root_mc_bus_dev->dev))) return -EINVAL; fsl_mc_device_remove(mc->root_mc_bus_dev); dev_info(&pdev->dev, "Root MC bus device removed"); return 0; }
/** * 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; }