162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Freescale data path resource container (DPRC) driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. 662306a36Sopenharmony_ci * Copyright 2019-2020 NXP 762306a36Sopenharmony_ci * Author: German Rivera <German.Rivera@freescale.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/fsl/mc.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "fsl-mc-private.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define FSL_MC_DPRC_DRIVER_NAME "fsl_mc_dprc" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct fsl_mc_child_objs { 2162306a36Sopenharmony_ci int child_count; 2262306a36Sopenharmony_ci struct fsl_mc_obj_desc *child_array; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic bool fsl_mc_device_match(struct fsl_mc_device *mc_dev, 2662306a36Sopenharmony_ci struct fsl_mc_obj_desc *obj_desc) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci return mc_dev->obj_desc.id == obj_desc->id && 2962306a36Sopenharmony_ci strcmp(mc_dev->obj_desc.type, obj_desc->type) == 0; 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic bool fsl_mc_obj_desc_is_allocatable(struct fsl_mc_obj_desc *obj) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci if (strcmp(obj->type, "dpmcp") == 0 || 3562306a36Sopenharmony_ci strcmp(obj->type, "dpcon") == 0 || 3662306a36Sopenharmony_ci strcmp(obj->type, "dpbp") == 0) 3762306a36Sopenharmony_ci return true; 3862306a36Sopenharmony_ci else 3962306a36Sopenharmony_ci return false; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci int i; 4562306a36Sopenharmony_ci struct fsl_mc_child_objs *objs; 4662306a36Sopenharmony_ci struct fsl_mc_device *mc_dev; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (!dev_is_fsl_mc(dev)) 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci mc_dev = to_fsl_mc_device(dev); 5262306a36Sopenharmony_ci objs = data; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci for (i = 0; i < objs->child_count; i++) { 5562306a36Sopenharmony_ci struct fsl_mc_obj_desc *obj_desc = &objs->child_array[i]; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (strlen(obj_desc->type) != 0 && 5862306a36Sopenharmony_ci fsl_mc_device_match(mc_dev, obj_desc)) 5962306a36Sopenharmony_ci break; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (i == objs->child_count) 6362306a36Sopenharmony_ci fsl_mc_device_remove(mc_dev); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int __fsl_mc_device_remove(struct device *dev, void *data) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci if (!dev_is_fsl_mc(dev)) 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci fsl_mc_device_remove(to_fsl_mc_device(dev)); 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/** 7862306a36Sopenharmony_ci * dprc_remove_devices - Removes devices for objects removed from a DPRC 7962306a36Sopenharmony_ci * 8062306a36Sopenharmony_ci * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object 8162306a36Sopenharmony_ci * @obj_desc_array: array of object descriptors for child objects currently 8262306a36Sopenharmony_ci * present in the DPRC in the MC. 8362306a36Sopenharmony_ci * @num_child_objects_in_mc: number of entries in obj_desc_array 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * Synchronizes the state of the Linux bus driver with the actual state of 8662306a36Sopenharmony_ci * the MC by removing devices that represent MC objects that have 8762306a36Sopenharmony_ci * been dynamically removed in the physical DPRC. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_civoid dprc_remove_devices(struct fsl_mc_device *mc_bus_dev, 9062306a36Sopenharmony_ci struct fsl_mc_obj_desc *obj_desc_array, 9162306a36Sopenharmony_ci int num_child_objects_in_mc) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci if (num_child_objects_in_mc != 0) { 9462306a36Sopenharmony_ci /* 9562306a36Sopenharmony_ci * Remove child objects that are in the DPRC in Linux, 9662306a36Sopenharmony_ci * but not in the MC: 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci struct fsl_mc_child_objs objs; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci objs.child_count = num_child_objects_in_mc; 10162306a36Sopenharmony_ci objs.child_array = obj_desc_array; 10262306a36Sopenharmony_ci device_for_each_child(&mc_bus_dev->dev, &objs, 10362306a36Sopenharmony_ci __fsl_mc_device_remove_if_not_in_mc); 10462306a36Sopenharmony_ci } else { 10562306a36Sopenharmony_ci /* 10662306a36Sopenharmony_ci * There are no child objects for this DPRC in the MC. 10762306a36Sopenharmony_ci * So, remove all the child devices from Linux: 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci device_for_each_child(&mc_bus_dev->dev, NULL, 11062306a36Sopenharmony_ci __fsl_mc_device_remove); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dprc_remove_devices); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int __fsl_mc_device_match(struct device *dev, void *data) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct fsl_mc_obj_desc *obj_desc = data; 11862306a36Sopenharmony_ci struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return fsl_mc_device_match(mc_dev, obj_desc); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistruct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc *obj_desc, 12462306a36Sopenharmony_ci struct fsl_mc_device *mc_bus_dev) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct device *dev; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci dev = device_find_child(&mc_bus_dev->dev, obj_desc, 12962306a36Sopenharmony_ci __fsl_mc_device_match); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return dev ? to_fsl_mc_device(dev) : NULL; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/** 13562306a36Sopenharmony_ci * check_plugged_state_change - Check change in an MC object's plugged state 13662306a36Sopenharmony_ci * 13762306a36Sopenharmony_ci * @mc_dev: pointer to the fsl-mc device for a given MC object 13862306a36Sopenharmony_ci * @obj_desc: pointer to the MC object's descriptor in the MC 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * If the plugged state has changed from unplugged to plugged, the fsl-mc 14162306a36Sopenharmony_ci * device is bound to the corresponding device driver. 14262306a36Sopenharmony_ci * If the plugged state has changed from plugged to unplugged, the fsl-mc 14362306a36Sopenharmony_ci * device is unbound from the corresponding device driver. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_cistatic void check_plugged_state_change(struct fsl_mc_device *mc_dev, 14662306a36Sopenharmony_ci struct fsl_mc_obj_desc *obj_desc) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci int error; 14962306a36Sopenharmony_ci u32 plugged_flag_at_mc = 15062306a36Sopenharmony_ci obj_desc->state & FSL_MC_OBJ_STATE_PLUGGED; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (plugged_flag_at_mc != 15362306a36Sopenharmony_ci (mc_dev->obj_desc.state & FSL_MC_OBJ_STATE_PLUGGED)) { 15462306a36Sopenharmony_ci if (plugged_flag_at_mc) { 15562306a36Sopenharmony_ci mc_dev->obj_desc.state |= FSL_MC_OBJ_STATE_PLUGGED; 15662306a36Sopenharmony_ci error = device_attach(&mc_dev->dev); 15762306a36Sopenharmony_ci if (error < 0) { 15862306a36Sopenharmony_ci dev_err(&mc_dev->dev, 15962306a36Sopenharmony_ci "device_attach() failed: %d\n", 16062306a36Sopenharmony_ci error); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci } else { 16362306a36Sopenharmony_ci mc_dev->obj_desc.state &= ~FSL_MC_OBJ_STATE_PLUGGED; 16462306a36Sopenharmony_ci device_release_driver(&mc_dev->dev); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void fsl_mc_obj_device_add(struct fsl_mc_device *mc_bus_dev, 17062306a36Sopenharmony_ci struct fsl_mc_obj_desc *obj_desc) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci int error; 17362306a36Sopenharmony_ci struct fsl_mc_device *child_dev; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* 17662306a36Sopenharmony_ci * Check if device is already known to Linux: 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci child_dev = fsl_mc_device_lookup(obj_desc, mc_bus_dev); 17962306a36Sopenharmony_ci if (child_dev) { 18062306a36Sopenharmony_ci check_plugged_state_change(child_dev, obj_desc); 18162306a36Sopenharmony_ci put_device(&child_dev->dev); 18262306a36Sopenharmony_ci } else { 18362306a36Sopenharmony_ci error = fsl_mc_device_add(obj_desc, NULL, &mc_bus_dev->dev, 18462306a36Sopenharmony_ci &child_dev); 18562306a36Sopenharmony_ci if (error < 0) 18662306a36Sopenharmony_ci return; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/** 19162306a36Sopenharmony_ci * dprc_add_new_devices - Adds devices to the logical bus for a DPRC 19262306a36Sopenharmony_ci * 19362306a36Sopenharmony_ci * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object 19462306a36Sopenharmony_ci * @obj_desc_array: array of device descriptors for child devices currently 19562306a36Sopenharmony_ci * present in the physical DPRC. 19662306a36Sopenharmony_ci * @num_child_objects_in_mc: number of entries in obj_desc_array 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * Synchronizes the state of the Linux bus driver with the actual 19962306a36Sopenharmony_ci * state of the MC by adding objects that have been newly discovered 20062306a36Sopenharmony_ci * in the physical DPRC. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_cistatic void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev, 20362306a36Sopenharmony_ci struct fsl_mc_obj_desc *obj_desc_array, 20462306a36Sopenharmony_ci int num_child_objects_in_mc) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci int i; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* probe the allocable objects first */ 20962306a36Sopenharmony_ci for (i = 0; i < num_child_objects_in_mc; i++) { 21062306a36Sopenharmony_ci struct fsl_mc_obj_desc *obj_desc = &obj_desc_array[i]; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (strlen(obj_desc->type) > 0 && 21362306a36Sopenharmony_ci fsl_mc_obj_desc_is_allocatable(obj_desc)) 21462306a36Sopenharmony_ci fsl_mc_obj_device_add(mc_bus_dev, obj_desc); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci for (i = 0; i < num_child_objects_in_mc; i++) { 21862306a36Sopenharmony_ci struct fsl_mc_obj_desc *obj_desc = &obj_desc_array[i]; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (strlen(obj_desc->type) > 0 && 22162306a36Sopenharmony_ci !fsl_mc_obj_desc_is_allocatable(obj_desc)) 22262306a36Sopenharmony_ci fsl_mc_obj_device_add(mc_bus_dev, obj_desc); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/** 22762306a36Sopenharmony_ci * dprc_scan_objects - Discover objects in a DPRC 22862306a36Sopenharmony_ci * 22962306a36Sopenharmony_ci * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object 23062306a36Sopenharmony_ci * @alloc_interrupts: if true the function allocates the interrupt pool, 23162306a36Sopenharmony_ci * otherwise the interrupt allocation is delayed 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * Detects objects added and removed from a DPRC and synchronizes the 23462306a36Sopenharmony_ci * state of the Linux bus driver, MC by adding and removing 23562306a36Sopenharmony_ci * devices accordingly. 23662306a36Sopenharmony_ci * Two types of devices can be found in a DPRC: allocatable objects (e.g., 23762306a36Sopenharmony_ci * dpbp, dpmcp) and non-allocatable devices (e.g., dprc, dpni). 23862306a36Sopenharmony_ci * All allocatable devices needed to be probed before all non-allocatable 23962306a36Sopenharmony_ci * devices, to ensure that device drivers for non-allocatable 24062306a36Sopenharmony_ci * devices can allocate any type of allocatable devices. 24162306a36Sopenharmony_ci * That is, we need to ensure that the corresponding resource pools are 24262306a36Sopenharmony_ci * populated before they can get allocation requests from probe callbacks 24362306a36Sopenharmony_ci * of the device drivers for the non-allocatable devices. 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_ciint dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, 24662306a36Sopenharmony_ci bool alloc_interrupts) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci int num_child_objects; 24962306a36Sopenharmony_ci int dprc_get_obj_failures; 25062306a36Sopenharmony_ci int error; 25162306a36Sopenharmony_ci unsigned int irq_count = mc_bus_dev->obj_desc.irq_count; 25262306a36Sopenharmony_ci struct fsl_mc_obj_desc *child_obj_desc_array = NULL; 25362306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci error = dprc_get_obj_count(mc_bus_dev->mc_io, 25662306a36Sopenharmony_ci 0, 25762306a36Sopenharmony_ci mc_bus_dev->mc_handle, 25862306a36Sopenharmony_ci &num_child_objects); 25962306a36Sopenharmony_ci if (error < 0) { 26062306a36Sopenharmony_ci dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n", 26162306a36Sopenharmony_ci error); 26262306a36Sopenharmony_ci return error; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (num_child_objects != 0) { 26662306a36Sopenharmony_ci int i; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci child_obj_desc_array = 26962306a36Sopenharmony_ci devm_kmalloc_array(&mc_bus_dev->dev, num_child_objects, 27062306a36Sopenharmony_ci sizeof(*child_obj_desc_array), 27162306a36Sopenharmony_ci GFP_KERNEL); 27262306a36Sopenharmony_ci if (!child_obj_desc_array) 27362306a36Sopenharmony_ci return -ENOMEM; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* 27662306a36Sopenharmony_ci * Discover objects currently present in the physical DPRC: 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci dprc_get_obj_failures = 0; 27962306a36Sopenharmony_ci for (i = 0; i < num_child_objects; i++) { 28062306a36Sopenharmony_ci struct fsl_mc_obj_desc *obj_desc = 28162306a36Sopenharmony_ci &child_obj_desc_array[i]; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci error = dprc_get_obj(mc_bus_dev->mc_io, 28462306a36Sopenharmony_ci 0, 28562306a36Sopenharmony_ci mc_bus_dev->mc_handle, 28662306a36Sopenharmony_ci i, obj_desc); 28762306a36Sopenharmony_ci if (error < 0) { 28862306a36Sopenharmony_ci dev_err(&mc_bus_dev->dev, 28962306a36Sopenharmony_ci "dprc_get_obj(i=%d) failed: %d\n", 29062306a36Sopenharmony_ci i, error); 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * Mark the obj entry as "invalid", by using the 29362306a36Sopenharmony_ci * empty string as obj type: 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci obj_desc->type[0] = '\0'; 29662306a36Sopenharmony_ci obj_desc->id = error; 29762306a36Sopenharmony_ci dprc_get_obj_failures++; 29862306a36Sopenharmony_ci continue; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* 30262306a36Sopenharmony_ci * add a quirk for all versions of dpsec < 4.0...none 30362306a36Sopenharmony_ci * are coherent regardless of what the MC reports. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ci if ((strcmp(obj_desc->type, "dpseci") == 0) && 30662306a36Sopenharmony_ci (obj_desc->ver_major < 4)) 30762306a36Sopenharmony_ci obj_desc->flags |= 30862306a36Sopenharmony_ci FSL_MC_OBJ_FLAG_NO_MEM_SHAREABILITY; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci irq_count += obj_desc->irq_count; 31162306a36Sopenharmony_ci dev_dbg(&mc_bus_dev->dev, 31262306a36Sopenharmony_ci "Discovered object: type %s, id %d\n", 31362306a36Sopenharmony_ci obj_desc->type, obj_desc->id); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (dprc_get_obj_failures != 0) { 31762306a36Sopenharmony_ci dev_err(&mc_bus_dev->dev, 31862306a36Sopenharmony_ci "%d out of %d devices could not be retrieved\n", 31962306a36Sopenharmony_ci dprc_get_obj_failures, num_child_objects); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* 32462306a36Sopenharmony_ci * Allocate IRQ's before binding the scanned devices with their 32562306a36Sopenharmony_ci * respective drivers. 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ci if (dev_get_msi_domain(&mc_bus_dev->dev)) { 32862306a36Sopenharmony_ci if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) { 32962306a36Sopenharmony_ci dev_warn(&mc_bus_dev->dev, 33062306a36Sopenharmony_ci "IRQs needed (%u) exceed IRQs preallocated (%u)\n", 33162306a36Sopenharmony_ci irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (alloc_interrupts && !mc_bus->irq_resources) { 33562306a36Sopenharmony_ci error = fsl_mc_populate_irq_pool(mc_bus_dev, 33662306a36Sopenharmony_ci FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); 33762306a36Sopenharmony_ci if (error < 0) 33862306a36Sopenharmony_ci return error; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci dprc_remove_devices(mc_bus_dev, child_obj_desc_array, 34362306a36Sopenharmony_ci num_child_objects); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci dprc_add_new_devices(mc_bus_dev, child_obj_desc_array, 34662306a36Sopenharmony_ci num_child_objects); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (child_obj_desc_array) 34962306a36Sopenharmony_ci devm_kfree(&mc_bus_dev->dev, child_obj_desc_array); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci/** 35562306a36Sopenharmony_ci * dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state 35662306a36Sopenharmony_ci * 35762306a36Sopenharmony_ci * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object 35862306a36Sopenharmony_ci * @alloc_interrupts: if true the function allocates the interrupt pool, 35962306a36Sopenharmony_ci * otherwise the interrupt allocation is delayed 36062306a36Sopenharmony_ci * Scans the physical DPRC and synchronizes the state of the Linux 36162306a36Sopenharmony_ci * bus driver with the actual state of the MC by adding and removing 36262306a36Sopenharmony_ci * devices as appropriate. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ciint dprc_scan_container(struct fsl_mc_device *mc_bus_dev, 36562306a36Sopenharmony_ci bool alloc_interrupts) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci int error = 0; 36862306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci fsl_mc_init_all_resource_pools(mc_bus_dev); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* 37362306a36Sopenharmony_ci * Discover objects in the DPRC: 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_ci mutex_lock(&mc_bus->scan_mutex); 37662306a36Sopenharmony_ci error = dprc_scan_objects(mc_bus_dev, alloc_interrupts); 37762306a36Sopenharmony_ci mutex_unlock(&mc_bus->scan_mutex); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return error; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dprc_scan_container); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/** 38462306a36Sopenharmony_ci * dprc_irq0_handler - Regular ISR for DPRC interrupt 0 38562306a36Sopenharmony_ci * 38662306a36Sopenharmony_ci * @irq_num: IRQ number of the interrupt being handled 38762306a36Sopenharmony_ci * @arg: Pointer to device structure 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_cistatic irqreturn_t dprc_irq0_handler(int irq_num, void *arg) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci/** 39562306a36Sopenharmony_ci * dprc_irq0_handler_thread - Handler thread function for DPRC interrupt 0 39662306a36Sopenharmony_ci * 39762306a36Sopenharmony_ci * @irq_num: IRQ number of the interrupt being handled 39862306a36Sopenharmony_ci * @arg: Pointer to device structure 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_cistatic irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci int error; 40362306a36Sopenharmony_ci u32 status; 40462306a36Sopenharmony_ci struct device *dev = arg; 40562306a36Sopenharmony_ci struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 40662306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); 40762306a36Sopenharmony_ci struct fsl_mc_io *mc_io = mc_dev->mc_io; 40862306a36Sopenharmony_ci int irq = mc_dev->irqs[0]->virq; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci dev_dbg(dev, "DPRC IRQ %d triggered on CPU %u\n", 41162306a36Sopenharmony_ci irq_num, smp_processor_id()); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (!(mc_dev->flags & FSL_MC_IS_DPRC)) 41462306a36Sopenharmony_ci return IRQ_HANDLED; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci mutex_lock(&mc_bus->scan_mutex); 41762306a36Sopenharmony_ci if (irq != (u32)irq_num) 41862306a36Sopenharmony_ci goto out; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci status = 0; 42162306a36Sopenharmony_ci error = dprc_get_irq_status(mc_io, 0, mc_dev->mc_handle, 0, 42262306a36Sopenharmony_ci &status); 42362306a36Sopenharmony_ci if (error < 0) { 42462306a36Sopenharmony_ci dev_err(dev, 42562306a36Sopenharmony_ci "dprc_get_irq_status() failed: %d\n", error); 42662306a36Sopenharmony_ci goto out; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0, 43062306a36Sopenharmony_ci status); 43162306a36Sopenharmony_ci if (error < 0) { 43262306a36Sopenharmony_ci dev_err(dev, 43362306a36Sopenharmony_ci "dprc_clear_irq_status() failed: %d\n", error); 43462306a36Sopenharmony_ci goto out; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (status & (DPRC_IRQ_EVENT_OBJ_ADDED | 43862306a36Sopenharmony_ci DPRC_IRQ_EVENT_OBJ_REMOVED | 43962306a36Sopenharmony_ci DPRC_IRQ_EVENT_CONTAINER_DESTROYED | 44062306a36Sopenharmony_ci DPRC_IRQ_EVENT_OBJ_DESTROYED | 44162306a36Sopenharmony_ci DPRC_IRQ_EVENT_OBJ_CREATED)) { 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci error = dprc_scan_objects(mc_dev, true); 44462306a36Sopenharmony_ci if (error < 0) { 44562306a36Sopenharmony_ci /* 44662306a36Sopenharmony_ci * If the error is -ENXIO, we ignore it, as it indicates 44762306a36Sopenharmony_ci * that the object scan was aborted, as we detected that 44862306a36Sopenharmony_ci * an object was removed from the DPRC in the MC, while 44962306a36Sopenharmony_ci * we were scanning the DPRC. 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ci if (error != -ENXIO) { 45262306a36Sopenharmony_ci dev_err(dev, "dprc_scan_objects() failed: %d\n", 45362306a36Sopenharmony_ci error); 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci goto out; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ciout: 46162306a36Sopenharmony_ci mutex_unlock(&mc_bus->scan_mutex); 46262306a36Sopenharmony_ci return IRQ_HANDLED; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci/* 46662306a36Sopenharmony_ci * Disable and clear interrupt for a given DPRC object 46762306a36Sopenharmony_ci */ 46862306a36Sopenharmony_ciint disable_dprc_irq(struct fsl_mc_device *mc_dev) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); 47162306a36Sopenharmony_ci int error; 47262306a36Sopenharmony_ci struct fsl_mc_io *mc_io = mc_dev->mc_io; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* 47562306a36Sopenharmony_ci * Disable generation of interrupt, while we configure it: 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci error = dprc_set_irq_enable(mc_io, 0, mc_dev->mc_handle, 0, 0); 47862306a36Sopenharmony_ci if (error < 0) { 47962306a36Sopenharmony_ci dev_err(&mc_dev->dev, 48062306a36Sopenharmony_ci "Disabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n", 48162306a36Sopenharmony_ci error); 48262306a36Sopenharmony_ci return error; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* 48662306a36Sopenharmony_ci * Disable all interrupt causes for the interrupt: 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_ci error = dprc_set_irq_mask(mc_io, 0, mc_dev->mc_handle, 0, 0x0); 48962306a36Sopenharmony_ci if (error < 0) { 49062306a36Sopenharmony_ci dev_err(&mc_dev->dev, 49162306a36Sopenharmony_ci "Disabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n", 49262306a36Sopenharmony_ci error); 49362306a36Sopenharmony_ci return error; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* 49762306a36Sopenharmony_ci * Clear any leftover interrupts: 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_ci error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0, ~0x0U); 50062306a36Sopenharmony_ci if (error < 0) { 50162306a36Sopenharmony_ci dev_err(&mc_dev->dev, 50262306a36Sopenharmony_ci "Disabling DPRC IRQ failed: dprc_clear_irq_status() failed: %d\n", 50362306a36Sopenharmony_ci error); 50462306a36Sopenharmony_ci return error; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci mc_bus->irq_enabled = 0; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return 0; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ciint get_dprc_irq_state(struct fsl_mc_device *mc_dev) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci return mc_bus->irq_enabled; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic int register_dprc_irq_handler(struct fsl_mc_device *mc_dev) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci int error; 52262306a36Sopenharmony_ci struct fsl_mc_device_irq *irq = mc_dev->irqs[0]; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* 52562306a36Sopenharmony_ci * NOTE: devm_request_threaded_irq() invokes the device-specific 52662306a36Sopenharmony_ci * function that programs the MSI physically in the device 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_ci error = devm_request_threaded_irq(&mc_dev->dev, 52962306a36Sopenharmony_ci irq->virq, 53062306a36Sopenharmony_ci dprc_irq0_handler, 53162306a36Sopenharmony_ci dprc_irq0_handler_thread, 53262306a36Sopenharmony_ci IRQF_NO_SUSPEND | IRQF_ONESHOT, 53362306a36Sopenharmony_ci dev_name(&mc_dev->dev), 53462306a36Sopenharmony_ci &mc_dev->dev); 53562306a36Sopenharmony_ci if (error < 0) { 53662306a36Sopenharmony_ci dev_err(&mc_dev->dev, 53762306a36Sopenharmony_ci "devm_request_threaded_irq() failed: %d\n", 53862306a36Sopenharmony_ci error); 53962306a36Sopenharmony_ci return error; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return 0; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ciint enable_dprc_irq(struct fsl_mc_device *mc_dev) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); 54862306a36Sopenharmony_ci int error; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* 55162306a36Sopenharmony_ci * Enable all interrupt causes for the interrupt: 55262306a36Sopenharmony_ci */ 55362306a36Sopenharmony_ci error = dprc_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, 0, 55462306a36Sopenharmony_ci ~0x0u); 55562306a36Sopenharmony_ci if (error < 0) { 55662306a36Sopenharmony_ci dev_err(&mc_dev->dev, 55762306a36Sopenharmony_ci "Enabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n", 55862306a36Sopenharmony_ci error); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci return error; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* 56462306a36Sopenharmony_ci * Enable generation of the interrupt: 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_ci error = dprc_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, 0, 1); 56762306a36Sopenharmony_ci if (error < 0) { 56862306a36Sopenharmony_ci dev_err(&mc_dev->dev, 56962306a36Sopenharmony_ci "Enabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n", 57062306a36Sopenharmony_ci error); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci return error; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci mc_bus->irq_enabled = 1; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci return 0; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci/* 58162306a36Sopenharmony_ci * Setup interrupt for a given DPRC device 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_cistatic int dprc_setup_irq(struct fsl_mc_device *mc_dev) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci int error; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci error = fsl_mc_allocate_irqs(mc_dev); 58862306a36Sopenharmony_ci if (error < 0) 58962306a36Sopenharmony_ci return error; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci error = disable_dprc_irq(mc_dev); 59262306a36Sopenharmony_ci if (error < 0) 59362306a36Sopenharmony_ci goto error_free_irqs; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci error = register_dprc_irq_handler(mc_dev); 59662306a36Sopenharmony_ci if (error < 0) 59762306a36Sopenharmony_ci goto error_free_irqs; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci error = enable_dprc_irq(mc_dev); 60062306a36Sopenharmony_ci if (error < 0) 60162306a36Sopenharmony_ci goto error_free_irqs; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return 0; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cierror_free_irqs: 60662306a36Sopenharmony_ci fsl_mc_free_irqs(mc_dev); 60762306a36Sopenharmony_ci return error; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci/** 61162306a36Sopenharmony_ci * dprc_setup - opens and creates a mc_io for DPRC 61262306a36Sopenharmony_ci * 61362306a36Sopenharmony_ci * @mc_dev: Pointer to fsl-mc device representing a DPRC 61462306a36Sopenharmony_ci * 61562306a36Sopenharmony_ci * It opens the physical DPRC in the MC. 61662306a36Sopenharmony_ci * It configures the DPRC portal used to communicate with MC 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ciint dprc_setup(struct fsl_mc_device *mc_dev) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci struct device *parent_dev = mc_dev->dev.parent; 62262306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); 62362306a36Sopenharmony_ci struct irq_domain *mc_msi_domain; 62462306a36Sopenharmony_ci bool mc_io_created = false; 62562306a36Sopenharmony_ci bool msi_domain_set = false; 62662306a36Sopenharmony_ci bool uapi_created = false; 62762306a36Sopenharmony_ci u16 major_ver, minor_ver; 62862306a36Sopenharmony_ci size_t region_size; 62962306a36Sopenharmony_ci int error; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (!is_fsl_mc_bus_dprc(mc_dev)) 63262306a36Sopenharmony_ci return -EINVAL; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (dev_get_msi_domain(&mc_dev->dev)) 63562306a36Sopenharmony_ci return -EINVAL; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (!mc_dev->mc_io) { 63862306a36Sopenharmony_ci /* 63962306a36Sopenharmony_ci * This is a child DPRC: 64062306a36Sopenharmony_ci */ 64162306a36Sopenharmony_ci if (!dev_is_fsl_mc(parent_dev)) 64262306a36Sopenharmony_ci return -EINVAL; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (mc_dev->obj_desc.region_count == 0) 64562306a36Sopenharmony_ci return -EINVAL; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci region_size = resource_size(mc_dev->regions); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci error = fsl_create_mc_io(&mc_dev->dev, 65062306a36Sopenharmony_ci mc_dev->regions[0].start, 65162306a36Sopenharmony_ci region_size, 65262306a36Sopenharmony_ci NULL, 65362306a36Sopenharmony_ci FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, 65462306a36Sopenharmony_ci &mc_dev->mc_io); 65562306a36Sopenharmony_ci if (error < 0) 65662306a36Sopenharmony_ci return error; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci mc_io_created = true; 65962306a36Sopenharmony_ci } else { 66062306a36Sopenharmony_ci error = fsl_mc_uapi_create_device_file(mc_bus); 66162306a36Sopenharmony_ci if (error < 0) 66262306a36Sopenharmony_ci return -EPROBE_DEFER; 66362306a36Sopenharmony_ci uapi_created = true; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci mc_msi_domain = fsl_mc_find_msi_domain(&mc_dev->dev); 66762306a36Sopenharmony_ci if (!mc_msi_domain) { 66862306a36Sopenharmony_ci dev_warn(&mc_dev->dev, 66962306a36Sopenharmony_ci "WARNING: MC bus without interrupt support\n"); 67062306a36Sopenharmony_ci } else { 67162306a36Sopenharmony_ci dev_set_msi_domain(&mc_dev->dev, mc_msi_domain); 67262306a36Sopenharmony_ci msi_domain_set = true; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, 67662306a36Sopenharmony_ci &mc_dev->mc_handle); 67762306a36Sopenharmony_ci if (error < 0) { 67862306a36Sopenharmony_ci dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error); 67962306a36Sopenharmony_ci goto error_cleanup_msi_domain; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci error = dprc_get_attributes(mc_dev->mc_io, 0, mc_dev->mc_handle, 68362306a36Sopenharmony_ci &mc_bus->dprc_attr); 68462306a36Sopenharmony_ci if (error < 0) { 68562306a36Sopenharmony_ci dev_err(&mc_dev->dev, "dprc_get_attributes() failed: %d\n", 68662306a36Sopenharmony_ci error); 68762306a36Sopenharmony_ci goto error_cleanup_open; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci error = dprc_get_api_version(mc_dev->mc_io, 0, 69162306a36Sopenharmony_ci &major_ver, 69262306a36Sopenharmony_ci &minor_ver); 69362306a36Sopenharmony_ci if (error < 0) { 69462306a36Sopenharmony_ci dev_err(&mc_dev->dev, "dprc_get_api_version() failed: %d\n", 69562306a36Sopenharmony_ci error); 69662306a36Sopenharmony_ci goto error_cleanup_open; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (major_ver < DPRC_MIN_VER_MAJOR) { 70062306a36Sopenharmony_ci dev_err(&mc_dev->dev, 70162306a36Sopenharmony_ci "ERROR: DPRC version %d.%d not supported\n", 70262306a36Sopenharmony_ci major_ver, minor_ver); 70362306a36Sopenharmony_ci error = -ENOTSUPP; 70462306a36Sopenharmony_ci goto error_cleanup_open; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cierror_cleanup_open: 71062306a36Sopenharmony_ci (void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cierror_cleanup_msi_domain: 71362306a36Sopenharmony_ci if (msi_domain_set) 71462306a36Sopenharmony_ci dev_set_msi_domain(&mc_dev->dev, NULL); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci if (mc_io_created) { 71762306a36Sopenharmony_ci fsl_destroy_mc_io(mc_dev->mc_io); 71862306a36Sopenharmony_ci mc_dev->mc_io = NULL; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (uapi_created) 72262306a36Sopenharmony_ci fsl_mc_uapi_remove_device_file(mc_bus); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci return error; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dprc_setup); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci/** 72962306a36Sopenharmony_ci * dprc_probe - callback invoked when a DPRC is being bound to this driver 73062306a36Sopenharmony_ci * 73162306a36Sopenharmony_ci * @mc_dev: Pointer to fsl-mc device representing a DPRC 73262306a36Sopenharmony_ci * 73362306a36Sopenharmony_ci * It opens the physical DPRC in the MC. 73462306a36Sopenharmony_ci * It scans the DPRC to discover the MC objects contained in it. 73562306a36Sopenharmony_ci * It creates the interrupt pool for the MC bus associated with the DPRC. 73662306a36Sopenharmony_ci * It configures the interrupts for the DPRC device itself. 73762306a36Sopenharmony_ci */ 73862306a36Sopenharmony_cistatic int dprc_probe(struct fsl_mc_device *mc_dev) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci int error; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci error = dprc_setup(mc_dev); 74362306a36Sopenharmony_ci if (error < 0) 74462306a36Sopenharmony_ci return error; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* 74762306a36Sopenharmony_ci * Discover MC objects in DPRC object: 74862306a36Sopenharmony_ci */ 74962306a36Sopenharmony_ci error = dprc_scan_container(mc_dev, true); 75062306a36Sopenharmony_ci if (error < 0) 75162306a36Sopenharmony_ci goto dprc_cleanup; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* 75462306a36Sopenharmony_ci * Configure interrupt for the DPRC object associated with this MC bus: 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_ci error = dprc_setup_irq(mc_dev); 75762306a36Sopenharmony_ci if (error < 0) 75862306a36Sopenharmony_ci goto scan_cleanup; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci dev_info(&mc_dev->dev, "DPRC device bound to driver"); 76162306a36Sopenharmony_ci return 0; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ciscan_cleanup: 76462306a36Sopenharmony_ci device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove); 76562306a36Sopenharmony_cidprc_cleanup: 76662306a36Sopenharmony_ci dprc_cleanup(mc_dev); 76762306a36Sopenharmony_ci return error; 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci/* 77162306a36Sopenharmony_ci * Tear down interrupt for a given DPRC object 77262306a36Sopenharmony_ci */ 77362306a36Sopenharmony_cistatic void dprc_teardown_irq(struct fsl_mc_device *mc_dev) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci struct fsl_mc_device_irq *irq = mc_dev->irqs[0]; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci (void)disable_dprc_irq(mc_dev); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci devm_free_irq(&mc_dev->dev, irq->virq, &mc_dev->dev); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci fsl_mc_free_irqs(mc_dev); 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci/** 78562306a36Sopenharmony_ci * dprc_cleanup - function that cleanups a DPRC 78662306a36Sopenharmony_ci * 78762306a36Sopenharmony_ci * @mc_dev: Pointer to fsl-mc device representing the DPRC 78862306a36Sopenharmony_ci * 78962306a36Sopenharmony_ci * It closes the DPRC device in the MC. 79062306a36Sopenharmony_ci * It destroys the interrupt pool associated with this MC bus. 79162306a36Sopenharmony_ci */ 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ciint dprc_cleanup(struct fsl_mc_device *mc_dev) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); 79662306a36Sopenharmony_ci int error; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* this function should be called only for DPRCs, it 79962306a36Sopenharmony_ci * is an error to call it for regular objects 80062306a36Sopenharmony_ci */ 80162306a36Sopenharmony_ci if (!is_fsl_mc_bus_dprc(mc_dev)) 80262306a36Sopenharmony_ci return -EINVAL; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if (dev_get_msi_domain(&mc_dev->dev)) { 80562306a36Sopenharmony_ci fsl_mc_cleanup_irq_pool(mc_dev); 80662306a36Sopenharmony_ci dev_set_msi_domain(&mc_dev->dev, NULL); 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci fsl_mc_cleanup_all_resource_pools(mc_dev); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci /* if this step fails we cannot go further with cleanup as there is no way of 81262306a36Sopenharmony_ci * communicating with the firmware 81362306a36Sopenharmony_ci */ 81462306a36Sopenharmony_ci if (!mc_dev->mc_io) { 81562306a36Sopenharmony_ci dev_err(&mc_dev->dev, "mc_io is NULL, tear down cannot be performed in firmware\n"); 81662306a36Sopenharmony_ci return -EINVAL; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); 82062306a36Sopenharmony_ci if (error < 0) 82162306a36Sopenharmony_ci dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (!fsl_mc_is_root_dprc(&mc_dev->dev)) { 82462306a36Sopenharmony_ci fsl_destroy_mc_io(mc_dev->mc_io); 82562306a36Sopenharmony_ci mc_dev->mc_io = NULL; 82662306a36Sopenharmony_ci } else { 82762306a36Sopenharmony_ci fsl_mc_uapi_remove_device_file(mc_bus); 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci return 0; 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dprc_cleanup); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci/** 83562306a36Sopenharmony_ci * dprc_remove - callback invoked when a DPRC is being unbound from this driver 83662306a36Sopenharmony_ci * 83762306a36Sopenharmony_ci * @mc_dev: Pointer to fsl-mc device representing the DPRC 83862306a36Sopenharmony_ci * 83962306a36Sopenharmony_ci * It removes the DPRC's child objects from Linux (not from the MC) and 84062306a36Sopenharmony_ci * closes the DPRC device in the MC. 84162306a36Sopenharmony_ci * It tears down the interrupts that were configured for the DPRC device. 84262306a36Sopenharmony_ci * It destroys the interrupt pool associated with this MC bus. 84362306a36Sopenharmony_ci */ 84462306a36Sopenharmony_cistatic void dprc_remove(struct fsl_mc_device *mc_dev) 84562306a36Sopenharmony_ci{ 84662306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (!mc_bus->irq_resources) { 84962306a36Sopenharmony_ci dev_err(&mc_dev->dev, "No irq resources, so unbinding the device failed\n"); 85062306a36Sopenharmony_ci return; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (dev_get_msi_domain(&mc_dev->dev)) 85462306a36Sopenharmony_ci dprc_teardown_irq(mc_dev); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci dprc_cleanup(mc_dev); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci dev_info(&mc_dev->dev, "DPRC device unbound from driver"); 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic const struct fsl_mc_device_id match_id_table[] = { 86462306a36Sopenharmony_ci { 86562306a36Sopenharmony_ci .vendor = FSL_MC_VENDOR_FREESCALE, 86662306a36Sopenharmony_ci .obj_type = "dprc"}, 86762306a36Sopenharmony_ci {.vendor = 0x0}, 86862306a36Sopenharmony_ci}; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic struct fsl_mc_driver dprc_driver = { 87162306a36Sopenharmony_ci .driver = { 87262306a36Sopenharmony_ci .name = FSL_MC_DPRC_DRIVER_NAME, 87362306a36Sopenharmony_ci .owner = THIS_MODULE, 87462306a36Sopenharmony_ci .pm = NULL, 87562306a36Sopenharmony_ci }, 87662306a36Sopenharmony_ci .match_id_table = match_id_table, 87762306a36Sopenharmony_ci .probe = dprc_probe, 87862306a36Sopenharmony_ci .remove = dprc_remove, 87962306a36Sopenharmony_ci}; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ciint __init dprc_driver_init(void) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci return fsl_mc_driver_register(&dprc_driver); 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_civoid dprc_driver_exit(void) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci fsl_mc_driver_unregister(&dprc_driver); 88962306a36Sopenharmony_ci} 890