Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754140AbbFIWKG (ORCPT ); Tue, 9 Jun 2015 18:10:06 -0400 Received: from mail-bl2on0128.outbound.protection.outlook.com ([65.55.169.128]:36628 "EHLO na01-bl2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1753914AbbFIWIz (ORCPT ); Tue, 9 Jun 2015 18:08:55 -0400 Authentication-Results: spf=fail (sender IP is 192.88.168.50) smtp.mailfrom=freescale.com; freescale.mail.onmicrosoft.com; dkim=none (message not signed) header.d=none; From: "J. German Rivera" To: , , , CC: , , , , , , , , , , "J. German Rivera" Subject: [PATCH v4 1/7] staging: fsl-mc: MC bus IRQ support Date: Tue, 9 Jun 2015 16:59:02 -0500 Message-ID: <1433887148-2310-2-git-send-email-German.Rivera@freescale.com> X-Mailer: git-send-email 2.3.3 In-Reply-To: <1433887148-2310-1-git-send-email-German.Rivera@freescale.com> References: <1433887148-2310-1-git-send-email-German.Rivera@freescale.com> X-EOPAttributedMessage: 0 X-Microsoft-Exchange-Diagnostics: 1;BY2FFO11FD005;1:dzG53CcSJVgdogETzX8mdbWgx4E3KSF6cwSSdKNP12pdGRwhaOx9R7bGvdmX908DY08HTsDNpReYPYHr69wyJsqijFZtJu34MlJuZbMvGQfD1p8OO4IHysRYZj0c2Ku5jK0PLjez32MQ5xzCMiUDGMqGZX3W3N1RzXxzf8MF5HMrQecBvk71NPUWU4Kv4CM3mbrTvUfnRV3E5INmfqmzTVhGoHAstc/jwXvyrZJd5cRTFvFVhD7wQG7HyQ6WNJduj6/v6Z3MdeEuwmh86s9Hrith082NODRayOFK+/nRZFxXgaxhuniEAiTgND3qmDKj3IZtCvoCZZFPCjCawAqopA== X-Forefront-Antispam-Report: CIP:192.88.168.50;CTRY:US;IPV:NLI;EFV:NLI;SFV:NSPM;SFS:(10019020)(6009001)(339900001)(199003)(189002)(229853001)(2201001)(104016003)(105606002)(77096005)(19580395003)(19580405001)(47776003)(575784001)(86362001)(106466001)(189998001)(87936001)(48376002)(107886002)(62966003)(77156002)(92566002)(50466002)(5001960100002)(5001770100001)(2950100001)(50226001)(6806004)(5890100001)(85426001)(46102003)(76176999)(36756003)(50986999)(4001430100001);DIR:OUT;SFP:1102;SCL:1;SRVR:BN1PR0301MB0642;H:tx30smr01.am.freescale.net;FPR:;SPF:Fail;MLV:sfv;A:1;MX:1;LANG:en; MIME-Version: 1.0 Content-Type: text/plain X-Microsoft-Exchange-Diagnostics: 1;BN1PR0301MB0642;2:NUWx6TJdzdGeUQz1AElT0ruN9VLcQWiAbGhb7gX8BHqGTob2XDulXG4lh2RRo+We;2:qKAXVxK3z7/zLGvS9FsE7pdG/rlcZECrN1bXpzOaWYxk7LjmKa2ZHT5vSjo8yuHHbcow03XlTQ4mq0xX1s8AvjOvcuM+eU18aNR8kzYxwY9rN3T9Y/sdFfJaajInqTec4YczjdSEHl6RaVXe4Rs+PsVqsC06qxNZ+F68flGelw3FC/Y4wI9BQVM+7/ZBXm9d98p55HzcoUE7r6gdJpbWja5FB6lrRl0fZvmZb1LKP6g=;6:eKH9/CpOSPSTTYGDFuPkTsibwLqpnU+Zlo23qXDUmXcFncAsTVuleJ+/vkipr7ephn7Jfn8EmP6lOXDal+xeA6m8kCrgsmaMqytJGwueL7pR1PMfOeQKELqodHyF94LD9/Ry6+cdIUT9TRSpB260lREw2X4tE5RmlZstoAUmEKqtKLNrghiXfSt8y38M+LUABXpvfUd6RmMKDCeQ1h+qPgslcp71d7LtdtAogqPIvf0pExg6EJ620E4sxH5h2VaKFSCSrlkP7G9TCGny5eBmSjASAjfbP7j+cBn0AiS2cgsU9yHPBnmtrt8RrLfpd7l0N3ELIwAmZLdSe2clNcqVeA== X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BN1PR0301MB0642; X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(601004)(5005006)(520003)(3002001);SRVR:BN1PR0301MB0642;BCL:0;PCL:0;RULEID:;SRVR:BN1PR0301MB0642; X-Microsoft-Exchange-Diagnostics: 1;BN1PR0301MB0642;3:3OZKoQ+WgO0DzRiRV7msXPNhNSWMBML4mWC+T9h7ujQ5Gww6ZgDnJJrWHwKHZa9GgXeWN/uaOoO5DVYq7e80wBKrgraoZvKp5rhCJeLdFlTq0ZmWgzxfqrSMtTdJ4SDX4Db0Owvy351SBjfIkWfiQd0pJ3/GbmWx3S92FY94N7ayRB14BnTUALaGqp/XtxNtboA87aGZgHaC9xtb+wjNiMt/D7gzXiH80LcqZlf3xGT67MlT+CuvI3EQSUOL3/JeSY4WU7gm98LWVu+v8DaGndf7F6pBVxa8E5aKFrNgBsPKAttzYCwzQ6rWLnvI5Cq0 X-Forefront-PRVS: 06022AA85F X-Microsoft-Exchange-Diagnostics: 1;BN1PR0301MB0642;9:jkru8chSyOZsxobDTw4u93fpqMdbqiF7S2OlqG4pA4+aER9ZqYEkyOb9QQwcZQCwK3G4+8zvHAAAIRnOUOabM2TRjml2hb9OJMnB3GHgzW3T1y0XNOdT3fEZ99EfCsjDRg0/UHQcdnycCGIvwNDzOM4cS6eC0RnRdU2ZwbCVguSnDmthBkVlm22AKMCdE3oV+gVU6rJrocjd9a1bUvNZfpsDKtZ2R8N5loc3EX5BdRLt1oaWnbu6Gi+jau6s4GE3FaGRHwOTWQlvCZ78d2ln5a2The+tnH8AX7VAd+uD++1UdItIwstAwx1w/vObs9B/nEz8sJjItKIym8YNVmH0WIOpOkJXX3q9pTd7pcs1m6LCJvShbtH+fTn5fACQZEe1iJ3i8AJnu6B7RxRGznTK2pJ+F4vexHtCHhriqi//V4tVTkKu6pQBOneE+25YgkYohXgy0TDti+LHi8tSgFeZx9bDGRqDTRbNugXnzzrPrlgz598Rr1Mvpsb+m7Inx3a/UVzH0bM4vYeWAmW1nuYfS3zfHgYK4++Zny1+g+K4++odLr1K4mhU036CQvsv3zU7cQlD8KIpgU+pLM3vre6Jq/4Wv79iZVZezjsDKivL0o79Rk4/3Ov3mD4HEcS9jJs+tIT4nyVuqkLThMEt5LRvwHtrVW91M8OjtjPopJhAfPZAHF6zHjyRcbcmmxLr7HM75yEFoDT2VE6ITciLSkMKXh/675DIJ1JxlggvJE11yZvtvF2OFLPmaCawUYURcNyZqmlJp1xIt1czI9fUOuBp2KR3c9zAqifC7jkuH1cAMi0QR2PU7n58n4fle1yNvD9xYwkyQxBJSJk9HbDdjrdb4B15iEJY8UEbhMSadrDe0jilNftU/LzcdI0eZW+gUl3dKazCgDk28RUR0zyiPzw+REno+wPjjN0ckLW420xYxcai849+9+YXn0F3B3Szin7y X-Microsoft-Exchange-Diagnostics: 1;BN1PR0301MB0642;3:eTishj/AEbtFk0wU9uuOP+1LwRVkMlUGGKU+85dNAxFF2BjXv9ObD0lXiSvNIFIAHzbPlQXBDuocKj6QqOZZ+uShajz9nUaYPfOvaVKPCfWET7LXaQMXnURMivUAu1jUe/ipZjvUe82iz5zNMSfPpA==;10:Snttal4wDHHdNqRPsIHvqoWnI5UaEYhvtXhP6pGL4a3aDzSIyP+r7M61Sqb03FBtqrlik6mOMAxlanR9cWQfllaQwDpsApmozTW5VOMA4xQ=;6:FEXC6uhzXsYVuZKPkf7z9eiyw3SgF1p6R0V5whOELDUl275RF7ocABA1oH30vFzU X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 09 Jun 2015 22:08:50.5257 (UTC) X-MS-Exchange-CrossTenant-Id: 710a03f5-10f6-4d38-9ff4-a80b81da590d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=710a03f5-10f6-4d38-9ff4-a80b81da590d;Ip=[192.88.168.50];Helo=[tx30smr01.am.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BN1PR0301MB0642 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 30936 Lines: 1067 All the IRQs for DPAA2 objects in the same DPRC must use the ICID of that DPRC, as their device Id in the GIC-ITS. Thus, all these IRQs must share the same ITT table in GIC. As a result, a pool of IRQs with the same device Id must be preallocated per DPRC (fsl-mc bus instance). So, the fsl-mc bus object allocator is extended to also provide services to allocate IRQs to DPAA2 devices, from their parent fsl-mc bus IRQ pool. Also, the interrupt handler for DPRC IRQs is added. DPRC IRQs are generated for hot plug events related to DPAA2 objects in a given DPRC. These events include, creating/destroying DPAA2 objects in the DPRC, changing the "plugged" state of DPAA2 objects and moving objects between DPRCs. Signed-off-by: J. German Rivera Reviewed-by: Stuart Yoder --- Changes in v4: - Addressed comments from Dan Carpenter and Greg Kroah-Hartman: * Removed all #ifdefed-out code - Fixed new checkpatch "check" warnings Changes in v3: - Addressed comments from Dan Carpenter: * Removed nested redundant #ifdef GIC_ITS_MC_SUPPORT Changes in v2: - Addressed comments from Dan Carpenter and Scott Wood: * Removed unnecessary lines from commit messages * Replaced single error cleanup label with multiple labels, one for each error case and return directly when no cleanup is needed. * Fixed conditional alignment * Removed error messages for devm_kzalloc() errors * Removed WARN_ON in dprc_irq0_handler_thread() * Removed devm_kfree() calls drivers/staging/fsl-mc/bus/dprc-driver.c | 353 ++++++++++++++++++++++++---- drivers/staging/fsl-mc/bus/mc-allocator.c | 102 +++++++- drivers/staging/fsl-mc/bus/mc-bus.c | 156 +++++++++++- drivers/staging/fsl-mc/include/mc-private.h | 26 +- drivers/staging/fsl-mc/include/mc.h | 29 ++- 5 files changed, 599 insertions(+), 67 deletions(-) diff --git a/drivers/staging/fsl-mc/bus/dprc-driver.c b/drivers/staging/fsl-mc/bus/dprc-driver.c index 35c06cf..5351170 100644 --- a/drivers/staging/fsl-mc/bus/dprc-driver.c +++ b/drivers/staging/fsl-mc/bus/dprc-driver.c @@ -13,6 +13,7 @@ #include "../include/mc-sys.h" #include #include +#include #include "dprc-cmd.h" struct dprc_child_objs { @@ -126,7 +127,7 @@ static void check_plugged_state_change(struct fsl_mc_device *mc_dev, struct dprc_obj_desc *obj_desc) { int error; - uint32_t plugged_flag_at_mc = + u32 plugged_flag_at_mc = (obj_desc->state & DPRC_OBJ_STATE_PLUGGED); if (plugged_flag_at_mc != @@ -206,41 +207,11 @@ static void dprc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev) } } -static void dprc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev, - enum fsl_mc_pool_type pool_type) -{ - struct fsl_mc_resource *resource; - struct fsl_mc_resource *next; - struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); - struct fsl_mc_resource_pool *res_pool = - &mc_bus->resource_pools[pool_type]; - int free_count = 0; - - WARN_ON(res_pool->type != pool_type); - WARN_ON(res_pool->free_count != res_pool->max_count); - - list_for_each_entry_safe(resource, next, &res_pool->free_list, node) { - free_count++; - WARN_ON(resource->type != res_pool->type); - WARN_ON(resource->parent_pool != res_pool); - devm_kfree(&mc_bus_dev->dev, resource); - } - - WARN_ON(free_count != res_pool->free_count); -} - -static void dprc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev) -{ - int pool_type; - - for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) - dprc_cleanup_resource_pool(mc_bus_dev, pool_type); -} - /** * dprc_scan_objects - Discover objects in a DPRC * * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object + * @total_irq_count: total number of IRQs needed by objects in the DPRC. * * Detects objects added and removed from a DPRC and synchronizes the * state of the Linux bus driver, MC by adding and removing @@ -254,11 +225,13 @@ static void dprc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev) * populated before they can get allocation requests from probe callbacks * of the device drivers for the non-allocatable devices. */ -int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev) +int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, + unsigned int *total_irq_count) { int num_child_objects; int dprc_get_obj_failures; int error; + unsigned int irq_count = mc_bus_dev->obj_desc.irq_count; struct dprc_obj_desc *child_obj_desc_array = NULL; error = dprc_get_obj_count(mc_bus_dev->mc_io, @@ -274,9 +247,9 @@ int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev) int i; child_obj_desc_array = - devm_kmalloc_array(&mc_bus_dev->dev, num_child_objects, - sizeof(*child_obj_desc_array), - GFP_KERNEL); + kmalloc_array(num_child_objects, + sizeof(*child_obj_desc_array), + GFP_KERNEL); if (!child_obj_desc_array) return -ENOMEM; @@ -305,6 +278,7 @@ int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev) continue; } + irq_count += obj_desc->irq_count; dev_dbg(&mc_bus_dev->dev, "Discovered object: type %s, id %d\n", obj_desc->type, obj_desc->id); @@ -317,15 +291,14 @@ int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev) } } + *total_irq_count = irq_count; dprc_remove_devices(mc_bus_dev, child_obj_desc_array, num_child_objects); dprc_add_new_devices(mc_bus_dev, child_obj_desc_array, num_child_objects); - if (child_obj_desc_array) - devm_kfree(&mc_bus_dev->dev, child_obj_desc_array); - + kfree(child_obj_desc_array); return 0; } EXPORT_SYMBOL_GPL(dprc_scan_objects); @@ -339,9 +312,10 @@ EXPORT_SYMBOL_GPL(dprc_scan_objects); * bus driver with the actual state of the MC by adding and removing * devices as appropriate. */ -int dprc_scan_container(struct fsl_mc_device *mc_bus_dev) +static int dprc_scan_container(struct fsl_mc_device *mc_bus_dev) { int error; + unsigned int irq_count; struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); dprc_init_all_resource_pools(mc_bus_dev); @@ -350,17 +324,280 @@ int dprc_scan_container(struct fsl_mc_device *mc_bus_dev) * Discover objects in the DPRC: */ mutex_lock(&mc_bus->scan_mutex); - error = dprc_scan_objects(mc_bus_dev); + error = dprc_scan_objects(mc_bus_dev, &irq_count); mutex_unlock(&mc_bus->scan_mutex); if (error < 0) - goto error; + return error; + + if (!mc_bus->irq_resources) { + irq_count += FSL_MC_IRQ_POOL_MAX_EXTRA_IRQS; + error = fsl_mc_populate_irq_pool(mc_bus, irq_count); + if (error < 0) + return error; + } + + return 0; +} + +/** + * dprc_irq0_handler - Regular ISR for DPRC interrupt 0 + * + * @irq: IRQ number of the interrupt being handled + * @arg: Pointer to device structure + */ +static irqreturn_t dprc_irq0_handler(int irq_num, void *arg) +{ + return IRQ_WAKE_THREAD; +} + +/** + * dprc_irq0_handler_thread - Handler thread function for DPRC interrupt 0 + * + * @irq: IRQ number of the interrupt being handled + * @arg: Pointer to device structure + */ +static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg) +{ + int error; + u32 status; + struct device *dev = (struct device *)arg; + struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); + struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); + struct fsl_mc_io *mc_io = mc_dev->mc_io; + int irq_index = 0; + + dev_dbg(dev, "DPRC IRQ %d\n", irq_num); + if (WARN_ON(!(mc_dev->flags & FSL_MC_IS_DPRC))) + return IRQ_HANDLED; + + mutex_lock(&mc_bus->scan_mutex); + if (WARN_ON(mc_dev->irqs[irq_index]->irq_number != (u32)irq_num)) + goto out; + + error = dprc_get_irq_status(mc_io, mc_dev->mc_handle, irq_index, + &status); + if (error < 0) { + dev_err(dev, + "dprc_get_irq_status() failed: %d\n", error); + goto out; + } + + error = dprc_clear_irq_status(mc_io, mc_dev->mc_handle, irq_index, + status); + if (error < 0) { + dev_err(dev, + "dprc_clear_irq_status() failed: %d\n", error); + goto out; + } + + if (status & (DPRC_IRQ_EVENT_OBJ_ADDED | + DPRC_IRQ_EVENT_OBJ_REMOVED | + DPRC_IRQ_EVENT_CONTAINER_DESTROYED | + DPRC_IRQ_EVENT_OBJ_DESTROYED | + DPRC_IRQ_EVENT_OBJ_CREATED)) { + unsigned int irq_count; + + error = dprc_scan_objects(mc_dev, &irq_count); + if (error < 0) { + dev_err(dev, "dprc_scan_objects() failed: %d\n", error); + goto out; + } + + if (irq_count > + mc_bus->resource_pools[FSL_MC_POOL_IRQ].max_count) { + dev_warn(dev, + "IRQs needed (%u) exceed IRQs preallocated (%u)\n", + irq_count, + mc_bus->resource_pools[FSL_MC_POOL_IRQ]. + max_count); + } + } + +out: + mutex_unlock(&mc_bus->scan_mutex); + return IRQ_HANDLED; +} + +/* + * Disable and clear interrupts for a given DPRC object + */ +static int disable_dprc_irqs(struct fsl_mc_device *mc_dev) +{ + int i; + int error; + struct fsl_mc_io *mc_io = mc_dev->mc_io; + int irq_count = mc_dev->obj_desc.irq_count; + + if (WARN_ON(irq_count == 0)) + return -EINVAL; + + for (i = 0; i < irq_count; i++) { + /* + * Disable generation of interrupt i, while we configure it: + */ + error = dprc_set_irq_enable(mc_io, mc_dev->mc_handle, i, 0); + if (error < 0) { + dev_err(&mc_dev->dev, + "dprc_set_irq_enable() failed: %d\n", error); + + return error; + } + + /* + * Disable all interrupt causes for interrupt i: + */ + error = dprc_set_irq_mask(mc_io, mc_dev->mc_handle, i, 0x0); + if (error < 0) { + dev_err(&mc_dev->dev, + "dprc_set_irq_mask() failed: %d\n", error); + + return error; + } + + /* + * Clear any leftover interrupt i: + */ + error = dprc_clear_irq_status(mc_io, mc_dev->mc_handle, i, + ~0x0U); + if (error < 0) { + dev_err(&mc_dev->dev, + "dprc_clear_irq_status() failed: %d\n", + error); + return error; + } + } + + return 0; +} + +static int register_dprc_irq_handlers(struct fsl_mc_device *mc_dev) +{ + static const struct irq_handler { + irq_handler_t irq_handler; + irq_handler_t irq_handler_thread; + const char *irq_name; + } irq_handlers[] = { + [0] = { + .irq_handler = dprc_irq0_handler, + .irq_handler_thread = dprc_irq0_handler_thread, + .irq_name = "FSL MC DPRC irq0", + }, + }; + + unsigned int i; + int error; + struct fsl_mc_device_irq *irq; + int irq_count = mc_dev->obj_desc.irq_count; + + if (WARN_ON(irq_count != ARRAY_SIZE(irq_handlers))) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(irq_handlers); i++) { + irq = mc_dev->irqs[i]; + error = devm_request_threaded_irq(&mc_dev->dev, + irq->irq_number, + irq_handlers[i].irq_handler, + irq_handlers[i]. + irq_handler_thread, + IRQF_NO_SUSPEND | + IRQF_ONESHOT, + irq_handlers[i].irq_name, + &mc_dev->dev); + if (error < 0) { + dev_err(&mc_dev->dev, + "devm_request_threaded_irq() failed: %d\n", + error); + return error; + } + + /* + * Program the MSI (paddr, value) pair in the device: + * + * TODO: This needs to be moved to mc_bus_msi_domain_write_msg() + * when the MC object-independent dprc_set_irq() flib API + * becomes available + */ + error = dprc_set_irq(mc_dev->mc_io, mc_dev->mc_handle, + i, irq->msi_paddr, + irq->msi_value, + irq->irq_number); + if (error < 0) { + dev_err(&mc_dev->dev, + "mc_set_irq() failed: %d\n", error); + return error; + } + } + + return 0; +} + +static int enable_dprc_irqs(struct fsl_mc_device *mc_dev) +{ + int i; + int error; + int irq_count = mc_dev->obj_desc.irq_count; + + for (i = 0; i < irq_count; i++) { + /* + * Enable all interrupt causes for the interrupt: + */ + error = dprc_set_irq_mask(mc_dev->mc_io, + mc_dev->mc_handle, + i, + ~0x0u); + if (error < 0) { + dev_err(&mc_dev->dev, + "dprc_set_irq_mask() failed: %d\n", error); + + return error; + } + + /* + * Enable generation of the interrupt: + */ + error = dprc_set_irq_enable(mc_dev->mc_io, + mc_dev->mc_handle, + i, 1); + if (error < 0) { + dev_err(&mc_dev->dev, + "dprc_set_irq_enable() failed: %d\n", error); + + return error; + } + } + + return 0; +} + +/* + * Setup interrupts for a given DPRC device + */ +static int dprc_setup_irqs(struct fsl_mc_device *mc_dev) +{ + int error; + + error = fsl_mc_allocate_irqs(mc_dev); + if (error < 0) + return error; + + error = disable_dprc_irqs(mc_dev); + if (error < 0) + goto error_free_irqs; + + error = register_dprc_irq_handlers(mc_dev); + if (error < 0) + goto error_free_irqs; + + error = enable_dprc_irqs(mc_dev); + if (error < 0) + goto error_free_irqs; return 0; -error: - dprc_cleanup_all_resource_pools(mc_bus_dev); + +error_free_irqs: + fsl_mc_free_irqs(mc_dev); return error; } -EXPORT_SYMBOL_GPL(dprc_scan_container); /** * dprc_probe - callback invoked when a DPRC is being bound to this driver @@ -415,10 +652,20 @@ static int dprc_probe(struct fsl_mc_device *mc_dev) if (error < 0) goto error_cleanup_open; + /* + * Configure interrupts for the DPRC object associated with this MC bus: + */ + error = dprc_setup_irqs(mc_dev); + if (error < 0) + goto error_cleanup_open; + dev_info(&mc_dev->dev, "DPRC device bound to driver"); return 0; error_cleanup_open: + if (mc_bus->irq_resources) + fsl_mc_cleanup_irq_pool(mc_bus); + (void)dprc_close(mc_dev->mc_io, mc_dev->mc_handle); error_cleanup_mc_io: @@ -426,6 +673,15 @@ error_cleanup_mc_io: return error; } +/* + * Tear down interrupts for a given DPRC object + */ +static void dprc_teardown_irqs(struct fsl_mc_device *mc_dev) +{ + (void)disable_dprc_irqs(mc_dev); + fsl_mc_free_irqs(mc_dev); +} + /** * dprc_remove - callback invoked when a DPRC is being unbound from this driver * @@ -439,18 +695,23 @@ error_cleanup_mc_io: static int dprc_remove(struct fsl_mc_device *mc_dev) { int error; + struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0)) return -EINVAL; if (WARN_ON(!mc_dev->mc_io)) return -EINVAL; + if (WARN_ON(!mc_bus->irq_resources)) + return -EINVAL; + + dprc_teardown_irqs(mc_dev); device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove); - dprc_cleanup_all_resource_pools(mc_dev); error = dprc_close(mc_dev->mc_io, mc_dev->mc_handle); if (error < 0) dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error); + fsl_mc_cleanup_irq_pool(mc_bus); dev_info(&mc_dev->dev, "DPRC device unbound from driver"); return 0; } diff --git a/drivers/staging/fsl-mc/bus/mc-allocator.c b/drivers/staging/fsl-mc/bus/mc-allocator.c index e36235d..aa8280a 100644 --- a/drivers/staging/fsl-mc/bus/mc-allocator.c +++ b/drivers/staging/fsl-mc/bus/mc-allocator.c @@ -66,8 +66,6 @@ static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus GFP_KERNEL); if (!resource) { error = -ENOMEM; - dev_err(&mc_bus_dev->dev, - "Failed to allocate memory for fsl_mc_resource\n"); goto out; } @@ -145,8 +143,6 @@ static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device INIT_LIST_HEAD(&resource->node); res_pool->free_count--; res_pool->max_count--; - - devm_kfree(&mc_bus_dev->dev, resource); mc_dev->resource = NULL; error = 0; out: @@ -160,6 +156,7 @@ static const char *const fsl_mc_pool_type_strings[] = { [FSL_MC_POOL_DPMCP] = "dpmcp", [FSL_MC_POOL_DPBP] = "dpbp", [FSL_MC_POOL_DPCON] = "dpcon", + [FSL_MC_POOL_IRQ] = "irq", }; static int __must_check object_type_to_pool_type(const char *object_type, @@ -473,6 +470,103 @@ void fsl_mc_object_free(struct fsl_mc_device *mc_adev) EXPORT_SYMBOL_GPL(fsl_mc_object_free); /** + * It allocates the IRQs required by a given MC object device. The + * IRQs are allocated from the interrupt pool associated with the + * MC bus that contains the device, if the device is not a DPRC device. + * Otherwise, the IRQs are allocated from the interrupt pool associated + * with the MC bus that represents the DPRC device itself. + */ +int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev) +{ + int i; + int irq_count; + int res_allocated_count = 0; + int error = -EINVAL; + struct fsl_mc_device_irq **irqs = NULL; + struct fsl_mc_bus *mc_bus; + struct fsl_mc_resource_pool *res_pool; + + if (WARN_ON(mc_dev->irqs)) + return -EINVAL; + + irq_count = mc_dev->obj_desc.irq_count; + if (WARN_ON(irq_count == 0)) + return -EINVAL; + + if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) + mc_bus = to_fsl_mc_bus(mc_dev); + else + mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); + + if (WARN_ON(!mc_bus->irq_resources)) + return -EINVAL; + + res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; + if (res_pool->free_count < irq_count) { + dev_err(&mc_dev->dev, + "Not able to allocate %u irqs for device\n", irq_count); + return -ENOSPC; + } + + irqs = devm_kzalloc(&mc_dev->dev, irq_count * sizeof(irqs[0]), + GFP_KERNEL); + if (!irqs) + return -ENOMEM; + + for (i = 0; i < irq_count; i++) { + struct fsl_mc_resource *resource; + + error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ, + &resource); + if (error < 0) + goto error_resource_alloc; + + irqs[i] = to_fsl_mc_irq(resource); + res_allocated_count++; + } + + mc_dev->irqs = irqs; + return 0; + +error_resource_alloc: + for (i = 0; i < res_allocated_count; i++) + fsl_mc_resource_free(&irqs[i]->resource); + + return error; +} +EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs); + +/* + * It frees the IRQs that were allocated for a MC object device, by + * returning them to the corresponding interrupt pool. + */ +void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev) +{ + int i; + int irq_count; + struct fsl_mc_bus *mc_bus; + + if (WARN_ON(!mc_dev->irqs)) + return; + + irq_count = mc_dev->obj_desc.irq_count; + + if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) + mc_bus = to_fsl_mc_bus(mc_dev); + else + mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); + + if (WARN_ON(!mc_bus->irq_resources)) + return; + + for (i = 0; i < irq_count; i++) + fsl_mc_resource_free(&mc_dev->irqs[i]->resource); + + mc_dev->irqs = NULL; +} +EXPORT_SYMBOL_GPL(fsl_mc_free_irqs); + +/** * fsl_mc_allocator_probe - callback invoked when an allocatable device is * being added to the system */ diff --git a/drivers/staging/fsl-mc/bus/mc-bus.c b/drivers/staging/fsl-mc/bus/mc-bus.c index 766a659..9d98828 100644 --- a/drivers/staging/fsl-mc/bus/mc-bus.c +++ b/drivers/staging/fsl-mc/bus/mc-bus.c @@ -15,11 +15,27 @@ #include #include #include +#include +#include +#include #include +#include #include "../include/dpmng.h" #include "../include/mc-sys.h" #include "dprc-cmd.h" +/* + * IOMMU stream ID flags + */ +#define STREAM_ID_PL_MASK BIT(9) /* privilege level */ +#define STREAM_ID_BMT_MASK BIT(8) /* bypass memory translation */ +#define STREAM_ID_VA_MASK BIT(7) /* virtual address translation + * (two-stage translation) */ +#define STREAM_ID_ICID_MASK (BIT(7) - 1) /* isolation context ID + * (translation context) */ + +#define MAX_STREAM_ID_ICID STREAM_ID_ICID_MASK + static struct kmem_cache *mc_dev_cache; /** @@ -295,8 +311,9 @@ static int fsl_mc_device_get_mmio_regions(struct fsl_mc_device *mc_dev, ®ions[i].start); if (error < 0) { dev_err(parent_dev, - "Invalid MC address: %#llx\n", - region_desc.base_paddr); + "Invalid MC address: %#llx (for %s.%d\'s region %d)\n", + region_desc.base_paddr, + obj_desc->type, obj_desc->id, i); goto error_cleanup_regions; } @@ -335,7 +352,7 @@ int fsl_mc_device_add(struct dprc_obj_desc *obj_desc, /* * Allocate an MC bus device object: */ - mc_bus = devm_kzalloc(parent_dev, sizeof(*mc_bus), GFP_KERNEL); + mc_bus = kzalloc(sizeof(*mc_bus), GFP_KERNEL); if (!mc_bus) return -ENOMEM; @@ -437,10 +454,10 @@ int fsl_mc_device_add(struct dprc_obj_desc *obj_desc, error_cleanup_dev: kfree(mc_dev->regions); - if (mc_bus) - devm_kfree(parent_dev, mc_bus); - else + if (!mc_bus) kmem_cache_free(mc_dev_cache, mc_dev); + else + kfree(mc_bus); return error; } @@ -475,13 +492,120 @@ void fsl_mc_device_remove(struct fsl_mc_device *mc_dev) fsl_mc_bus_type.dev_root = NULL; } - if (mc_bus) - devm_kfree(mc_dev->dev.parent, mc_bus); - else + if (!mc_bus) kmem_cache_free(mc_dev_cache, mc_dev); + else + kfree(mc_bus); } EXPORT_SYMBOL_GPL(fsl_mc_device_remove); +static int create_mc_irq_domain(struct platform_device *mc_pdev, + struct irq_domain **new_irq_domain) +{ + int error; + struct device_node *its_of_node; + struct irq_domain *its_domain; + struct device_node *mc_of_node = mc_pdev->dev.of_node; + + its_of_node = of_parse_phandle(mc_of_node, "msi-parent", 0); + if (!its_of_node) { + dev_err(&mc_pdev->dev, + "msi-parent phandle missing for %s\n", + mc_of_node->full_name); + return -ENOENT; + } + + /* + * Extract MSI parent node: + */ + its_domain = irq_find_host(its_of_node); + if (!its_domain) { + dev_err(&mc_pdev->dev, "Unable to find parent domain\n"); + error = -ENOENT; + goto cleanup_its_of_node; + } + + /* + * TODO: Call msi_create_irq_domain() to create IRQ domain for MC MSIs + * when complete GIC-ITS support * for generic MSIs is upstream. On + * success, set mc->gic_supported to true. + */ + *new_irq_domain = NULL; + dev_err(&mc_pdev->dev, "Cannot create MSI domain\n"); + error = -ENOTSUPP; + +cleanup_its_of_node: + of_node_put(its_of_node); + return error; +} + +/* + * Initialize the interrupt pool associated with a MC bus. + * It allocates a block of IRQs from the GIC-ITS + */ +int __must_check fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus, + unsigned int irq_count) +{ + unsigned int i; + struct fsl_mc_device_irq *irq_resources; + struct fsl_mc_device_irq *irq_res; + struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; + struct fsl_mc_resource_pool *res_pool = + &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; + + if (WARN_ON(irq_count == 0 || + irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS)) + return -EINVAL; + + irq_resources = + devm_kzalloc(&mc_bus_dev->dev, + sizeof(*irq_resources) * irq_count, + GFP_KERNEL); + if (!irq_resources) + return -ENOMEM; + + for (i = 0; i < irq_count; i++) { + irq_res = &irq_resources[i]; + /* + * TODO: set irq_res->msi_paddr and irq_res->irq_number with + * values obtained from the GIC-ITS. + * Also, set irq_res->resource.id to irq_number. + */ + irq_res->resource.type = res_pool->type; + irq_res->resource.data = irq_res; + irq_res->resource.parent_pool = res_pool; + INIT_LIST_HEAD(&irq_res->resource.node); + list_add_tail(&irq_res->resource.node, &res_pool->free_list); + } + + res_pool->max_count = irq_count; + res_pool->free_count = irq_count; + mc_bus->irq_resources = irq_resources; + return 0; +} +EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool); + +/** + * Teardown the interrupt pool associated with an MC bus. + * It frees the IRQs that were allocated to the pool, back to the GIC-ITS. + */ +void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus) +{ + struct fsl_mc_resource_pool *res_pool = + &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; + + if (WARN_ON(res_pool->max_count == 0)) + return; + + if (WARN_ON(res_pool->free_count != res_pool->max_count)) + return; + + res_pool->max_count = 0; + res_pool->free_count = 0; + mc_bus->irq_resources = NULL; +} +EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool); + static int parse_mc_ranges(struct device *dev, int *paddr_cells, int *mc_addr_cells, @@ -600,7 +724,7 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) struct fsl_mc_io *mc_io = NULL; int container_id; phys_addr_t mc_portal_phys_addr; - uint32_t mc_portal_size; + u32 mc_portal_size; struct mc_version mc_version; struct resource res; @@ -611,6 +735,9 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, mc); + error = create_mc_irq_domain(pdev, &mc->irq_domain); + if (error < 0) + return error; /* * Get physical address of MC portal for the root DPRC: @@ -620,7 +747,7 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) dev_err(&pdev->dev, "of_address_to_resource() failed for %s\n", pdev->dev.of_node->full_name); - return error; + goto error_cleanup_irq_domain; } mc_portal_phys_addr = res.start; @@ -628,7 +755,7 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) error = fsl_create_mc_io(&pdev->dev, mc_portal_phys_addr, mc_portal_size, NULL, 0, &mc_io); if (error < 0) - return error; + goto error_cleanup_irq_domain; error = mc_get_version(mc_io, &mc_version); if (error != 0) { @@ -673,6 +800,7 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) obj_desc.id = container_id; obj_desc.ver_major = DPRC_VER_MAJOR; obj_desc.ver_minor = DPRC_VER_MINOR; + obj_desc.irq_count = 1; obj_desc.region_count = 0; error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, &mc_bus_dev); @@ -684,6 +812,9 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) error_cleanup_mc_io: fsl_destroy_mc_io(mc_io); + +error_cleanup_irq_domain: + irq_domain_remove(mc->irq_domain); return error; } @@ -698,6 +829,7 @@ static int fsl_mc_bus_remove(struct platform_device *pdev) if (WARN_ON(&mc->root_mc_bus_dev->dev != fsl_mc_bus_type.dev_root)) return -EINVAL; + irq_domain_remove(mc->irq_domain); fsl_mc_device_remove(mc->root_mc_bus_dev); dev_info(&pdev->dev, "Root MC bus device removed"); return 0; diff --git a/drivers/staging/fsl-mc/include/mc-private.h b/drivers/staging/fsl-mc/include/mc-private.h index c045f49..6e33942 100644 --- a/drivers/staging/fsl-mc/include/mc-private.h +++ b/drivers/staging/fsl-mc/include/mc-private.h @@ -27,12 +27,26 @@ strcmp(_obj_type, "dpcon") == 0) /** + * Maximum number of total IRQs that can be pre-allocated for an MC bus' + * IRQ pool + */ +#define FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS 256 + +/** + * Maximum number of extra IRQs pre-reallocated for an MC bus' IRQ pool, + * to be used by dynamically created MC objects + */ +#define FSL_MC_IRQ_POOL_MAX_EXTRA_IRQS 64 + +/** * struct fsl_mc - Private data of a "fsl,qoriq-mc" platform device * @root_mc_bus_dev: MC object device representing the root DPRC + * @irq_domain: IRQ domain for the fsl-mc bus type * @addr_translation_ranges: array of bus to system address translation ranges */ struct fsl_mc { struct fsl_mc_device *root_mc_bus_dev; + struct irq_domain *irq_domain; uint8_t num_translation_ranges; struct fsl_mc_addr_translation_range *translation_ranges; }; @@ -76,11 +90,13 @@ struct fsl_mc_resource_pool { * @resource_pools: array of resource pools (one pool per resource type) * for this MC bus. These resources represent allocatable entities * from the physical DPRC. + * @irq_resources: Pointer to array of IRQ objects for the IRQ pool. * @scan_mutex: Serializes bus scanning */ struct fsl_mc_bus { struct fsl_mc_device mc_dev; struct fsl_mc_resource_pool resource_pools[FSL_MC_NUM_POOL_TYPES]; + struct fsl_mc_device_irq *irq_resources; struct mutex scan_mutex; /* serializes bus scanning */ }; @@ -94,9 +110,8 @@ int __must_check fsl_mc_device_add(struct dprc_obj_desc *obj_desc, void fsl_mc_device_remove(struct fsl_mc_device *mc_dev); -int dprc_scan_container(struct fsl_mc_device *mc_bus_dev); - -int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev); +int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, + unsigned int *total_irq_count); int __init dprc_driver_init(void); @@ -113,4 +128,9 @@ int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus, void fsl_mc_resource_free(struct fsl_mc_resource *resource); +int __must_check fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus, + unsigned int irq_count); + +void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus); + #endif /* _FSL_MC_PRIVATE_H_ */ diff --git a/drivers/staging/fsl-mc/include/mc.h b/drivers/staging/fsl-mc/include/mc.h index fa02ef0..c543a84 100644 --- a/drivers/staging/fsl-mc/include/mc.h +++ b/drivers/staging/fsl-mc/include/mc.h @@ -14,6 +14,7 @@ #include #include #include +#include #include "../include/dprc.h" #define FSL_MC_VENDOR_FREESCALE 0x1957 @@ -61,8 +62,8 @@ struct fsl_mc_driver { struct fsl_mc_device_match_id { uint16_t vendor; const char obj_type[16]; - uint32_t ver_major; - uint32_t ver_minor; + u32 ver_major; + u32 ver_minor; }; /** @@ -75,6 +76,7 @@ enum fsl_mc_pool_type { FSL_MC_POOL_DPMCP = 0x0, /* corresponds to "dpmcp" in the MC */ FSL_MC_POOL_DPBP, /* corresponds to "dpbp" in the MC */ FSL_MC_POOL_DPCON, /* corresponds to "dpcon" in the MC */ + FSL_MC_POOL_IRQ, /* * NOTE: New resource pool types must be added before this entry @@ -104,6 +106,23 @@ struct fsl_mc_resource { }; /** + * struct fsl_mc_device_irq - MC object device message-based interrupt + * @msi_paddr: message-based interrupt physical address + * @msi_value: message-based interrupt data value + * @irq_number: Linux IRQ number assigned to the interrupt + * @resource: MC generic resource associated with the interrupt + */ +struct fsl_mc_device_irq { + phys_addr_t msi_paddr; + u32 msi_value; + u32 irq_number; + struct fsl_mc_resource resource; +}; + +#define to_fsl_mc_irq(_mc_resource) \ + container_of(_mc_resource, struct fsl_mc_device_irq, resource) + +/** * Bit masks for a MC object device (struct fsl_mc_device) flags */ #define FSL_MC_IS_DPRC 0x0001 @@ -124,6 +143,7 @@ struct fsl_mc_resource { * NULL if none. * @obj_desc: MC description of the DPAA device * @regions: pointer to array of MMIO region entries + * @irqs: pointer to array of pointers to interrupts allocated to this device * @resource: generic resource associated with this MC object device, if any. * * Generic device object for MC object devices that are "attached" to a @@ -155,6 +175,7 @@ struct fsl_mc_device { struct fsl_mc_io *mc_io; struct dprc_obj_desc obj_desc; struct resource *regions; + struct fsl_mc_device_irq **irqs; struct fsl_mc_resource *resource; }; @@ -196,6 +217,10 @@ int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev, void fsl_mc_object_free(struct fsl_mc_device *mc_adev); +int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev); + +void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev); + extern struct bus_type fsl_mc_bus_type; #endif /* _FSL_MC_H_ */ -- 2.3.3 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/