18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Freescale Management Complex (MC) bus driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. 68c2ecf20Sopenharmony_ci * Copyright 2019-2020 NXP 78c2ecf20Sopenharmony_ci * Author: German Rivera <German.Rivera@freescale.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "fsl-mc: " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/of_device.h> 158c2ecf20Sopenharmony_ci#include <linux/of_address.h> 168c2ecf20Sopenharmony_ci#include <linux/ioport.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/limits.h> 198c2ecf20Sopenharmony_ci#include <linux/bitops.h> 208c2ecf20Sopenharmony_ci#include <linux/msi.h> 218c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 228c2ecf20Sopenharmony_ci#include <linux/acpi.h> 238c2ecf20Sopenharmony_ci#include <linux/iommu.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "fsl-mc-private.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/** 288c2ecf20Sopenharmony_ci * Default DMA mask for devices on a fsl-mc bus 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci#define FSL_MC_DEFAULT_DMA_MASK (~0ULL) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic struct fsl_mc_version mc_version; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/** 358c2ecf20Sopenharmony_ci * struct fsl_mc - Private data of a "fsl,qoriq-mc" platform device 368c2ecf20Sopenharmony_ci * @root_mc_bus_dev: fsl-mc device representing the root DPRC 378c2ecf20Sopenharmony_ci * @num_translation_ranges: number of entries in addr_translation_ranges 388c2ecf20Sopenharmony_ci * @translation_ranges: array of bus to system address translation ranges 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistruct fsl_mc { 418c2ecf20Sopenharmony_ci struct fsl_mc_device *root_mc_bus_dev; 428c2ecf20Sopenharmony_ci u8 num_translation_ranges; 438c2ecf20Sopenharmony_ci struct fsl_mc_addr_translation_range *translation_ranges; 448c2ecf20Sopenharmony_ci void *fsl_mc_regs; 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/** 488c2ecf20Sopenharmony_ci * struct fsl_mc_addr_translation_range - bus to system address translation 498c2ecf20Sopenharmony_ci * range 508c2ecf20Sopenharmony_ci * @mc_region_type: Type of MC region for the range being translated 518c2ecf20Sopenharmony_ci * @start_mc_offset: Start MC offset of the range being translated 528c2ecf20Sopenharmony_ci * @end_mc_offset: MC offset of the first byte after the range (last MC 538c2ecf20Sopenharmony_ci * offset of the range is end_mc_offset - 1) 548c2ecf20Sopenharmony_ci * @start_phys_addr: system physical address corresponding to start_mc_addr 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_cistruct fsl_mc_addr_translation_range { 578c2ecf20Sopenharmony_ci enum dprc_region_type mc_region_type; 588c2ecf20Sopenharmony_ci u64 start_mc_offset; 598c2ecf20Sopenharmony_ci u64 end_mc_offset; 608c2ecf20Sopenharmony_ci phys_addr_t start_phys_addr; 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define FSL_MC_FAPR 0x28 648c2ecf20Sopenharmony_ci#define MC_FAPR_PL BIT(18) 658c2ecf20Sopenharmony_ci#define MC_FAPR_BMT BIT(17) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic phys_addr_t mc_portal_base_phys_addr; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/** 708c2ecf20Sopenharmony_ci * fsl_mc_bus_match - device to driver matching callback 718c2ecf20Sopenharmony_ci * @dev: the fsl-mc device to match against 728c2ecf20Sopenharmony_ci * @drv: the device driver to search for matching fsl-mc object type 738c2ecf20Sopenharmony_ci * structures 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * Returns 1 on success, 0 otherwise. 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_cistatic int fsl_mc_bus_match(struct device *dev, struct device_driver *drv) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci const struct fsl_mc_device_id *id; 808c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 818c2ecf20Sopenharmony_ci struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv); 828c2ecf20Sopenharmony_ci bool found = false; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* When driver_override is set, only bind to the matching driver */ 858c2ecf20Sopenharmony_ci if (mc_dev->driver_override) { 868c2ecf20Sopenharmony_ci found = !strcmp(mc_dev->driver_override, mc_drv->driver.name); 878c2ecf20Sopenharmony_ci goto out; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (!mc_drv->match_id_table) 918c2ecf20Sopenharmony_ci goto out; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * If the object is not 'plugged' don't match. 958c2ecf20Sopenharmony_ci * Only exception is the root DPRC, which is a special case. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci if ((mc_dev->obj_desc.state & FSL_MC_OBJ_STATE_PLUGGED) == 0 && 988c2ecf20Sopenharmony_ci !fsl_mc_is_root_dprc(&mc_dev->dev)) 998c2ecf20Sopenharmony_ci goto out; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* 1028c2ecf20Sopenharmony_ci * Traverse the match_id table of the given driver, trying to find 1038c2ecf20Sopenharmony_ci * a matching for the given device. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci for (id = mc_drv->match_id_table; id->vendor != 0x0; id++) { 1068c2ecf20Sopenharmony_ci if (id->vendor == mc_dev->obj_desc.vendor && 1078c2ecf20Sopenharmony_ci strcmp(id->obj_type, mc_dev->obj_desc.type) == 0) { 1088c2ecf20Sopenharmony_ci found = true; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ciout: 1158c2ecf20Sopenharmony_ci dev_dbg(dev, "%smatched\n", found ? "" : "not "); 1168c2ecf20Sopenharmony_ci return found; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/** 1208c2ecf20Sopenharmony_ci * fsl_mc_bus_uevent - callback invoked when a device is added 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_cistatic int fsl_mc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (add_uevent_var(env, "MODALIAS=fsl-mc:v%08Xd%s", 1278c2ecf20Sopenharmony_ci mc_dev->obj_desc.vendor, 1288c2ecf20Sopenharmony_ci mc_dev->obj_desc.type)) 1298c2ecf20Sopenharmony_ci return -ENOMEM; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int fsl_mc_dma_configure(struct device *dev) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct device *dma_dev = dev; 1378c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 1388c2ecf20Sopenharmony_ci u32 input_id = mc_dev->icid; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci while (dev_is_fsl_mc(dma_dev)) 1418c2ecf20Sopenharmony_ci dma_dev = dma_dev->parent; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (dev_of_node(dma_dev)) 1448c2ecf20Sopenharmony_ci return of_dma_configure_id(dev, dma_dev->of_node, 0, &input_id); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return acpi_dma_configure_id(dev, DEV_DMA_COHERENT, &input_id); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 1508c2ecf20Sopenharmony_ci char *buf) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return sprintf(buf, "fsl-mc:v%08Xd%s\n", mc_dev->obj_desc.vendor, 1558c2ecf20Sopenharmony_ci mc_dev->obj_desc.type); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(modalias); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic ssize_t driver_override_store(struct device *dev, 1608c2ecf20Sopenharmony_ci struct device_attribute *attr, 1618c2ecf20Sopenharmony_ci const char *buf, size_t count) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 1648c2ecf20Sopenharmony_ci char *driver_override, *old = mc_dev->driver_override; 1658c2ecf20Sopenharmony_ci char *cp; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (WARN_ON(dev->bus != &fsl_mc_bus_type)) 1688c2ecf20Sopenharmony_ci return -EINVAL; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (count >= (PAGE_SIZE - 1)) 1718c2ecf20Sopenharmony_ci return -EINVAL; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci driver_override = kstrndup(buf, count, GFP_KERNEL); 1748c2ecf20Sopenharmony_ci if (!driver_override) 1758c2ecf20Sopenharmony_ci return -ENOMEM; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci cp = strchr(driver_override, '\n'); 1788c2ecf20Sopenharmony_ci if (cp) 1798c2ecf20Sopenharmony_ci *cp = '\0'; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (strlen(driver_override)) { 1828c2ecf20Sopenharmony_ci mc_dev->driver_override = driver_override; 1838c2ecf20Sopenharmony_ci } else { 1848c2ecf20Sopenharmony_ci kfree(driver_override); 1858c2ecf20Sopenharmony_ci mc_dev->driver_override = NULL; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci kfree(old); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return count; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic ssize_t driver_override_show(struct device *dev, 1948c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", mc_dev->driver_override); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(driver_override); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic struct attribute *fsl_mc_dev_attrs[] = { 2038c2ecf20Sopenharmony_ci &dev_attr_modalias.attr, 2048c2ecf20Sopenharmony_ci &dev_attr_driver_override.attr, 2058c2ecf20Sopenharmony_ci NULL, 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(fsl_mc_dev); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistruct bus_type fsl_mc_bus_type = { 2118c2ecf20Sopenharmony_ci .name = "fsl-mc", 2128c2ecf20Sopenharmony_ci .match = fsl_mc_bus_match, 2138c2ecf20Sopenharmony_ci .uevent = fsl_mc_bus_uevent, 2148c2ecf20Sopenharmony_ci .dma_configure = fsl_mc_dma_configure, 2158c2ecf20Sopenharmony_ci .dev_groups = fsl_mc_dev_groups, 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_type); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistruct device_type fsl_mc_bus_dprc_type = { 2208c2ecf20Sopenharmony_ci .name = "fsl_mc_bus_dprc" 2218c2ecf20Sopenharmony_ci}; 2228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_dprc_type); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistruct device_type fsl_mc_bus_dpni_type = { 2258c2ecf20Sopenharmony_ci .name = "fsl_mc_bus_dpni" 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_dpni_type); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistruct device_type fsl_mc_bus_dpio_type = { 2308c2ecf20Sopenharmony_ci .name = "fsl_mc_bus_dpio" 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_dpio_type); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistruct device_type fsl_mc_bus_dpsw_type = { 2358c2ecf20Sopenharmony_ci .name = "fsl_mc_bus_dpsw" 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_dpsw_type); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistruct device_type fsl_mc_bus_dpbp_type = { 2408c2ecf20Sopenharmony_ci .name = "fsl_mc_bus_dpbp" 2418c2ecf20Sopenharmony_ci}; 2428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_dpbp_type); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistruct device_type fsl_mc_bus_dpcon_type = { 2458c2ecf20Sopenharmony_ci .name = "fsl_mc_bus_dpcon" 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_dpcon_type); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistruct device_type fsl_mc_bus_dpmcp_type = { 2508c2ecf20Sopenharmony_ci .name = "fsl_mc_bus_dpmcp" 2518c2ecf20Sopenharmony_ci}; 2528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_dpmcp_type); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistruct device_type fsl_mc_bus_dpmac_type = { 2558c2ecf20Sopenharmony_ci .name = "fsl_mc_bus_dpmac" 2568c2ecf20Sopenharmony_ci}; 2578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_dpmac_type); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistruct device_type fsl_mc_bus_dprtc_type = { 2608c2ecf20Sopenharmony_ci .name = "fsl_mc_bus_dprtc" 2618c2ecf20Sopenharmony_ci}; 2628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_dprtc_type); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistruct device_type fsl_mc_bus_dpseci_type = { 2658c2ecf20Sopenharmony_ci .name = "fsl_mc_bus_dpseci" 2668c2ecf20Sopenharmony_ci}; 2678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_dpseci_type); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistruct device_type fsl_mc_bus_dpdmux_type = { 2708c2ecf20Sopenharmony_ci .name = "fsl_mc_bus_dpdmux" 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_dpdmux_type); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistruct device_type fsl_mc_bus_dpdcei_type = { 2758c2ecf20Sopenharmony_ci .name = "fsl_mc_bus_dpdcei" 2768c2ecf20Sopenharmony_ci}; 2778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_dpdcei_type); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistruct device_type fsl_mc_bus_dpaiop_type = { 2808c2ecf20Sopenharmony_ci .name = "fsl_mc_bus_dpaiop" 2818c2ecf20Sopenharmony_ci}; 2828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_dpaiop_type); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistruct device_type fsl_mc_bus_dpci_type = { 2858c2ecf20Sopenharmony_ci .name = "fsl_mc_bus_dpci" 2868c2ecf20Sopenharmony_ci}; 2878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_dpci_type); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistruct device_type fsl_mc_bus_dpdmai_type = { 2908c2ecf20Sopenharmony_ci .name = "fsl_mc_bus_dpdmai" 2918c2ecf20Sopenharmony_ci}; 2928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_bus_dpdmai_type); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic struct device_type *fsl_mc_get_device_type(const char *type) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci static const struct { 2978c2ecf20Sopenharmony_ci struct device_type *dev_type; 2988c2ecf20Sopenharmony_ci const char *type; 2998c2ecf20Sopenharmony_ci } dev_types[] = { 3008c2ecf20Sopenharmony_ci { &fsl_mc_bus_dprc_type, "dprc" }, 3018c2ecf20Sopenharmony_ci { &fsl_mc_bus_dpni_type, "dpni" }, 3028c2ecf20Sopenharmony_ci { &fsl_mc_bus_dpio_type, "dpio" }, 3038c2ecf20Sopenharmony_ci { &fsl_mc_bus_dpsw_type, "dpsw" }, 3048c2ecf20Sopenharmony_ci { &fsl_mc_bus_dpbp_type, "dpbp" }, 3058c2ecf20Sopenharmony_ci { &fsl_mc_bus_dpcon_type, "dpcon" }, 3068c2ecf20Sopenharmony_ci { &fsl_mc_bus_dpmcp_type, "dpmcp" }, 3078c2ecf20Sopenharmony_ci { &fsl_mc_bus_dpmac_type, "dpmac" }, 3088c2ecf20Sopenharmony_ci { &fsl_mc_bus_dprtc_type, "dprtc" }, 3098c2ecf20Sopenharmony_ci { &fsl_mc_bus_dpseci_type, "dpseci" }, 3108c2ecf20Sopenharmony_ci { &fsl_mc_bus_dpdmux_type, "dpdmux" }, 3118c2ecf20Sopenharmony_ci { &fsl_mc_bus_dpdcei_type, "dpdcei" }, 3128c2ecf20Sopenharmony_ci { &fsl_mc_bus_dpaiop_type, "dpaiop" }, 3138c2ecf20Sopenharmony_ci { &fsl_mc_bus_dpci_type, "dpci" }, 3148c2ecf20Sopenharmony_ci { &fsl_mc_bus_dpdmai_type, "dpdmai" }, 3158c2ecf20Sopenharmony_ci { NULL, NULL } 3168c2ecf20Sopenharmony_ci }; 3178c2ecf20Sopenharmony_ci int i; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci for (i = 0; dev_types[i].dev_type; i++) 3208c2ecf20Sopenharmony_ci if (!strcmp(dev_types[i].type, type)) 3218c2ecf20Sopenharmony_ci return dev_types[i].dev_type; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return NULL; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic int fsl_mc_driver_probe(struct device *dev) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct fsl_mc_driver *mc_drv; 3298c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 3308c2ecf20Sopenharmony_ci int error; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci mc_drv = to_fsl_mc_driver(dev->driver); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci error = mc_drv->probe(mc_dev); 3358c2ecf20Sopenharmony_ci if (error < 0) { 3368c2ecf20Sopenharmony_ci if (error != -EPROBE_DEFER) 3378c2ecf20Sopenharmony_ci dev_err(dev, "%s failed: %d\n", __func__, error); 3388c2ecf20Sopenharmony_ci return error; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return 0; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic int fsl_mc_driver_remove(struct device *dev) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); 3478c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 3488c2ecf20Sopenharmony_ci int error; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci error = mc_drv->remove(mc_dev); 3518c2ecf20Sopenharmony_ci if (error < 0) { 3528c2ecf20Sopenharmony_ci dev_err(dev, "%s failed: %d\n", __func__, error); 3538c2ecf20Sopenharmony_ci return error; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic void fsl_mc_driver_shutdown(struct device *dev) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); 3628c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci mc_drv->shutdown(mc_dev); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci/** 3688c2ecf20Sopenharmony_ci * __fsl_mc_driver_register - registers a child device driver with the 3698c2ecf20Sopenharmony_ci * MC bus 3708c2ecf20Sopenharmony_ci * 3718c2ecf20Sopenharmony_ci * This function is implicitly invoked from the registration function of 3728c2ecf20Sopenharmony_ci * fsl_mc device drivers, which is generated by the 3738c2ecf20Sopenharmony_ci * module_fsl_mc_driver() macro. 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_ciint __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver, 3768c2ecf20Sopenharmony_ci struct module *owner) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci int error; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci mc_driver->driver.owner = owner; 3818c2ecf20Sopenharmony_ci mc_driver->driver.bus = &fsl_mc_bus_type; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (mc_driver->probe) 3848c2ecf20Sopenharmony_ci mc_driver->driver.probe = fsl_mc_driver_probe; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (mc_driver->remove) 3878c2ecf20Sopenharmony_ci mc_driver->driver.remove = fsl_mc_driver_remove; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (mc_driver->shutdown) 3908c2ecf20Sopenharmony_ci mc_driver->driver.shutdown = fsl_mc_driver_shutdown; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci error = driver_register(&mc_driver->driver); 3938c2ecf20Sopenharmony_ci if (error < 0) { 3948c2ecf20Sopenharmony_ci pr_err("driver_register() failed for %s: %d\n", 3958c2ecf20Sopenharmony_ci mc_driver->driver.name, error); 3968c2ecf20Sopenharmony_ci return error; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return 0; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__fsl_mc_driver_register); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci/** 4048c2ecf20Sopenharmony_ci * fsl_mc_driver_unregister - unregisters a device driver from the 4058c2ecf20Sopenharmony_ci * MC bus 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_civoid fsl_mc_driver_unregister(struct fsl_mc_driver *mc_driver) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci driver_unregister(&mc_driver->driver); 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_driver_unregister); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci/** 4148c2ecf20Sopenharmony_ci * mc_get_version() - Retrieves the Management Complex firmware 4158c2ecf20Sopenharmony_ci * version information 4168c2ecf20Sopenharmony_ci * @mc_io: Pointer to opaque I/O object 4178c2ecf20Sopenharmony_ci * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' 4188c2ecf20Sopenharmony_ci * @mc_ver_info: Returned version information structure 4198c2ecf20Sopenharmony_ci * 4208c2ecf20Sopenharmony_ci * Return: '0' on Success; Error code otherwise. 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_cistatic int mc_get_version(struct fsl_mc_io *mc_io, 4238c2ecf20Sopenharmony_ci u32 cmd_flags, 4248c2ecf20Sopenharmony_ci struct fsl_mc_version *mc_ver_info) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct fsl_mc_command cmd = { 0 }; 4278c2ecf20Sopenharmony_ci struct dpmng_rsp_get_version *rsp_params; 4288c2ecf20Sopenharmony_ci int err; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* prepare command */ 4318c2ecf20Sopenharmony_ci cmd.header = mc_encode_cmd_header(DPMNG_CMDID_GET_VERSION, 4328c2ecf20Sopenharmony_ci cmd_flags, 4338c2ecf20Sopenharmony_ci 0); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* send command to mc*/ 4368c2ecf20Sopenharmony_ci err = mc_send_command(mc_io, &cmd); 4378c2ecf20Sopenharmony_ci if (err) 4388c2ecf20Sopenharmony_ci return err; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* retrieve response parameters */ 4418c2ecf20Sopenharmony_ci rsp_params = (struct dpmng_rsp_get_version *)cmd.params; 4428c2ecf20Sopenharmony_ci mc_ver_info->revision = le32_to_cpu(rsp_params->revision); 4438c2ecf20Sopenharmony_ci mc_ver_info->major = le32_to_cpu(rsp_params->version_major); 4448c2ecf20Sopenharmony_ci mc_ver_info->minor = le32_to_cpu(rsp_params->version_minor); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return 0; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci/** 4508c2ecf20Sopenharmony_ci * fsl_mc_get_version - function to retrieve the MC f/w version information 4518c2ecf20Sopenharmony_ci * 4528c2ecf20Sopenharmony_ci * Return: mc version when called after fsl-mc-bus probe; NULL otherwise. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_cistruct fsl_mc_version *fsl_mc_get_version(void) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci if (mc_version.major) 4578c2ecf20Sopenharmony_ci return &mc_version; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci return NULL; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_get_version); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci/** 4648c2ecf20Sopenharmony_ci * fsl_mc_get_root_dprc - function to traverse to the root dprc 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_civoid fsl_mc_get_root_dprc(struct device *dev, 4678c2ecf20Sopenharmony_ci struct device **root_dprc_dev) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci if (!dev) { 4708c2ecf20Sopenharmony_ci *root_dprc_dev = NULL; 4718c2ecf20Sopenharmony_ci } else if (!dev_is_fsl_mc(dev)) { 4728c2ecf20Sopenharmony_ci *root_dprc_dev = NULL; 4738c2ecf20Sopenharmony_ci } else { 4748c2ecf20Sopenharmony_ci *root_dprc_dev = dev; 4758c2ecf20Sopenharmony_ci while (dev_is_fsl_mc((*root_dprc_dev)->parent)) 4768c2ecf20Sopenharmony_ci *root_dprc_dev = (*root_dprc_dev)->parent; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int get_dprc_attr(struct fsl_mc_io *mc_io, 4818c2ecf20Sopenharmony_ci int container_id, struct dprc_attributes *attr) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci u16 dprc_handle; 4848c2ecf20Sopenharmony_ci int error; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci error = dprc_open(mc_io, 0, container_id, &dprc_handle); 4878c2ecf20Sopenharmony_ci if (error < 0) { 4888c2ecf20Sopenharmony_ci dev_err(mc_io->dev, "dprc_open() failed: %d\n", error); 4898c2ecf20Sopenharmony_ci return error; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci memset(attr, 0, sizeof(struct dprc_attributes)); 4938c2ecf20Sopenharmony_ci error = dprc_get_attributes(mc_io, 0, dprc_handle, attr); 4948c2ecf20Sopenharmony_ci if (error < 0) { 4958c2ecf20Sopenharmony_ci dev_err(mc_io->dev, "dprc_get_attributes() failed: %d\n", 4968c2ecf20Sopenharmony_ci error); 4978c2ecf20Sopenharmony_ci goto common_cleanup; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci error = 0; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cicommon_cleanup: 5038c2ecf20Sopenharmony_ci (void)dprc_close(mc_io, 0, dprc_handle); 5048c2ecf20Sopenharmony_ci return error; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int get_dprc_icid(struct fsl_mc_io *mc_io, 5088c2ecf20Sopenharmony_ci int container_id, u32 *icid) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci struct dprc_attributes attr; 5118c2ecf20Sopenharmony_ci int error; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci error = get_dprc_attr(mc_io, container_id, &attr); 5148c2ecf20Sopenharmony_ci if (error == 0) 5158c2ecf20Sopenharmony_ci *icid = attr.icid; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return error; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic int translate_mc_addr(struct fsl_mc_device *mc_dev, 5218c2ecf20Sopenharmony_ci enum dprc_region_type mc_region_type, 5228c2ecf20Sopenharmony_ci u64 mc_offset, phys_addr_t *phys_addr) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci int i; 5258c2ecf20Sopenharmony_ci struct device *root_dprc_dev; 5268c2ecf20Sopenharmony_ci struct fsl_mc *mc; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci fsl_mc_get_root_dprc(&mc_dev->dev, &root_dprc_dev); 5298c2ecf20Sopenharmony_ci mc = dev_get_drvdata(root_dprc_dev->parent); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (mc->num_translation_ranges == 0) { 5328c2ecf20Sopenharmony_ci /* 5338c2ecf20Sopenharmony_ci * Do identity mapping: 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_ci *phys_addr = mc_offset; 5368c2ecf20Sopenharmony_ci return 0; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci for (i = 0; i < mc->num_translation_ranges; i++) { 5408c2ecf20Sopenharmony_ci struct fsl_mc_addr_translation_range *range = 5418c2ecf20Sopenharmony_ci &mc->translation_ranges[i]; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (mc_region_type == range->mc_region_type && 5448c2ecf20Sopenharmony_ci mc_offset >= range->start_mc_offset && 5458c2ecf20Sopenharmony_ci mc_offset < range->end_mc_offset) { 5468c2ecf20Sopenharmony_ci *phys_addr = range->start_phys_addr + 5478c2ecf20Sopenharmony_ci (mc_offset - range->start_mc_offset); 5488c2ecf20Sopenharmony_ci return 0; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci return -EFAULT; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic int fsl_mc_device_get_mmio_regions(struct fsl_mc_device *mc_dev, 5568c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_bus_dev) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci int i; 5598c2ecf20Sopenharmony_ci int error; 5608c2ecf20Sopenharmony_ci struct resource *regions; 5618c2ecf20Sopenharmony_ci struct fsl_mc_obj_desc *obj_desc = &mc_dev->obj_desc; 5628c2ecf20Sopenharmony_ci struct device *parent_dev = mc_dev->dev.parent; 5638c2ecf20Sopenharmony_ci enum dprc_region_type mc_region_type; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (is_fsl_mc_bus_dprc(mc_dev) || 5668c2ecf20Sopenharmony_ci is_fsl_mc_bus_dpmcp(mc_dev)) { 5678c2ecf20Sopenharmony_ci mc_region_type = DPRC_REGION_TYPE_MC_PORTAL; 5688c2ecf20Sopenharmony_ci } else if (is_fsl_mc_bus_dpio(mc_dev)) { 5698c2ecf20Sopenharmony_ci mc_region_type = DPRC_REGION_TYPE_QBMAN_PORTAL; 5708c2ecf20Sopenharmony_ci } else { 5718c2ecf20Sopenharmony_ci /* 5728c2ecf20Sopenharmony_ci * This function should not have been called for this MC object 5738c2ecf20Sopenharmony_ci * type, as this object type is not supposed to have MMIO 5748c2ecf20Sopenharmony_ci * regions 5758c2ecf20Sopenharmony_ci */ 5768c2ecf20Sopenharmony_ci return -EINVAL; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci regions = kmalloc_array(obj_desc->region_count, 5808c2ecf20Sopenharmony_ci sizeof(regions[0]), GFP_KERNEL); 5818c2ecf20Sopenharmony_ci if (!regions) 5828c2ecf20Sopenharmony_ci return -ENOMEM; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci for (i = 0; i < obj_desc->region_count; i++) { 5858c2ecf20Sopenharmony_ci struct dprc_region_desc region_desc; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci error = dprc_get_obj_region(mc_bus_dev->mc_io, 5888c2ecf20Sopenharmony_ci 0, 5898c2ecf20Sopenharmony_ci mc_bus_dev->mc_handle, 5908c2ecf20Sopenharmony_ci obj_desc->type, 5918c2ecf20Sopenharmony_ci obj_desc->id, i, ®ion_desc); 5928c2ecf20Sopenharmony_ci if (error < 0) { 5938c2ecf20Sopenharmony_ci dev_err(parent_dev, 5948c2ecf20Sopenharmony_ci "dprc_get_obj_region() failed: %d\n", error); 5958c2ecf20Sopenharmony_ci goto error_cleanup_regions; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci /* 5988c2ecf20Sopenharmony_ci * Older MC only returned region offset and no base address 5998c2ecf20Sopenharmony_ci * If base address is in the region_desc use it otherwise 6008c2ecf20Sopenharmony_ci * revert to old mechanism 6018c2ecf20Sopenharmony_ci */ 6028c2ecf20Sopenharmony_ci if (region_desc.base_address) { 6038c2ecf20Sopenharmony_ci regions[i].start = region_desc.base_address + 6048c2ecf20Sopenharmony_ci region_desc.base_offset; 6058c2ecf20Sopenharmony_ci } else { 6068c2ecf20Sopenharmony_ci error = translate_mc_addr(mc_dev, mc_region_type, 6078c2ecf20Sopenharmony_ci region_desc.base_offset, 6088c2ecf20Sopenharmony_ci ®ions[i].start); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci /* 6118c2ecf20Sopenharmony_ci * Some versions of the MC firmware wrongly report 6128c2ecf20Sopenharmony_ci * 0 for register base address of the DPMCP associated 6138c2ecf20Sopenharmony_ci * with child DPRC objects thus rendering them unusable. 6148c2ecf20Sopenharmony_ci * This is particularly troublesome in ACPI boot 6158c2ecf20Sopenharmony_ci * scenarios where the legacy way of extracting this 6168c2ecf20Sopenharmony_ci * base address from the device tree does not apply. 6178c2ecf20Sopenharmony_ci * Given that DPMCPs share the same base address, 6188c2ecf20Sopenharmony_ci * workaround this by using the base address extracted 6198c2ecf20Sopenharmony_ci * from the root DPRC container. 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_ci if (is_fsl_mc_bus_dprc(mc_dev) && 6228c2ecf20Sopenharmony_ci regions[i].start == region_desc.base_offset) 6238c2ecf20Sopenharmony_ci regions[i].start += mc_portal_base_phys_addr; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (error < 0) { 6278c2ecf20Sopenharmony_ci dev_err(parent_dev, 6288c2ecf20Sopenharmony_ci "Invalid MC offset: %#x (for %s.%d\'s region %d)\n", 6298c2ecf20Sopenharmony_ci region_desc.base_offset, 6308c2ecf20Sopenharmony_ci obj_desc->type, obj_desc->id, i); 6318c2ecf20Sopenharmony_ci goto error_cleanup_regions; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci regions[i].end = regions[i].start + region_desc.size - 1; 6358c2ecf20Sopenharmony_ci regions[i].name = "fsl-mc object MMIO region"; 6368c2ecf20Sopenharmony_ci regions[i].flags = region_desc.flags & IORESOURCE_BITS; 6378c2ecf20Sopenharmony_ci regions[i].flags |= IORESOURCE_MEM; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci mc_dev->regions = regions; 6418c2ecf20Sopenharmony_ci return 0; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cierror_cleanup_regions: 6448c2ecf20Sopenharmony_ci kfree(regions); 6458c2ecf20Sopenharmony_ci return error; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci/** 6498c2ecf20Sopenharmony_ci * fsl_mc_is_root_dprc - function to check if a given device is a root dprc 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_cibool fsl_mc_is_root_dprc(struct device *dev) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct device *root_dprc_dev; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci fsl_mc_get_root_dprc(dev, &root_dprc_dev); 6568c2ecf20Sopenharmony_ci if (!root_dprc_dev) 6578c2ecf20Sopenharmony_ci return false; 6588c2ecf20Sopenharmony_ci return dev == root_dprc_dev; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic void fsl_mc_device_release(struct device *dev) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci kfree(mc_dev->regions); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (is_fsl_mc_bus_dprc(mc_dev)) 6688c2ecf20Sopenharmony_ci kfree(to_fsl_mc_bus(mc_dev)); 6698c2ecf20Sopenharmony_ci else 6708c2ecf20Sopenharmony_ci kfree(mc_dev); 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci/** 6748c2ecf20Sopenharmony_ci * Add a newly discovered fsl-mc device to be visible in Linux 6758c2ecf20Sopenharmony_ci */ 6768c2ecf20Sopenharmony_ciint fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc, 6778c2ecf20Sopenharmony_ci struct fsl_mc_io *mc_io, 6788c2ecf20Sopenharmony_ci struct device *parent_dev, 6798c2ecf20Sopenharmony_ci struct fsl_mc_device **new_mc_dev) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci int error; 6828c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = NULL; 6838c2ecf20Sopenharmony_ci struct fsl_mc_bus *mc_bus = NULL; 6848c2ecf20Sopenharmony_ci struct fsl_mc_device *parent_mc_dev; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (dev_is_fsl_mc(parent_dev)) 6878c2ecf20Sopenharmony_ci parent_mc_dev = to_fsl_mc_device(parent_dev); 6888c2ecf20Sopenharmony_ci else 6898c2ecf20Sopenharmony_ci parent_mc_dev = NULL; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (strcmp(obj_desc->type, "dprc") == 0) { 6928c2ecf20Sopenharmony_ci /* 6938c2ecf20Sopenharmony_ci * Allocate an MC bus device object: 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_ci mc_bus = kzalloc(sizeof(*mc_bus), GFP_KERNEL); 6968c2ecf20Sopenharmony_ci if (!mc_bus) 6978c2ecf20Sopenharmony_ci return -ENOMEM; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci mutex_init(&mc_bus->scan_mutex); 7008c2ecf20Sopenharmony_ci mc_dev = &mc_bus->mc_dev; 7018c2ecf20Sopenharmony_ci } else { 7028c2ecf20Sopenharmony_ci /* 7038c2ecf20Sopenharmony_ci * Allocate a regular fsl_mc_device object: 7048c2ecf20Sopenharmony_ci */ 7058c2ecf20Sopenharmony_ci mc_dev = kzalloc(sizeof(*mc_dev), GFP_KERNEL); 7068c2ecf20Sopenharmony_ci if (!mc_dev) 7078c2ecf20Sopenharmony_ci return -ENOMEM; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci mc_dev->obj_desc = *obj_desc; 7118c2ecf20Sopenharmony_ci mc_dev->mc_io = mc_io; 7128c2ecf20Sopenharmony_ci device_initialize(&mc_dev->dev); 7138c2ecf20Sopenharmony_ci mc_dev->dev.parent = parent_dev; 7148c2ecf20Sopenharmony_ci mc_dev->dev.bus = &fsl_mc_bus_type; 7158c2ecf20Sopenharmony_ci mc_dev->dev.release = fsl_mc_device_release; 7168c2ecf20Sopenharmony_ci mc_dev->dev.type = fsl_mc_get_device_type(obj_desc->type); 7178c2ecf20Sopenharmony_ci if (!mc_dev->dev.type) { 7188c2ecf20Sopenharmony_ci error = -ENODEV; 7198c2ecf20Sopenharmony_ci dev_err(parent_dev, "unknown device type %s\n", obj_desc->type); 7208c2ecf20Sopenharmony_ci goto error_cleanup_dev; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci dev_set_name(&mc_dev->dev, "%s.%d", obj_desc->type, obj_desc->id); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (strcmp(obj_desc->type, "dprc") == 0) { 7258c2ecf20Sopenharmony_ci struct fsl_mc_io *mc_io2; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci mc_dev->flags |= FSL_MC_IS_DPRC; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci /* 7308c2ecf20Sopenharmony_ci * To get the DPRC's ICID, we need to open the DPRC 7318c2ecf20Sopenharmony_ci * in get_dprc_icid(). For child DPRCs, we do so using the 7328c2ecf20Sopenharmony_ci * parent DPRC's MC portal instead of the child DPRC's MC 7338c2ecf20Sopenharmony_ci * portal, in case the child DPRC is already opened with 7348c2ecf20Sopenharmony_ci * its own portal (e.g., the DPRC used by AIOP). 7358c2ecf20Sopenharmony_ci * 7368c2ecf20Sopenharmony_ci * NOTE: There cannot be more than one active open for a 7378c2ecf20Sopenharmony_ci * given MC object, using the same MC portal. 7388c2ecf20Sopenharmony_ci */ 7398c2ecf20Sopenharmony_ci if (parent_mc_dev) { 7408c2ecf20Sopenharmony_ci /* 7418c2ecf20Sopenharmony_ci * device being added is a child DPRC device 7428c2ecf20Sopenharmony_ci */ 7438c2ecf20Sopenharmony_ci mc_io2 = parent_mc_dev->mc_io; 7448c2ecf20Sopenharmony_ci } else { 7458c2ecf20Sopenharmony_ci /* 7468c2ecf20Sopenharmony_ci * device being added is the root DPRC device 7478c2ecf20Sopenharmony_ci */ 7488c2ecf20Sopenharmony_ci if (!mc_io) { 7498c2ecf20Sopenharmony_ci error = -EINVAL; 7508c2ecf20Sopenharmony_ci goto error_cleanup_dev; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci mc_io2 = mc_io; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci error = get_dprc_icid(mc_io2, obj_desc->id, &mc_dev->icid); 7578c2ecf20Sopenharmony_ci if (error < 0) 7588c2ecf20Sopenharmony_ci goto error_cleanup_dev; 7598c2ecf20Sopenharmony_ci } else { 7608c2ecf20Sopenharmony_ci /* 7618c2ecf20Sopenharmony_ci * A non-DPRC object has to be a child of a DPRC, use the 7628c2ecf20Sopenharmony_ci * parent's ICID and interrupt domain. 7638c2ecf20Sopenharmony_ci */ 7648c2ecf20Sopenharmony_ci mc_dev->icid = parent_mc_dev->icid; 7658c2ecf20Sopenharmony_ci mc_dev->dma_mask = FSL_MC_DEFAULT_DMA_MASK; 7668c2ecf20Sopenharmony_ci mc_dev->dev.dma_mask = &mc_dev->dma_mask; 7678c2ecf20Sopenharmony_ci mc_dev->dev.coherent_dma_mask = mc_dev->dma_mask; 7688c2ecf20Sopenharmony_ci dev_set_msi_domain(&mc_dev->dev, 7698c2ecf20Sopenharmony_ci dev_get_msi_domain(&parent_mc_dev->dev)); 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* 7738c2ecf20Sopenharmony_ci * Get MMIO regions for the device from the MC: 7748c2ecf20Sopenharmony_ci * 7758c2ecf20Sopenharmony_ci * NOTE: the root DPRC is a special case as its MMIO region is 7768c2ecf20Sopenharmony_ci * obtained from the device tree 7778c2ecf20Sopenharmony_ci */ 7788c2ecf20Sopenharmony_ci if (parent_mc_dev && obj_desc->region_count != 0) { 7798c2ecf20Sopenharmony_ci error = fsl_mc_device_get_mmio_regions(mc_dev, 7808c2ecf20Sopenharmony_ci parent_mc_dev); 7818c2ecf20Sopenharmony_ci if (error < 0) 7828c2ecf20Sopenharmony_ci goto error_cleanup_dev; 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci /* 7868c2ecf20Sopenharmony_ci * The device-specific probe callback will get invoked by device_add() 7878c2ecf20Sopenharmony_ci */ 7888c2ecf20Sopenharmony_ci error = device_add(&mc_dev->dev); 7898c2ecf20Sopenharmony_ci if (error < 0) { 7908c2ecf20Sopenharmony_ci dev_err(parent_dev, 7918c2ecf20Sopenharmony_ci "device_add() failed for device %s: %d\n", 7928c2ecf20Sopenharmony_ci dev_name(&mc_dev->dev), error); 7938c2ecf20Sopenharmony_ci goto error_cleanup_dev; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci dev_dbg(parent_dev, "added %s\n", dev_name(&mc_dev->dev)); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci *new_mc_dev = mc_dev; 7998c2ecf20Sopenharmony_ci return 0; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cierror_cleanup_dev: 8028c2ecf20Sopenharmony_ci kfree(mc_dev->regions); 8038c2ecf20Sopenharmony_ci kfree(mc_bus); 8048c2ecf20Sopenharmony_ci kfree(mc_dev); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci return error; 8078c2ecf20Sopenharmony_ci} 8088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_device_add); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci/** 8118c2ecf20Sopenharmony_ci * fsl_mc_device_remove - Remove an fsl-mc device from being visible to 8128c2ecf20Sopenharmony_ci * Linux 8138c2ecf20Sopenharmony_ci * 8148c2ecf20Sopenharmony_ci * @mc_dev: Pointer to an fsl-mc device 8158c2ecf20Sopenharmony_ci */ 8168c2ecf20Sopenharmony_civoid fsl_mc_device_remove(struct fsl_mc_device *mc_dev) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci kfree(mc_dev->driver_override); 8198c2ecf20Sopenharmony_ci mc_dev->driver_override = NULL; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci /* 8228c2ecf20Sopenharmony_ci * The device-specific remove callback will get invoked by device_del() 8238c2ecf20Sopenharmony_ci */ 8248c2ecf20Sopenharmony_ci device_del(&mc_dev->dev); 8258c2ecf20Sopenharmony_ci put_device(&mc_dev->dev); 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_device_remove); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_cistruct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev) 8308c2ecf20Sopenharmony_ci{ 8318c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_bus_dev, *endpoint; 8328c2ecf20Sopenharmony_ci struct fsl_mc_obj_desc endpoint_desc = {{ 0 }}; 8338c2ecf20Sopenharmony_ci struct dprc_endpoint endpoint1 = {{ 0 }}; 8348c2ecf20Sopenharmony_ci struct dprc_endpoint endpoint2 = {{ 0 }}; 8358c2ecf20Sopenharmony_ci int state, err; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); 8388c2ecf20Sopenharmony_ci strcpy(endpoint1.type, mc_dev->obj_desc.type); 8398c2ecf20Sopenharmony_ci endpoint1.id = mc_dev->obj_desc.id; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci err = dprc_get_connection(mc_bus_dev->mc_io, 0, 8428c2ecf20Sopenharmony_ci mc_bus_dev->mc_handle, 8438c2ecf20Sopenharmony_ci &endpoint1, &endpoint2, 8448c2ecf20Sopenharmony_ci &state); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (err == -ENOTCONN || state == -1) 8478c2ecf20Sopenharmony_ci return ERR_PTR(-ENOTCONN); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci if (err < 0) { 8508c2ecf20Sopenharmony_ci dev_err(&mc_bus_dev->dev, "dprc_get_connection() = %d\n", err); 8518c2ecf20Sopenharmony_ci return ERR_PTR(err); 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci strcpy(endpoint_desc.type, endpoint2.type); 8558c2ecf20Sopenharmony_ci endpoint_desc.id = endpoint2.id; 8568c2ecf20Sopenharmony_ci endpoint = fsl_mc_device_lookup(&endpoint_desc, mc_bus_dev); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci return endpoint; 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_get_endpoint); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cistatic int parse_mc_ranges(struct device *dev, 8638c2ecf20Sopenharmony_ci int *paddr_cells, 8648c2ecf20Sopenharmony_ci int *mc_addr_cells, 8658c2ecf20Sopenharmony_ci int *mc_size_cells, 8668c2ecf20Sopenharmony_ci const __be32 **ranges_start) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci const __be32 *prop; 8698c2ecf20Sopenharmony_ci int range_tuple_cell_count; 8708c2ecf20Sopenharmony_ci int ranges_len; 8718c2ecf20Sopenharmony_ci int tuple_len; 8728c2ecf20Sopenharmony_ci struct device_node *mc_node = dev->of_node; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci *ranges_start = of_get_property(mc_node, "ranges", &ranges_len); 8758c2ecf20Sopenharmony_ci if (!(*ranges_start) || !ranges_len) { 8768c2ecf20Sopenharmony_ci dev_warn(dev, 8778c2ecf20Sopenharmony_ci "missing or empty ranges property for device tree node '%pOFn'\n", 8788c2ecf20Sopenharmony_ci mc_node); 8798c2ecf20Sopenharmony_ci return 0; 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci *paddr_cells = of_n_addr_cells(mc_node); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci prop = of_get_property(mc_node, "#address-cells", NULL); 8858c2ecf20Sopenharmony_ci if (prop) 8868c2ecf20Sopenharmony_ci *mc_addr_cells = be32_to_cpup(prop); 8878c2ecf20Sopenharmony_ci else 8888c2ecf20Sopenharmony_ci *mc_addr_cells = *paddr_cells; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci prop = of_get_property(mc_node, "#size-cells", NULL); 8918c2ecf20Sopenharmony_ci if (prop) 8928c2ecf20Sopenharmony_ci *mc_size_cells = be32_to_cpup(prop); 8938c2ecf20Sopenharmony_ci else 8948c2ecf20Sopenharmony_ci *mc_size_cells = of_n_size_cells(mc_node); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci range_tuple_cell_count = *paddr_cells + *mc_addr_cells + 8978c2ecf20Sopenharmony_ci *mc_size_cells; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci tuple_len = range_tuple_cell_count * sizeof(__be32); 9008c2ecf20Sopenharmony_ci if (ranges_len % tuple_len != 0) { 9018c2ecf20Sopenharmony_ci dev_err(dev, "malformed ranges property '%pOFn'\n", mc_node); 9028c2ecf20Sopenharmony_ci return -EINVAL; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci return ranges_len / tuple_len; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic int get_mc_addr_translation_ranges(struct device *dev, 9098c2ecf20Sopenharmony_ci struct fsl_mc_addr_translation_range 9108c2ecf20Sopenharmony_ci **ranges, 9118c2ecf20Sopenharmony_ci u8 *num_ranges) 9128c2ecf20Sopenharmony_ci{ 9138c2ecf20Sopenharmony_ci int ret; 9148c2ecf20Sopenharmony_ci int paddr_cells; 9158c2ecf20Sopenharmony_ci int mc_addr_cells; 9168c2ecf20Sopenharmony_ci int mc_size_cells; 9178c2ecf20Sopenharmony_ci int i; 9188c2ecf20Sopenharmony_ci const __be32 *ranges_start; 9198c2ecf20Sopenharmony_ci const __be32 *cell; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci ret = parse_mc_ranges(dev, 9228c2ecf20Sopenharmony_ci &paddr_cells, 9238c2ecf20Sopenharmony_ci &mc_addr_cells, 9248c2ecf20Sopenharmony_ci &mc_size_cells, 9258c2ecf20Sopenharmony_ci &ranges_start); 9268c2ecf20Sopenharmony_ci if (ret < 0) 9278c2ecf20Sopenharmony_ci return ret; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci *num_ranges = ret; 9308c2ecf20Sopenharmony_ci if (!ret) { 9318c2ecf20Sopenharmony_ci /* 9328c2ecf20Sopenharmony_ci * Missing or empty ranges property ("ranges;") for the 9338c2ecf20Sopenharmony_ci * 'fsl,qoriq-mc' node. In this case, identity mapping 9348c2ecf20Sopenharmony_ci * will be used. 9358c2ecf20Sopenharmony_ci */ 9368c2ecf20Sopenharmony_ci *ranges = NULL; 9378c2ecf20Sopenharmony_ci return 0; 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci *ranges = devm_kcalloc(dev, *num_ranges, 9418c2ecf20Sopenharmony_ci sizeof(struct fsl_mc_addr_translation_range), 9428c2ecf20Sopenharmony_ci GFP_KERNEL); 9438c2ecf20Sopenharmony_ci if (!(*ranges)) 9448c2ecf20Sopenharmony_ci return -ENOMEM; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci cell = ranges_start; 9478c2ecf20Sopenharmony_ci for (i = 0; i < *num_ranges; ++i) { 9488c2ecf20Sopenharmony_ci struct fsl_mc_addr_translation_range *range = &(*ranges)[i]; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci range->mc_region_type = of_read_number(cell, 1); 9518c2ecf20Sopenharmony_ci range->start_mc_offset = of_read_number(cell + 1, 9528c2ecf20Sopenharmony_ci mc_addr_cells - 1); 9538c2ecf20Sopenharmony_ci cell += mc_addr_cells; 9548c2ecf20Sopenharmony_ci range->start_phys_addr = of_read_number(cell, paddr_cells); 9558c2ecf20Sopenharmony_ci cell += paddr_cells; 9568c2ecf20Sopenharmony_ci range->end_mc_offset = range->start_mc_offset + 9578c2ecf20Sopenharmony_ci of_read_number(cell, mc_size_cells); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci cell += mc_size_cells; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci return 0; 9638c2ecf20Sopenharmony_ci} 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci/** 9668c2ecf20Sopenharmony_ci * fsl_mc_bus_probe - callback invoked when the root MC bus is being 9678c2ecf20Sopenharmony_ci * added 9688c2ecf20Sopenharmony_ci */ 9698c2ecf20Sopenharmony_cistatic int fsl_mc_bus_probe(struct platform_device *pdev) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci struct fsl_mc_obj_desc obj_desc; 9728c2ecf20Sopenharmony_ci int error; 9738c2ecf20Sopenharmony_ci struct fsl_mc *mc; 9748c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_bus_dev = NULL; 9758c2ecf20Sopenharmony_ci struct fsl_mc_io *mc_io = NULL; 9768c2ecf20Sopenharmony_ci int container_id; 9778c2ecf20Sopenharmony_ci phys_addr_t mc_portal_phys_addr; 9788c2ecf20Sopenharmony_ci u32 mc_portal_size, mc_stream_id; 9798c2ecf20Sopenharmony_ci struct resource *plat_res; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); 9828c2ecf20Sopenharmony_ci if (!mc) 9838c2ecf20Sopenharmony_ci return -ENOMEM; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mc); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci plat_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 9888c2ecf20Sopenharmony_ci if (plat_res) { 9898c2ecf20Sopenharmony_ci mc->fsl_mc_regs = devm_ioremap_resource(&pdev->dev, plat_res); 9908c2ecf20Sopenharmony_ci if (IS_ERR(mc->fsl_mc_regs)) 9918c2ecf20Sopenharmony_ci return PTR_ERR(mc->fsl_mc_regs); 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci if (mc->fsl_mc_regs && IS_ENABLED(CONFIG_ACPI) && 9958c2ecf20Sopenharmony_ci !dev_of_node(&pdev->dev)) { 9968c2ecf20Sopenharmony_ci mc_stream_id = readl(mc->fsl_mc_regs + FSL_MC_FAPR); 9978c2ecf20Sopenharmony_ci /* 9988c2ecf20Sopenharmony_ci * HW ORs the PL and BMT bit, places the result in bit 15 of 9998c2ecf20Sopenharmony_ci * the StreamID and ORs in the ICID. Calculate it accordingly. 10008c2ecf20Sopenharmony_ci */ 10018c2ecf20Sopenharmony_ci mc_stream_id = (mc_stream_id & 0xffff) | 10028c2ecf20Sopenharmony_ci ((mc_stream_id & (MC_FAPR_PL | MC_FAPR_BMT)) ? 10038c2ecf20Sopenharmony_ci 0x4000 : 0); 10048c2ecf20Sopenharmony_ci error = acpi_dma_configure_id(&pdev->dev, DEV_DMA_COHERENT, 10058c2ecf20Sopenharmony_ci &mc_stream_id); 10068c2ecf20Sopenharmony_ci if (error) 10078c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "failed to configure dma: %d.\n", 10088c2ecf20Sopenharmony_ci error); 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci /* 10128c2ecf20Sopenharmony_ci * Get physical address of MC portal for the root DPRC: 10138c2ecf20Sopenharmony_ci */ 10148c2ecf20Sopenharmony_ci plat_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 10158c2ecf20Sopenharmony_ci mc_portal_phys_addr = plat_res->start; 10168c2ecf20Sopenharmony_ci mc_portal_size = resource_size(plat_res); 10178c2ecf20Sopenharmony_ci mc_portal_base_phys_addr = mc_portal_phys_addr & ~0x3ffffff; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci error = fsl_create_mc_io(&pdev->dev, mc_portal_phys_addr, 10208c2ecf20Sopenharmony_ci mc_portal_size, NULL, 10218c2ecf20Sopenharmony_ci FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, &mc_io); 10228c2ecf20Sopenharmony_ci if (error < 0) 10238c2ecf20Sopenharmony_ci return error; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci error = mc_get_version(mc_io, 0, &mc_version); 10268c2ecf20Sopenharmony_ci if (error != 0) { 10278c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 10288c2ecf20Sopenharmony_ci "mc_get_version() failed with error %d\n", error); 10298c2ecf20Sopenharmony_ci goto error_cleanup_mc_io; 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "MC firmware version: %u.%u.%u\n", 10338c2ecf20Sopenharmony_ci mc_version.major, mc_version.minor, mc_version.revision); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci if (dev_of_node(&pdev->dev)) { 10368c2ecf20Sopenharmony_ci error = get_mc_addr_translation_ranges(&pdev->dev, 10378c2ecf20Sopenharmony_ci &mc->translation_ranges, 10388c2ecf20Sopenharmony_ci &mc->num_translation_ranges); 10398c2ecf20Sopenharmony_ci if (error < 0) 10408c2ecf20Sopenharmony_ci goto error_cleanup_mc_io; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci error = dprc_get_container_id(mc_io, 0, &container_id); 10448c2ecf20Sopenharmony_ci if (error < 0) { 10458c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 10468c2ecf20Sopenharmony_ci "dprc_get_container_id() failed: %d\n", error); 10478c2ecf20Sopenharmony_ci goto error_cleanup_mc_io; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci memset(&obj_desc, 0, sizeof(struct fsl_mc_obj_desc)); 10518c2ecf20Sopenharmony_ci error = dprc_get_api_version(mc_io, 0, 10528c2ecf20Sopenharmony_ci &obj_desc.ver_major, 10538c2ecf20Sopenharmony_ci &obj_desc.ver_minor); 10548c2ecf20Sopenharmony_ci if (error < 0) 10558c2ecf20Sopenharmony_ci goto error_cleanup_mc_io; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci obj_desc.vendor = FSL_MC_VENDOR_FREESCALE; 10588c2ecf20Sopenharmony_ci strcpy(obj_desc.type, "dprc"); 10598c2ecf20Sopenharmony_ci obj_desc.id = container_id; 10608c2ecf20Sopenharmony_ci obj_desc.irq_count = 1; 10618c2ecf20Sopenharmony_ci obj_desc.region_count = 0; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, &mc_bus_dev); 10648c2ecf20Sopenharmony_ci if (error < 0) 10658c2ecf20Sopenharmony_ci goto error_cleanup_mc_io; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci mc->root_mc_bus_dev = mc_bus_dev; 10688c2ecf20Sopenharmony_ci mc_bus_dev->dev.fwnode = pdev->dev.fwnode; 10698c2ecf20Sopenharmony_ci return 0; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_cierror_cleanup_mc_io: 10728c2ecf20Sopenharmony_ci fsl_destroy_mc_io(mc_io); 10738c2ecf20Sopenharmony_ci return error; 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci/** 10778c2ecf20Sopenharmony_ci * fsl_mc_bus_remove - callback invoked when the root MC bus is being 10788c2ecf20Sopenharmony_ci * removed 10798c2ecf20Sopenharmony_ci */ 10808c2ecf20Sopenharmony_cistatic int fsl_mc_bus_remove(struct platform_device *pdev) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci struct fsl_mc *mc = platform_get_drvdata(pdev); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci if (!fsl_mc_is_root_dprc(&mc->root_mc_bus_dev->dev)) 10858c2ecf20Sopenharmony_ci return -EINVAL; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci fsl_mc_device_remove(mc->root_mc_bus_dev); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci fsl_destroy_mc_io(mc->root_mc_bus_dev->mc_io); 10908c2ecf20Sopenharmony_ci mc->root_mc_bus_dev->mc_io = NULL; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci return 0; 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic const struct of_device_id fsl_mc_bus_match_table[] = { 10968c2ecf20Sopenharmony_ci {.compatible = "fsl,qoriq-mc",}, 10978c2ecf20Sopenharmony_ci {}, 10988c2ecf20Sopenharmony_ci}; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_mc_bus_match_table); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_cistatic const struct acpi_device_id fsl_mc_bus_acpi_match_table[] = { 11038c2ecf20Sopenharmony_ci {"NXP0008", 0 }, 11048c2ecf20Sopenharmony_ci { } 11058c2ecf20Sopenharmony_ci}; 11068c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, fsl_mc_bus_acpi_match_table); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic struct platform_driver fsl_mc_bus_driver = { 11098c2ecf20Sopenharmony_ci .driver = { 11108c2ecf20Sopenharmony_ci .name = "fsl_mc_bus", 11118c2ecf20Sopenharmony_ci .pm = NULL, 11128c2ecf20Sopenharmony_ci .of_match_table = fsl_mc_bus_match_table, 11138c2ecf20Sopenharmony_ci .acpi_match_table = fsl_mc_bus_acpi_match_table, 11148c2ecf20Sopenharmony_ci }, 11158c2ecf20Sopenharmony_ci .probe = fsl_mc_bus_probe, 11168c2ecf20Sopenharmony_ci .remove = fsl_mc_bus_remove, 11178c2ecf20Sopenharmony_ci}; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_cistatic int __init fsl_mc_bus_driver_init(void) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci int error; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci error = bus_register(&fsl_mc_bus_type); 11248c2ecf20Sopenharmony_ci if (error < 0) { 11258c2ecf20Sopenharmony_ci pr_err("bus type registration failed: %d\n", error); 11268c2ecf20Sopenharmony_ci goto error_cleanup_cache; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci error = platform_driver_register(&fsl_mc_bus_driver); 11308c2ecf20Sopenharmony_ci if (error < 0) { 11318c2ecf20Sopenharmony_ci pr_err("platform_driver_register() failed: %d\n", error); 11328c2ecf20Sopenharmony_ci goto error_cleanup_bus; 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci error = dprc_driver_init(); 11368c2ecf20Sopenharmony_ci if (error < 0) 11378c2ecf20Sopenharmony_ci goto error_cleanup_driver; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci error = fsl_mc_allocator_driver_init(); 11408c2ecf20Sopenharmony_ci if (error < 0) 11418c2ecf20Sopenharmony_ci goto error_cleanup_dprc_driver; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci return 0; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_cierror_cleanup_dprc_driver: 11468c2ecf20Sopenharmony_ci dprc_driver_exit(); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_cierror_cleanup_driver: 11498c2ecf20Sopenharmony_ci platform_driver_unregister(&fsl_mc_bus_driver); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_cierror_cleanup_bus: 11528c2ecf20Sopenharmony_ci bus_unregister(&fsl_mc_bus_type); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_cierror_cleanup_cache: 11558c2ecf20Sopenharmony_ci return error; 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_cipostcore_initcall(fsl_mc_bus_driver_init); 1158