From 275220ca6e694c3fc1702487a14432dc414df58f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 26 Apr 2018 22:07:46 +0200 Subject: [PATCH 01/36] genirq/irq_sim: Remove the license boilerplate There is the SPDX license identifier now in the irq simulator. Remove the license boilerplate. While at it: update the copyright notice, since I did some changes in 2018. Signed-off-by: Bartosz Golaszewski Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20180426200747.8344-1-brgl@bgdev.pl --- kernel/irq/irq_sim.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/kernel/irq/irq_sim.c b/kernel/irq/irq_sim.c index fc4f361a86bb..dd20d0d528d4 100644 --- a/kernel/irq/irq_sim.c +++ b/kernel/irq/irq_sim.c @@ -1,11 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2017 Bartosz Golaszewski - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * Copyright (C) 2017-2018 Bartosz Golaszewski */ #include From b5c5f3959bb3c018a68f5659bcd6adf05e141a03 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 26 Apr 2018 22:07:47 +0200 Subject: [PATCH 02/36] genirq/irq_sim: Use the SPDX license identifier in the header Use C-style comment for the identifier as per Documentation/process/license-rules.rst and remove the license boilerplate. Signed-off-by: Bartosz Golaszewski Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20180426200747.8344-2-brgl@bgdev.pl --- include/linux/irq_sim.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/include/linux/irq_sim.h b/include/linux/irq_sim.h index 0380d899b955..630a57e55db6 100644 --- a/include/linux/irq_sim.h +++ b/include/linux/irq_sim.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2017-2018 Bartosz Golaszewski + */ + #ifndef _LINUX_IRQ_SIM_H #define _LINUX_IRQ_SIM_H -/* - * Copyright (C) 2017 Bartosz Golaszewski - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ #include #include From 0be8153cbc2af9a96e9ab8631fc3ba23bb52dbe3 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 8 May 2018 13:14:30 +0100 Subject: [PATCH 03/36] genirq/msi: Allow level-triggered MSIs to be exposed by MSI providers So far, MSIs have been used to signal edge-triggered interrupts, as a write is a good model for an edge (you can't "unwrite" something). On the other hand, routing zillions of wires in an SoC because you need level interrupts is a bit extreme. People have come up with a variety of schemes to support this, which involves sending two messages: one to signal the interrupt, and one to clear it. Since the kernel cannot represent this, we've ended up with side-band mechanisms that are pretty awful. Instead, let's acknoledge the requirement, and ensure that, under the right circumstances, the irq_compose_msg and irq_write_msg can take as a parameter an array of two messages instead of a pointer to a single one. We also add some checking that the compose method only clobbers the second message if the MSI domain has been created with the MSI_FLAG_LEVEL_CAPABLE flags. Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Cc: Rob Herring Cc: Jason Cooper Cc: Ard Biesheuvel Cc: Srinivas Kandagatla Cc: Thomas Petazzoni Cc: Miquel Raynal Link: https://lkml.kernel.org/r/20180508121438.11301-2-marc.zyngier@arm.com --- include/linux/msi.h | 2 ++ kernel/irq/msi.c | 33 ++++++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/include/linux/msi.h b/include/linux/msi.h index 1f1bbb5b4679..5839d8062dfc 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -289,6 +289,8 @@ enum { * MSI_FLAG_ACTIVATE_EARLY has been set. */ MSI_FLAG_MUST_REACTIVATE = (1 << 5), + /* Is level-triggered capable, using two messages */ + MSI_FLAG_LEVEL_CAPABLE = (1 << 6), }; int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask, diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 2a8571f72b17..4ca2fd46645d 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -76,6 +76,19 @@ static inline void irq_chip_write_msi_msg(struct irq_data *data, data->chip->irq_write_msi_msg(data, msg); } +static void msi_check_level(struct irq_domain *domain, struct msi_msg *msg) +{ + struct msi_domain_info *info = domain->host_data; + + /* + * If the MSI provider has messed with the second message and + * not advertized that it is level-capable, signal the breakage. + */ + WARN_ON(!((info->flags & MSI_FLAG_LEVEL_CAPABLE) && + (info->chip->flags & IRQCHIP_SUPPORTS_LEVEL_MSI)) && + (msg[1].address_lo || msg[1].address_hi || msg[1].data)); +} + /** * msi_domain_set_affinity - Generic affinity setter function for MSI domains * @irq_data: The irq data associated to the interrupt @@ -89,13 +102,14 @@ int msi_domain_set_affinity(struct irq_data *irq_data, const struct cpumask *mask, bool force) { struct irq_data *parent = irq_data->parent_data; - struct msi_msg msg; + struct msi_msg msg[2] = { [1] = { }, }; int ret; ret = parent->chip->irq_set_affinity(parent, mask, force); if (ret >= 0 && ret != IRQ_SET_MASK_OK_DONE) { - BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg)); - irq_chip_write_msi_msg(irq_data, &msg); + BUG_ON(irq_chip_compose_msi_msg(irq_data, msg)); + msi_check_level(irq_data->domain, msg); + irq_chip_write_msi_msg(irq_data, msg); } return ret; @@ -104,20 +118,21 @@ int msi_domain_set_affinity(struct irq_data *irq_data, static int msi_domain_activate(struct irq_domain *domain, struct irq_data *irq_data, bool early) { - struct msi_msg msg; + struct msi_msg msg[2] = { [1] = { }, }; - BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg)); - irq_chip_write_msi_msg(irq_data, &msg); + BUG_ON(irq_chip_compose_msi_msg(irq_data, msg)); + msi_check_level(irq_data->domain, msg); + irq_chip_write_msi_msg(irq_data, msg); return 0; } static void msi_domain_deactivate(struct irq_domain *domain, struct irq_data *irq_data) { - struct msi_msg msg; + struct msi_msg msg[2]; - memset(&msg, 0, sizeof(msg)); - irq_chip_write_msi_msg(irq_data, &msg); + memset(msg, 0, sizeof(msg)); + irq_chip_write_msi_msg(irq_data, msg); } static int msi_domain_alloc(struct irq_domain *domain, unsigned int virq, From 6988e0e0d28328467e218f59589b2770675a9ebd Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 8 May 2018 13:14:31 +0100 Subject: [PATCH 04/36] genirq/msi: Limit level-triggered MSI to platform devices Nobody would be insane enough to try and use level triggered MSIs on PCI, but let's make sure it doesn't happen. Also, let's mandate that the irqchip backing the platform MSI domain is providing the IRQCHIP_SUPPORTS_LEVEL_MSI flag. Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Cc: Rob Herring Cc: Jason Cooper Cc: Ard Biesheuvel Cc: Srinivas Kandagatla Cc: Thomas Petazzoni Cc: Miquel Raynal Link: https://lkml.kernel.org/r/20180508121438.11301-3-marc.zyngier@arm.com --- drivers/base/platform-msi.c | 3 +++ drivers/bus/fsl-mc/fsl-mc-msi.c | 2 ++ drivers/pci/msi.c | 3 +++ include/linux/irq.h | 1 + 4 files changed, 9 insertions(+) diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c index 8e22073aeeed..60d6cc618f1c 100644 --- a/drivers/base/platform-msi.c +++ b/drivers/base/platform-msi.c @@ -101,6 +101,9 @@ static void platform_msi_update_chip_ops(struct msi_domain_info *info) chip->irq_set_affinity = msi_domain_set_affinity; if (!chip->irq_write_msi_msg) chip->irq_write_msi_msg = platform_msi_write_msg; + if (WARN_ON((info->flags & MSI_FLAG_LEVEL_CAPABLE) && + !(chip->flags & IRQCHIP_SUPPORTS_LEVEL_MSI))) + info->flags &= ~MSI_FLAG_LEVEL_CAPABLE; } static void platform_msi_free_descs(struct device *dev, int base, int nvec) diff --git a/drivers/bus/fsl-mc/fsl-mc-msi.c b/drivers/bus/fsl-mc/fsl-mc-msi.c index ec35e255b496..8b9c66d7c4ff 100644 --- a/drivers/bus/fsl-mc/fsl-mc-msi.c +++ b/drivers/bus/fsl-mc/fsl-mc-msi.c @@ -163,6 +163,8 @@ struct irq_domain *fsl_mc_msi_create_irq_domain(struct fwnode_handle *fwnode, { struct irq_domain *domain; + if (WARN_ON((info->flags & MSI_FLAG_LEVEL_CAPABLE))) + info->flags &= ~MSI_FLAG_LEVEL_CAPABLE; if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) fsl_mc_msi_update_dom_ops(info); if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 30250631efe7..f45b74fcc059 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -1434,6 +1434,9 @@ struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode, { struct irq_domain *domain; + if (WARN_ON(info->flags & MSI_FLAG_LEVEL_CAPABLE)) + info->flags &= ~MSI_FLAG_LEVEL_CAPABLE; + if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) pci_msi_domain_update_dom_ops(info); if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) diff --git a/include/linux/irq.h b/include/linux/irq.h index 65916a305f3d..b2067083aa94 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -512,6 +512,7 @@ enum { IRQCHIP_SKIP_SET_WAKE = (1 << 4), IRQCHIP_ONESHOT_SAFE = (1 << 5), IRQCHIP_EOI_THREADED = (1 << 6), + IRQCHIP_SUPPORTS_LEVEL_MSI = (1 << 7), }; #include From 25eaaabb51c9925dc65f5b54fd9a362bf118e70a Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 8 May 2018 13:14:32 +0100 Subject: [PATCH 05/36] irqchip/mvebu-gicp: Use level-triggered MSIs between ICU and GICP The ICU and GICP drivers are using an ugly side-band mechanism to find out about the "clear" doorbell when using level interrupts. Let's convert it to level-triggered MSIs, which result in a nice cleanup. Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Tested-by: Miquel Raynal Cc: Rob Herring Cc: Jason Cooper Cc: Ard Biesheuvel Cc: Srinivas Kandagatla Cc: Thomas Petazzoni Link: https://lkml.kernel.org/r/20180508121438.11301-4-marc.zyngier@arm.com --- drivers/irqchip/irq-mvebu-gicp.c | 36 +++++++++----------------------- drivers/irqchip/irq-mvebu-gicp.h | 12 ----------- drivers/irqchip/irq-mvebu-icu.c | 33 +++++++++++++++-------------- 3 files changed, 27 insertions(+), 54 deletions(-) delete mode 100644 drivers/irqchip/irq-mvebu-gicp.h diff --git a/drivers/irqchip/irq-mvebu-gicp.c b/drivers/irqchip/irq-mvebu-gicp.c index 17a4a7b6cdbb..4e17f7081efc 100644 --- a/drivers/irqchip/irq-mvebu-gicp.c +++ b/drivers/irqchip/irq-mvebu-gicp.c @@ -19,8 +19,6 @@ #include -#include "irq-mvebu-gicp.h" - #define GICP_SETSPI_NSR_OFFSET 0x0 #define GICP_CLRSPI_NSR_OFFSET 0x8 @@ -55,34 +53,18 @@ static int gicp_idx_to_spi(struct mvebu_gicp *gicp, int idx) return -EINVAL; } -int mvebu_gicp_get_doorbells(struct device_node *dn, phys_addr_t *setspi, - phys_addr_t *clrspi) -{ - struct platform_device *pdev; - struct mvebu_gicp *gicp; - - pdev = of_find_device_by_node(dn); - if (!pdev) - return -ENODEV; - - gicp = platform_get_drvdata(pdev); - if (!gicp) - return -ENODEV; - - *setspi = gicp->res->start + GICP_SETSPI_NSR_OFFSET; - *clrspi = gicp->res->start + GICP_CLRSPI_NSR_OFFSET; - - return 0; -} - static void gicp_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) { struct mvebu_gicp *gicp = data->chip_data; phys_addr_t setspi = gicp->res->start + GICP_SETSPI_NSR_OFFSET; + phys_addr_t clrspi = gicp->res->start + GICP_CLRSPI_NSR_OFFSET; - msg->data = data->hwirq; - msg->address_lo = lower_32_bits(setspi); - msg->address_hi = upper_32_bits(setspi); + msg[0].data = data->hwirq; + msg[0].address_lo = lower_32_bits(setspi); + msg[0].address_hi = upper_32_bits(setspi); + msg[1].data = data->hwirq; + msg[1].address_lo = lower_32_bits(clrspi); + msg[1].address_hi = upper_32_bits(clrspi); } static struct irq_chip gicp_irq_chip = { @@ -170,13 +152,15 @@ static const struct irq_domain_ops gicp_domain_ops = { static struct irq_chip gicp_msi_irq_chip = { .name = "GICP", .irq_set_type = irq_chip_set_type_parent, + .flags = IRQCHIP_SUPPORTS_LEVEL_MSI, }; static struct msi_domain_ops gicp_msi_ops = { }; static struct msi_domain_info gicp_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_LEVEL_CAPABLE), .ops = &gicp_msi_ops, .chip = &gicp_msi_irq_chip, }; diff --git a/drivers/irqchip/irq-mvebu-gicp.h b/drivers/irqchip/irq-mvebu-gicp.h deleted file mode 100644 index eaa12fb72102..000000000000 --- a/drivers/irqchip/irq-mvebu-gicp.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __MVEBU_GICP_H__ -#define __MVEBU_GICP_H__ - -#include - -struct device_node; - -int mvebu_gicp_get_doorbells(struct device_node *dn, phys_addr_t *setspi, - phys_addr_t *clrspi); - -#endif /* __MVEBU_GICP_H__ */ diff --git a/drivers/irqchip/irq-mvebu-icu.c b/drivers/irqchip/irq-mvebu-icu.c index e18c48d3a92e..13063339b416 100644 --- a/drivers/irqchip/irq-mvebu-icu.c +++ b/drivers/irqchip/irq-mvebu-icu.c @@ -21,8 +21,6 @@ #include -#include "irq-mvebu-gicp.h" - /* ICU registers */ #define ICU_SETSPI_NSR_AL 0x10 #define ICU_SETSPI_NSR_AH 0x14 @@ -43,6 +41,7 @@ struct mvebu_icu { void __iomem *base; struct irq_domain *domain; struct device *dev; + atomic_t initialized; }; struct mvebu_icu_irq_data { @@ -51,6 +50,18 @@ struct mvebu_icu_irq_data { unsigned int type; }; +static void mvebu_icu_init(struct mvebu_icu *icu, struct msi_msg *msg) +{ + if (atomic_cmpxchg(&icu->initialized, false, true)) + return; + + /* Set Clear/Set ICU SPI message address in AP */ + writel_relaxed(msg[0].address_hi, icu->base + ICU_SETSPI_NSR_AH); + writel_relaxed(msg[0].address_lo, icu->base + ICU_SETSPI_NSR_AL); + writel_relaxed(msg[1].address_hi, icu->base + ICU_CLRSPI_NSR_AH); + writel_relaxed(msg[1].address_lo, icu->base + ICU_CLRSPI_NSR_AL); +} + static void mvebu_icu_write_msg(struct msi_desc *desc, struct msi_msg *msg) { struct irq_data *d = irq_get_irq_data(desc->irq); @@ -59,6 +70,8 @@ static void mvebu_icu_write_msg(struct msi_desc *desc, struct msi_msg *msg) unsigned int icu_int; if (msg->address_lo || msg->address_hi) { + /* One off initialization */ + mvebu_icu_init(icu, msg); /* Configure the ICU with irq number & type */ icu_int = msg->data | ICU_INT_ENABLE; if (icu_irqd->type & IRQ_TYPE_EDGE_RISING) @@ -197,9 +210,7 @@ static int mvebu_icu_probe(struct platform_device *pdev) struct device_node *node = pdev->dev.of_node; struct device_node *gicp_dn; struct resource *res; - phys_addr_t setspi, clrspi; - u32 i, icu_int; - int ret; + int i; icu = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_icu), GFP_KERNEL); @@ -242,22 +253,12 @@ static int mvebu_icu_probe(struct platform_device *pdev) if (!gicp_dn) return -ENODEV; - ret = mvebu_gicp_get_doorbells(gicp_dn, &setspi, &clrspi); - if (ret) - return ret; - - /* Set Clear/Set ICU SPI message address in AP */ - writel_relaxed(upper_32_bits(setspi), icu->base + ICU_SETSPI_NSR_AH); - writel_relaxed(lower_32_bits(setspi), icu->base + ICU_SETSPI_NSR_AL); - writel_relaxed(upper_32_bits(clrspi), icu->base + ICU_CLRSPI_NSR_AH); - writel_relaxed(lower_32_bits(clrspi), icu->base + ICU_CLRSPI_NSR_AL); - /* * Clean all ICU interrupts with type SPI_NSR, required to * avoid unpredictable SPI assignments done by firmware. */ for (i = 0 ; i < ICU_MAX_IRQS ; i++) { - icu_int = readl(icu->base + ICU_INT_CFG(i)); + u32 icu_int = readl_relaxed(icu->base + ICU_INT_CFG(i)); if ((icu_int >> ICU_GROUP_SHIFT) == ICU_GRP_NSR) writel_relaxed(0x0, icu->base + ICU_INT_CFG(i)); } From 8a22a3e1e768c309b718f99bd86f9f25a453e0dc Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 8 May 2018 13:14:33 +0100 Subject: [PATCH 06/36] dma-iommu: Fix compilation when !CONFIG_IOMMU_DMA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inclusion of include/dma-iommu.h when CONFIG_IOMMU_DMA is not selected results in the following splat: In file included from drivers/irqchip/irq-gic-v3-mbi.c:20:0: ./include/linux/dma-iommu.h:95:69: error: unknown type name ‘dma_addr_t’ static inline int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base) ^~~~~~~~~~ ./include/linux/dma-iommu.h:108:74: warning: ‘struct list_head’ declared inside parameter list will not be visible outside of this definition or declaration static inline void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list) ^~~~~~~~~ scripts/Makefile.build:312: recipe for target 'drivers/irqchip/irq-gic-v3-mbi.o' failed Fix it by including linux/types.h. Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Cc: Rob Herring Cc: Jason Cooper Cc: Ard Biesheuvel Cc: Srinivas Kandagatla Cc: Thomas Petazzoni Cc: Miquel Raynal Link: https://lkml.kernel.org/r/20180508121438.11301-5-marc.zyngier@arm.com --- include/linux/dma-iommu.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h index 92f20832fd28..e8ca5e654277 100644 --- a/include/linux/dma-iommu.h +++ b/include/linux/dma-iommu.h @@ -17,6 +17,7 @@ #define __DMA_IOMMU_H #ifdef __KERNEL__ +#include #include #ifdef CONFIG_IOMMU_DMA From 6461934371bfc1bfe6b424b197a546b4effd0a32 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 8 May 2018 13:14:34 +0100 Subject: [PATCH 07/36] irqdomain: Let irq_find_host default to DOMAIN_BUS_WIRED At the beginning of times, irq_find_host() was simple. Each device node implemented at most one irq domain, and we were happy. Over time, things have become more complex, and we now have nodes implementing a plurality of domains, tagged by "bus_token". Crutially, users of irq_find_host() all expect the most basic domain to be returned, and not any other domain such as a bus-specific MSI domain. So let's change irq_find_host() to first look for a DOMAIN_BUS_WIRED domain, and only if this fails fallback to DOMAIN_BUS_ANY. Note that this is consistent with what irq_create_fwspec_mapping is already doing, see 530cbe100ef7 ("irqdomain: Allow domain lookup with DOMAIN_BUS_WIRED token"). Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Cc: Rob Herring Cc: Jason Cooper Cc: Ard Biesheuvel Cc: Srinivas Kandagatla Cc: Thomas Petazzoni Cc: Miquel Raynal Link: https://lkml.kernel.org/r/20180508121438.11301-6-marc.zyngier@arm.com --- include/linux/irqdomain.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 48c7e86bb556..dccfa65aee96 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -301,7 +301,13 @@ static inline struct irq_domain *irq_find_matching_host(struct device_node *node static inline struct irq_domain *irq_find_host(struct device_node *node) { - return irq_find_matching_host(node, DOMAIN_BUS_ANY); + struct irq_domain *d; + + d = irq_find_matching_host(node, DOMAIN_BUS_WIRED); + if (!d) + d = irq_find_matching_host(node, DOMAIN_BUS_ANY); + + return d; } /** From b2425b51bed7437c08c11ce71bb0f308f4516451 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 8 May 2018 13:14:35 +0100 Subject: [PATCH 08/36] irqchip/gic-v3: Mark the base irq domain as DOMAIN_BUS_WIRED As we're about to introduce MSI domains based on top of the GICv3 domain, we must make sure nothing the new domains do not alias with the core domain. So let's tag that core domain with the DOMAIN_BUS_WIRED attribute, ensuring it gets picked up by other drivers that use irq_find_host(). Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Cc: Rob Herring Cc: Jason Cooper Cc: Ard Biesheuvel Cc: Srinivas Kandagatla Cc: Thomas Petazzoni Cc: Miquel Raynal Link: https://lkml.kernel.org/r/20180508121438.11301-7-marc.zyngier@arm.com --- drivers/irqchip/irq-gic-v3.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index e5d101418390..94164d7b87a6 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -1099,6 +1099,7 @@ static int __init gic_init_bases(void __iomem *dist_base, gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops, &gic_data); + irq_domain_update_bus_token(gic_data.domain, DOMAIN_BUS_WIRED); gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist)); gic_data.rdists.has_vlpis = true; gic_data.rdists.has_direct_lpi = true; From 505287525c24d5c78b662fd73721ad9900b91fcc Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 8 May 2018 13:14:36 +0100 Subject: [PATCH 09/36] irqchip/gic-v3: Add support for Message Based Interrupts as an MSI controller GICv3 offers the possibility to signal SPIs using a pair of doorbells (SETPI, CLRSPI) under the name of Message Based Interrupts (MBI). They can be used as either traditional (edge) MSIs, or the more exotic level-triggered flavour. Let's implement support for platform MSI, which is the original intent for this feature. Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Cc: Rob Herring Cc: Jason Cooper Cc: Ard Biesheuvel Cc: Srinivas Kandagatla Cc: Thomas Petazzoni Cc: Miquel Raynal Link: https://lkml.kernel.org/r/20180508121438.11301-8-marc.zyngier@arm.com --- drivers/irqchip/Makefile | 2 +- drivers/irqchip/irq-gic-v3-mbi.c | 275 +++++++++++++++++++++++++++++ drivers/irqchip/irq-gic-v3.c | 6 + include/linux/irqchip/arm-gic-v3.h | 1 + 4 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 drivers/irqchip/irq-gic-v3-mbi.c diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 5ed465ab1c76..15f268f646bf 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -27,7 +27,7 @@ obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o obj-$(CONFIG_ARM_GIC_PM) += irq-gic-pm.o obj-$(CONFIG_ARCH_REALVIEW) += irq-gic-realview.o obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o -obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o +obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-v3-mbi.o irq-gic-common.o obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v3-its-platform-msi.o irq-gic-v4.o obj-$(CONFIG_ARM_GIC_V3_ITS_PCI) += irq-gic-v3-its-pci-msi.o obj-$(CONFIG_ARM_GIC_V3_ITS_FSL_MC) += irq-gic-v3-its-fsl-mc-msi.o diff --git a/drivers/irqchip/irq-gic-v3-mbi.c b/drivers/irqchip/irq-gic-v3-mbi.c new file mode 100644 index 000000000000..2b3b767050aa --- /dev/null +++ b/drivers/irqchip/irq-gic-v3-mbi.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 ARM Limited, All Rights Reserved. + * Author: Marc Zyngier + */ + +#define pr_fmt(fmt) "GICv3: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct mbi_range { + u32 spi_start; + u32 nr_spis; + unsigned long *bm; +}; + +static struct mutex mbi_lock; +static phys_addr_t mbi_phys_base; +static struct mbi_range *mbi_ranges; +static unsigned int mbi_range_nr; + +static struct irq_chip mbi_irq_chip = { + .name = "MBI", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, +}; + +static int mbi_irq_gic_domain_alloc(struct irq_domain *domain, + unsigned int virq, + irq_hw_number_t hwirq) +{ + struct irq_fwspec fwspec; + struct irq_data *d; + int err; + + /* + * Using ACPI? There is no MBI support in the spec, you + * shouldn't even be here. + */ + if (!is_of_node(domain->parent->fwnode)) + return -EINVAL; + + /* + * Let's default to edge. This is consistent with traditional + * MSIs, and systems requiring level signaling will just + * enforce the trigger on their own. + */ + fwspec.fwnode = domain->parent->fwnode; + fwspec.param_count = 3; + fwspec.param[0] = 0; + fwspec.param[1] = hwirq - 32; + fwspec.param[2] = IRQ_TYPE_EDGE_RISING; + + err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); + if (err) + return err; + + d = irq_domain_get_irq_data(domain->parent, virq); + return d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING); +} + +static void mbi_free_msi(struct mbi_range *mbi, unsigned int hwirq, + int nr_irqs) +{ + mutex_lock(&mbi_lock); + bitmap_release_region(mbi->bm, hwirq - mbi->spi_start, + get_count_order(nr_irqs)); + mutex_unlock(&mbi_lock); +} + +static int mbi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct mbi_range *mbi = NULL; + int hwirq, offset, i, err = 0; + + mutex_lock(&mbi_lock); + for (i = 0; i < mbi_range_nr; i++) { + offset = bitmap_find_free_region(mbi_ranges[i].bm, + mbi_ranges[i].nr_spis, + get_count_order(nr_irqs)); + if (offset >= 0) { + mbi = &mbi_ranges[i]; + break; + } + } + mutex_unlock(&mbi_lock); + + if (!mbi) + return -ENOSPC; + + hwirq = mbi->spi_start + offset; + + for (i = 0; i < nr_irqs; i++) { + err = mbi_irq_gic_domain_alloc(domain, virq + i, hwirq + i); + if (err) + goto fail; + + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &mbi_irq_chip, mbi); + } + + return 0; + +fail: + irq_domain_free_irqs_parent(domain, virq, nr_irqs); + mbi_free_msi(mbi, hwirq, nr_irqs); + return err; +} + +static void mbi_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 mbi_range *mbi = irq_data_get_irq_chip_data(d); + + mbi_free_msi(mbi, d->hwirq, nr_irqs); + irq_domain_free_irqs_parent(domain, virq, nr_irqs); +} + +static const struct irq_domain_ops mbi_domain_ops = { + .alloc = mbi_irq_domain_alloc, + .free = mbi_irq_domain_free, +}; + +static void mbi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + msg[0].address_hi = upper_32_bits(mbi_phys_base + GICD_SETSPI_NSR); + msg[0].address_lo = lower_32_bits(mbi_phys_base + GICD_SETSPI_NSR); + msg[0].data = data->parent_data->hwirq; + + iommu_dma_map_msi_msg(data->irq, msg); +} + +static void mbi_compose_mbi_msg(struct irq_data *data, struct msi_msg *msg) +{ + mbi_compose_msi_msg(data, msg); + + msg[1].address_hi = upper_32_bits(mbi_phys_base + GICD_CLRSPI_NSR); + msg[1].address_lo = lower_32_bits(mbi_phys_base + GICD_CLRSPI_NSR); + msg[1].data = data->parent_data->hwirq; + + iommu_dma_map_msi_msg(data->irq, &msg[1]); +} + +/* Platform-MSI specific irqchip */ +static struct irq_chip mbi_pmsi_irq_chip = { + .name = "pMSI", + .irq_set_type = irq_chip_set_type_parent, + .irq_compose_msi_msg = mbi_compose_mbi_msg, + .flags = IRQCHIP_SUPPORTS_LEVEL_MSI, +}; + +static struct msi_domain_ops mbi_pmsi_ops = { +}; + +static struct msi_domain_info mbi_pmsi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_LEVEL_CAPABLE), + .ops = &mbi_pmsi_ops, + .chip = &mbi_pmsi_irq_chip, +}; + +static int mbi_allocate_domains(struct irq_domain *parent) +{ + struct irq_domain *nexus_domain, *plat_domain; + + nexus_domain = irq_domain_create_tree(parent->fwnode, + &mbi_domain_ops, NULL); + if (!nexus_domain) + return -ENOMEM; + + irq_domain_update_bus_token(nexus_domain, DOMAIN_BUS_NEXUS); + nexus_domain->parent = parent; + + plat_domain = platform_msi_create_irq_domain(parent->fwnode, + &mbi_pmsi_domain_info, + nexus_domain); + + if (!plat_domain) { + irq_domain_remove(plat_domain); + irq_domain_remove(nexus_domain); + return -ENOMEM; + } + + return 0; +} + +int __init mbi_init(struct fwnode_handle *fwnode, struct irq_domain *parent) +{ + struct device_node *np; + const __be32 *reg; + int ret, n; + + np = to_of_node(fwnode); + + if (!of_property_read_bool(np, "msi-controller")) + return 0; + + n = of_property_count_elems_of_size(np, "mbi-ranges", sizeof(u32)); + if (n <= 0 || n % 2) + return -EINVAL; + + mbi_range_nr = n / 2; + mbi_ranges = kcalloc(mbi_range_nr, sizeof(*mbi_ranges), GFP_KERNEL); + if (!mbi_ranges) + return -ENOMEM; + + for (n = 0; n < mbi_range_nr; n++) { + ret = of_property_read_u32_index(np, "mbi-ranges", n * 2, + &mbi_ranges[n].spi_start); + if (ret) + goto err_free_mbi; + ret = of_property_read_u32_index(np, "mbi-ranges", n * 2 + 1, + &mbi_ranges[n].nr_spis); + if (ret) + goto err_free_mbi; + + mbi_ranges[n].bm = kcalloc(BITS_TO_LONGS(mbi_ranges[n].nr_spis), + sizeof(long), GFP_KERNEL); + if (!mbi_ranges[n].bm) { + ret = -ENOMEM; + goto err_free_mbi; + } + pr_info("MBI range [%d:%d]\n", mbi_ranges[n].spi_start, + mbi_ranges[n].spi_start + mbi_ranges[n].nr_spis - 1); + } + + reg = of_get_property(np, "mbi-alias", NULL); + if (reg) { + mbi_phys_base = of_translate_address(np, reg); + if (mbi_phys_base == OF_BAD_ADDR) { + ret = -ENXIO; + goto err_free_mbi; + } + } else { + struct resource res; + + if (of_address_to_resource(np, 0, &res)) { + ret = -ENXIO; + goto err_free_mbi; + } + + mbi_phys_base = res.start; + } + + pr_info("Using MBI frame %pa\n", &mbi_phys_base); + + ret = mbi_allocate_domains(parent); + if (ret) + goto err_free_mbi; + + return 0; + +err_free_mbi: + if (mbi_ranges) { + for (n = 0; n < mbi_range_nr; n++) + kfree(mbi_ranges[n].bm); + kfree(mbi_ranges); + } + + return ret; +} diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 94164d7b87a6..5a67ec084588 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -1113,6 +1113,12 @@ static int __init gic_init_bases(void __iomem *dist_base, pr_info("Distributor has %sRange Selector support\n", gic_data.has_rss ? "" : "no "); + if (typer & GICD_TYPER_MBIS) { + err = mbi_init(handle, gic_data.domain); + if (err) + pr_err("Failed to initialize MBIs\n"); + } + set_handle_irq(gic_handle_irq); gic_update_vlpi_properties(); diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index f5af3b594e6e..cbb872c1b607 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h @@ -587,6 +587,7 @@ struct fwnode_handle; int its_cpu_init(void); int its_init(struct fwnode_handle *handle, struct rdists *rdists, struct irq_domain *domain); +int mbi_init(struct fwnode_handle *fwnode, struct irq_domain *parent); static inline bool gic_enable_sre(void) { From 38985351492b4ef2f63ffe527ef7cdfa66680f94 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 8 May 2018 13:14:37 +0100 Subject: [PATCH 10/36] irqchip/gic-v3: Add PCI/MSI support to the GICv3 MBI sub-driver You would hope that if you have a GICv3 in your system, you'd use the ITS, as it provides a large interrupt ID space and device isolation. Sadly, some SoC integrations are less than perfect, and the ITS is not usesable on those. The only solution for these systems is to use the MBI interface, and rely on a very small number of possible vectors. This patch thus adds minimal support for PCI/MSI on top of the GICv3 MBI driver. Please don't use it if you can avoid it. Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Tested-by: Ard Biesheuvel Tested-by: Srinivas Kandagatla Cc: Rob Herring Cc: Jason Cooper Cc: Thomas Petazzoni Cc: Miquel Raynal Link: https://lkml.kernel.org/r/20180508121438.11301-9-marc.zyngier@arm.com --- drivers/irqchip/irq-gic-v3-mbi.c | 62 ++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-mbi.c b/drivers/irqchip/irq-gic-v3-mbi.c index 2b3b767050aa..ad70e7c416e3 100644 --- a/drivers/irqchip/irq-gic-v3-mbi.c +++ b/drivers/irqchip/irq-gic-v3-mbi.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -144,6 +145,55 @@ static void mbi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) iommu_dma_map_msi_msg(data->irq, msg); } +#ifdef CONFIG_PCI_MSI +/* PCI-specific irqchip */ +static void mbi_mask_msi_irq(struct irq_data *d) +{ + pci_msi_mask_irq(d); + irq_chip_mask_parent(d); +} + +static void mbi_unmask_msi_irq(struct irq_data *d) +{ + pci_msi_unmask_irq(d); + irq_chip_unmask_parent(d); +} + +static struct irq_chip mbi_msi_irq_chip = { + .name = "MSI", + .irq_mask = mbi_mask_msi_irq, + .irq_unmask = mbi_unmask_msi_irq, + .irq_eoi = irq_chip_eoi_parent, + .irq_compose_msi_msg = mbi_compose_msi_msg, + .irq_write_msi_msg = pci_msi_domain_write_msg, +}; + +static struct msi_domain_info mbi_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), + .chip = &mbi_msi_irq_chip, +}; + +static int mbi_allocate_pci_domain(struct irq_domain *nexus_domain, + struct irq_domain **pci_domain) +{ + *pci_domain = pci_msi_create_irq_domain(nexus_domain->parent->fwnode, + &mbi_msi_domain_info, + nexus_domain); + if (!*pci_domain) + return -ENOMEM; + + return 0; +} +#else +static int mbi_allocate_pci_domain(struct irq_domain *nexus_domain, + struct irq_domain **pci_domain) +{ + *pci_domain = NULL; + return 0; +} +#endif + static void mbi_compose_mbi_msg(struct irq_data *data, struct msi_msg *msg) { mbi_compose_msi_msg(data, msg); @@ -175,7 +225,8 @@ static struct msi_domain_info mbi_pmsi_domain_info = { static int mbi_allocate_domains(struct irq_domain *parent) { - struct irq_domain *nexus_domain, *plat_domain; + struct irq_domain *nexus_domain, *pci_domain, *plat_domain; + int err; nexus_domain = irq_domain_create_tree(parent->fwnode, &mbi_domain_ops, NULL); @@ -185,12 +236,17 @@ static int mbi_allocate_domains(struct irq_domain *parent) irq_domain_update_bus_token(nexus_domain, DOMAIN_BUS_NEXUS); nexus_domain->parent = parent; + err = mbi_allocate_pci_domain(nexus_domain, &pci_domain); + plat_domain = platform_msi_create_irq_domain(parent->fwnode, &mbi_pmsi_domain_info, nexus_domain); - if (!plat_domain) { - irq_domain_remove(plat_domain); + if (err || !plat_domain) { + if (plat_domain) + irq_domain_remove(plat_domain); + if (pci_domain) + irq_domain_remove(pci_domain); irq_domain_remove(nexus_domain); return -ENOMEM; } From 53667c670fe00d63246fb3cfb4480bb1ba247bcc Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 8 May 2018 13:14:38 +0100 Subject: [PATCH 11/36] dt-bindings/gic-v3: Add documentation for MBI support Add the required properties to support the MBI feature on GICv3. Signed-off-by: Marc Zyngier Signed-off-by: Thomas Gleixner Cc: Rob Herring Cc: Jason Cooper Cc: Ard Biesheuvel Cc: Srinivas Kandagatla Cc: Thomas Petazzoni Cc: Miquel Raynal Link: https://lkml.kernel.org/r/20180508121438.11301-10-marc.zyngier@arm.com --- .../interrupt-controller/arm,gic-v3.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt index 0a57f2f4167d..3ea78c4ef887 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt @@ -57,6 +57,20 @@ Optional occupied by the redistributors. Required if more than one such region is present. +- msi-controller: Boolean property. Identifies the node as an MSI + controller. Only present if the Message Based Interrupt + functionnality is being exposed by the HW, and the mbi-ranges + property present. + +- mbi-ranges: A list of pairs , where "intid" is the first + SPI of a range that can be used an MBI, and "span" the size of that + range. Multiple ranges can be provided. Requires "msi-controller" to + be set. + +- mbi-alias: Address property. Base address of an alias of the GICD + region containing only the {SET,CLR}SPI registers to be used if + isolation is required, and if supported by the HW. + Sub-nodes: PPI affinity can be expressed as a single "ppi-partitions" node, @@ -99,6 +113,9 @@ Examples: <0x0 0x2c020000 0 0x2000>; // GICV interrupts = <1 9 4>; + msi-controller; + mbi-ranges = <256 128>; + gic-its@2c200000 { compatible = "arm,gic-v3-its"; msi-controller; From 2e5c4632dcc0365a97c36817a368507e6a4c89b2 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 8 May 2018 15:38:16 +0200 Subject: [PATCH 12/36] softirq/ia64: Convert local_softirq_pending() to per-cpu ops In order to consolidate and optimize generic softirq mask accesses, we first need to convert architectures to use per-cpu operations when possible. Signed-off-by: Frederic Weisbecker Acked-by: Thomas Gleixner Acked-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: David S. Miller Cc: Fenghua Yu Cc: Heiko Carstens Cc: Helge Deller Cc: James E.J. Bottomley Cc: Linus Torvalds Cc: Martin Schwidefsky Cc: Michael Ellerman Cc: Paul Mackerras Cc: Rich Felker Cc: Sebastian Andrzej Siewior Cc: Tony Luck Cc: Yoshinori Sato Link: http://lkml.kernel.org/r/1525786706-22846-2-git-send-email-frederic@kernel.org Signed-off-by: Ingo Molnar --- arch/ia64/include/asm/hardirq.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/ia64/include/asm/hardirq.h b/arch/ia64/include/asm/hardirq.h index bdc4669c71c3..22fae710e420 100644 --- a/arch/ia64/include/asm/hardirq.h +++ b/arch/ia64/include/asm/hardirq.h @@ -13,7 +13,7 @@ #define __ARCH_IRQ_STAT 1 -#define local_softirq_pending() (local_cpu_data->softirq_pending) +#define local_softirq_pending() (*this_cpu_ptr(&ia64_cpu_info.softirq_pending)) #include #include From 49892dbc2cb349f78eccfc1f55eac0ec718f44bb Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 8 May 2018 15:38:17 +0200 Subject: [PATCH 13/36] softirq/sparc: Convert local_softirq_pending() to use per-cpu op In order to consolidate and optimize generic softirq mask accesses, we first need to convert architectures to use per-cpu operations when possible. Signed-off-by: Frederic Weisbecker Acked-by: Thomas Gleixner Acked-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: David S. Miller Cc: Fenghua Yu Cc: Heiko Carstens Cc: Helge Deller Cc: James E.J. Bottomley Cc: Linus Torvalds Cc: Martin Schwidefsky Cc: Michael Ellerman Cc: Paul Mackerras Cc: Rich Felker Cc: Sebastian Andrzej Siewior Cc: Tony Luck Cc: Yoshinori Sato Link: http://lkml.kernel.org/r/1525786706-22846-3-git-send-email-frederic@kernel.org Signed-off-by: Ingo Molnar --- arch/sparc/include/asm/hardirq_64.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sparc/include/asm/hardirq_64.h b/arch/sparc/include/asm/hardirq_64.h index f56540271993..6aba904819a4 100644 --- a/arch/sparc/include/asm/hardirq_64.h +++ b/arch/sparc/include/asm/hardirq_64.h @@ -11,7 +11,7 @@ #define __ARCH_IRQ_STAT #define local_softirq_pending() \ - (local_cpu_data().__softirq_pending) + (*this_cpu_ptr(&__cpu_data.__softirq_pending)) void ack_bad_irq(unsigned int irq); From 30d723340c30ff9afe200ef5ecbbdc77e6d1f816 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 8 May 2018 15:38:18 +0200 Subject: [PATCH 14/36] softirq/sh: Use nmi_count() on /proc/interrupts print out Use nmi_count() instead of accessing directly the irq_stat structure. Its implementation is going to change to use per-CPU, so defer the guts to standard API instead. Signed-off-by: Frederic Weisbecker Acked-by: Thomas Gleixner Acked-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: David S. Miller Cc: Fenghua Yu Cc: Heiko Carstens Cc: Helge Deller Cc: James E.J. Bottomley Cc: Linus Torvalds Cc: Martin Schwidefsky Cc: Michael Ellerman Cc: Paul Mackerras Cc: Rich Felker Cc: Sebastian Andrzej Siewior Cc: Tony Luck Cc: Yoshinori Sato Link: http://lkml.kernel.org/r/1525786706-22846-4-git-send-email-frederic@kernel.org Signed-off-by: Ingo Molnar --- arch/sh/kernel/irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c index 245dbeb20afe..5717c7cbdd97 100644 --- a/arch/sh/kernel/irq.c +++ b/arch/sh/kernel/irq.c @@ -44,7 +44,7 @@ int arch_show_interrupts(struct seq_file *p, int prec) seq_printf(p, "%*s: ", prec, "NMI"); for_each_online_cpu(j) - seq_printf(p, "%10u ", irq_stat[j].__nmi_count); + seq_printf(p, "%10u ", nmi_count(j)); seq_printf(p, " Non-maskable interrupts\n"); seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count)); From 0f6f47bacba514f4e9f61de0d85940dfb41498cc Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 8 May 2018 15:38:19 +0200 Subject: [PATCH 15/36] softirq/core: Turn default irq_cpustat_t to standard per-cpu In order to optimize and consolidate softirq mask accesses, let's convert the default irq_cpustat_t implementation to per-CPU standard API. Signed-off-by: Frederic Weisbecker Acked-by: Thomas Gleixner Acked-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: David S. Miller Cc: Fenghua Yu Cc: Heiko Carstens Cc: Helge Deller Cc: James E.J. Bottomley Cc: Linus Torvalds Cc: Martin Schwidefsky Cc: Michael Ellerman Cc: Paul Mackerras Cc: Rich Felker Cc: Sebastian Andrzej Siewior Cc: Tony Luck Cc: Yoshinori Sato Link: http://lkml.kernel.org/r/1525786706-22846-5-git-send-email-frederic@kernel.org Signed-off-by: Ingo Molnar --- include/linux/irq_cpustat.h | 4 ++-- kernel/softirq.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/linux/irq_cpustat.h b/include/linux/irq_cpustat.h index 4954948d1973..ddea03c7c39d 100644 --- a/include/linux/irq_cpustat.h +++ b/include/linux/irq_cpustat.h @@ -18,8 +18,8 @@ */ #ifndef __ARCH_IRQ_STAT -extern irq_cpustat_t irq_stat[]; /* defined in asm/hardirq.h */ -#define __IRQ_STAT(cpu, member) (irq_stat[cpu].member) +DECLARE_PER_CPU_ALIGNED(irq_cpustat_t, irq_stat); /* defined in asm/hardirq.h */ +#define __IRQ_STAT(cpu, member) (per_cpu(irq_stat.member, cpu)) #endif /* arch independent irq_stat fields */ diff --git a/kernel/softirq.c b/kernel/softirq.c index 177de3640c78..c5fafd792df1 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -49,8 +49,8 @@ */ #ifndef __ARCH_IRQ_STAT -irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned; -EXPORT_SYMBOL(irq_stat); +DEFINE_PER_CPU_ALIGNED(irq_cpustat_t, irq_stat); +EXPORT_PER_CPU_SYMBOL(irq_stat); #endif static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp; From 0fd7d86285290ccebc0dc6eb536b6b043dd6a1e4 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 8 May 2018 15:38:20 +0200 Subject: [PATCH 16/36] softirq/core: Consolidate default local_softirq_pending() implementations Consolidate and optimize default softirq mask API implementations. Per-CPU operations are expected to be faster and a few architectures already rely on them to implement local_softirq_pending() and related accessors/mutators. Those will be migrated to the new generic code. Signed-off-by: Frederic Weisbecker Acked-by: Thomas Gleixner Acked-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: David S. Miller Cc: Fenghua Yu Cc: Heiko Carstens Cc: Helge Deller Cc: James E.J. Bottomley Cc: Linus Torvalds Cc: Martin Schwidefsky Cc: Michael Ellerman Cc: Paul Mackerras Cc: Rich Felker Cc: Sebastian Andrzej Siewior Cc: Tony Luck Cc: Yoshinori Sato Link: http://lkml.kernel.org/r/1525786706-22846-6-git-send-email-frederic@kernel.org Signed-off-by: Ingo Molnar --- include/linux/interrupt.h | 14 ++++++++++++++ include/linux/irq_cpustat.h | 6 +----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 5426627f9c55..7a11f73c5c3b 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -432,11 +432,25 @@ extern bool force_irqthreads; #define force_irqthreads (0) #endif +#ifndef local_softirq_pending + +#ifndef local_softirq_pending_ref +#define local_softirq_pending_ref irq_stat.__softirq_pending +#endif + +#define local_softirq_pending() (__this_cpu_read(local_softirq_pending_ref)) +#define set_softirq_pending(x) (__this_cpu_write(local_softirq_pending_ref, (x))) +#define or_softirq_pending(x) (__this_cpu_or(local_softirq_pending_ref, (x))) + +#else /* local_softirq_pending */ + #ifndef __ARCH_SET_SOFTIRQ_PENDING #define set_softirq_pending(x) (local_softirq_pending() = (x)) #define or_softirq_pending(x) (local_softirq_pending() |= (x)) #endif +#endif /* local_softirq_pending */ + /* Some architectures might implement lazy enabling/disabling of * interrupts. In some cases, such as stop_machine, we might want * to ensure that after a local_irq_disable(), interrupts have diff --git a/include/linux/irq_cpustat.h b/include/linux/irq_cpustat.h index ddea03c7c39d..6e8895cd4d92 100644 --- a/include/linux/irq_cpustat.h +++ b/include/linux/irq_cpustat.h @@ -22,11 +22,7 @@ DECLARE_PER_CPU_ALIGNED(irq_cpustat_t, irq_stat); /* defined in asm/hardirq.h */ #define __IRQ_STAT(cpu, member) (per_cpu(irq_stat.member, cpu)) #endif - /* arch independent irq_stat fields */ -#define local_softirq_pending() \ - __IRQ_STAT(smp_processor_id(), __softirq_pending) - - /* arch dependent irq_stat fields */ +/* arch dependent irq_stat fields */ #define nmi_count(cpu) __IRQ_STAT((cpu), __nmi_count) /* i386 */ #endif /* __irq_cpustat_h */ From a58bdf25b98bf765b4b732f2c56097ddcb9f2d5a Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 8 May 2018 15:38:21 +0200 Subject: [PATCH 17/36] softirq/ia64: Switch to generic local_softirq_pending() implementation Benefit from the generic softirq mask implementation that rely on per-CPU mutators instead of working with raw operators on top of this_cpu_ptr(). Signed-off-by: Frederic Weisbecker Acked-by: Thomas Gleixner Acked-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: David S. Miller Cc: Fenghua Yu Cc: Heiko Carstens Cc: Helge Deller Cc: James E.J. Bottomley Cc: Linus Torvalds Cc: Martin Schwidefsky Cc: Michael Ellerman Cc: Paul Mackerras Cc: Rich Felker Cc: Sebastian Andrzej Siewior Cc: Tony Luck Cc: Yoshinori Sato Link: http://lkml.kernel.org/r/1525786706-22846-7-git-send-email-frederic@kernel.org Signed-off-by: Ingo Molnar --- arch/ia64/include/asm/hardirq.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/ia64/include/asm/hardirq.h b/arch/ia64/include/asm/hardirq.h index 22fae710e420..ccde7c2ba00f 100644 --- a/arch/ia64/include/asm/hardirq.h +++ b/arch/ia64/include/asm/hardirq.h @@ -13,7 +13,7 @@ #define __ARCH_IRQ_STAT 1 -#define local_softirq_pending() (*this_cpu_ptr(&ia64_cpu_info.softirq_pending)) +#define local_softirq_pending_ref ia64_cpu_info.softirq_pending #include #include From 03979f8a72e6576248e7b9e3abb72a760312dd7d Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 8 May 2018 15:38:22 +0200 Subject: [PATCH 18/36] softirq/parisc: Switch to generic local_softirq_pending() implementation Remove the ad-hoc implementation, the generic code now allows us not to reinvent the wheel. Signed-off-by: Frederic Weisbecker Acked-by: Thomas Gleixner Acked-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: David S. Miller Cc: Fenghua Yu Cc: Heiko Carstens Cc: Helge Deller Cc: James E.J. Bottomley Cc: Linus Torvalds Cc: Martin Schwidefsky Cc: Michael Ellerman Cc: Paul Mackerras Cc: Rich Felker Cc: Sebastian Andrzej Siewior Cc: Tony Luck Cc: Yoshinori Sato Link: http://lkml.kernel.org/r/1525786706-22846-8-git-send-email-frederic@kernel.org Signed-off-by: Ingo Molnar --- arch/parisc/include/asm/hardirq.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/arch/parisc/include/asm/hardirq.h b/arch/parisc/include/asm/hardirq.h index 077815169258..1a1235a9d533 100644 --- a/arch/parisc/include/asm/hardirq.h +++ b/arch/parisc/include/asm/hardirq.h @@ -34,14 +34,6 @@ DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); #define __IRQ_STAT(cpu, member) (irq_stat[cpu].member) #define inc_irq_stat(member) this_cpu_inc(irq_stat.member) #define __inc_irq_stat(member) __this_cpu_inc(irq_stat.member) -#define local_softirq_pending() this_cpu_read(irq_stat.__softirq_pending) - -#define __ARCH_SET_SOFTIRQ_PENDING - -#define set_softirq_pending(x) \ - this_cpu_write(irq_stat.__softirq_pending, (x)) -#define or_softirq_pending(x) this_cpu_or(irq_stat.__softirq_pending, (x)) - #define ack_bad_irq(irq) WARN(1, "unexpected IRQ trap at vector %02x\n", irq) #endif /* _PARISC_HARDIRQ_H */ From 1321a5de1ecb0d2981394ff2111c75c4dcb0c237 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 8 May 2018 15:38:23 +0200 Subject: [PATCH 19/36] softirq/powerpc: Switch to generic local_softirq_pending() implementation Remove the ad-hoc implementation, the generic code now allows us not to reinvent the wheel. Signed-off-by: Frederic Weisbecker Acked-by: Thomas Gleixner Acked-by: Peter Zijlstra Acked-by: Michael Ellerman Cc: Benjamin Herrenschmidt Cc: David S. Miller Cc: Fenghua Yu Cc: Heiko Carstens Cc: Helge Deller Cc: James E.J. Bottomley Cc: Linus Torvalds Cc: Martin Schwidefsky Cc: Paul Mackerras Cc: Rich Felker Cc: Sebastian Andrzej Siewior Cc: Tony Luck Cc: Yoshinori Sato Link: http://lkml.kernel.org/r/1525786706-22846-9-git-send-email-frederic@kernel.org Signed-off-by: Ingo Molnar --- arch/powerpc/include/asm/hardirq.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/powerpc/include/asm/hardirq.h b/arch/powerpc/include/asm/hardirq.h index 5986d473722b..383f628acbf8 100644 --- a/arch/powerpc/include/asm/hardirq.h +++ b/arch/powerpc/include/asm/hardirq.h @@ -25,15 +25,8 @@ typedef struct { DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); #define __ARCH_IRQ_STAT - -#define local_softirq_pending() __this_cpu_read(irq_stat.__softirq_pending) - -#define __ARCH_SET_SOFTIRQ_PENDING #define __ARCH_IRQ_EXIT_IRQS_DISABLED -#define set_softirq_pending(x) __this_cpu_write(irq_stat.__softirq_pending, (x)) -#define or_softirq_pending(x) __this_cpu_or(irq_stat.__softirq_pending, (x)) - static inline void ack_bad_irq(unsigned int irq) { printk(KERN_CRIT "unexpected IRQ trap at vector %02x\n", irq); From 424f7d3e3b950c88a4127b7dfa78ea54e287413e Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 8 May 2018 15:38:24 +0200 Subject: [PATCH 20/36] softirq/sparc: Switch to generic local_softirq_pending() implementation Benefit from the generic softirq mask implementation that rely on per-CPU mutators instead of working with raw operators on top of this_cpu_ptr(). Signed-off-by: Frederic Weisbecker Acked-by: Thomas Gleixner Acked-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: David S. Miller Cc: Fenghua Yu Cc: Heiko Carstens Cc: Helge Deller Cc: James E.J. Bottomley Cc: Linus Torvalds Cc: Martin Schwidefsky Cc: Michael Ellerman Cc: Paul Mackerras Cc: Rich Felker Cc: Sebastian Andrzej Siewior Cc: Tony Luck Cc: Yoshinori Sato Link: http://lkml.kernel.org/r/1525786706-22846-10-git-send-email-frederic@kernel.org Signed-off-by: Ingo Molnar --- arch/sparc/include/asm/hardirq_64.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/sparc/include/asm/hardirq_64.h b/arch/sparc/include/asm/hardirq_64.h index 6aba904819a4..75b92bfe04b5 100644 --- a/arch/sparc/include/asm/hardirq_64.h +++ b/arch/sparc/include/asm/hardirq_64.h @@ -10,8 +10,9 @@ #include #define __ARCH_IRQ_STAT -#define local_softirq_pending() \ - (*this_cpu_ptr(&__cpu_data.__softirq_pending)) + +#define local_softirq_pending_ref \ + __cpu_data.__softirq_pending void ack_bad_irq(unsigned int irq); From 1a8bc8f8d6a7980a999975edbd29578fbce09359 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 8 May 2018 15:38:25 +0200 Subject: [PATCH 21/36] softirq/x86: Switch to generic local_softirq_pending() implementation Remove the ad-hoc implementation, the generic code now allows us not to reinvent the wheel. Signed-off-by: Frederic Weisbecker Acked-by: Thomas Gleixner Acked-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: David S. Miller Cc: Fenghua Yu Cc: Heiko Carstens Cc: Helge Deller Cc: James E.J. Bottomley Cc: Linus Torvalds Cc: Martin Schwidefsky Cc: Michael Ellerman Cc: Paul Mackerras Cc: Rich Felker Cc: Sebastian Andrzej Siewior Cc: Tony Luck Cc: Yoshinori Sato Link: http://lkml.kernel.org/r/1525786706-22846-11-git-send-email-frederic@kernel.org Signed-off-by: Ingo Molnar --- arch/x86/include/asm/hardirq.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/arch/x86/include/asm/hardirq.h b/arch/x86/include/asm/hardirq.h index 5ea2afd4c871..740a428acf1e 100644 --- a/arch/x86/include/asm/hardirq.h +++ b/arch/x86/include/asm/hardirq.h @@ -50,14 +50,6 @@ DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); #define inc_irq_stat(member) this_cpu_inc(irq_stat.member) -#define local_softirq_pending() this_cpu_read(irq_stat.__softirq_pending) - -#define __ARCH_SET_SOFTIRQ_PENDING - -#define set_softirq_pending(x) \ - this_cpu_write(irq_stat.__softirq_pending, (x)) -#define or_softirq_pending(x) this_cpu_or(irq_stat.__softirq_pending, (x)) - extern void ack_bad_irq(unsigned int irq); extern u64 arch_irq_stat_cpu(unsigned int cpu); From 48bda43eabb8d086204f543cf8bbad696b8c6391 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 8 May 2018 15:38:26 +0200 Subject: [PATCH 22/36] softirq/s390: Move default mutators of overwritten softirq mask to s390 s390 is now the last architecture that entirely overwrites local_softirq_pending() and uses the according default definitions of set_softirq_pending() and or_softirq_pending(). Just move these to s390 to debloat the generic code complexity. Suggested-by: Peter Zijlstra Signed-off-by: Frederic Weisbecker Acked-by: Thomas Gleixner Acked-by: Peter Zijlstra Cc: Benjamin Herrenschmidt Cc: David S. Miller Cc: Fenghua Yu Cc: Heiko Carstens Cc: Helge Deller Cc: James E.J. Bottomley Cc: Linus Torvalds Cc: Martin Schwidefsky Cc: Michael Ellerman Cc: Paul Mackerras Cc: Rich Felker Cc: Sebastian Andrzej Siewior Cc: Tony Luck Cc: Yoshinori Sato Link: http://lkml.kernel.org/r/1525786706-22846-12-git-send-email-frederic@kernel.org Signed-off-by: Ingo Molnar --- arch/s390/include/asm/hardirq.h | 2 ++ include/linux/interrupt.h | 7 ------- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/arch/s390/include/asm/hardirq.h b/arch/s390/include/asm/hardirq.h index a296c6acfd07..dfbc3c6c0674 100644 --- a/arch/s390/include/asm/hardirq.h +++ b/arch/s390/include/asm/hardirq.h @@ -14,6 +14,8 @@ #include #define local_softirq_pending() (S390_lowcore.softirq_pending) +#define set_softirq_pending(x) (S390_lowcore.softirq_pending = (x)) +#define or_softirq_pending(x) (S390_lowcore.softirq_pending |= (x)) #define __ARCH_IRQ_STAT #define __ARCH_HAS_DO_SOFTIRQ diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 7a11f73c5c3b..eeceac3376fc 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -442,13 +442,6 @@ extern bool force_irqthreads; #define set_softirq_pending(x) (__this_cpu_write(local_softirq_pending_ref, (x))) #define or_softirq_pending(x) (__this_cpu_or(local_softirq_pending_ref, (x))) -#else /* local_softirq_pending */ - -#ifndef __ARCH_SET_SOFTIRQ_PENDING -#define set_softirq_pending(x) (local_softirq_pending() = (x)) -#define or_softirq_pending(x) (local_softirq_pending() |= (x)) -#endif - #endif /* local_softirq_pending */ /* Some architectures might implement lazy enabling/disabling of From 0e41635b7ad9b91a08a1af045642968a21a6a767 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Sun, 8 Apr 2018 14:56:58 +0000 Subject: [PATCH 23/36] dt-bindings: interrupt-controller: Fix the double quotes The double quotes seems not ASCII type, fix it here. Reviewed-by: Rob Herring Signed-off-by: Yixun Lan Signed-off-by: Marc Zyngier --- .../interrupt-controller/amlogic,meson-gpio-intc.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt index a83f9a5734ca..c753d99d43db 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt @@ -9,11 +9,11 @@ number of interrupt exposed depends on the SoC. Required properties: -- compatible : must have "amlogic,meson8-gpio-intc” and either - “amlogic,meson8-gpio-intc” for meson8 SoCs (S802) or - “amlogic,meson8b-gpio-intc” for meson8b SoCs (S805) or - “amlogic,meson-gxbb-gpio-intc” for GXBB SoCs (S905) or - “amlogic,meson-gxl-gpio-intc” for GXL SoCs (S905X, S912) +- compatible : must have "amlogic,meson8-gpio-intc" and either + "amlogic,meson8-gpio-intc" for meson8 SoCs (S802) or + "amlogic,meson8b-gpio-intc" for meson8b SoCs (S805) or + "amlogic,meson-gxbb-gpio-intc" for GXBB SoCs (S905) or + "amlogic,meson-gxl-gpio-intc" for GXL SoCs (S905X, S912) - interrupt-parent : a phandle to the GIC the interrupts are routed to. Usually this is provided at the root level of the device tree as it is common to most of the SoC. From 3212dca4f1e0ce6e309d9d1b6ecb6705e3890579 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Sun, 8 Apr 2018 14:56:59 +0000 Subject: [PATCH 24/36] dt-bindings: interrupt-controller: New binding for Meson-AXG SoC Update the dt-binding documentation to support new compatible string for the GPIO interrupt controller which found in Amlogic's Meson-AXG SoC. Reviewed-by: Rob Herring Signed-off-by: Yixun Lan Signed-off-by: Marc Zyngier --- .../bindings/interrupt-controller/amlogic,meson-gpio-intc.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt index c753d99d43db..89674ad8a097 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt @@ -14,6 +14,7 @@ Required properties: "amlogic,meson8b-gpio-intc" for meson8b SoCs (S805) or "amlogic,meson-gxbb-gpio-intc" for GXBB SoCs (S905) or "amlogic,meson-gxl-gpio-intc" for GXL SoCs (S905X, S912) + "amlogic,meson-axg-gpio-intc" for AXG SoCs (A113D, A113X) - interrupt-parent : a phandle to the GIC the interrupts are routed to. Usually this is provided at the root level of the device tree as it is common to most of the SoC. From 868c4e07533c67f7b6525a4561d8f59dbfdd5e06 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Sun, 8 Apr 2018 14:57:00 +0000 Subject: [PATCH 25/36] irqchip/meson-gpio: Add support for Meson-AXG SoCs The Meson-AXG SoC uses the same GPIO interrupt controller IP block as the other Meson SoCs. A total of 100 pins can be spied on, which is the sum of: - 255:100 Undefined(no interrupt) - 99:84, 16 pins on bank GPIOY - 83:61, 23 pins on bank GPIOX - 60:40, 21 pins on bank GPIOA - 39:25, 15 pins on bank BOOT - 24:14, 11 pins on bank GPIOZ - 13:0 , 14 pins in the AO domain Signed-off-by: Yixun Lan Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-meson-gpio.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/irqchip/irq-meson-gpio.c b/drivers/irqchip/irq-meson-gpio.c index a59bdbc0b9bb..7b531fd075b8 100644 --- a/drivers/irqchip/irq-meson-gpio.c +++ b/drivers/irqchip/irq-meson-gpio.c @@ -63,11 +63,16 @@ static const struct meson_gpio_irq_params gxl_params = { .nr_hwirq = 110, }; +static const struct meson_gpio_irq_params axg_params = { + .nr_hwirq = 100, +}; + static const struct of_device_id meson_irq_gpio_matches[] = { { .compatible = "amlogic,meson8-gpio-intc", .data = &meson8_params }, { .compatible = "amlogic,meson8b-gpio-intc", .data = &meson8b_params }, { .compatible = "amlogic,meson-gxbb-gpio-intc", .data = &gxbb_params }, { .compatible = "amlogic,meson-gxl-gpio-intc", .data = &gxl_params }, + { .compatible = "amlogic,meson-axg-gpio-intc", .data = &axg_params }, { } }; From ff78716d431726ceda50300b8c1b982e00acf6ab Mon Sep 17 00:00:00 2001 From: Radoslaw Pietrzyk Date: Thu, 26 Apr 2018 18:18:24 +0200 Subject: [PATCH 26/36] irqchip/stm32: Optimizes and cleans up stm32-exti irq_domain - In stm32_exti_alloc function, discards irq_domain_set_info with handle_simple_irq. This overwrite the setting defined while init of generic chips. Exti controller manages edge irq type. - Removes acking in chained irq handler as this is done by irq_chip itself inside handle_edge_irq - removes unneeded irq_domain_ops.xlate callback Acked-by: Ludovic Barre Tested-by: Ludovic Barre Signed-off-by: Radoslaw Pietrzyk Signed-off-by: Ludovic Barre Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-stm32-exti.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index 36f0fbe36c35..8013a87179da 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -79,13 +79,6 @@ static unsigned long stm32_exti_pending(struct irq_chip_generic *gc) return irq_reg_readl(gc, stm32_bank->pr_ofst); } -static void stm32_exti_irq_ack(struct irq_chip_generic *gc, u32 mask) -{ - const struct stm32_exti_bank *stm32_bank = gc->private; - - irq_reg_writel(gc, mask, stm32_bank->pr_ofst); -} - static void stm32_irq_handler(struct irq_desc *desc) { struct irq_domain *domain = irq_desc_get_handler_data(desc); @@ -106,7 +99,6 @@ static void stm32_irq_handler(struct irq_desc *desc) for_each_set_bit(n, &pending, IRQS_PER_BANK) { virq = irq_find_mapping(domain, irq_base + n); generic_handle_irq(virq); - stm32_exti_irq_ack(gc, BIT(n)); } } } @@ -176,16 +168,12 @@ static int stm32_irq_set_wake(struct irq_data *data, unsigned int on) static int stm32_exti_alloc(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs, void *data) { - struct irq_chip_generic *gc; struct irq_fwspec *fwspec = data; irq_hw_number_t hwirq; hwirq = fwspec->param[0]; - gc = irq_get_domain_generic_chip(d, hwirq); irq_map_generic_chip(d, virq, hwirq); - irq_domain_set_info(d, virq, hwirq, &gc->chip_types->chip, gc, - handle_simple_irq, NULL, NULL); return 0; } @@ -200,7 +188,6 @@ static void stm32_exti_free(struct irq_domain *d, unsigned int virq, struct irq_domain_ops irq_exti_domain_ops = { .map = irq_map_generic_chip, - .xlate = irq_domain_xlate_onetwocell, .alloc = stm32_exti_alloc, .free = stm32_exti_free, }; From ea80aa2a1a594fb21c8b5aec192e1d6deee686b0 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Thu, 26 Apr 2018 18:18:25 +0200 Subject: [PATCH 27/36] irqchip/stm32: Checkpatch fix -WARNING: struct irq_domain_ops should normally be const -CHECK: Alignment should match open parenthesis Signed-off-by: Ludovic Barre Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-stm32-exti.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index 8013a87179da..b91a8c289836 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -186,7 +186,7 @@ static void stm32_exti_free(struct irq_domain *d, unsigned int virq, irq_domain_reset_irq_data(data); } -struct irq_domain_ops irq_exti_domain_ops = { +static const struct irq_domain_ops irq_exti_domain_ops = { .map = irq_map_generic_chip, .alloc = stm32_exti_alloc, .free = stm32_exti_free, @@ -221,7 +221,7 @@ __init stm32_exti_init(const struct stm32_exti_bank **stm32_exti_banks, handle_edge_irq, clr, 0, 0); if (ret) { pr_err("%pOF: Could not allocate generic interrupt chip.\n", - node); + node); goto out_free_domain; } From be6230f0c2bd5d2fe7530d04d015d454f235f21d Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Thu, 26 Apr 2018 18:18:26 +0200 Subject: [PATCH 28/36] irqchip/stm32: Add falling pending register support This patch adds support of rising/falling pending registers. Falling pending register (fpr) is needed for next revision. Signed-off-by: Ludovic Barre Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-stm32-exti.c | 47 ++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index b91a8c289836..69a4453c2952 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -23,16 +23,20 @@ struct stm32_exti_bank { u32 rtsr_ofst; u32 ftsr_ofst; u32 swier_ofst; - u32 pr_ofst; + u32 rpr_ofst; + u32 fpr_ofst; }; +#define UNDEF_REG ~0 + static const struct stm32_exti_bank stm32f4xx_exti_b1 = { .imr_ofst = 0x00, .emr_ofst = 0x04, .rtsr_ofst = 0x08, .ftsr_ofst = 0x0C, .swier_ofst = 0x10, - .pr_ofst = 0x14, + .rpr_ofst = 0x14, + .fpr_ofst = UNDEF_REG, }; static const struct stm32_exti_bank *stm32f4xx_exti_banks[] = { @@ -45,7 +49,8 @@ static const struct stm32_exti_bank stm32h7xx_exti_b1 = { .rtsr_ofst = 0x00, .ftsr_ofst = 0x04, .swier_ofst = 0x08, - .pr_ofst = 0x88, + .rpr_ofst = 0x88, + .fpr_ofst = UNDEF_REG, }; static const struct stm32_exti_bank stm32h7xx_exti_b2 = { @@ -54,7 +59,8 @@ static const struct stm32_exti_bank stm32h7xx_exti_b2 = { .rtsr_ofst = 0x20, .ftsr_ofst = 0x24, .swier_ofst = 0x28, - .pr_ofst = 0x98, + .rpr_ofst = 0x98, + .fpr_ofst = UNDEF_REG, }; static const struct stm32_exti_bank stm32h7xx_exti_b3 = { @@ -63,7 +69,8 @@ static const struct stm32_exti_bank stm32h7xx_exti_b3 = { .rtsr_ofst = 0x40, .ftsr_ofst = 0x44, .swier_ofst = 0x48, - .pr_ofst = 0xA8, + .rpr_ofst = 0xA8, + .fpr_ofst = UNDEF_REG, }; static const struct stm32_exti_bank *stm32h7xx_exti_banks[] = { @@ -75,8 +82,13 @@ static const struct stm32_exti_bank *stm32h7xx_exti_banks[] = { static unsigned long stm32_exti_pending(struct irq_chip_generic *gc) { const struct stm32_exti_bank *stm32_bank = gc->private; + unsigned long pending; - return irq_reg_readl(gc, stm32_bank->pr_ofst); + pending = irq_reg_readl(gc, stm32_bank->rpr_ofst); + if (stm32_bank->fpr_ofst != UNDEF_REG) + pending |= irq_reg_readl(gc, stm32_bank->fpr_ofst); + + return pending; } static void stm32_irq_handler(struct irq_desc *desc) @@ -85,7 +97,6 @@ static void stm32_irq_handler(struct irq_desc *desc) struct irq_chip *chip = irq_desc_get_chip(desc); unsigned int virq, nbanks = domain->gc->num_chips; struct irq_chip_generic *gc; - const struct stm32_exti_bank *stm32_bank; unsigned long pending; int n, i, irq_base = 0; @@ -93,7 +104,6 @@ static void stm32_irq_handler(struct irq_desc *desc) for (i = 0; i < nbanks; i++, irq_base += IRQS_PER_BANK) { gc = irq_get_domain_generic_chip(domain, irq_base); - stm32_bank = gc->private; while ((pending = stm32_exti_pending(gc))) { for_each_set_bit(n, &pending, IRQS_PER_BANK) { @@ -192,6 +202,20 @@ static const struct irq_domain_ops irq_exti_domain_ops = { .free = stm32_exti_free, }; +static void stm32_irq_ack(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + const struct stm32_exti_bank *stm32_bank = gc->private; + + irq_gc_lock(gc); + + irq_reg_writel(gc, d->mask, stm32_bank->rpr_ofst); + if (stm32_bank->fpr_ofst != UNDEF_REG) + irq_reg_writel(gc, d->mask, stm32_bank->fpr_ofst); + + irq_gc_unlock(gc); +} + static int __init stm32_exti_init(const struct stm32_exti_bank **stm32_exti_banks, int bank_nr, struct device_node *node) @@ -233,12 +257,11 @@ __init stm32_exti_init(const struct stm32_exti_bank **stm32_exti_banks, gc->reg_base = base; gc->chip_types->type = IRQ_TYPE_EDGE_BOTH; - gc->chip_types->chip.irq_ack = irq_gc_ack_set_bit; + gc->chip_types->chip.irq_ack = stm32_irq_ack; gc->chip_types->chip.irq_mask = irq_gc_mask_clr_bit; gc->chip_types->chip.irq_unmask = irq_gc_mask_set_bit; gc->chip_types->chip.irq_set_type = stm32_irq_set_type; gc->chip_types->chip.irq_set_wake = stm32_irq_set_wake; - gc->chip_types->regs.ack = stm32_bank->pr_ofst; gc->chip_types->regs.mask = stm32_bank->imr_ofst; gc->private = (void *)stm32_bank; @@ -255,7 +278,9 @@ __init stm32_exti_init(const struct stm32_exti_bank **stm32_exti_banks, writel_relaxed(0, base + stm32_bank->emr_ofst); writel_relaxed(0, base + stm32_bank->rtsr_ofst); writel_relaxed(0, base + stm32_bank->ftsr_ofst); - writel_relaxed(~0UL, base + stm32_bank->pr_ofst); + writel_relaxed(~0UL, base + stm32_bank->rpr_ofst); + if (stm32_bank->fpr_ofst != UNDEF_REG) + writel_relaxed(~0UL, base + stm32_bank->fpr_ofst); pr_info("%s: bank%d, External IRQs available:%#x\n", node->full_name, i, irqs_mask); From d9e2b19b02744068b1cc16ca7d5249a62e789601 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Thu, 26 Apr 2018 18:18:27 +0200 Subject: [PATCH 29/36] irqchip/stm32: Add suspend support This patch adds suspend feature. -Use default irq_set_wake function to store wakeup request. -Suspend function set wake_active into imr of each bank and save rising/falling trigger registers. -Resume function restore the mask_cache interrupt into imr of each bank and restore rising/falling trigger registers. Signed-off-by: Ludovic Barre Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-stm32-exti.c | 69 ++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index 69a4453c2952..1e0966714e83 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -29,6 +29,14 @@ struct stm32_exti_bank { #define UNDEF_REG ~0 +struct stm32_exti_chip_data { + const struct stm32_exti_bank *reg_bank; + u32 rtsr_cache; + u32 ftsr_cache; +}; + +static struct stm32_exti_chip_data *stm32_exti_data; + static const struct stm32_exti_bank stm32f4xx_exti_b1 = { .imr_ofst = 0x00, .emr_ofst = 0x04, @@ -81,7 +89,8 @@ static const struct stm32_exti_bank *stm32h7xx_exti_banks[] = { static unsigned long stm32_exti_pending(struct irq_chip_generic *gc) { - const struct stm32_exti_bank *stm32_bank = gc->private; + struct stm32_exti_chip_data *chip_data = gc->private; + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; unsigned long pending; pending = irq_reg_readl(gc, stm32_bank->rpr_ofst); @@ -119,7 +128,8 @@ static void stm32_irq_handler(struct irq_desc *desc) static int stm32_irq_set_type(struct irq_data *data, unsigned int type) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); - const struct stm32_exti_bank *stm32_bank = gc->private; + struct stm32_exti_chip_data *chip_data = gc->private; + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; int pin = data->hwirq % IRQS_PER_BANK; u32 rtsr, ftsr; @@ -154,25 +164,36 @@ static int stm32_irq_set_type(struct irq_data *data, unsigned int type) return 0; } -static int stm32_irq_set_wake(struct irq_data *data, unsigned int on) +static void stm32_irq_suspend(struct irq_chip_generic *gc) { - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); - const struct stm32_exti_bank *stm32_bank = gc->private; - int pin = data->hwirq % IRQS_PER_BANK; - u32 imr; + struct stm32_exti_chip_data *chip_data = gc->private; + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; irq_gc_lock(gc); - imr = irq_reg_readl(gc, stm32_bank->imr_ofst); - if (on) - imr |= BIT(pin); - else - imr &= ~BIT(pin); - irq_reg_writel(gc, imr, stm32_bank->imr_ofst); + /* save rtsr, ftsr registers */ + chip_data->rtsr_cache = irq_reg_readl(gc, stm32_bank->rtsr_ofst); + chip_data->ftsr_cache = irq_reg_readl(gc, stm32_bank->ftsr_ofst); + + irq_reg_writel(gc, gc->wake_active, stm32_bank->imr_ofst); irq_gc_unlock(gc); +} - return 0; +static void stm32_irq_resume(struct irq_chip_generic *gc) +{ + struct stm32_exti_chip_data *chip_data = gc->private; + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + + irq_gc_lock(gc); + + /* restore rtsr, ftsr registers */ + irq_reg_writel(gc, chip_data->rtsr_cache, stm32_bank->rtsr_ofst); + irq_reg_writel(gc, chip_data->ftsr_cache, stm32_bank->ftsr_ofst); + + irq_reg_writel(gc, gc->mask_cache, stm32_bank->imr_ofst); + + irq_gc_unlock(gc); } static int stm32_exti_alloc(struct irq_domain *d, unsigned int virq, @@ -205,7 +226,8 @@ static const struct irq_domain_ops irq_exti_domain_ops = { static void stm32_irq_ack(struct irq_data *d) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - const struct stm32_exti_bank *stm32_bank = gc->private; + struct stm32_exti_chip_data *chip_data = gc->private; + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; irq_gc_lock(gc); @@ -232,6 +254,11 @@ __init stm32_exti_init(const struct stm32_exti_bank **stm32_exti_banks, return -ENOMEM; } + stm32_exti_data = kcalloc(bank_nr, sizeof(*stm32_exti_data), + GFP_KERNEL); + if (!stm32_exti_data) + return -ENOMEM; + domain = irq_domain_add_linear(node, bank_nr * IRQS_PER_BANK, &irq_exti_domain_ops, NULL); if (!domain) { @@ -251,8 +278,11 @@ __init stm32_exti_init(const struct stm32_exti_bank **stm32_exti_banks, for (i = 0; i < bank_nr; i++) { const struct stm32_exti_bank *stm32_bank = stm32_exti_banks[i]; + struct stm32_exti_chip_data *chip_data = &stm32_exti_data[i]; u32 irqs_mask; + chip_data->reg_bank = stm32_bank; + gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK); gc->reg_base = base; @@ -261,9 +291,13 @@ __init stm32_exti_init(const struct stm32_exti_bank **stm32_exti_banks, gc->chip_types->chip.irq_mask = irq_gc_mask_clr_bit; gc->chip_types->chip.irq_unmask = irq_gc_mask_set_bit; gc->chip_types->chip.irq_set_type = stm32_irq_set_type; - gc->chip_types->chip.irq_set_wake = stm32_irq_set_wake; + gc->chip_types->chip.irq_set_wake = irq_gc_set_wake; + gc->suspend = stm32_irq_suspend; + gc->resume = stm32_irq_resume; + gc->wake_enabled = IRQ_MSK(IRQS_PER_BANK); + gc->chip_types->regs.mask = stm32_bank->imr_ofst; - gc->private = (void *)stm32_bank; + gc->private = (void *)chip_data; /* Determine number of irqs supported */ writel_relaxed(~0UL, base + stm32_bank->rtsr_ofst); @@ -300,6 +334,7 @@ out_free_domain: irq_domain_remove(domain); out_unmap: iounmap(base); + kfree(stm32_exti_data); return ret; } From f9fc1745501e7b91e5b853e790de63ff9e1a3404 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Thu, 26 Apr 2018 18:18:28 +0200 Subject: [PATCH 30/36] irqchip/stm32: Add host and driver data structures This patch adds host and driver data structures to support different stm32 exti controllers with variants. Signed-off-by: Ludovic Barre Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-stm32-exti.c | 156 +++++++++++++++++++++---------- 1 file changed, 106 insertions(+), 50 deletions(-) diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index 1e0966714e83..9655a574c86f 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -29,13 +29,23 @@ struct stm32_exti_bank { #define UNDEF_REG ~0 +struct stm32_exti_drv_data { + const struct stm32_exti_bank **exti_banks; + u32 bank_nr; +}; + struct stm32_exti_chip_data { + struct stm32_exti_host_data *host_data; const struct stm32_exti_bank *reg_bank; u32 rtsr_cache; u32 ftsr_cache; }; -static struct stm32_exti_chip_data *stm32_exti_data; +struct stm32_exti_host_data { + void __iomem *base; + struct stm32_exti_chip_data *chips_data; + const struct stm32_exti_drv_data *drv_data; +}; static const struct stm32_exti_bank stm32f4xx_exti_b1 = { .imr_ofst = 0x00, @@ -51,6 +61,11 @@ static const struct stm32_exti_bank *stm32f4xx_exti_banks[] = { &stm32f4xx_exti_b1, }; +static const struct stm32_exti_drv_data stm32f4xx_drv_data = { + .exti_banks = stm32f4xx_exti_banks, + .bank_nr = ARRAY_SIZE(stm32f4xx_exti_banks), +}; + static const struct stm32_exti_bank stm32h7xx_exti_b1 = { .imr_ofst = 0x80, .emr_ofst = 0x84, @@ -87,6 +102,11 @@ static const struct stm32_exti_bank *stm32h7xx_exti_banks[] = { &stm32h7xx_exti_b3, }; +static const struct stm32_exti_drv_data stm32h7xx_drv_data = { + .exti_banks = stm32h7xx_exti_banks, + .bank_nr = ARRAY_SIZE(stm32h7xx_exti_banks), +}; + static unsigned long stm32_exti_pending(struct irq_chip_generic *gc) { struct stm32_exti_chip_data *chip_data = gc->private; @@ -237,29 +257,85 @@ static void stm32_irq_ack(struct irq_data *d) irq_gc_unlock(gc); } - -static int -__init stm32_exti_init(const struct stm32_exti_bank **stm32_exti_banks, - int bank_nr, struct device_node *node) +static struct +stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, + struct device_node *node) { - unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; - int nr_irqs, nr_exti, ret, i; - struct irq_chip_generic *gc; - struct irq_domain *domain; - void *base; + struct stm32_exti_host_data *host_data; - base = of_iomap(node, 0); - if (!base) { + host_data = kzalloc(sizeof(*host_data), GFP_KERNEL); + if (!host_data) + return NULL; + + host_data->drv_data = dd; + host_data->chips_data = kcalloc(dd->bank_nr, + sizeof(struct stm32_exti_chip_data), + GFP_KERNEL); + if (!host_data->chips_data) + return NULL; + + host_data->base = of_iomap(node, 0); + if (!host_data->base) { pr_err("%pOF: Unable to map registers\n", node); - return -ENOMEM; + return NULL; } - stm32_exti_data = kcalloc(bank_nr, sizeof(*stm32_exti_data), - GFP_KERNEL); - if (!stm32_exti_data) - return -ENOMEM; + return host_data; +} - domain = irq_domain_add_linear(node, bank_nr * IRQS_PER_BANK, +static struct +stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, + u32 bank_idx, + struct device_node *node) +{ + const struct stm32_exti_bank *stm32_bank; + struct stm32_exti_chip_data *chip_data; + void __iomem *base = h_data->base; + u32 irqs_mask; + + stm32_bank = h_data->drv_data->exti_banks[bank_idx]; + chip_data = &h_data->chips_data[bank_idx]; + chip_data->host_data = h_data; + chip_data->reg_bank = stm32_bank; + + /* Determine number of irqs supported */ + writel_relaxed(~0UL, base + stm32_bank->rtsr_ofst); + irqs_mask = readl_relaxed(base + stm32_bank->rtsr_ofst); + + /* + * This IP has no reset, so after hot reboot we should + * clear registers to avoid residue + */ + writel_relaxed(0, base + stm32_bank->imr_ofst); + writel_relaxed(0, base + stm32_bank->emr_ofst); + writel_relaxed(0, base + stm32_bank->rtsr_ofst); + writel_relaxed(0, base + stm32_bank->ftsr_ofst); + writel_relaxed(~0UL, base + stm32_bank->rpr_ofst); + if (stm32_bank->fpr_ofst != UNDEF_REG) + writel_relaxed(~0UL, base + stm32_bank->fpr_ofst); + + pr_info("%s: bank%d, External IRQs available:%#x\n", + node->full_name, bank_idx, irqs_mask); + + return chip_data; +} + +static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data, + struct device_node *node) +{ + struct stm32_exti_host_data *host_data; + unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; + int nr_irqs, ret, i; + struct irq_chip_generic *gc; + struct irq_domain *domain; + + host_data = stm32_exti_host_init(drv_data, node); + if (!host_data) { + ret = -ENOMEM; + goto out_free_mem; + } + + domain = irq_domain_add_linear(node, drv_data->bank_nr * IRQS_PER_BANK, &irq_exti_domain_ops, NULL); if (!domain) { pr_err("%s: Could not register interrupt domain.\n", @@ -276,16 +352,16 @@ __init stm32_exti_init(const struct stm32_exti_bank **stm32_exti_banks, goto out_free_domain; } - for (i = 0; i < bank_nr; i++) { - const struct stm32_exti_bank *stm32_bank = stm32_exti_banks[i]; - struct stm32_exti_chip_data *chip_data = &stm32_exti_data[i]; - u32 irqs_mask; + for (i = 0; i < drv_data->bank_nr; i++) { + const struct stm32_exti_bank *stm32_bank; + struct stm32_exti_chip_data *chip_data; - chip_data->reg_bank = stm32_bank; + stm32_bank = drv_data->exti_banks[i]; + chip_data = stm32_exti_chip_init(host_data, i, node); gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK); - gc->reg_base = base; + gc->reg_base = host_data->base; gc->chip_types->type = IRQ_TYPE_EDGE_BOTH; gc->chip_types->chip.irq_ack = stm32_irq_ack; gc->chip_types->chip.irq_mask = irq_gc_mask_clr_bit; @@ -298,26 +374,6 @@ __init stm32_exti_init(const struct stm32_exti_bank **stm32_exti_banks, gc->chip_types->regs.mask = stm32_bank->imr_ofst; gc->private = (void *)chip_data; - - /* Determine number of irqs supported */ - writel_relaxed(~0UL, base + stm32_bank->rtsr_ofst); - irqs_mask = readl_relaxed(base + stm32_bank->rtsr_ofst); - nr_exti = fls(readl_relaxed(base + stm32_bank->rtsr_ofst)); - - /* - * This IP has no reset, so after hot reboot we should - * clear registers to avoid residue - */ - writel_relaxed(0, base + stm32_bank->imr_ofst); - writel_relaxed(0, base + stm32_bank->emr_ofst); - writel_relaxed(0, base + stm32_bank->rtsr_ofst); - writel_relaxed(0, base + stm32_bank->ftsr_ofst); - writel_relaxed(~0UL, base + stm32_bank->rpr_ofst); - if (stm32_bank->fpr_ofst != UNDEF_REG) - writel_relaxed(~0UL, base + stm32_bank->fpr_ofst); - - pr_info("%s: bank%d, External IRQs available:%#x\n", - node->full_name, i, irqs_mask); } nr_irqs = of_irq_count(node); @@ -333,16 +389,17 @@ __init stm32_exti_init(const struct stm32_exti_bank **stm32_exti_banks, out_free_domain: irq_domain_remove(domain); out_unmap: - iounmap(base); - kfree(stm32_exti_data); + iounmap(host_data->base); +out_free_mem: + kfree(host_data->chips_data); + kfree(host_data); return ret; } static int __init stm32f4_exti_of_init(struct device_node *np, struct device_node *parent) { - return stm32_exti_init(stm32f4xx_exti_banks, - ARRAY_SIZE(stm32f4xx_exti_banks), np); + return stm32_exti_init(&stm32f4xx_drv_data, np); } IRQCHIP_DECLARE(stm32f4_exti, "st,stm32-exti", stm32f4_exti_of_init); @@ -350,8 +407,7 @@ IRQCHIP_DECLARE(stm32f4_exti, "st,stm32-exti", stm32f4_exti_of_init); static int __init stm32h7_exti_of_init(struct device_node *np, struct device_node *parent) { - return stm32_exti_init(stm32h7xx_exti_banks, - ARRAY_SIZE(stm32h7xx_exti_banks), np); + return stm32_exti_init(&stm32h7xx_drv_data, np); } IRQCHIP_DECLARE(stm32h7_exti, "st,stm32h7-exti", stm32h7_exti_of_init); From 5a2490e029c29065c6bc2f63c10a985f85522998 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Thu, 26 Apr 2018 18:18:29 +0200 Subject: [PATCH 31/36] irqchip/stm32: Prepare common functions This patch prepares functions which could be reused by next variant of stm32 exti controller. Signed-off-by: Ludovic Barre Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-stm32-exti.c | 93 ++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 34 deletions(-) diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index 9655a574c86f..b38c6555d66f 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -145,35 +145,48 @@ static void stm32_irq_handler(struct irq_desc *desc) chained_irq_exit(chip, desc); } -static int stm32_irq_set_type(struct irq_data *data, unsigned int type) +static int stm32_exti_set_type(struct irq_data *d, + unsigned int type, u32 *rtsr, u32 *ftsr) { - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); + u32 mask = BIT(d->hwirq % IRQS_PER_BANK); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + *rtsr |= mask; + *ftsr &= ~mask; + break; + case IRQ_TYPE_EDGE_FALLING: + *rtsr &= ~mask; + *ftsr |= mask; + break; + case IRQ_TYPE_EDGE_BOTH: + *rtsr |= mask; + *ftsr |= mask; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int stm32_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct stm32_exti_chip_data *chip_data = gc->private; const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - int pin = data->hwirq % IRQS_PER_BANK; u32 rtsr, ftsr; + int err; irq_gc_lock(gc); rtsr = irq_reg_readl(gc, stm32_bank->rtsr_ofst); ftsr = irq_reg_readl(gc, stm32_bank->ftsr_ofst); - switch (type) { - case IRQ_TYPE_EDGE_RISING: - rtsr |= BIT(pin); - ftsr &= ~BIT(pin); - break; - case IRQ_TYPE_EDGE_FALLING: - rtsr &= ~BIT(pin); - ftsr |= BIT(pin); - break; - case IRQ_TYPE_EDGE_BOTH: - rtsr |= BIT(pin); - ftsr |= BIT(pin); - break; - default: + err = stm32_exti_set_type(d, type, &rtsr, &ftsr); + if (err) { irq_gc_unlock(gc); - return -EINVAL; + return err; } irq_reg_writel(gc, rtsr, stm32_bank->rtsr_ofst); @@ -184,35 +197,47 @@ static int stm32_irq_set_type(struct irq_data *data, unsigned int type) return 0; } +static void stm32_chip_suspend(struct stm32_exti_chip_data *chip_data, + u32 wake_active) +{ + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + void __iomem *base = chip_data->host_data->base; + + /* save rtsr, ftsr registers */ + chip_data->rtsr_cache = readl_relaxed(base + stm32_bank->rtsr_ofst); + chip_data->ftsr_cache = readl_relaxed(base + stm32_bank->ftsr_ofst); + + writel_relaxed(wake_active, base + stm32_bank->imr_ofst); +} + +static void stm32_chip_resume(struct stm32_exti_chip_data *chip_data, + u32 mask_cache) +{ + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + void __iomem *base = chip_data->host_data->base; + + /* restore rtsr, ftsr, registers */ + writel_relaxed(chip_data->rtsr_cache, base + stm32_bank->rtsr_ofst); + writel_relaxed(chip_data->ftsr_cache, base + stm32_bank->ftsr_ofst); + + writel_relaxed(mask_cache, base + stm32_bank->imr_ofst); +} + static void stm32_irq_suspend(struct irq_chip_generic *gc) { struct stm32_exti_chip_data *chip_data = gc->private; - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; irq_gc_lock(gc); - - /* save rtsr, ftsr registers */ - chip_data->rtsr_cache = irq_reg_readl(gc, stm32_bank->rtsr_ofst); - chip_data->ftsr_cache = irq_reg_readl(gc, stm32_bank->ftsr_ofst); - - irq_reg_writel(gc, gc->wake_active, stm32_bank->imr_ofst); - + stm32_chip_suspend(chip_data, gc->wake_active); irq_gc_unlock(gc); } static void stm32_irq_resume(struct irq_chip_generic *gc) { struct stm32_exti_chip_data *chip_data = gc->private; - const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; irq_gc_lock(gc); - - /* restore rtsr, ftsr registers */ - irq_reg_writel(gc, chip_data->rtsr_cache, stm32_bank->rtsr_ofst); - irq_reg_writel(gc, chip_data->ftsr_cache, stm32_bank->ftsr_ofst); - - irq_reg_writel(gc, gc->mask_cache, stm32_bank->imr_ofst); - + stm32_chip_resume(chip_data, gc->mask_cache); irq_gc_unlock(gc); } From 927abfc4461e7fd76d5bba593dabdec381571021 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Thu, 26 Apr 2018 18:18:30 +0200 Subject: [PATCH 32/36] irqchip/stm32: Add stm32mp1 support with hierarchy domain Exti controller has been differently integrated on stm32mp1 SoC. A parent irq has only one external interrupt. A hierachy domain could be used. Handlers are call by parent, each parent interrupt could be masked and unmasked according to the needs. Signed-off-by: Ludovic Barre Signed-off-by: Marc Zyngier --- .../interrupt-controller/st,stm32-exti.txt | 3 + drivers/irqchip/irq-stm32-exti.c | 322 ++++++++++++++++++ 2 files changed, 325 insertions(+) diff --git a/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt b/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt index edf03f09244b..136bd612bd83 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt @@ -5,11 +5,14 @@ Required properties: - compatible: Should be: "st,stm32-exti" "st,stm32h7-exti" + "st,stm32mp1-exti" - reg: Specifies base physical address and size of the registers - interrupt-controller: Indentifies the node as an interrupt controller - #interrupt-cells: Specifies the number of cells to encode an interrupt specifier, shall be 2 - interrupts: interrupts references to primary interrupt controller + (only needed for exti controller with multiple exti under + same parent interrupt: st,stm32-exti and st,stm32h7-exti") Example: diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index b38c6555d66f..ebf71460c668 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -15,6 +15,8 @@ #include #include +#include + #define IRQS_PER_BANK 32 struct stm32_exti_bank { @@ -29,14 +31,24 @@ struct stm32_exti_bank { #define UNDEF_REG ~0 +struct stm32_desc_irq { + u32 exti; + u32 irq_parent; +}; + struct stm32_exti_drv_data { const struct stm32_exti_bank **exti_banks; + const struct stm32_desc_irq *desc_irqs; u32 bank_nr; + u32 irq_nr; }; struct stm32_exti_chip_data { struct stm32_exti_host_data *host_data; const struct stm32_exti_bank *reg_bank; + struct raw_spinlock rlock; + u32 wake_active; + u32 mask_cache; u32 rtsr_cache; u32 ftsr_cache; }; @@ -107,6 +119,89 @@ static const struct stm32_exti_drv_data stm32h7xx_drv_data = { .bank_nr = ARRAY_SIZE(stm32h7xx_exti_banks), }; +static const struct stm32_exti_bank stm32mp1_exti_b1 = { + .imr_ofst = 0x80, + .emr_ofst = 0x84, + .rtsr_ofst = 0x00, + .ftsr_ofst = 0x04, + .swier_ofst = 0x08, + .rpr_ofst = 0x0C, + .fpr_ofst = 0x10, +}; + +static const struct stm32_exti_bank stm32mp1_exti_b2 = { + .imr_ofst = 0x90, + .emr_ofst = 0x94, + .rtsr_ofst = 0x20, + .ftsr_ofst = 0x24, + .swier_ofst = 0x28, + .rpr_ofst = 0x2C, + .fpr_ofst = 0x30, +}; + +static const struct stm32_exti_bank stm32mp1_exti_b3 = { + .imr_ofst = 0xA0, + .emr_ofst = 0xA4, + .rtsr_ofst = 0x40, + .ftsr_ofst = 0x44, + .swier_ofst = 0x48, + .rpr_ofst = 0x4C, + .fpr_ofst = 0x50, +}; + +static const struct stm32_exti_bank *stm32mp1_exti_banks[] = { + &stm32mp1_exti_b1, + &stm32mp1_exti_b2, + &stm32mp1_exti_b3, +}; + +static const struct stm32_desc_irq stm32mp1_desc_irq[] = { + { .exti = 1, .irq_parent = 7 }, + { .exti = 2, .irq_parent = 8 }, + { .exti = 3, .irq_parent = 9 }, + { .exti = 4, .irq_parent = 10 }, + { .exti = 5, .irq_parent = 23 }, + { .exti = 6, .irq_parent = 64 }, + { .exti = 7, .irq_parent = 65 }, + { .exti = 8, .irq_parent = 66 }, + { .exti = 9, .irq_parent = 67 }, + { .exti = 10, .irq_parent = 40 }, + { .exti = 11, .irq_parent = 42 }, + { .exti = 12, .irq_parent = 76 }, + { .exti = 13, .irq_parent = 77 }, + { .exti = 14, .irq_parent = 121 }, + { .exti = 15, .irq_parent = 127 }, + { .exti = 16, .irq_parent = 1 }, + { .exti = 65, .irq_parent = 144 }, + { .exti = 68, .irq_parent = 143 }, + { .exti = 73, .irq_parent = 129 }, +}; + +static const struct stm32_exti_drv_data stm32mp1_drv_data = { + .exti_banks = stm32mp1_exti_banks, + .bank_nr = ARRAY_SIZE(stm32mp1_exti_banks), + .desc_irqs = stm32mp1_desc_irq, + .irq_nr = ARRAY_SIZE(stm32mp1_desc_irq), +}; + +static int stm32_exti_to_irq(const struct stm32_exti_drv_data *drv_data, + irq_hw_number_t hwirq) +{ + const struct stm32_desc_irq *desc_irq; + int i; + + if (!drv_data->desc_irqs) + return -EINVAL; + + for (i = 0; i < drv_data->irq_nr; i++) { + desc_irq = &drv_data->desc_irqs[i]; + if (desc_irq->exti == hwirq) + return desc_irq->irq_parent; + } + + return -EINVAL; +} + static unsigned long stm32_exti_pending(struct irq_chip_generic *gc) { struct stm32_exti_chip_data *chip_data = gc->private; @@ -282,6 +377,173 @@ static void stm32_irq_ack(struct irq_data *d) irq_gc_unlock(gc); } + +static inline u32 stm32_exti_set_bit(struct irq_data *d, u32 reg) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + void __iomem *base = chip_data->host_data->base; + u32 val; + + val = readl_relaxed(base + reg); + val |= BIT(d->hwirq % IRQS_PER_BANK); + writel_relaxed(val, base + reg); + + return val; +} + +static inline u32 stm32_exti_clr_bit(struct irq_data *d, u32 reg) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + void __iomem *base = chip_data->host_data->base; + u32 val; + + val = readl_relaxed(base + reg); + val &= ~BIT(d->hwirq % IRQS_PER_BANK); + writel_relaxed(val, base + reg); + + return val; +} + +static void stm32_exti_h_eoi(struct irq_data *d) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + + raw_spin_lock(&chip_data->rlock); + + stm32_exti_set_bit(d, stm32_bank->rpr_ofst); + if (stm32_bank->fpr_ofst != UNDEF_REG) + stm32_exti_set_bit(d, stm32_bank->fpr_ofst); + + raw_spin_unlock(&chip_data->rlock); + + if (d->parent_data->chip) + irq_chip_eoi_parent(d); +} + +static void stm32_exti_h_mask(struct irq_data *d) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + + raw_spin_lock(&chip_data->rlock); + chip_data->mask_cache = stm32_exti_clr_bit(d, stm32_bank->imr_ofst); + raw_spin_unlock(&chip_data->rlock); + + if (d->parent_data->chip) + irq_chip_mask_parent(d); +} + +static void stm32_exti_h_unmask(struct irq_data *d) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + + raw_spin_lock(&chip_data->rlock); + chip_data->mask_cache = stm32_exti_set_bit(d, stm32_bank->imr_ofst); + raw_spin_unlock(&chip_data->rlock); + + if (d->parent_data->chip) + irq_chip_unmask_parent(d); +} + +static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + void __iomem *base = chip_data->host_data->base; + u32 rtsr, ftsr; + int err; + + raw_spin_lock(&chip_data->rlock); + rtsr = readl_relaxed(base + stm32_bank->rtsr_ofst); + ftsr = readl_relaxed(base + stm32_bank->ftsr_ofst); + + err = stm32_exti_set_type(d, type, &rtsr, &ftsr); + if (err) { + raw_spin_unlock(&chip_data->rlock); + return err; + } + + writel_relaxed(rtsr, base + stm32_bank->rtsr_ofst); + writel_relaxed(ftsr, base + stm32_bank->ftsr_ofst); + raw_spin_unlock(&chip_data->rlock); + + return 0; +} + +static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on) +{ + struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); + u32 mask = BIT(d->hwirq % IRQS_PER_BANK); + + raw_spin_lock(&chip_data->rlock); + + if (on) + chip_data->wake_active |= mask; + else + chip_data->wake_active &= ~mask; + + raw_spin_unlock(&chip_data->rlock); + + return 0; +} + +static int stm32_exti_h_set_affinity(struct irq_data *d, + const struct cpumask *dest, bool force) +{ + if (d->parent_data->chip) + return irq_chip_set_affinity_parent(d, dest, force); + + return -EINVAL; +} + +static struct irq_chip stm32_exti_h_chip = { + .name = "stm32-exti-h", + .irq_eoi = stm32_exti_h_eoi, + .irq_mask = stm32_exti_h_mask, + .irq_unmask = stm32_exti_h_unmask, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = stm32_exti_h_set_type, + .irq_set_wake = stm32_exti_h_set_wake, + .flags = IRQCHIP_MASK_ON_SUSPEND, +#ifdef CONFIG_SMP + .irq_set_affinity = stm32_exti_h_set_affinity, +#endif +}; + +static int stm32_exti_h_domain_alloc(struct irq_domain *dm, + unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct stm32_exti_host_data *host_data = dm->host_data; + struct stm32_exti_chip_data *chip_data; + struct irq_fwspec *fwspec = data; + struct irq_fwspec p_fwspec; + irq_hw_number_t hwirq; + int p_irq, bank; + + hwirq = fwspec->param[0]; + bank = hwirq / IRQS_PER_BANK; + chip_data = &host_data->chips_data[bank]; + + irq_domain_set_hwirq_and_chip(dm, virq, hwirq, + &stm32_exti_h_chip, chip_data); + + p_irq = stm32_exti_to_irq(host_data->drv_data, hwirq); + if (p_irq >= 0) { + p_fwspec.fwnode = dm->parent->fwnode; + p_fwspec.param_count = 3; + p_fwspec.param[0] = GIC_SPI; + p_fwspec.param[1] = p_irq; + p_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; + + return irq_domain_alloc_irqs_parent(dm, virq, 1, &p_fwspec); + } + + return 0; +} + static struct stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, struct device_node *node) @@ -323,6 +585,8 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, chip_data->host_data = h_data; chip_data->reg_bank = stm32_bank; + raw_spin_lock_init(&chip_data->rlock); + /* Determine number of irqs supported */ writel_relaxed(~0UL, base + stm32_bank->rtsr_ofst); irqs_mask = readl_relaxed(base + stm32_bank->rtsr_ofst); @@ -421,6 +685,56 @@ out_free_mem: return ret; } +static const struct irq_domain_ops stm32_exti_h_domain_ops = { + .alloc = stm32_exti_h_domain_alloc, + .free = irq_domain_free_irqs_common, +}; + +static int +__init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data, + struct device_node *node, + struct device_node *parent) +{ + struct irq_domain *parent_domain, *domain; + struct stm32_exti_host_data *host_data; + int ret, i; + + parent_domain = irq_find_host(parent); + if (!parent_domain) { + pr_err("interrupt-parent not found\n"); + return -EINVAL; + } + + host_data = stm32_exti_host_init(drv_data, node); + if (!host_data) { + ret = -ENOMEM; + goto out_free_mem; + } + + for (i = 0; i < drv_data->bank_nr; i++) + stm32_exti_chip_init(host_data, i, node); + + domain = irq_domain_add_hierarchy(parent_domain, 0, + drv_data->bank_nr * IRQS_PER_BANK, + node, &stm32_exti_h_domain_ops, + host_data); + + if (!domain) { + pr_err("%s: Could not register exti domain.\n", node->name); + ret = -ENOMEM; + goto out_unmap; + } + + return 0; + +out_unmap: + iounmap(host_data->base); +out_free_mem: + kfree(host_data->chips_data); + kfree(host_data); + return ret; +} + static int __init stm32f4_exti_of_init(struct device_node *np, struct device_node *parent) { @@ -436,3 +750,11 @@ static int __init stm32h7_exti_of_init(struct device_node *np, } IRQCHIP_DECLARE(stm32h7_exti, "st,stm32h7-exti", stm32h7_exti_of_init); + +static int __init stm32mp1_exti_of_init(struct device_node *np, + struct device_node *parent) +{ + return stm32_exti_hierarchy_init(&stm32mp1_drv_data, np, parent); +} + +IRQCHIP_DECLARE(stm32mp1_exti, "st,stm32mp1-exti", stm32mp1_exti_of_init); From 73958b31c1c1d94010ba840d7f5ac118bbbdafb5 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Thu, 26 Apr 2018 18:18:31 +0200 Subject: [PATCH 33/36] irqchip/stm32: Add suspend/resume support for hierarchy domain This patch adds suspend/resume feature for exti hierarchy domain. -suspend function sets wake_active into imr of each banks -resume function restores the mask_cache interrupt into imr of each banks Signed-off-by: Ludovic Barre Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-stm32-exti.c | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index ebf71460c668..5089c1e2838d 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -59,6 +60,8 @@ struct stm32_exti_host_data { const struct stm32_exti_drv_data *drv_data; }; +static struct stm32_exti_host_data *stm32_host_data; + static const struct stm32_exti_bank stm32f4xx_exti_b1 = { .imr_ofst = 0x00, .emr_ofst = 0x04, @@ -498,6 +501,48 @@ static int stm32_exti_h_set_affinity(struct irq_data *d, return -EINVAL; } +#ifdef CONFIG_PM +static int stm32_exti_h_suspend(void) +{ + struct stm32_exti_chip_data *chip_data; + int i; + + for (i = 0; i < stm32_host_data->drv_data->bank_nr; i++) { + chip_data = &stm32_host_data->chips_data[i]; + raw_spin_lock(&chip_data->rlock); + stm32_chip_suspend(chip_data, chip_data->wake_active); + raw_spin_unlock(&chip_data->rlock); + } + + return 0; +} + +static void stm32_exti_h_resume(void) +{ + struct stm32_exti_chip_data *chip_data; + int i; + + for (i = 0; i < stm32_host_data->drv_data->bank_nr; i++) { + chip_data = &stm32_host_data->chips_data[i]; + raw_spin_lock(&chip_data->rlock); + stm32_chip_resume(chip_data, chip_data->mask_cache); + raw_spin_unlock(&chip_data->rlock); + } +} + +static struct syscore_ops stm32_exti_h_syscore_ops = { + .suspend = stm32_exti_h_suspend, + .resume = stm32_exti_h_resume, +}; + +static void stm32_exti_h_syscore_init(void) +{ + register_syscore_ops(&stm32_exti_h_syscore_ops); +} +#else +static inline void stm32_exti_h_syscore_init(void) {} +#endif + static struct irq_chip stm32_exti_h_chip = { .name = "stm32-exti-h", .irq_eoi = stm32_exti_h_eoi, @@ -567,6 +612,8 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, return NULL; } + stm32_host_data = host_data; + return host_data; } @@ -725,6 +772,8 @@ __init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data, goto out_unmap; } + stm32_exti_h_syscore_init(); + return 0; out_unmap: From c12fcfb12e48f8a34bebd3f31b5c4b0084768578 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Thu, 26 Apr 2018 18:18:32 +0200 Subject: [PATCH 34/36] pinctrl/stm32: Add irq_eoi for stm32gpio irqchip -Parent domain of stm32gpio evolves to hierarchy domain and could have a handle_fasteoi_irq. So an irq_eoi parent callback is needed for children. -Replace space by tabulation. Signed-off-by: Ludovic Barre Signed-off-by: Marc Zyngier --- drivers/pinctrl/stm32/pinctrl-stm32.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c index 6cbcff42ba47..dfed60982a8a 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32.c +++ b/drivers/pinctrl/stm32/pinctrl-stm32.c @@ -267,12 +267,13 @@ static void stm32_gpio_irq_release_resources(struct irq_data *irq_data) } static struct irq_chip stm32_gpio_irq_chip = { - .name = "stm32gpio", - .irq_ack = irq_chip_ack_parent, - .irq_mask = irq_chip_mask_parent, - .irq_unmask = irq_chip_unmask_parent, - .irq_set_type = irq_chip_set_type_parent, - .irq_set_wake = irq_chip_set_wake_parent, + .name = "stm32gpio", + .irq_eoi = irq_chip_eoi_parent, + .irq_ack = irq_chip_ack_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_wake = irq_chip_set_wake_parent, .irq_request_resources = stm32_gpio_irq_request_resources, .irq_release_resources = stm32_gpio_irq_release_resources, }; From 5f0e9d2557d75d93a5a08de629b8f0a558100694 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Thu, 26 Apr 2018 18:18:33 +0200 Subject: [PATCH 35/36] ARM: dts: stm32: Add exti support for stm32mp157c This patch adds external interrupt (exti) support on stm32mp157c SoC. Signed-off-by: Ludovic Barre Signed-off-by: Marc Zyngier --- arch/arm/boot/dts/stm32mp157c.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi index 9e17e42b02b2..4fa0df853c8a 100644 --- a/arch/arm/boot/dts/stm32mp157c.dtsi +++ b/arch/arm/boot/dts/stm32mp157c.dtsi @@ -183,6 +183,13 @@ status = "disabled"; }; + exti: interrupt-controller@5000d000 { + compatible = "st,stm32mp1-exti", "syscon"; + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x5000d000 0x400>; + }; + usart1: serial@5c000000 { compatible = "st,stm32h7-uart"; reg = <0x5c000000 0x400>; From 6a88c221acbaaa0460d190f99093ab8114f23c09 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Thu, 26 Apr 2018 18:18:34 +0200 Subject: [PATCH 36/36] ARM: dts: stm32: Add exti support to stm32mp157 pinctrl This patch adds support of external interrupt for gpio[a..k], gpioz Signed-off-by: Ludovic Barre Signed-off-by: Marc Zyngier --- arch/arm/boot/dts/stm32mp157-pinctrl.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi index c0743305f31b..eb96ac3e6c1d 100644 --- a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi +++ b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi @@ -12,6 +12,8 @@ #size-cells = <1>; compatible = "st,stm32mp157-pinctrl"; ranges = <0 0x50002000 0xa400>; + interrupt-parent = <&exti>; + st,syscfg = <&exti 0x60 0xff>; pins-are-numbered; gpioa: gpio@50002000 { @@ -166,6 +168,8 @@ compatible = "st,stm32mp157-z-pinctrl"; ranges = <0 0x54004000 0x400>; pins-are-numbered; + interrupt-parent = <&exti>; + st,syscfg = <&exti 0x60 0xff>; status = "disabled"; gpioz: gpio@54004000 {