From patchwork Thu Oct 8 07:48:38 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Duc Dang X-Patchwork-Id: 7349871 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id D17BCBEEA4 for ; Thu, 8 Oct 2015 07:52:27 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 9BA9A20763 for ; Thu, 8 Oct 2015 07:52:26 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 08BDE2076B for ; Thu, 8 Oct 2015 07:52:25 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Zk5xb-0000Zc-EZ; Thu, 08 Oct 2015 07:49:55 +0000 Received: from mail-pa0-x22a.google.com ([2607:f8b0:400e:c03::22a]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Zk5xX-0000JC-Tt for linux-arm-kernel@lists.infradead.org; Thu, 08 Oct 2015 07:49:53 +0000 Received: by pacex6 with SMTP id ex6so47411786pac.0 for ; Thu, 08 Oct 2015 00:49:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=apm.com; s=apm; h=from:to:cc:subject:date:message-id; bh=sBGl5dKndVxv1kkKVaIJ2CekMrElTZNQf2wNglesOsc=; b=FjGGYb4p7JsfCqtCYM1qFUImH72bBn6KMae3kJOz83GfU3VWD1sgqEh1DRCvKdle4k DoncdIb8yTm+E9V8ZUQsDwH7Y2faA0KwUI11ihR580QEt8/Ak12JaWxi9KGlbl6PduUI CSLodL9RUmVli6XZiG3HRU/gpM3T07Ho6LFnQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=sBGl5dKndVxv1kkKVaIJ2CekMrElTZNQf2wNglesOsc=; b=kIKt5tIRdj9kKQ5AHe9pzZsoUi7BhRQFW84TDxavVdq7/TTh4Vrtonv5n0iFtfn4yH Bjos35n/g6m7FIBYM4vKj2lImNMG1gNAfSut9G0Z+ucUlZiF/eQp2SoVbH4hZnUEJR0b 65aCsdFtHUidujp5WRA0pCnnYzF4G6NoYVGeWHSFKlVr45Eo6A3Yj4t0BYO06nhMNs04 cVOJAu80/81KwaDWbW7Y7zOnb55QUpTZBwunC0ine2AAt3JeqxKu4cEmN7u1bRtppMZe xU72bnbt+wKzYG+aBMb48aM7dJgJs+Kk5hbHNOkuf11qcemEIBy0bX4IR5eL6dBRdKhU jxMw== X-Gm-Message-State: ALoCoQm2kIR9Rx8MF0d6XfR+iCmM7n31Y6yQW0QDhIfrcfO2aeA/YYyRNLuwl+NaFP6dA64LJEtH X-Received: by 10.67.12.166 with SMTP id er6mr6467531pad.40.1444290569744; Thu, 08 Oct 2015 00:49:29 -0700 (PDT) Received: from dhdang-Precision-WorkStation-T3400.amcc.com (70-35-53-82.static.wiline.com. [70.35.53.82]) by smtp.gmail.com with ESMTPSA id mk5sm43973048pab.44.2015.10.08.00.49.28 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 08 Oct 2015 00:49:29 -0700 (PDT) From: Duc Dang To: Marc Zyngier , Suravee Suthikulpanit , Jason Cooper , Thomas Gleixner Subject: [PATCH RFC 1/1] irqchip/GICv2m: Add support for multiple v2m frames Date: Thu, 8 Oct 2015 00:48:38 -0700 Message-Id: <1444290518-18527-1-git-send-email-dhdang@apm.com> X-Mailer: git-send-email 1.9.1 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20151008_004952_060449_D0006048 X-CRM114-Status: GOOD ( 23.60 ) X-Spam-Score: -2.7 (--) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Duc Dang , patches@apm.com, linux-kernel@vger.kernel.org, linux-arm MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED, T_DKIM_INVALID, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP GICv2m driver currently only supports single v2m frame. This patch extend this driver to support multiple v2m frames. All of the v2m frames will be own by a single MSI domain. Each PCIe node can specify msi-parent as the first frame of the v2m frame list to be able to use all available v2m frames for MSI interrupts. This patch should be applied on top of patch (irqchip/GICv2m: Add workaround for APM X-Gene GICv2m erratum): https://lkml.org/lkml/2015/10/6/922 Signed-off-by: Duc Dang --- drivers/irqchip/irq-gic-v2m.c | 221 ++++++++++++++++++++++++++++++------------ 1 file changed, 159 insertions(+), 62 deletions(-) diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index 4c17c18..8ecaf9e 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -51,13 +51,19 @@ #define GICV2M_NEEDS_SPI_OFFSET 0x00000001 struct v2m_data { - spinlock_t msi_cnt_lock; struct resource res; /* GICv2m resource */ void __iomem *base; /* GICv2m virt address */ u32 spi_start; /* The SPI number that MSIs start */ u32 nr_spis; /* The number of SPIs for MSIs */ - unsigned long *bm; /* MSI vector bitmap */ u32 flags; /* v2m flags for specific implementation */ + struct list_head list; /* Link to other v2m frames */ +}; + +struct gicv2m_ctrlr { + spinlock_t v2m_ctrlr_lock; /* Lock for all v2m frames */ + u32 nr_vecs; /* Total MSI vectors */ + unsigned long *bm; /* MSI vector bitmap */ + struct list_head v2m_frms; /* List of v2m frames */ }; static void gicv2m_mask_msi_irq(struct irq_data *d) @@ -98,11 +104,29 @@ static int gicv2m_set_affinity(struct irq_data *irq_data, return ret; } +static struct v2m_data *irq_data_to_v2m_frm(struct irq_data *data, + struct gicv2m_ctrlr *v2m_ctrlr) +{ + struct v2m_data *v2m; + + list_for_each_entry(v2m, &v2m_ctrlr->v2m_frms, list) { + if ((data->hwirq >= v2m->spi_start) && + (data->hwirq < (v2m->spi_start + v2m->nr_spis))) + return v2m; + } + + return NULL; +} + static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) { - struct v2m_data *v2m = irq_data_get_irq_chip_data(data); - phys_addr_t addr = v2m->res.start + V2M_MSI_SETSPI_NS; + struct gicv2m_ctrlr *v2m_ctrlr = irq_data_get_irq_chip_data(data); + struct v2m_data *v2m; + phys_addr_t addr; + v2m = irq_data_to_v2m_frm(data, v2m_ctrlr); + WARN_ON(!v2m); + addr = v2m->res.start + V2M_MSI_SETSPI_NS; msg->address_hi = (u32) (addr >> 32); msg->address_lo = (u32) (addr); msg->data = data->hwirq; @@ -144,48 +168,85 @@ static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain, return 0; } -static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq) +static int hwirq_to_bm_pos(struct gicv2m_ctrlr *v2m_ctrlr, unsigned int hwirq) +{ + struct v2m_data *v2m; + int pos = 0; + + list_for_each_entry(v2m, &v2m_ctrlr->v2m_frms, list) { + if ((hwirq >= v2m->spi_start) && + (hwirq < (v2m->spi_start + v2m->nr_spis))) { + pos += hwirq - v2m->spi_start; + break; + } + pos += v2m->nr_spis; + } + + return pos; +} + +static void gicv2m_unalloc_msi(struct gicv2m_ctrlr *v2m_ctrlr, + unsigned int hwirq) { int pos; - pos = hwirq - v2m->spi_start; - if (pos < 0 || pos >= v2m->nr_spis) { + pos = hwirq_to_bm_pos(v2m_ctrlr, hwirq); + if (pos >= v2m_ctrlr->nr_vecs) { pr_err("Failed to teardown msi. Invalid hwirq %d\n", hwirq); return; } - spin_lock(&v2m->msi_cnt_lock); - __clear_bit(pos, v2m->bm); - spin_unlock(&v2m->msi_cnt_lock); + spin_lock(&v2m_ctrlr->v2m_ctrlr_lock); + __clear_bit(pos, v2m_ctrlr->bm); + spin_unlock(&v2m_ctrlr->v2m_ctrlr_lock); +} + +static int bm_pos_to_hwirq(struct gicv2m_ctrlr *v2m_ctrlr, int pos) +{ + struct v2m_data *v2m; + int hwirq = -1; + int offset = 0; + + list_for_each_entry(v2m, &v2m_ctrlr->v2m_frms, list) { + if ((pos >= offset) && (pos < (offset + v2m->nr_spis))) { + hwirq = v2m->spi_start + pos - offset; + break; + } + offset += v2m->nr_spis; + } + + return hwirq; } static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *args) { - struct v2m_data *v2m = domain->host_data; - int hwirq, offset, err = 0; + struct gicv2m_ctrlr *v2m_ctrlr = domain->host_data; + int hwirq, pos, err = 0; - spin_lock(&v2m->msi_cnt_lock); - offset = find_first_zero_bit(v2m->bm, v2m->nr_spis); - if (offset < v2m->nr_spis) - __set_bit(offset, v2m->bm); + spin_lock(&v2m_ctrlr->v2m_ctrlr_lock); + pos = find_first_zero_bit(v2m_ctrlr->bm, v2m_ctrlr->nr_vecs); + if (pos < v2m_ctrlr->nr_vecs) + __set_bit(pos, v2m_ctrlr->bm); else err = -ENOSPC; - spin_unlock(&v2m->msi_cnt_lock); + spin_unlock(&v2m_ctrlr->v2m_ctrlr_lock); if (err) return err; - hwirq = v2m->spi_start + offset; + hwirq = bm_pos_to_hwirq(v2m_ctrlr, pos); + if (WARN_ON(hwirq < 0)) + return -EINVAL; err = gicv2m_irq_gic_domain_alloc(domain, virq, hwirq); if (err) { - gicv2m_unalloc_msi(v2m, hwirq); + gicv2m_unalloc_msi(v2m_ctrlr, hwirq); return err; } irq_domain_set_hwirq_and_chip(domain, virq, hwirq, - &gicv2m_irq_chip, v2m); + &gicv2m_irq_chip, v2m_ctrlr); return 0; } @@ -194,10 +255,10 @@ static void gicv2m_irq_domain_free(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs) { struct irq_data *d = irq_domain_get_irq_data(domain, virq); - struct v2m_data *v2m = irq_data_get_irq_chip_data(d); + struct gicv2m_ctrlr *v2m_ctrlr = irq_data_get_irq_chip_data(d); BUG_ON(nr_irqs != 1); - gicv2m_unalloc_msi(v2m, d->hwirq); + gicv2m_unalloc_msi(v2m_ctrlr, d->hwirq); irq_domain_free_irqs_parent(domain, virq, nr_irqs); } @@ -236,11 +297,10 @@ static struct msi_domain_info gicv2m_pmsi_domain_info = { }; static int __init gicv2m_init_one(struct device_node *node, - struct irq_domain *parent) + struct gicv2m_ctrlr *gicv2m_ctrlr) { int ret; struct v2m_data *v2m; - struct irq_domain *inner_domain, *pci_domain, *plat_domain; v2m = kzalloc(sizeof(struct v2m_data), GFP_KERNEL); if (!v2m) { @@ -248,6 +308,8 @@ static int __init gicv2m_init_one(struct device_node *node, return -ENOMEM; } + INIT_LIST_HEAD(&v2m->list); + ret = of_address_to_resource(node, 0, &v2m->res); if (ret) { pr_err("Failed to allocate v2m resource.\n"); @@ -288,34 +350,10 @@ static int __init gicv2m_init_one(struct device_node *node, if (readl_relaxed(v2m->base + V2M_MSI_IIDR) == XGENE_GICV2M_MSI_IIDR) v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; - v2m->bm = kzalloc(sizeof(long) * BITS_TO_LONGS(v2m->nr_spis), - GFP_KERNEL); - if (!v2m->bm) { - ret = -ENOMEM; - goto err_iounmap; - } - - inner_domain = irq_domain_add_tree(node, &gicv2m_domain_ops, v2m); - if (!inner_domain) { - pr_err("Failed to create GICv2m domain\n"); - ret = -ENOMEM; - goto err_free_bm; - } - - inner_domain->bus_token = DOMAIN_BUS_NEXUS; - inner_domain->parent = parent; - pci_domain = pci_msi_create_irq_domain(node, &gicv2m_msi_domain_info, - inner_domain); - plat_domain = platform_msi_create_irq_domain(node, - &gicv2m_pmsi_domain_info, - inner_domain); - if (!pci_domain || !plat_domain) { - pr_err("Failed to create MSI domains\n"); - ret = -ENOMEM; - goto err_free_domains; - } - - spin_lock_init(&v2m->msi_cnt_lock); + /* Add v2m frame into GICv2m controller's frame list */ + list_add_tail(&v2m->list, &gicv2m_ctrlr->v2m_frms); + /* Update total MSI vectors */ + gicv2m_ctrlr->nr_vecs += v2m->nr_spis; pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name, (unsigned long)v2m->res.start, (unsigned long)v2m->res.end, @@ -323,19 +361,11 @@ static int __init gicv2m_init_one(struct device_node *node, return 0; -err_free_domains: - if (plat_domain) - irq_domain_remove(plat_domain); - if (pci_domain) - irq_domain_remove(pci_domain); - if (inner_domain) - irq_domain_remove(inner_domain); -err_free_bm: - kfree(v2m->bm); err_iounmap: iounmap(v2m->base); err_free_v2m: kfree(v2m); + return ret; } @@ -348,18 +378,85 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent) { int ret = 0; struct device_node *child; + struct irq_domain *inner_domain, *pci_domain, *plat_domain; + struct gicv2m_ctrlr *gicv2m_ctrlr; + struct device_node *first_v2m_node = NULL; + + gicv2m_ctrlr = kzalloc(sizeof(struct gicv2m_ctrlr), GFP_KERNEL); + if (!gicv2m_ctrlr) { + pr_err("Failed to allocate struct gicv2m_ctrlr.\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&gicv2m_ctrlr->v2m_frms); for (child = of_find_matching_node(node, gicv2m_device_id); child; child = of_find_matching_node(child, gicv2m_device_id)) { if (!of_find_property(child, "msi-controller", NULL)) continue; - ret = gicv2m_init_one(child, parent); + ret = gicv2m_init_one(child, gicv2m_ctrlr); if (ret) { of_node_put(node); break; } + if (!first_v2m_node) + first_v2m_node = child; + } + + /* Return if no v2m frame is found */ + if (!first_v2m_node) + goto err_free_ctrlr; + + /* Allocate bitmap for all MSI vectors on all frames */ + gicv2m_ctrlr->bm = kzalloc(sizeof(long) * + BITS_TO_LONGS(gicv2m_ctrlr->nr_vecs), + GFP_KERNEL); + if (!gicv2m_ctrlr->bm) { + ret = -ENOMEM; + goto err_free_ctrlr; + } + + inner_domain = irq_domain_add_tree(first_v2m_node, &gicv2m_domain_ops, + gicv2m_ctrlr); + if (!inner_domain) { + pr_err("Failed to create GICv2m domain\n"); + ret = -ENOMEM; + goto err_free_bm; } + inner_domain->bus_token = DOMAIN_BUS_NEXUS; + inner_domain->parent = parent; + pci_domain = pci_msi_create_irq_domain(first_v2m_node, + &gicv2m_msi_domain_info, + inner_domain); + plat_domain = platform_msi_create_irq_domain(first_v2m_node, + &gicv2m_pmsi_domain_info, + inner_domain); + if (!pci_domain || !plat_domain) { + pr_err("Failed to create MSI domains\n"); + ret = -ENOMEM; + goto err_free_domains; + } + + spin_lock_init(&gicv2m_ctrlr->v2m_ctrlr_lock); + + pr_info("MSI IRQ domain created with %d vectors\n", + gicv2m_ctrlr->nr_vecs); + + return 0; + +err_free_domains: + if (plat_domain) + irq_domain_remove(plat_domain); + if (pci_domain) + irq_domain_remove(pci_domain); + if (inner_domain) + irq_domain_remove(inner_domain); +err_free_bm: + kfree(gicv2m_ctrlr->bm); +err_free_ctrlr: + kfree(gicv2m_ctrlr); + return ret; }