From patchwork Mon Aug 21 19:29:06 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Biesheuvel X-Patchwork-Id: 9913559 X-Patchwork-Delegate: bhelgaas@google.com Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id D2CFB600C8 for ; Mon, 21 Aug 2017 19:29:43 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C3314287D7 for ; Mon, 21 Aug 2017 19:29:43 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B79C0287E3; Mon, 21 Aug 2017 19:29:43 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EC913287D7 for ; Mon, 21 Aug 2017 19:29:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754158AbdHUT3m (ORCPT ); Mon, 21 Aug 2017 15:29:42 -0400 Received: from mail-wr0-f169.google.com ([209.85.128.169]:38764 "EHLO mail-wr0-f169.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754030AbdHUT3l (ORCPT ); Mon, 21 Aug 2017 15:29:41 -0400 Received: by mail-wr0-f169.google.com with SMTP id p8so43218070wrf.5 for ; Mon, 21 Aug 2017 12:29:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=7iUbOF6yE+wDwvirqinrfdDw03Ehwtax2qPHIe1YSyU=; b=IkZtpkXJl3sJ0i2WQVzPOQdv/7SOhLFyAs1xMt4umBgJNu5zhg9otoIqxFPyL8v3nB EohO5Z4Crja9BQzFRovCPu6O3thclnJWlDgQDUC/DVgOJctWSaY0BEl1KvrVTLuGKfbY C/KmJGG68hcFTT079GwQv66NtuIIn0klMDahE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=7iUbOF6yE+wDwvirqinrfdDw03Ehwtax2qPHIe1YSyU=; b=aHOyvlsLs8FG8UgXxOvhw1FT9zfKrIG9ovSIRF/v4XZyhbk/u/iES7RMb+Qef+LxRI WCVjsTSzmKDt63cYL+i9HIOMai1cDEVivNx87d+O+YnJrqWxghjdsaRsHm69VUXIn9MA /666kFJE9mPO7e9KH76ttzhIDY6aWN0XkeHX5CBqU4KreqfK41+zK0n9AQya7sZivvXE Dbo/X8Cp1D++xL/AvtloXgsWtCOB64iYx7c6o97YGGK/f7eQb15eHHzEITii5oCuHuY/ v0wNFRPb2xCzjI91lCOQHNf7z/r3To7eCIsQUG6SnL6lGc5gaSfVtK2gAQFsVsPt2RII SxkQ== X-Gm-Message-State: AHYfb5iRaFiFBjxMRkBvLSu7ONB/y43B4mcEHo5foARs+rKsTbuvK05/ KPtjcfs87ipdPShs0HZMFA== X-Received: by 10.28.26.5 with SMTP id a5mr7686241wma.80.1503343779618; Mon, 21 Aug 2017 12:29:39 -0700 (PDT) Received: from localhost.localdomain ([154.146.161.128]) by smtp.gmail.com with ESMTPSA id 2sm8998556wrp.63.2017.08.21.12.29.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 21 Aug 2017 12:29:38 -0700 (PDT) From: Ard Biesheuvel To: linux-pci@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, Ard Biesheuvel , Leif Lindholm , Graeme Gregory , Bjorn Helgaas , Jingoo Han , Joao Pinto , Marc Zyngier Subject: [PATCH 2/3] pci: designware: add separate driver for the MSI part of the RC Date: Mon, 21 Aug 2017 20:29:06 +0100 Message-Id: <20170821192907.8695-3-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170821192907.8695-1-ard.biesheuvel@linaro.org> References: <20170821192907.8695-1-ard.biesheuvel@linaro.org> Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Most drivers that currently exist for the Synopsys Designware PCIe controller in RC mode hardcode the relation with the embedded MSI controller. This makes it more difficult than necessary to use a generic driver to drive the RC, which is especially unfortunate in cases where the firmware already configures the RC to the extent that it can be driven by the generic ECAM driver. It also makes it impossible to use an existing driver but use another IP block for MSI support, i.e., a GICv2m or GICv3-ITS. So add a separate driver for the MSI part, which can be referenced from the DT node describing the RC via its msi-parent property. Signed-off-by: Ard Biesheuvel --- drivers/pci/dwc/Makefile | 3 +- drivers/pci/dwc/pcie-designware-msi.c | 255 ++++++++++++++++++++ 2 files changed, 257 insertions(+), 1 deletion(-) diff --git a/drivers/pci/dwc/Makefile b/drivers/pci/dwc/Makefile index 7d5a23e5b767..5891211b04be 100644 --- a/drivers/pci/dwc/Makefile +++ b/drivers/pci/dwc/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o -obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o +msi-obj-$(CONFIG_PCI_MSI) += pcie-designware-msi.o +obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o $(msi-obj-y) obj-$(CONFIG_PCIE_DW_HOST_ECAM) += pcie-designware-ecam.o obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o diff --git a/drivers/pci/dwc/pcie-designware-msi.c b/drivers/pci/dwc/pcie-designware-msi.c new file mode 100644 index 000000000000..c8c454145c0d --- /dev/null +++ b/drivers/pci/dwc/pcie-designware-msi.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2017 Linaro Limited + * + * Based on code posted for the tango platform by + * Marc Gonzalez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +struct dw_pcie_msi { + void __iomem *regbase; + int irq; + struct irq_domain *irqd; + struct irq_domain *msid; + DECLARE_BITMAP( used_msi, MAX_MSI_IRQS); + spinlock_t used_msi_lock; + u32 doorbell; +}; + +static void dw_pcie_msi_isr(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct dw_pcie_msi *dw_msi = irq_desc_get_handler_data(desc); + unsigned long status, base, virq, idx, pos; + + chained_irq_enter(chip, desc); + spin_lock(&dw_msi->used_msi_lock); + + for (pos = 0; pos < MAX_MSI_IRQS; + pos = find_next_bit(dw_msi->used_msi, MAX_MSI_IRQS, pos)) { + base = round_down(pos, 32); + status = readl_relaxed(dw_msi->regbase + PCIE_MSI_INTR0_STATUS + + (base / 32) * 12); + for_each_set_bit(idx, &status, 32) { + virq = irq_find_mapping(dw_msi->irqd, base + idx); + generic_handle_irq(virq); + } + pos = base + 32; + } + + spin_unlock(&dw_msi->used_msi_lock); + chained_irq_exit(chip, desc); +} + +static void dw_pcie_ack(struct irq_data *d) +{ + struct dw_pcie_msi *dw_msi = d->chip_data; + u32 offset = (d->hwirq / 32) * 12; + u32 bit = BIT(d->hwirq % 32); + + writel_relaxed(bit, dw_msi->regbase + PCIE_MSI_INTR0_STATUS + offset); +} + +static void dw_pcie_update_msi_enable(struct irq_data *d, bool unmask) +{ + unsigned long flags; + struct dw_pcie_msi *dw_msi = d->chip_data; + u32 offset = (d->hwirq / 32) * 12; + u32 bit = BIT(d->hwirq % 32); + u32 val; + + spin_lock_irqsave(&dw_msi->used_msi_lock, flags); + val = readl_relaxed(dw_msi->regbase + PCIE_MSI_INTR0_ENABLE + offset); + val = unmask ? (val | bit) : (val & ~bit); + writel_relaxed(val, dw_msi->regbase + PCIE_MSI_INTR0_ENABLE + offset); + spin_unlock_irqrestore(&dw_msi->used_msi_lock, flags); +} + +static void dw_pcie_mask(struct irq_data *d) +{ + dw_pcie_update_msi_enable(d, false); +} + +static void dw_pcie_unmask(struct irq_data *d) +{ + dw_pcie_update_msi_enable(d, true); +} + +static int dw_pcie_set_affinity(struct irq_data *d, + const struct cpumask *mask, bool force) +{ + return -EINVAL; +} + +static void dw_pcie_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) +{ + struct dw_pcie_msi *dw_msi = d->chip_data; + + msg->address_lo = lower_32_bits(virt_to_phys(&dw_msi->doorbell)); + msg->address_hi = upper_32_bits(virt_to_phys(&dw_msi->doorbell)); + msg->data = d->hwirq; +} + +static struct irq_chip dw_pcie_chip = { + .irq_ack = dw_pcie_ack, + .irq_mask = dw_pcie_mask, + .irq_unmask = dw_pcie_unmask, + .irq_set_affinity = dw_pcie_set_affinity, + .irq_compose_msi_msg = dw_pcie_compose_msi_msg, +}; + +static void dw_pcie_msi_ack(struct irq_data *d) +{ + irq_chip_ack_parent(d); +} + +static void dw_pcie_msi_mask(struct irq_data *d) +{ + pci_msi_mask_irq(d); + irq_chip_mask_parent(d); +} + +static void dw_pcie_msi_unmask(struct irq_data *d) +{ + pci_msi_unmask_irq(d); + irq_chip_unmask_parent(d); +} + +static struct irq_chip dw_pcie_msi_chip = { + .name = "DW-MSI", + .irq_ack = dw_pcie_msi_ack, + .irq_mask = dw_pcie_msi_mask, + .irq_unmask = dw_pcie_msi_unmask, +}; + +static struct msi_domain_info dw_pcie_msi_dom_info = { + .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS, + .chip = &dw_pcie_msi_chip, +}; + +static int dw_pcie_msi_irq_domain_alloc(struct irq_domain *dom, + unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct dw_pcie_msi *dw_msi = dom->host_data; + unsigned long flags; + int pos; + + spin_lock_irqsave(&dw_msi->used_msi_lock, flags); + pos = find_first_zero_bit(dw_msi->used_msi, MAX_MSI_IRQS); + if (pos >= MAX_MSI_IRQS) { + spin_unlock_irqrestore(&dw_msi->used_msi_lock, flags); + return -ENOSPC; + } + __set_bit(pos, dw_msi->used_msi); + spin_unlock_irqrestore(&dw_msi->used_msi_lock, flags); + irq_domain_set_info(dom, virq, pos, &dw_pcie_chip, dw_msi, + handle_edge_irq, NULL, NULL); + + return 0; +} + +static void dw_pcie_msi_irq_domain_free(struct irq_domain *dom, + unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *d = irq_domain_get_irq_data(dom, virq); + struct dw_pcie_msi *dw_msi = d->chip_data; + unsigned long flags; + + spin_lock_irqsave(&dw_msi->used_msi_lock, flags); + __clear_bit(d->hwirq, dw_msi->used_msi); + spin_unlock_irqrestore(&dw_msi->used_msi_lock, flags); +} + +static const struct irq_domain_ops irq_dom_ops = { + .alloc = dw_pcie_msi_irq_domain_alloc, + .free = dw_pcie_msi_irq_domain_free, +}; + +static int dw_pcie_msi_probe(struct platform_device *pdev) +{ + struct fwnode_handle *fwnode = of_node_to_fwnode(pdev->dev.of_node); + struct device *dev = &pdev->dev; + struct dw_pcie_msi *dw_msi; + struct resource *res; + + dw_msi = devm_kzalloc(dev, sizeof(*dw_msi), GFP_KERNEL); + if (!dw_msi) + return -ENOMEM; + + /* get the control register and map it */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dw_msi->regbase = devm_ioremap_resource(dev, res); + if (IS_ERR(dw_msi->regbase)) + return PTR_ERR(dw_msi->regbase); + + /* get the wired interrupt that gets raised when we receive an MSI */ + dw_msi->irq = platform_get_irq(pdev, 0); + if (dw_msi->irq <= 0) { + pr_err("Failed to map IRQ\n"); + return -ENXIO; + } + + dw_msi->irqd = irq_domain_create_linear(fwnode, MAX_MSI_IRQS, + &irq_dom_ops, dw_msi); + if (!dw_msi->irqd) { + dev_err(dev, "Failed to create IRQ domain\n"); + return -ENOMEM; + } + + dw_msi->msid = pci_msi_create_irq_domain(fwnode, &dw_pcie_msi_dom_info, + dw_msi->irqd); + if (!dw_msi->msid) { + dev_err(dev, "Failed to create MSI domain\n"); + irq_domain_remove(dw_msi->irqd); + return -ENOMEM; + } + + irq_set_chained_handler_and_data(dw_msi->irq, dw_pcie_msi_isr, dw_msi); + platform_set_drvdata(pdev, dw_msi); + + /* program the msi_data */ + writel_relaxed(lower_32_bits(virt_to_phys(&dw_msi->doorbell)), + dw_msi->regbase + PCIE_MSI_ADDR_LO); + writel_relaxed(upper_32_bits(virt_to_phys(&dw_msi->doorbell)), + dw_msi->regbase + PCIE_MSI_ADDR_HI); + + return 0; +} + +static int dw_pcie_msi_remove(struct platform_device *pdev) +{ + struct dw_pcie_msi *dw_msi = platform_get_drvdata(pdev); + + irq_set_chained_handler_and_data(dw_msi->irq, NULL, NULL); + irq_domain_remove(dw_msi->msid); + irq_domain_remove(dw_msi->irqd); + + return 0; +} + +static const struct of_device_id dw_pcie_dw_msi_of_match[] = { + { .compatible = "snps,dw-pcie-msi" }, + { }, +}; + +static struct platform_driver pci_dw_msi_driver = { + .driver.name = "pcie-designware-msi", + .driver.of_match_table = dw_pcie_dw_msi_of_match, + .probe = dw_pcie_msi_probe, + .remove = dw_pcie_msi_remove, +}; +builtin_platform_driver(pci_dw_msi_driver);