/* * Default DMA mask for devices on a fsl-mc bus
*/ #define FSL_MC_DEFAULT_DMA_MASK (~0ULL)
staticstruct fsl_mc_version mc_version;
/** * struct fsl_mc - Private data of a "fsl,qoriq-mc" platform device * @root_mc_bus_dev: fsl-mc device representing the root DPRC * @num_translation_ranges: number of entries in addr_translation_ranges * @translation_ranges: array of bus to system address translation ranges * @fsl_mc_regs: base address of register bank
*/ struct fsl_mc { struct fsl_mc_device *root_mc_bus_dev;
u8 num_translation_ranges; struct fsl_mc_addr_translation_range *translation_ranges; void __iomem *fsl_mc_regs;
};
/** * struct fsl_mc_addr_translation_range - bus to system address translation * range * @mc_region_type: Type of MC region for the range being translated * @start_mc_offset: Start MC offset of the range being translated * @end_mc_offset: MC offset of the first byte after the range (last MC * offset of the range is end_mc_offset - 1) * @start_phys_addr: system physical address corresponding to start_mc_addr
*/ struct fsl_mc_addr_translation_range { enum dprc_region_type mc_region_type;
u64 start_mc_offset;
u64 end_mc_offset;
phys_addr_t start_phys_addr;
};
/** * fsl_mc_bus_match - device to driver matching callback * @dev: the fsl-mc device to match against * @drv: the device driver to search for matching fsl-mc object type * structures * * Returns 1 on success, 0 otherwise.
*/ staticint fsl_mc_bus_match(struct device *dev, conststruct device_driver *drv)
{ conststruct fsl_mc_device_id *id; struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); conststruct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv); bool found = false;
/* When driver_override is set, only bind to the matching driver */ if (mc_dev->driver_override) {
found = !strcmp(mc_dev->driver_override, mc_drv->driver.name); 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 & FSL_MC_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 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) {
found = true;
while (dev_is_fsl_mc(dma_dev))
dma_dev = dma_dev->parent;
if (dev_of_node(dma_dev))
ret = of_dma_configure_id(dev, dma_dev->of_node, 0, &input_id); else
ret = acpi_dma_configure_id(dev, DEV_DMA_COHERENT, &input_id);
/* @drv may not be valid when we're called from the IOMMU layer */ if (!ret && drv && !to_fsl_mc_driver(drv)->driver_managed_dma) {
ret = iommu_device_use_default_domain(dev); if (ret)
arch_teardown_dma_ops(dev);
}
/* * __fsl_mc_driver_register - registers a child device driver with the * MC bus * * This function is implicitly invoked from the registration function of * fsl_mc device drivers, which is generated by the * module_fsl_mc_driver() macro.
*/ int __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver, struct module *owner)
{ int error;
/* * fsl_mc_driver_unregister - unregisters a device driver from the * MC bus
*/ void fsl_mc_driver_unregister(struct fsl_mc_driver *mc_driver)
{
driver_unregister(&mc_driver->driver);
}
EXPORT_SYMBOL_GPL(fsl_mc_driver_unregister);
/** * mc_get_version() - Retrieves the Management Complex firmware * version information * @mc_io: Pointer to opaque I/O object * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' * @mc_ver_info: Returned version information structure * * Return: '0' on Success; Error code otherwise.
*/ staticint mc_get_version(struct fsl_mc_io *mc_io,
u32 cmd_flags, struct fsl_mc_version *mc_ver_info)
{ struct fsl_mc_command cmd = { 0 }; struct dpmng_rsp_get_version *rsp_params; int err;
/** * fsl_mc_get_version - function to retrieve the MC f/w version information * * Return: mc version when called after fsl-mc-bus probe; NULL otherwise.
*/ struct fsl_mc_version *fsl_mc_get_version(void)
{ if (mc_version.major) return &mc_version;
if (is_fsl_mc_bus_dprc(mc_dev) ||
is_fsl_mc_bus_dpmcp(mc_dev)) {
mc_region_type = DPRC_REGION_TYPE_MC_PORTAL;
} elseif (is_fsl_mc_bus_dpio(mc_dev)) {
mc_region_type = DPRC_REGION_TYPE_QBMAN_PORTAL;
} else { /* * This function should not have been called for this MC object * type, as this object type is not supposed to have MMIO * regions
*/ return -EINVAL;
}
regions = kmalloc_array(obj_desc->region_count, sizeof(regions[0]), GFP_KERNEL); if (!regions) return -ENOMEM;
for (i = 0; i < obj_desc->region_count; i++) { struct dprc_region_desc region_desc;
error = dprc_get_obj_region(mc_bus_dev->mc_io,
0,
mc_bus_dev->mc_handle,
obj_desc->type,
obj_desc->id, i, ®ion_desc); if (error < 0) {
dev_err(parent_dev, "dprc_get_obj_region() failed: %d\n", error); goto error_cleanup_regions;
} /* * Older MC only returned region offset and no base address * If base address is in the region_desc use it otherwise * revert to old mechanism
*/ if (region_desc.base_address) {
regions[i].start = region_desc.base_address +
region_desc.base_offset;
} else {
error = translate_mc_addr(mc_dev, mc_region_type,
region_desc.base_offset,
®ions[i].start);
/* * Some versions of the MC firmware wrongly report * 0 for register base address of the DPMCP associated * with child DPRC objects thus rendering them unusable. * This is particularly troublesome in ACPI boot * scenarios where the legacy way of extracting this * base address from the device tree does not apply. * Given that DPMCPs share the same base address, * workaround this by using the base address extracted * from the root DPRC container.
*/ if (is_fsl_mc_bus_dprc(mc_dev) &&
regions[i].start == region_desc.base_offset)
regions[i].start += mc_portal_base_phys_addr;
}
if (error < 0) {
dev_err(parent_dev, "Invalid MC offset: %#x (for %s.%d\'s region %d)\n",
region_desc.base_offset,
obj_desc->type, obj_desc->id, i); goto error_cleanup_regions;
}
/* * fsl_mc_is_root_dprc - function to check if a given device is a root dprc
*/ bool fsl_mc_is_root_dprc(struct device *dev)
{ struct device *root_dprc_dev;
fsl_mc_get_root_dprc(dev, &root_dprc_dev); if (!root_dprc_dev) returnfalse; return dev == root_dprc_dev;
}
if (is_fsl_mc_bus_dprc(mc_dev))
kfree(to_fsl_mc_bus(mc_dev)); else
kfree(mc_dev);
}
/* * Add a newly discovered fsl-mc device to be visible in Linux
*/ int fsl_mc_device_add(struct fsl_mc_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 (dev_is_fsl_mc(parent_dev))
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 = kzalloc(sizeof(*mc_bus), GFP_KERNEL); if (!mc_bus) return -ENOMEM;
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 (!mc_io) {
error = -EINVAL; goto error_cleanup_dev;
}
mc_io2 = mc_io;
}
error = get_dprc_icid(mc_io2, obj_desc->id, &mc_dev->icid); if (error < 0) goto error_cleanup_dev;
} else { /* * A non-DPRC object has to be a child of a DPRC, use the * parent's ICID and interrupt domain.
*/
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;
mc_dev->dev.coherent_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;
}
/** * fsl_mc_device_remove - Remove an fsl-mc device from being visible to * Linux * * @mc_dev: Pointer to an fsl-mc device
*/ void fsl_mc_device_remove(struct fsl_mc_device *mc_dev)
{
kfree(mc_dev->driver_override);
mc_dev->driver_override = NULL;
/* * The device-specific remove callback will get invoked by device_del()
*/
device_del(&mc_dev->dev);
put_device(&mc_dev->dev);
}
EXPORT_SYMBOL_GPL(fsl_mc_device_remove);
/* * We know that the device has an endpoint because we verified by * interrogating the firmware. This is the case when the device was not * yet discovered by the fsl-mc bus, thus the lookup returned NULL. * Force a rescan of the devices in this container and retry the lookup.
*/
mc_bus = to_fsl_mc_bus(mc_bus_dev); if (mutex_trylock(&mc_bus->scan_mutex)) {
err = dprc_scan_objects(mc_bus_dev, true);
mutex_unlock(&mc_bus->scan_mutex);
} if (err < 0) return ERR_PTR(err);
endpoint = fsl_mc_device_lookup(&endpoint_desc, mc_bus_dev); /* * This means that the endpoint might reside in a different isolation * context (DPRC/container). Not much to do, so return a permssion * error.
*/ if (!endpoint) return ERR_PTR(-EPERM);
of_range_parser_init(&parser, dev->of_node);
*num_ranges = of_range_count(&parser); if (!*num_ranges) { /* * Missing or empty ranges property ("ranges;") for the * 'fsl,qoriq-mc' node. In this case, identity mapping * will be used.
*/
*ranges = NULL; return 0;
}
/* * fsl_mc_bus_probe - callback invoked when the root MC bus is being * added
*/ staticint fsl_mc_bus_probe(struct platform_device *pdev)
{ struct fsl_mc_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, mc_stream_id; struct resource *plat_res;
mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); if (!mc) return -ENOMEM;
platform_set_drvdata(pdev, mc);
plat_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (plat_res) {
mc->fsl_mc_regs = devm_ioremap_resource(&pdev->dev, plat_res); if (IS_ERR(mc->fsl_mc_regs)) return PTR_ERR(mc->fsl_mc_regs);
}
if (mc->fsl_mc_regs) { if (IS_ENABLED(CONFIG_ACPI) && !dev_of_node(&pdev->dev)) {
mc_stream_id = readl(mc->fsl_mc_regs + FSL_MC_FAPR); /* * HW ORs the PL and BMT bit, places the result in bit * 14 of the StreamID and ORs in the ICID. Calculate it * accordingly.
*/
mc_stream_id = (mc_stream_id & 0xffff) |
((mc_stream_id & (MC_FAPR_PL | MC_FAPR_BMT)) ?
BIT(14) : 0);
error = acpi_dma_configure_id(&pdev->dev,
DEV_DMA_COHERENT,
&mc_stream_id); if (error == -EPROBE_DEFER) return error; if (error)
dev_warn(&pdev->dev, "failed to configure dma: %d.\n",
error);
}
/* * Some bootloaders pause the MC firmware before booting the * kernel so that MC will not cause faults as soon as the * SMMU probes due to the fact that there's no configuration * in place for MC. * At this point MC should have all its SMMU setup done so make * sure it is resumed.
*/
writel(readl(mc->fsl_mc_regs + FSL_MC_GCR1) &
(~(GCR1_P1_STOP | GCR1_P2_STOP)),
mc->fsl_mc_regs + FSL_MC_GCR1);
}
/* * Get physical address of MC portal for the root DPRC:
*/
plat_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!plat_res) return -EINVAL;
/* * fsl_mc_bus_remove - callback invoked when the root MC bus is being * removed
*/ staticvoid fsl_mc_bus_remove(struct platform_device *pdev)
{ struct fsl_mc *mc = platform_get_drvdata(pdev); struct fsl_mc_io *mc_io;
if (mc->fsl_mc_regs) { /* * Pause the MC firmware so that it doesn't crash in certain * scenarios, such as kexec.
*/
writel(readl(mc->fsl_mc_regs + FSL_MC_GCR1) |
(GCR1_P1_STOP | GCR1_P2_STOP),
mc->fsl_mc_regs + FSL_MC_GCR1);
}
}
if (!of_match_device(fsl_mc_bus_match_table, dev) &&
!acpi_match_device(fsl_mc_bus_acpi_match_table, dev)) return 0;
res = platform_get_resource(to_platform_device(dev), IORESOURCE_MEM, 1); if (!res) return 0;
fsl_mc_regs = ioremap(res->start, resource_size(res)); if (!fsl_mc_regs) return 0;
/* * Make sure that the MC firmware is paused before the IOMMU setup for * it is done or otherwise the firmware will crash right after the SMMU * gets probed and enabled.
*/
writel(readl(fsl_mc_regs + FSL_MC_GCR1) | (GCR1_P1_STOP | GCR1_P2_STOP),
fsl_mc_regs + FSL_MC_GCR1);
iounmap(fsl_mc_regs);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.