From 20e407e195b29a4f5a18d713a61f54a75f992bd5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 8 Nov 2016 17:15:01 -0800 Subject: [PATCH 01/25] genirq/affinity: Introduce struct irq_affinity Some drivers (various network and RDMA adapter for example) have a MSI-X vector layout where most of the vectors are used for I/O queues and should have CPU affinity assigned to them, but some (usually 1 but sometimes more) at the beginning or end are used for low-performance admin or configuration work and should not have any explicit affinity assigned to them. Add a new irq_affinity structure, which will be passed through a variant of pci_irq_alloc_vectors that allows to specify these requirements (and is extensible to any future quirks in that area) so that the core IRQ affinity algorithm can take this quirks into account. Signed-off-by: Christoph Hellwig Reviewed-by: Johannes Thumshirn Reviewed-by: Hannes Reinecke Acked-by: Jens Axboe Cc: linux-block@vger.kernel.org Cc: linux-pci@vger.kernel.org Link: http://lkml.kernel.org/r/1478654107-7384-2-git-send-email-hch@lst.de Signed-off-by: Thomas Gleixner --- include/linux/interrupt.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 72f0721f75e73..6b5268688a81a 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -232,6 +232,18 @@ struct irq_affinity_notify { void (*release)(struct kref *ref); }; +/** + * struct irq_affinity - Description for automatic irq affinity assignements + * @pre_vectors: Don't apply affinity to @pre_vectors at beginning of + * the MSI(-X) vector space + * @post_vectors: Don't apply affinity to @post_vectors at end of + * the MSI(-X) vector space + */ +struct irq_affinity { + int pre_vectors; + int post_vectors; +}; + #if defined(CONFIG_SMP) extern cpumask_var_t irq_default_affinity; From 212bd846223c718b6577d4df16fd8d05a55ad914 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 8 Nov 2016 17:15:02 -0800 Subject: [PATCH 02/25] genirq/affinity: Handle pre/post vectors in irq_calc_affinity_vectors() Only calculate the affinity for the main I/O vectors, and skip the pre or post vectors specified by struct irq_affinity. Also remove the irq_affinity cpumask argument that has never been used. If we ever need it in the future we can pass it through struct irq_affinity. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Acked-by: Jens Axboe Cc: linux-block@vger.kernel.org Cc: linux-pci@vger.kernel.org Link: http://lkml.kernel.org/r/1478654107-7384-3-git-send-email-hch@lst.de Signed-off-by: Thomas Gleixner --- drivers/pci/msi.c | 8 ++++---- include/linux/interrupt.h | 4 ++-- kernel/irq/affinity.c | 24 ++++++++++-------------- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index ad70507cfb566..dad2da7cf80e4 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -1061,6 +1061,7 @@ EXPORT_SYMBOL(pci_msi_enabled); static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec, unsigned int flags) { + static const struct irq_affinity default_affd; bool affinity = flags & PCI_IRQ_AFFINITY; int nvec; int rc; @@ -1091,8 +1092,7 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec, for (;;) { if (affinity) { - nvec = irq_calc_affinity_vectors(dev->irq_affinity, - nvec); + nvec = irq_calc_affinity_vectors(nvec, &default_affd); if (nvec < minvec) return -ENOSPC; } @@ -1132,6 +1132,7 @@ static int __pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, int minvec, int maxvec, unsigned int flags) { + static const struct irq_affinity default_affd; bool affinity = flags & PCI_IRQ_AFFINITY; int rc, nvec = maxvec; @@ -1140,8 +1141,7 @@ static int __pci_enable_msix_range(struct pci_dev *dev, for (;;) { if (affinity) { - nvec = irq_calc_affinity_vectors(dev->irq_affinity, - nvec); + nvec = irq_calc_affinity_vectors(nvec, &default_affd); if (nvec < minvec) return -ENOSPC; } diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 6b5268688a81a..9081f23bc0ff2 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -291,7 +291,7 @@ extern int irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify); struct cpumask *irq_create_affinity_masks(const struct cpumask *affinity, int nvec); -int irq_calc_affinity_vectors(const struct cpumask *affinity, int maxvec); +int irq_calc_affinity_vectors(int maxvec, const struct irq_affinity *affd); #else /* CONFIG_SMP */ @@ -331,7 +331,7 @@ irq_create_affinity_masks(const struct cpumask *affinity, int nvec) } static inline int -irq_calc_affinity_vectors(const struct cpumask *affinity, int maxvec) +irq_calc_affinity_vectors(int maxvec, const struct irq_affinity *affd) { return maxvec; } diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c index 17f51d63da563..8d9259727cb42 100644 --- a/kernel/irq/affinity.c +++ b/kernel/irq/affinity.c @@ -131,24 +131,20 @@ struct cpumask *irq_create_affinity_masks(const struct cpumask *affinity, } /** - * irq_calc_affinity_vectors - Calculate to optimal number of vectors for a given affinity mask - * @affinity: The affinity mask to spread. If NULL cpu_online_mask - * is used - * @maxvec: The maximum number of vectors available + * irq_calc_affinity_vectors - Calculate the optimal number of vectors + * @maxvec: The maximum number of vectors available + * @affd: Description of the affinity requirements */ -int irq_calc_affinity_vectors(const struct cpumask *affinity, int maxvec) +int irq_calc_affinity_vectors(int maxvec, const struct irq_affinity *affd) { - int cpus, ret; + int resv = affd->pre_vectors + affd->post_vectors; + int vecs = maxvec - resv; + int cpus; /* Stabilize the cpumasks */ get_online_cpus(); - /* If the supplied affinity mask is NULL, use cpu online mask */ - if (!affinity) - affinity = cpu_online_mask; - - cpus = cpumask_weight(affinity); - ret = (cpus < maxvec) ? cpus : maxvec; - + cpus = cpumask_weight(cpu_online_mask); put_online_cpus(); - return ret; + + return min(cpus, vecs) + resv; } From 67c93c218dc5d1b45d547771f1fdb44a381e1faf Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 8 Nov 2016 17:15:03 -0800 Subject: [PATCH 03/25] genirq/affinity: Handle pre/post vectors in irq_create_affinity_masks() Only calculate the affinity for the main I/O vectors, and skip the pre or post vectors specified by struct irq_affinity. Also remove the irq_affinity cpumask argument that has never been used. If we ever need it in the future we can pass it through struct irq_affinity. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Acked-by: Bjorn Helgaas Acked-by: Jens Axboe Cc: linux-block@vger.kernel.org Cc: linux-pci@vger.kernel.org Link: http://lkml.kernel.org/r/1478654107-7384-4-git-send-email-hch@lst.de Signed-off-by: Thomas Gleixner --- drivers/pci/msi.c | 6 +++-- include/linux/interrupt.h | 4 ++-- kernel/irq/affinity.c | 46 +++++++++++++++++++++------------------ 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index dad2da7cf80e4..f4a108b59336b 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -553,12 +553,13 @@ static int populate_msi_sysfs(struct pci_dev *pdev) static struct msi_desc * msi_setup_entry(struct pci_dev *dev, int nvec, bool affinity) { + static const struct irq_affinity default_affd; struct cpumask *masks = NULL; struct msi_desc *entry; u16 control; if (affinity) { - masks = irq_create_affinity_masks(dev->irq_affinity, nvec); + masks = irq_create_affinity_masks(nvec, &default_affd); if (!masks) pr_err("Unable to allocate affinity masks, ignoring\n"); } @@ -692,12 +693,13 @@ static int msix_setup_entries(struct pci_dev *dev, void __iomem *base, struct msix_entry *entries, int nvec, bool affinity) { + static const struct irq_affinity default_affd; struct cpumask *curmsk, *masks = NULL; struct msi_desc *entry; int ret, i; if (affinity) { - masks = irq_create_affinity_masks(dev->irq_affinity, nvec); + masks = irq_create_affinity_masks(nvec, &default_affd); if (!masks) pr_err("Unable to allocate affinity masks, ignoring\n"); } diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 9081f23bc0ff2..53144e78a3691 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -290,7 +290,7 @@ extern int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m); extern int irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify); -struct cpumask *irq_create_affinity_masks(const struct cpumask *affinity, int nvec); +struct cpumask *irq_create_affinity_masks(int nvec, const struct irq_affinity *affd); int irq_calc_affinity_vectors(int maxvec, const struct irq_affinity *affd); #else /* CONFIG_SMP */ @@ -325,7 +325,7 @@ irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify) } static inline struct cpumask * -irq_create_affinity_masks(const struct cpumask *affinity, int nvec) +irq_create_affinity_masks(int nvec, const struct irq_affinity *affd) { return NULL; } diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c index 8d9259727cb42..17360bd9619bd 100644 --- a/kernel/irq/affinity.c +++ b/kernel/irq/affinity.c @@ -51,16 +51,16 @@ static int get_nodes_in_cpumask(const struct cpumask *mask, nodemask_t *nodemsk) /** * irq_create_affinity_masks - Create affinity masks for multiqueue spreading - * @affinity: The affinity mask to spread. If NULL cpu_online_mask - * is used - * @nvecs: The number of vectors + * @nvecs: The total number of vectors + * @affd: Description of the affinity requirements * * Returns the masks pointer or NULL if allocation failed. */ -struct cpumask *irq_create_affinity_masks(const struct cpumask *affinity, - int nvec) +struct cpumask * +irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) { - int n, nodes, vecs_per_node, cpus_per_vec, extra_vecs, curvec = 0; + int n, nodes, vecs_per_node, cpus_per_vec, extra_vecs, curvec; + int affv = nvecs - affd->pre_vectors - affd->post_vectors; nodemask_t nodemsk = NODE_MASK_NONE; struct cpumask *masks; cpumask_var_t nmsk; @@ -68,46 +68,46 @@ struct cpumask *irq_create_affinity_masks(const struct cpumask *affinity, if (!zalloc_cpumask_var(&nmsk, GFP_KERNEL)) return NULL; - masks = kzalloc(nvec * sizeof(*masks), GFP_KERNEL); + masks = kcalloc(nvecs, sizeof(*masks), GFP_KERNEL); if (!masks) goto out; + /* Fill out vectors at the beginning that don't need affinity */ + for (curvec = 0; curvec < affd->pre_vectors; curvec++) + cpumask_copy(masks + curvec, cpu_possible_mask); + /* Stabilize the cpumasks */ get_online_cpus(); - /* If the supplied affinity mask is NULL, use cpu online mask */ - if (!affinity) - affinity = cpu_online_mask; - - nodes = get_nodes_in_cpumask(affinity, &nodemsk); + nodes = get_nodes_in_cpumask(cpu_online_mask, &nodemsk); /* * If the number of nodes in the mask is less than or equal the * number of vectors we just spread the vectors across the nodes. */ - if (nvec <= nodes) { + if (affv <= nodes) { for_each_node_mask(n, nodemsk) { cpumask_copy(masks + curvec, cpumask_of_node(n)); - if (++curvec == nvec) + if (++curvec == affv) break; } - goto outonl; + goto done; } /* Spread the vectors per node */ - vecs_per_node = nvec / nodes; + vecs_per_node = affv / nodes; /* Account for rounding errors */ - extra_vecs = nvec - (nodes * vecs_per_node); + extra_vecs = affv - (nodes * vecs_per_node); for_each_node_mask(n, nodemsk) { int ncpus, v, vecs_to_assign = vecs_per_node; /* Get the cpus on this node which are in the mask */ - cpumask_and(nmsk, affinity, cpumask_of_node(n)); + cpumask_and(nmsk, cpu_online_mask, cpumask_of_node(n)); /* Calculate the number of cpus per vector */ ncpus = cpumask_weight(nmsk); - for (v = 0; curvec < nvec && v < vecs_to_assign; curvec++, v++) { + for (v = 0; curvec < affv && v < vecs_to_assign; curvec++, v++) { cpus_per_vec = ncpus / vecs_to_assign; /* Account for extra vectors to compensate rounding errors */ @@ -119,12 +119,16 @@ struct cpumask *irq_create_affinity_masks(const struct cpumask *affinity, irq_spread_init_one(masks + curvec, nmsk, cpus_per_vec); } - if (curvec >= nvec) + if (curvec >= affv) break; } -outonl: +done: put_online_cpus(); + + /* Fill out vectors at the end that don't need affinity */ + for (; curvec < nvecs; curvec++) + cpumask_copy(masks + curvec, cpu_possible_mask); out: free_cpumask_var(nmsk); return masks; From 61e1c5905290efe48bacda5e342d4af4cb1b923b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 8 Nov 2016 17:15:04 -0800 Subject: [PATCH 04/25] PCI/MSI: Propagate IRQ affinity description through the MSI code No API change yet, just pass it down all the way from pci_alloc_irq_vectors() to the core MSI code. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Reviewed-by: Johannes Thumshirn Acked-by: Bjorn Helgaas Acked-by: Jens Axboe Cc: linux-block@vger.kernel.org Cc: linux-pci@vger.kernel.org Link: http://lkml.kernel.org/r/1478654107-7384-5-git-send-email-hch@lst.de Signed-off-by: Thomas Gleixner --- drivers/pci/msi.c | 66 +++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index f4a108b59336b..512f388a74f26 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -551,15 +551,14 @@ static int populate_msi_sysfs(struct pci_dev *pdev) } static struct msi_desc * -msi_setup_entry(struct pci_dev *dev, int nvec, bool affinity) +msi_setup_entry(struct pci_dev *dev, int nvec, const struct irq_affinity *affd) { - static const struct irq_affinity default_affd; struct cpumask *masks = NULL; struct msi_desc *entry; u16 control; - if (affinity) { - masks = irq_create_affinity_masks(nvec, &default_affd); + if (affd) { + masks = irq_create_affinity_masks(nvec, affd); if (!masks) pr_err("Unable to allocate affinity masks, ignoring\n"); } @@ -619,7 +618,8 @@ static int msi_verify_entries(struct pci_dev *dev) * an error, and a positive return value indicates the number of interrupts * which could have been allocated. */ -static int msi_capability_init(struct pci_dev *dev, int nvec, bool affinity) +static int msi_capability_init(struct pci_dev *dev, int nvec, + const struct irq_affinity *affd) { struct msi_desc *entry; int ret; @@ -627,7 +627,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec, bool affinity) pci_msi_set_enable(dev, 0); /* Disable MSI during set up */ - entry = msi_setup_entry(dev, nvec, affinity); + entry = msi_setup_entry(dev, nvec, affd); if (!entry) return -ENOMEM; @@ -691,15 +691,14 @@ static void __iomem *msix_map_region(struct pci_dev *dev, unsigned nr_entries) static int msix_setup_entries(struct pci_dev *dev, void __iomem *base, struct msix_entry *entries, int nvec, - bool affinity) + const struct irq_affinity *affd) { - static const struct irq_affinity default_affd; struct cpumask *curmsk, *masks = NULL; struct msi_desc *entry; int ret, i; - if (affinity) { - masks = irq_create_affinity_masks(nvec, &default_affd); + if (affd) { + masks = irq_create_affinity_masks(nvec, affd); if (!masks) pr_err("Unable to allocate affinity masks, ignoring\n"); } @@ -755,14 +754,14 @@ static void msix_program_entries(struct pci_dev *dev, * @dev: pointer to the pci_dev data structure of MSI-X device function * @entries: pointer to an array of struct msix_entry entries * @nvec: number of @entries - * @affinity: flag to indicate cpu irq affinity mask should be set + * @affd: Optional pointer to enable automatic affinity assignement * * Setup the MSI-X capability structure of device function with a * single MSI-X irq. A return of zero indicates the successful setup of * requested MSI-X entries with allocated irqs or non-zero for otherwise. **/ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, - int nvec, bool affinity) + int nvec, const struct irq_affinity *affd) { int ret; u16 control; @@ -777,7 +776,7 @@ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, if (!base) return -ENOMEM; - ret = msix_setup_entries(dev, base, entries, nvec, affinity); + ret = msix_setup_entries(dev, base, entries, nvec, affd); if (ret) return ret; @@ -958,7 +957,7 @@ int pci_msix_vec_count(struct pci_dev *dev) EXPORT_SYMBOL(pci_msix_vec_count); static int __pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, - int nvec, bool affinity) + int nvec, const struct irq_affinity *affd) { int nr_entries; int i, j; @@ -990,7 +989,7 @@ static int __pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, dev_info(&dev->dev, "can't enable MSI-X (MSI IRQ already assigned)\n"); return -EINVAL; } - return msix_capability_init(dev, entries, nvec, affinity); + return msix_capability_init(dev, entries, nvec, affd); } /** @@ -1010,7 +1009,7 @@ static int __pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, **/ int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec) { - return __pci_enable_msix(dev, entries, nvec, false); + return __pci_enable_msix(dev, entries, nvec, NULL); } EXPORT_SYMBOL(pci_enable_msix); @@ -1061,10 +1060,8 @@ int pci_msi_enabled(void) EXPORT_SYMBOL(pci_msi_enabled); static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec, - unsigned int flags) + const struct irq_affinity *affd) { - static const struct irq_affinity default_affd; - bool affinity = flags & PCI_IRQ_AFFINITY; int nvec; int rc; @@ -1093,13 +1090,13 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec, nvec = maxvec; for (;;) { - if (affinity) { - nvec = irq_calc_affinity_vectors(nvec, &default_affd); + if (affd) { + nvec = irq_calc_affinity_vectors(nvec, affd); if (nvec < minvec) return -ENOSPC; } - rc = msi_capability_init(dev, nvec, affinity); + rc = msi_capability_init(dev, nvec, affd); if (rc == 0) return nvec; @@ -1126,29 +1123,27 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec, **/ int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec) { - return __pci_enable_msi_range(dev, minvec, maxvec, 0); + return __pci_enable_msi_range(dev, minvec, maxvec, NULL); } EXPORT_SYMBOL(pci_enable_msi_range); static int __pci_enable_msix_range(struct pci_dev *dev, - struct msix_entry *entries, int minvec, int maxvec, - unsigned int flags) + struct msix_entry *entries, int minvec, + int maxvec, const struct irq_affinity *affd) { - static const struct irq_affinity default_affd; - bool affinity = flags & PCI_IRQ_AFFINITY; int rc, nvec = maxvec; if (maxvec < minvec) return -ERANGE; for (;;) { - if (affinity) { - nvec = irq_calc_affinity_vectors(nvec, &default_affd); + if (affd) { + nvec = irq_calc_affinity_vectors(nvec, affd); if (nvec < minvec) return -ENOSPC; } - rc = __pci_enable_msix(dev, entries, nvec, affinity); + rc = __pci_enable_msix(dev, entries, nvec, affd); if (rc == 0) return nvec; @@ -1179,7 +1174,7 @@ static int __pci_enable_msix_range(struct pci_dev *dev, int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, int minvec, int maxvec) { - return __pci_enable_msix_range(dev, entries, minvec, maxvec, 0); + return __pci_enable_msix_range(dev, entries, minvec, maxvec, NULL); } EXPORT_SYMBOL(pci_enable_msix_range); @@ -1203,17 +1198,22 @@ EXPORT_SYMBOL(pci_enable_msix_range); int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs, unsigned int max_vecs, unsigned int flags) { + static const struct irq_affinity msi_default_affd; + const struct irq_affinity *affd = NULL; int vecs = -ENOSPC; + if (flags & PCI_IRQ_AFFINITY) + affd = &msi_default_affd; + if (flags & PCI_IRQ_MSIX) { vecs = __pci_enable_msix_range(dev, NULL, min_vecs, max_vecs, - flags); + affd); if (vecs > 0) return vecs; } if (flags & PCI_IRQ_MSI) { - vecs = __pci_enable_msi_range(dev, min_vecs, max_vecs, flags); + vecs = __pci_enable_msi_range(dev, min_vecs, max_vecs, affd); if (vecs > 0) return vecs; } From 402723ad5c625ee052432698ae5e56b02d38d4ec Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 8 Nov 2016 17:15:05 -0800 Subject: [PATCH 05/25] PCI/MSI: Provide pci_alloc_irq_vectors_affinity() This is a variant of pci_alloc_irq_vectors() that allows passing a struct irq_affinity to provide fine-grained IRQ affinity control. For now this means being able to exclude vectors at the beginning or end of the MSI vector space, but it could also be used for any other quirks needed in the future (e.g. more vectors than CPUs, or excluding CPUs from the spreading). Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Reviewed-by: Johannes Thumshirn Acked-by: Bjorn Helgaas Acked-by: Jens Axboe Cc: linux-block@vger.kernel.org Cc: linux-pci@vger.kernel.org Link: http://lkml.kernel.org/r/1478654107-7384-6-git-send-email-hch@lst.de Signed-off-by: Thomas Gleixner --- drivers/pci/msi.c | 20 +++++++++++++------- include/linux/pci.h | 24 +++++++++++++++++++----- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 512f388a74f26..dd27f73a45fc1 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -1179,11 +1179,12 @@ int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, EXPORT_SYMBOL(pci_enable_msix_range); /** - * pci_alloc_irq_vectors - allocate multiple IRQs for a device + * pci_alloc_irq_vectors_affinity - allocate multiple IRQs for a device * @dev: PCI device to operate on * @min_vecs: minimum number of vectors required (must be >= 1) * @max_vecs: maximum (desired) number of vectors * @flags: flags or quirks for the allocation + * @affd: optional description of the affinity requirements * * Allocate up to @max_vecs interrupt vectors for @dev, using MSI-X or MSI * vectors if available, and fall back to a single legacy vector @@ -1195,15 +1196,20 @@ EXPORT_SYMBOL(pci_enable_msix_range); * To get the Linux IRQ number used for a vector that can be passed to * request_irq() use the pci_irq_vector() helper. */ -int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs, - unsigned int max_vecs, unsigned int flags) +int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs, + unsigned int max_vecs, unsigned int flags, + const struct irq_affinity *affd) { static const struct irq_affinity msi_default_affd; - const struct irq_affinity *affd = NULL; int vecs = -ENOSPC; - if (flags & PCI_IRQ_AFFINITY) - affd = &msi_default_affd; + if (flags & PCI_IRQ_AFFINITY) { + if (!affd) + affd = &msi_default_affd; + } else { + if (WARN_ON(affd)) + affd = NULL; + } if (flags & PCI_IRQ_MSIX) { vecs = __pci_enable_msix_range(dev, NULL, min_vecs, max_vecs, @@ -1226,7 +1232,7 @@ int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs, return vecs; } -EXPORT_SYMBOL(pci_alloc_irq_vectors); +EXPORT_SYMBOL(pci_alloc_irq_vectors_affinity); /** * pci_free_irq_vectors - free previously allocated IRQs for a device diff --git a/include/linux/pci.h b/include/linux/pci.h index 0e49f70dbd9b0..7090f5ff72528 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -244,6 +244,7 @@ struct pci_cap_saved_state { struct pci_cap_saved_data cap; }; +struct irq_affinity; struct pcie_link_state; struct pci_vpd; struct pci_sriov; @@ -1310,8 +1311,10 @@ static inline int pci_enable_msix_exact(struct pci_dev *dev, return rc; return 0; } -int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs, - unsigned int max_vecs, unsigned int flags); +int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs, + unsigned int max_vecs, unsigned int flags, + const struct irq_affinity *affd); + void pci_free_irq_vectors(struct pci_dev *dev); int pci_irq_vector(struct pci_dev *dev, unsigned int nr); const struct cpumask *pci_irq_get_affinity(struct pci_dev *pdev, int vec); @@ -1339,14 +1342,17 @@ static inline int pci_enable_msix_range(struct pci_dev *dev, static inline int pci_enable_msix_exact(struct pci_dev *dev, struct msix_entry *entries, int nvec) { return -ENOSYS; } -static inline int pci_alloc_irq_vectors(struct pci_dev *dev, - unsigned int min_vecs, unsigned int max_vecs, - unsigned int flags) + +static inline int +pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs, + unsigned int max_vecs, unsigned int flags, + const struct irq_affinity *aff_desc) { if (min_vecs > 1) return -EINVAL; return 1; } + static inline void pci_free_irq_vectors(struct pci_dev *dev) { } @@ -1364,6 +1370,14 @@ static inline const struct cpumask *pci_irq_get_affinity(struct pci_dev *pdev, } #endif +static inline int +pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs, + unsigned int max_vecs, unsigned int flags) +{ + return pci_alloc_irq_vectors_affinity(dev, min_vecs, max_vecs, flags, + NULL); +} + #ifdef CONFIG_PCIEPORTBUS extern bool pcie_ports_disabled; extern bool pcie_ports_auto; From 0cf71b04467bc34063cecae577f12481da6cc565 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 8 Nov 2016 17:15:06 -0800 Subject: [PATCH 06/25] PCI: Remove the irq_affinity mask from struct pci_dev This has never been used, and now is totally unreferenced. Nuke it. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Reviewed-by: Johannes Thumshirn Acked-by: Bjorn Helgaas Acked-by: Jens Axboe Cc: linux-block@vger.kernel.org Cc: linux-pci@vger.kernel.org Link: http://lkml.kernel.org/r/1478654107-7384-7-git-send-email-hch@lst.de Signed-off-by: Thomas Gleixner --- include/linux/pci.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/pci.h b/include/linux/pci.h index 7090f5ff72528..f2ba6ac21c754 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -333,7 +333,6 @@ struct pci_dev { * directly, use the values stored here. They might be different! */ unsigned int irq; - struct cpumask *irq_affinity; struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */ bool match_driver; /* Skip attaching driver */ From bfe130773862bb3a02cdc4d4c2169f7f0210a46b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 15 Nov 2016 10:12:58 +0100 Subject: [PATCH 07/25] genirq/affinity: Take reserved vectors into account when spreading irqs The recent addition of reserved vectors at the beginning or the end of the vector space did not take the reserved vectors at the beginning into account for the various loop exit conditions. As a consequence the last vectors of the spread area are not included into the spread algorithm and are treated like the reserved vectors at the end of the vector space and get the default affinity mask assigned. Sum up the affinity vectors and the reserved vectors at the beginning and use the sum as exit condition. [ tglx: Fixed all conditions instead of only one and massaged changelog ] Signed-off-by: Christoph Hellwig Link: http://lkml.kernel.org/r/1479201178-29604-2-git-send-email-hch@lst.de Signed-off-by: Thomas Gleixner --- kernel/irq/affinity.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c index 17360bd9619bd..49eb38d488168 100644 --- a/kernel/irq/affinity.c +++ b/kernel/irq/affinity.c @@ -61,6 +61,7 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) { int n, nodes, vecs_per_node, cpus_per_vec, extra_vecs, curvec; int affv = nvecs - affd->pre_vectors - affd->post_vectors; + int last_affv = affv + affd->pre_vectors; nodemask_t nodemsk = NODE_MASK_NONE; struct cpumask *masks; cpumask_var_t nmsk; @@ -87,7 +88,7 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) if (affv <= nodes) { for_each_node_mask(n, nodemsk) { cpumask_copy(masks + curvec, cpumask_of_node(n)); - if (++curvec == affv) + if (++curvec == last_affv) break; } goto done; @@ -107,7 +108,8 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) /* Calculate the number of cpus per vector */ ncpus = cpumask_weight(nmsk); - for (v = 0; curvec < affv && v < vecs_to_assign; curvec++, v++) { + for (v = 0; curvec < last_affv && v < vecs_to_assign; + curvec++, v++) { cpus_per_vec = ncpus / vecs_to_assign; /* Account for extra vectors to compensate rounding errors */ @@ -119,7 +121,7 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) irq_spread_init_one(masks + curvec, nmsk, cpus_per_vec); } - if (curvec >= affv) + if (curvec >= last_affv) break; } From b6e5d5b947527558afac4aa0cdfa2ac586332e03 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Nov 2016 18:36:44 +0100 Subject: [PATCH 08/25] genirq/affinity: Use default affinity mask for reserved vectors The reserved vectors at the beginning and the end of the vector space get cpu_possible_mask assigned as their affinity mask. All other non-auto affine interrupts get the default irq affinity mask assigned. Using cpu_possible_mask breaks that rule. Treat them like any other interrupt and use irq_default_affinity as target mask. Signed-off-by: Thomas Gleixner Cc: Christoph Hellwig --- kernel/irq/affinity.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c index 49eb38d488168..9be9bda7c1f94 100644 --- a/kernel/irq/affinity.c +++ b/kernel/irq/affinity.c @@ -75,7 +75,7 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) /* Fill out vectors at the beginning that don't need affinity */ for (curvec = 0; curvec < affd->pre_vectors; curvec++) - cpumask_copy(masks + curvec, cpu_possible_mask); + cpumask_copy(masks + curvec, irq_default_affinity); /* Stabilize the cpumasks */ get_online_cpus(); @@ -130,7 +130,7 @@ irq_create_affinity_masks(int nvecs, const struct irq_affinity *affd) /* Fill out vectors at the end that don't need affinity */ for (; curvec < nvecs; curvec++) - cpumask_copy(masks + curvec, cpu_possible_mask); + cpumask_copy(masks + curvec, irq_default_affinity); out: free_cpumask_var(nmsk); return masks; From 34c535793bcbf9263cf22f8a52101f796cdfab8e Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 31 Oct 2016 14:17:35 -0700 Subject: [PATCH 09/25] irqchip/bcm7038-l1: Implement irq_cpu_offline() callback We did not implement an irq_cpu_offline callback for our irqchip, yet we support setting a given IRQ's affinity. This resulted in interrupts whose affinity mask included CPUs being taken offline not to work correctly once the CPU had been put offline. Fixes: 5f7f0317ed28 ("IRQCHIP: Add new driver for BCM7038-style level 1 interrupt controllers") Signed-off-by: Florian Fainelli Cc: linux-mips@linux-mips.org Cc: jason@lakedaemon.net Cc: marc.zyngier@arm.com Cc: cernekee@gmail.com Cc: jaedon.shin@gmail.com Cc: ralf@linux-mips.org Cc: justinpopo6@gmail.com Link: http://lkml.kernel.org/r/1477948656-12966-2-git-send-email-f.fainelli@gmail.com Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-bcm7038-l1.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c index 353c549862118..c2662a1bfdd37 100644 --- a/drivers/irqchip/irq-bcm7038-l1.c +++ b/drivers/irqchip/irq-bcm7038-l1.c @@ -215,6 +215,31 @@ static int bcm7038_l1_set_affinity(struct irq_data *d, return 0; } +static void bcm7038_l1_cpu_offline(struct irq_data *d) +{ + struct cpumask *mask = irq_data_get_affinity_mask(d); + int cpu = smp_processor_id(); + cpumask_t new_affinity; + + /* This CPU was not on the affinity mask */ + if (!cpumask_test_cpu(cpu, mask)) + return; + + if (cpumask_weight(mask) > 1) { + /* + * Multiple CPU affinity, remove this CPU from the affinity + * mask + */ + cpumask_copy(&new_affinity, mask); + cpumask_clear_cpu(cpu, &new_affinity); + } else { + /* Only CPU, put on the lowest online CPU */ + cpumask_clear(&new_affinity); + cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity); + } + irq_set_affinity_locked(d, &new_affinity, false); +} + static int __init bcm7038_l1_init_one(struct device_node *dn, unsigned int idx, struct bcm7038_l1_chip *intc) @@ -266,6 +291,7 @@ static struct irq_chip bcm7038_l1_irq_chip = { .irq_mask = bcm7038_l1_mask, .irq_unmask = bcm7038_l1_unmask, .irq_set_affinity = bcm7038_l1_set_affinity, + .irq_cpu_offline = bcm7038_l1_cpu_offline, }; static int bcm7038_l1_map(struct irq_domain *d, unsigned int virq, From 4e201566402c878a225d4425df8a4a664c6f251e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 22 Nov 2016 09:21:16 +0000 Subject: [PATCH 10/25] genirq/msi: Drop artificial PCI dependency The generic MSI layer doesn't have any PCI ties anymore, and the build hack should have been removed some time ago. Fixes: d9109698be6e ("genirq: Introduce msi_domain_alloc/free_irqs()") Signed-off-by: Marc Zyngier Link: http://lkml.kernel.org/r/1479806476-20801-1-git-send-email-marc.zyngier@arm.com Signed-off-by: Thomas Gleixner --- kernel/irq/msi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 8a3e872798f34..ee230063f033c 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -14,9 +14,7 @@ #include #include #include - -/* Temparory solution for building, will be removed later */ -#include +#include /** * alloc_msi_entry - Allocate an initialize msi_entry From d44ffa5ae70a15a15190aa9ffa6f6acdeae1d25c Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 28 Oct 2016 12:23:57 +0100 Subject: [PATCH 11/25] irqchip/gic-v3: Convert arm64 GIC accessors to {read,write}_sysreg_s The GIC system registers are accessed using open-coded wrappers around the mrs_s/msr_s asm macros. This patch moves the code over to the {read,wrote}_sysreg_s accessors instead, reducing the amount of explicit asm blocks in the arch headers. Reviewed-by: Mark Rutland Signed-off-by: Will Deacon Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/arch_gicv3.h | 45 +++++++++++------------------ 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h index f8ae6d6e4767e..fdf34f8b4ee07 100644 --- a/arch/arm64/include/asm/arch_gicv3.h +++ b/arch/arm64/include/asm/arch_gicv3.h @@ -80,18 +80,8 @@ #include #include -#define read_gicreg(r) \ - ({ \ - u64 reg; \ - asm volatile("mrs_s %0, " __stringify(r) : "=r" (reg)); \ - reg; \ - }) - -#define write_gicreg(v,r) \ - do { \ - u64 __val = (v); \ - asm volatile("msr_s " __stringify(r) ", %0" : : "r" (__val));\ - } while (0) +#define read_gicreg read_sysreg_s +#define write_gicreg write_sysreg_s /* * Low-level accessors @@ -102,13 +92,13 @@ static inline void gic_write_eoir(u32 irq) { - asm volatile("msr_s " __stringify(ICC_EOIR1_EL1) ", %0" : : "r" ((u64)irq)); + write_sysreg_s(irq, ICC_EOIR1_EL1); isb(); } static inline void gic_write_dir(u32 irq) { - asm volatile("msr_s " __stringify(ICC_DIR_EL1) ", %0" : : "r" ((u64)irq)); + write_sysreg_s(irq, ICC_DIR_EL1); isb(); } @@ -116,7 +106,7 @@ static inline u64 gic_read_iar_common(void) { u64 irqstat; - asm volatile("mrs_s %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat)); + irqstat = read_sysreg_s(ICC_IAR1_EL1); dsb(sy); return irqstat; } @@ -134,10 +124,12 @@ static inline u64 gic_read_iar_cavium_thunderx(void) asm volatile( "nop;nop;nop;nop\n\t" - "nop;nop;nop;nop\n\t" - "mrs_s %0, " __stringify(ICC_IAR1_EL1) "\n\t" - "nop;nop;nop;nop" - : "=r" (irqstat)); + "nop;nop;nop;nop"); + + irqstat = read_sysreg_s(ICC_IAR1_EL1); + + asm volatile( + "nop;nop;nop;nop"); mb(); return irqstat; @@ -145,37 +137,34 @@ static inline u64 gic_read_iar_cavium_thunderx(void) static inline void gic_write_pmr(u32 val) { - asm volatile("msr_s " __stringify(ICC_PMR_EL1) ", %0" : : "r" ((u64)val)); + write_sysreg_s(val, ICC_PMR_EL1); } static inline void gic_write_ctlr(u32 val) { - asm volatile("msr_s " __stringify(ICC_CTLR_EL1) ", %0" : : "r" ((u64)val)); + write_sysreg_s(val, ICC_CTLR_EL1); isb(); } static inline void gic_write_grpen1(u32 val) { - asm volatile("msr_s " __stringify(ICC_GRPEN1_EL1) ", %0" : : "r" ((u64)val)); + write_sysreg_s(val, ICC_GRPEN1_EL1); isb(); } static inline void gic_write_sgi1r(u64 val) { - asm volatile("msr_s " __stringify(ICC_SGI1R_EL1) ", %0" : : "r" (val)); + write_sysreg_s(val, ICC_SGI1R_EL1); } static inline u32 gic_read_sre(void) { - u64 val; - - asm volatile("mrs_s %0, " __stringify(ICC_SRE_EL1) : "=r" (val)); - return val; + return read_sysreg_s(ICC_SRE_EL1); } static inline void gic_write_sre(u32 val) { - asm volatile("msr_s " __stringify(ICC_SRE_EL1) ", %0" : : "r" ((u64)val)); + write_sysreg_s(val, ICC_SRE_EL1); isb(); } From 016f98afd050f876845c49d3a0ad6dd0c30c5681 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 28 Oct 2016 12:23:58 +0100 Subject: [PATCH 12/25] irqchip/gic-v3: Use nops macro for Cavium ThunderX erratum 23154 The workaround for Cavium ThunderX erratum 23154 has a homebrew pipeflush built out of NOP sequences around the read of the IAR. This patch converts the code to use the new nops macro, which makes it a little easier to read. Reviewed-by: Mark Rutland Signed-off-by: Will Deacon Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/arch_gicv3.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h index fdf34f8b4ee07..0313670a3e3fb 100644 --- a/arch/arm64/include/asm/arch_gicv3.h +++ b/arch/arm64/include/asm/arch_gicv3.h @@ -122,14 +122,9 @@ static inline u64 gic_read_iar_cavium_thunderx(void) { u64 irqstat; - asm volatile( - "nop;nop;nop;nop\n\t" - "nop;nop;nop;nop"); - + nops(8); irqstat = read_sysreg_s(ICC_IAR1_EL1); - - asm volatile( - "nop;nop;nop;nop"); + nops(4); mb(); return irqstat; From b11283eb89b0697984cadee6016dabbcf511af27 Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Wed, 2 Nov 2016 11:54:03 +0000 Subject: [PATCH 13/25] irqchip/gic-v3-its: Change unsigned types for AArch32 compatibility Make sure that constants which are supposed to be applied on 64-bit data is actually unsigned long long, so they won't be truncated when used in 32-bit mode. Signed-off-by: Vladimir Murzin Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 28 ++++++++++++++-------------- include/linux/irqchip/arm-gic-v3.h | 4 ++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index c5dee300e8a3e..bca125e6c8e81 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -196,7 +196,7 @@ typedef struct its_collection *(*its_cmd_builder_t)(struct its_cmd_block *, static void its_encode_cmd(struct its_cmd_block *cmd, u8 cmd_nr) { - cmd->raw_cmd[0] &= ~0xffUL; + cmd->raw_cmd[0] &= ~0xffULL; cmd->raw_cmd[0] |= cmd_nr; } @@ -208,43 +208,43 @@ static void its_encode_devid(struct its_cmd_block *cmd, u32 devid) static void its_encode_event_id(struct its_cmd_block *cmd, u32 id) { - cmd->raw_cmd[1] &= ~0xffffffffUL; + cmd->raw_cmd[1] &= ~0xffffffffULL; cmd->raw_cmd[1] |= id; } static void its_encode_phys_id(struct its_cmd_block *cmd, u32 phys_id) { - cmd->raw_cmd[1] &= 0xffffffffUL; + cmd->raw_cmd[1] &= 0xffffffffULL; cmd->raw_cmd[1] |= ((u64)phys_id) << 32; } static void its_encode_size(struct its_cmd_block *cmd, u8 size) { - cmd->raw_cmd[1] &= ~0x1fUL; + cmd->raw_cmd[1] &= ~0x1fULL; cmd->raw_cmd[1] |= size & 0x1f; } static void its_encode_itt(struct its_cmd_block *cmd, u64 itt_addr) { - cmd->raw_cmd[2] &= ~0xffffffffffffUL; - cmd->raw_cmd[2] |= itt_addr & 0xffffffffff00UL; + cmd->raw_cmd[2] &= ~0xffffffffffffULL; + cmd->raw_cmd[2] |= itt_addr & 0xffffffffff00ULL; } static void its_encode_valid(struct its_cmd_block *cmd, int valid) { - cmd->raw_cmd[2] &= ~(1UL << 63); + cmd->raw_cmd[2] &= ~(1ULL << 63); cmd->raw_cmd[2] |= ((u64)!!valid) << 63; } static void its_encode_target(struct its_cmd_block *cmd, u64 target_addr) { - cmd->raw_cmd[2] &= ~(0xffffffffUL << 16); - cmd->raw_cmd[2] |= (target_addr & (0xffffffffUL << 16)); + cmd->raw_cmd[2] &= ~(0xffffffffULL << 16); + cmd->raw_cmd[2] |= (target_addr & (0xffffffffULL << 16)); } static void its_encode_collection(struct its_cmd_block *cmd, u16 col) { - cmd->raw_cmd[2] &= ~0xffffUL; + cmd->raw_cmd[2] &= ~0xffffULL; cmd->raw_cmd[2] |= col; } @@ -657,8 +657,8 @@ static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) its = its_dev->its; addr = its->phys_base + GITS_TRANSLATER; - msg->address_lo = addr & ((1UL << 32) - 1); - msg->address_hi = addr >> 32; + msg->address_lo = lower_32_bits(addr); + msg->address_hi = upper_32_bits(addr); msg->data = its_get_event_id(d); iommu_dma_map_msi_msg(d->irq, msg); @@ -935,9 +935,9 @@ static int its_setup_baser(struct its_node *its, struct its_baser *baser, } if (val != tmp) { - pr_err("ITS@%pa: %s doesn't stick: %lx %lx\n", + pr_err("ITS@%pa: %s doesn't stick: %llx %llx\n", &its->phys_base, its_base_type_string[type], - (unsigned long) val, (unsigned long) tmp); + val, tmp); free_pages((unsigned long)base, order); return -ENXIO; } diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index b7e34313cdfe4..5118d3a0c9ca4 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h @@ -239,7 +239,7 @@ #define GITS_TYPER_PTA (1UL << 19) #define GITS_TYPER_HWCOLLCNT_SHIFT 24 -#define GITS_CBASER_VALID (1UL << 63) +#define GITS_CBASER_VALID (1ULL << 63) #define GITS_CBASER_SHAREABILITY_SHIFT (10) #define GITS_CBASER_INNER_CACHEABILITY_SHIFT (59) #define GITS_CBASER_OUTER_CACHEABILITY_SHIFT (53) @@ -265,7 +265,7 @@ #define GITS_BASER_NR_REGS 8 -#define GITS_BASER_VALID (1UL << 63) +#define GITS_BASER_VALID (1ULL << 63) #define GITS_BASER_INDIRECT (1ULL << 62) #define GITS_BASER_INNER_CACHEABILITY_SHIFT (59) From d524eaa2a8c08efcdf905acf07cfa770e481ca6b Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Wed, 2 Nov 2016 11:54:04 +0000 Subject: [PATCH 14/25] irqchip/gic-v3-its: Narrow down Entry Size when used as a divider GITS_BASER's Entry Size is much smaller than 64-bit, but when it used as a divider it forces compiler to generate __aeabi_uldivmod if build in 32-bit mode. So, casting it to int (like it is done in other places) where used as a divider would give a hint to compiler that 32-bit division can be used. Signed-off-by: Vladimir Murzin Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index bca125e6c8e81..312dd55dfaae8 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -948,7 +948,7 @@ static int its_setup_baser(struct its_node *its, struct its_baser *baser, tmp = indirect ? GITS_LVL1_ENTRY_SIZE : esz; pr_info("ITS@%pa: allocated %d %s @%lx (%s, esz %d, psz %dK, shr %d)\n", - &its->phys_base, (int)(PAGE_ORDER_TO_SIZE(order) / tmp), + &its->phys_base, (int)(PAGE_ORDER_TO_SIZE(order) / (int)tmp), its_base_type_string[type], (unsigned long)virt_to_phys(base), indirect ? "indirect" : "flat", (int)esz, @@ -983,7 +983,7 @@ static bool its_parse_baser_device(struct its_node *its, struct its_baser *baser * which is reported by ITS hardware times lvl1 table * entry size. */ - ids -= ilog2(psz / esz); + ids -= ilog2(psz / (int)esz); esz = GITS_LVL1_ENTRY_SIZE; } } @@ -998,7 +998,7 @@ static bool its_parse_baser_device(struct its_node *its, struct its_baser *baser new_order = max_t(u32, get_order(esz << ids), new_order); if (new_order >= MAX_ORDER) { new_order = MAX_ORDER - 1; - ids = ilog2(PAGE_ORDER_TO_SIZE(new_order) / esz); + ids = ilog2(PAGE_ORDER_TO_SIZE(new_order) / (int)esz); pr_warn("ITS@%pa: Device Table too large, reduce ids %u->%u\n", &its->phys_base, its->device_ids, ids); } From 328191c05ed72762c382bdb835607dd5bd56b0bc Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Wed, 2 Nov 2016 11:54:05 +0000 Subject: [PATCH 15/25] irqchip/gic-v3-its: Specialise flush_dcache operation It'd be better to switch to CMA... but before that done redirect flush_dcache operation, so 32-bit implementation could be wired latter. Signed-off-by: Vladimir Murzin Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/arch_gicv3.h | 3 +++ drivers/irqchip/irq-gic-v3-its.c | 17 ++++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h index 0313670a3e3fb..546f92b32b625 100644 --- a/arch/arm64/include/asm/arch_gicv3.h +++ b/arch/arm64/include/asm/arch_gicv3.h @@ -79,6 +79,7 @@ #include #include +#include #define read_gicreg read_sysreg_s #define write_gicreg write_sysreg_s @@ -171,5 +172,7 @@ static inline void gic_write_bpr1(u32 val) #define gic_read_typer(c) readq_relaxed(c) #define gic_write_irouter(v, c) writeq_relaxed(v, c) +#define gic_flush_dcache_to_poc(a,l) __flush_dcache_area((a), (l)) + #endif /* __ASSEMBLY__ */ #endif /* __ASM_ARCH_GICV3_H */ diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 312dd55dfaae8..b2a6e7b0bf9af 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -37,7 +37,6 @@ #include #include -#include #include #include @@ -433,7 +432,7 @@ static void its_flush_cmd(struct its_node *its, struct its_cmd_block *cmd) * the ITS. */ if (its->flags & ITS_FLAGS_CMDQ_NEEDS_FLUSHING) - __flush_dcache_area(cmd, sizeof(*cmd)); + gic_flush_dcache_to_poc(cmd, sizeof(*cmd)); else dsb(ishst); } @@ -602,7 +601,7 @@ static void lpi_set_config(struct irq_data *d, bool enable) * Humpf... */ if (gic_rdists->flags & RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING) - __flush_dcache_area(cfg, sizeof(*cfg)); + gic_flush_dcache_to_poc(cfg, sizeof(*cfg)); else dsb(ishst); its_send_inv(its_dev, id); @@ -817,7 +816,7 @@ static int __init its_alloc_lpi_tables(void) LPI_PROPBASE_SZ); /* Make sure the GIC will observe the written configuration */ - __flush_dcache_area(page_address(gic_rdists->prop_page), LPI_PROPBASE_SZ); + gic_flush_dcache_to_poc(page_address(gic_rdists->prop_page), LPI_PROPBASE_SZ); return 0; } @@ -910,7 +909,7 @@ static int its_setup_baser(struct its_node *its, struct its_baser *baser, shr = tmp & GITS_BASER_SHAREABILITY_MASK; if (!shr) { cache = GITS_BASER_nC; - __flush_dcache_area(base, PAGE_ORDER_TO_SIZE(order)); + gic_flush_dcache_to_poc(base, PAGE_ORDER_TO_SIZE(order)); } goto retry_baser; } @@ -1102,7 +1101,7 @@ static void its_cpu_init_lpis(void) } /* Make sure the GIC will observe the zero-ed page */ - __flush_dcache_area(page_address(pend_page), LPI_PENDBASE_SZ); + gic_flush_dcache_to_poc(page_address(pend_page), LPI_PENDBASE_SZ); paddr = page_to_phys(pend_page); pr_info("CPU%d: using LPI pending table @%pa\n", @@ -1287,13 +1286,13 @@ static bool its_alloc_device_table(struct its_node *its, u32 dev_id) /* Flush Lvl2 table to PoC if hw doesn't support coherency */ if (!(baser->val & GITS_BASER_SHAREABILITY_MASK)) - __flush_dcache_area(page_address(page), baser->psz); + gic_flush_dcache_to_poc(page_address(page), baser->psz); table[idx] = cpu_to_le64(page_to_phys(page) | GITS_BASER_VALID); /* Flush Lvl1 entry to PoC if hw doesn't support coherency */ if (!(baser->val & GITS_BASER_SHAREABILITY_MASK)) - __flush_dcache_area(table + idx, GITS_LVL1_ENTRY_SIZE); + gic_flush_dcache_to_poc(table + idx, GITS_LVL1_ENTRY_SIZE); /* Ensure updated table contents are visible to ITS hardware */ dsb(sy); @@ -1340,7 +1339,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, return NULL; } - __flush_dcache_area(itt, sz); + gic_flush_dcache_to_poc(itt, sz); dev->its = its; dev->itt = itt; From 0968a61918a9140d39959a318f796412354ec24d Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Wed, 2 Nov 2016 11:54:06 +0000 Subject: [PATCH 16/25] irqchip/gic-v3-its: Specialise readq and writeq accesses readq and writeq type of assessors are not supported in AArch32, so we need to specialise them and glue later with series of 32-bit accesses on AArch32 side. Signed-off-by: Vladimir Murzin Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/arch_gicv3.h | 14 ++++++++++++++ drivers/irqchip/irq-gic-v3-its.c | 24 ++++++++++++------------ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h index 546f92b32b625..f37e3a21f6e76 100644 --- a/arch/arm64/include/asm/arch_gicv3.h +++ b/arch/arm64/include/asm/arch_gicv3.h @@ -174,5 +174,19 @@ static inline void gic_write_bpr1(u32 val) #define gic_flush_dcache_to_poc(a,l) __flush_dcache_area((a), (l)) +#define gits_read_baser(c) readq_relaxed(c) +#define gits_write_baser(v, c) writeq_relaxed(v, c) + +#define gits_read_cbaser(c) readq_relaxed(c) +#define gits_write_cbaser(v, c) writeq_relaxed(v, c) + +#define gits_write_cwriter(v, c) writeq_relaxed(v, c) + +#define gicr_read_propbaser(c) readq_relaxed(c) +#define gicr_write_propbaser(v, c) writeq_relaxed(v, c) + +#define gicr_write_pendbaser(v, c) writeq_relaxed(v, c) +#define gicr_read_pendbaser(c) readq_relaxed(c) + #endif /* __ASSEMBLY__ */ #endif /* __ASM_ARCH_GICV3_H */ diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index b2a6e7b0bf9af..69b040f47d56a 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -835,7 +835,7 @@ static u64 its_read_baser(struct its_node *its, struct its_baser *baser) { u32 idx = baser - its->tables; - return readq_relaxed(its->base + GITS_BASER + (idx << 3)); + return gits_read_baser(its->base + GITS_BASER + (idx << 3)); } static void its_write_baser(struct its_node *its, struct its_baser *baser, @@ -843,7 +843,7 @@ static void its_write_baser(struct its_node *its, struct its_baser *baser, { u32 idx = baser - its->tables; - writeq_relaxed(val, its->base + GITS_BASER + (idx << 3)); + gits_write_baser(val, its->base + GITS_BASER + (idx << 3)); baser->val = its_read_baser(its, baser); } @@ -1125,8 +1125,8 @@ static void its_cpu_init_lpis(void) GICR_PROPBASER_WaWb | ((LPI_NRBITS - 1) & GICR_PROPBASER_IDBITS_MASK)); - writeq_relaxed(val, rbase + GICR_PROPBASER); - tmp = readq_relaxed(rbase + GICR_PROPBASER); + gicr_write_propbaser(val, rbase + GICR_PROPBASER); + tmp = gicr_read_propbaser(rbase + GICR_PROPBASER); if ((tmp ^ val) & GICR_PROPBASER_SHAREABILITY_MASK) { if (!(tmp & GICR_PROPBASER_SHAREABILITY_MASK)) { @@ -1138,7 +1138,7 @@ static void its_cpu_init_lpis(void) val &= ~(GICR_PROPBASER_SHAREABILITY_MASK | GICR_PROPBASER_CACHEABILITY_MASK); val |= GICR_PROPBASER_nC; - writeq_relaxed(val, rbase + GICR_PROPBASER); + gicr_write_propbaser(val, rbase + GICR_PROPBASER); } pr_info_once("GIC: using cache flushing for LPI property table\n"); gic_rdists->flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING; @@ -1149,8 +1149,8 @@ static void its_cpu_init_lpis(void) GICR_PENDBASER_InnerShareable | GICR_PENDBASER_WaWb); - writeq_relaxed(val, rbase + GICR_PENDBASER); - tmp = readq_relaxed(rbase + GICR_PENDBASER); + gicr_write_pendbaser(val, rbase + GICR_PENDBASER); + tmp = gicr_read_pendbaser(rbase + GICR_PENDBASER); if (!(tmp & GICR_PENDBASER_SHAREABILITY_MASK)) { /* @@ -1160,7 +1160,7 @@ static void its_cpu_init_lpis(void) val &= ~(GICR_PENDBASER_SHAREABILITY_MASK | GICR_PENDBASER_CACHEABILITY_MASK); val |= GICR_PENDBASER_nC; - writeq_relaxed(val, rbase + GICR_PENDBASER); + gicr_write_pendbaser(val, rbase + GICR_PENDBASER); } /* Enable LPIs */ @@ -1716,8 +1716,8 @@ static int __init its_probe_one(struct resource *res, (ITS_CMD_QUEUE_SZ / SZ_4K - 1) | GITS_CBASER_VALID); - writeq_relaxed(baser, its->base + GITS_CBASER); - tmp = readq_relaxed(its->base + GITS_CBASER); + gits_write_cbaser(baser, its->base + GITS_CBASER); + tmp = gits_read_cbaser(its->base + GITS_CBASER); if ((tmp ^ baser) & GITS_CBASER_SHAREABILITY_MASK) { if (!(tmp & GITS_CBASER_SHAREABILITY_MASK)) { @@ -1729,13 +1729,13 @@ static int __init its_probe_one(struct resource *res, baser &= ~(GITS_CBASER_SHAREABILITY_MASK | GITS_CBASER_CACHEABILITY_MASK); baser |= GITS_CBASER_nC; - writeq_relaxed(baser, its->base + GITS_CBASER); + gits_write_cbaser(baser, its->base + GITS_CBASER); } pr_info("ITS: using cache flushing for cmd queue\n"); its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING; } - writeq_relaxed(0, its->base + GITS_CWRITER); + gits_write_cwriter(0, its->base + GITS_CWRITER); writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR); err = its_init_domain(handle, its); From 92116b804a02a9869fb8ef843473a5673ed64d3c Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Wed, 2 Nov 2016 11:54:07 +0000 Subject: [PATCH 17/25] ARM: gic-v3-its: Add 32bit support to GICv3 ITS Wire-up flush_dcache, readq- and writeq-like gic-v3-its assessors, so GICv3 ITS gets all it needs to be built and run. Signed-off-by: Vladimir Murzin Signed-off-by: Marc Zyngier --- arch/arm/include/asm/arch_gicv3.h | 54 +++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/arch/arm/include/asm/arch_gicv3.h b/arch/arm/include/asm/arch_gicv3.h index a8088290b7787..27475904e0966 100644 --- a/arch/arm/include/asm/arch_gicv3.h +++ b/arch/arm/include/asm/arch_gicv3.h @@ -22,6 +22,7 @@ #include #include +#include #include #define ICC_EOIR1 __ACCESS_CP15(c12, 0, c12, 1) @@ -230,19 +231,14 @@ static inline void gic_write_bpr1(u32 val) * AArch32, since the syndrome register doesn't provide any information for * them. * Consequently, the following IO helpers use 32bit accesses. - * - * There are only two registers that need 64bit accesses in this driver: - * - GICD_IROUTERn, contain the affinity values associated to each interrupt. - * The upper-word (aff3) will always be 0, so there is no need for a lock. - * - GICR_TYPER is an ID register and doesn't need atomicity. */ -static inline void gic_write_irouter(u64 val, volatile void __iomem *addr) +static inline void __gic_writeq_nonatomic(u64 val, volatile void __iomem *addr) { writel_relaxed((u32)val, addr); writel_relaxed((u32)(val >> 32), addr + 4); } -static inline u64 gic_read_typer(const volatile void __iomem *addr) +static inline u64 __gic_readq_nonatomic(const volatile void __iomem *addr) { u64 val; @@ -251,5 +247,49 @@ static inline u64 gic_read_typer(const volatile void __iomem *addr) return val; } +#define gic_flush_dcache_to_poc(a,l) __cpuc_flush_dcache_area((a), (l)) + +/* + * GICD_IROUTERn, contain the affinity values associated to each interrupt. + * The upper-word (aff3) will always be 0, so there is no need for a lock. + */ +#define gic_write_irouter(v, c) __gic_writeq_nonatomic(v, c) + +/* + * GICR_TYPER is an ID register and doesn't need atomicity. + */ +#define gic_read_typer(c) __gic_readq_nonatomic(c) + +/* + * GITS_BASER - hi and lo bits may be accessed independently. + */ +#define gits_read_baser(c) __gic_readq_nonatomic(c) +#define gits_write_baser(v, c) __gic_writeq_nonatomic(v, c) + +/* + * GICR_PENDBASER and GICR_PROPBASE are changed with LPIs disabled, so they + * won't be being used during any updates and can be changed non-atomically + */ +#define gicr_read_propbaser(c) __gic_readq_nonatomic(c) +#define gicr_write_propbaser(v, c) __gic_writeq_nonatomic(v, c) +#define gicr_read_pendbaser(c) __gic_readq_nonatomic(c) +#define gicr_write_pendbaser(v, c) __gic_writeq_nonatomic(v, c) + +/* + * GITS_TYPER is an ID register and doesn't need atomicity. + */ +#define gits_read_typer(c) __gic_readq_nonatomic(c) + +/* + * GITS_CBASER - hi and lo bits may be accessed independently. + */ +#define gits_read_cbaser(c) __gic_readq_nonatomic(c) +#define gits_write_cbaser(v, c) __gic_writeq_nonatomic(v, c) + +/* + * GITS_CWRITER - hi and lo bits may be accessed independently. + */ +#define gits_write_cwriter(v, c) __gic_writeq_nonatomic(v, c) + #endif /* !__ASSEMBLY__ */ #endif /* !__ASM_ARCH_GICV3_H */ From bb29cecb3bcde74438a8a694a5b2ba4852c8eac1 Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Wed, 2 Nov 2016 11:54:08 +0000 Subject: [PATCH 18/25] ARM: virt: Select ARM_GIC_V3_ITS This patch allows ARM guests to use GICv3 ITS on an arm64 host Signed-off-by: Vladimir Murzin Signed-off-by: Marc Zyngier --- arch/arm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index b5d529fdffab2..caef68429b08b 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -703,6 +703,7 @@ config ARCH_VIRT select ARM_GIC select ARM_GIC_V2M if PCI select ARM_GIC_V3 + select ARM_GIC_V3_ITS if PCI select ARM_PSCI select HAVE_ARM_ARCH_TIMER From 0547dc7885856c93b0c8bbf1f590ceccf0c87835 Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Mon, 14 Nov 2016 12:13:45 +0000 Subject: [PATCH 19/25] microblaze/irqchip: Move intc driver to irqchip The Xilinx AXI Interrupt Controller IP block is used by the MIPS based xilfpga platform and a few PowerPC based platforms. Move the interrupt controller code out of arch/microblaze so that it can be used by everyone Tested-by: Michal Simek Signed-off-by: Zubair Lutfullah Kakakhel Signed-off-by: Marc Zyngier --- arch/microblaze/Kconfig | 1 + arch/microblaze/kernel/Makefile | 2 +- drivers/irqchip/Kconfig | 4 ++++ drivers/irqchip/Makefile | 1 + .../kernel/intc.c => drivers/irqchip/irq-xilinx-intc.c | 0 5 files changed, 7 insertions(+), 1 deletion(-) rename arch/microblaze/kernel/intc.c => drivers/irqchip/irq-xilinx-intc.c (100%) diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig index 86f65721e629e..85885a501dcec 100644 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig @@ -27,6 +27,7 @@ config MICROBLAZE select HAVE_MEMBLOCK_NODE_MAP select HAVE_OPROFILE select IRQ_DOMAIN + select XILINX_INTC select MODULES_USE_ELF_RELA select OF select OF_EARLY_FLATTREE diff --git a/arch/microblaze/kernel/Makefile b/arch/microblaze/kernel/Makefile index f08bacaf8a951..e098381af9283 100644 --- a/arch/microblaze/kernel/Makefile +++ b/arch/microblaze/kernel/Makefile @@ -15,7 +15,7 @@ endif extra-y := head.o vmlinux.lds obj-y += dma.o exceptions.o \ - hw_exception_handler.o intc.o irq.o \ + hw_exception_handler.o irq.o \ platform.o process.o prom.o ptrace.o \ reset.o setup.o signal.o sys_microblaze.o timer.o traps.o unwind.o diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index bc0af3307bbfd..ae96731cd2fbd 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -211,6 +211,10 @@ config XTENSA_MX bool select IRQ_DOMAIN +config XILINX_INTC + bool + select IRQ_DOMAIN + config IRQ_CROSSBAR bool help diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index e4dbfc85abdb7..0e55d94065bf5 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o obj-$(CONFIG_TS4800_IRQ) += irq-ts4800.o obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o +obj-$(CONFIG_XILINX_INTC) += irq-xilinx-intc.o obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o obj-$(CONFIG_BCM6345_L1_IRQ) += irq-bcm6345-l1.o diff --git a/arch/microblaze/kernel/intc.c b/drivers/irqchip/irq-xilinx-intc.c similarity index 100% rename from arch/microblaze/kernel/intc.c rename to drivers/irqchip/irq-xilinx-intc.c From a5734de263e70c75e61778be038add78441610e3 Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Mon, 14 Nov 2016 12:13:46 +0000 Subject: [PATCH 20/25] irqchip/xilinx: Clean up print messages Remove __func__ and prefix irq-xilinx in various debug prints Acked-by: Michal Simek Signed-off-by: Zubair Lutfullah Kakakhel Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-xilinx-intc.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/irqchip/irq-xilinx-intc.c b/drivers/irqchip/irq-xilinx-intc.c index 90bec7d71f856..096c1ed51d5a1 100644 --- a/drivers/irqchip/irq-xilinx-intc.c +++ b/drivers/irqchip/irq-xilinx-intc.c @@ -58,7 +58,7 @@ static void intc_enable_or_unmask(struct irq_data *d) { unsigned long mask = 1 << d->hwirq; - pr_debug("enable_or_unmask: %ld\n", d->hwirq); + pr_debug("irq-xilinx: enable_or_unmask: %ld\n", d->hwirq); /* ack level irqs because they can't be acked during * ack function since the handle_level_irq function @@ -72,13 +72,13 @@ static void intc_enable_or_unmask(struct irq_data *d) static void intc_disable_or_mask(struct irq_data *d) { - pr_debug("disable: %ld\n", d->hwirq); + pr_debug("irq-xilinx: disable: %ld\n", d->hwirq); write_fn(1 << d->hwirq, intc_baseaddr + CIE); } static void intc_ack(struct irq_data *d) { - pr_debug("ack: %ld\n", d->hwirq); + pr_debug("irq-xilinx: ack: %ld\n", d->hwirq); write_fn(1 << d->hwirq, intc_baseaddr + IAR); } @@ -86,7 +86,7 @@ static void intc_mask_ack(struct irq_data *d) { unsigned long mask = 1 << d->hwirq; - pr_debug("disable_and_ack: %ld\n", d->hwirq); + pr_debug("irq-xilinx: disable_and_ack: %ld\n", d->hwirq); write_fn(mask, intc_baseaddr + CIE); write_fn(mask, intc_baseaddr + IAR); } @@ -109,7 +109,7 @@ unsigned int get_irq(void) if (hwirq != -1U) irq = irq_find_mapping(root_domain, hwirq); - pr_debug("get_irq: hwirq=%d, irq=%d\n", hwirq, irq); + pr_debug("irq-xilinx: hwirq=%d, irq=%d\n", hwirq, irq); return irq; } @@ -146,20 +146,20 @@ static int __init xilinx_intc_of_init(struct device_node *intc, ret = of_property_read_u32(intc, "xlnx,num-intr-inputs", &nr_irq); if (ret < 0) { - pr_err("%s: unable to read xlnx,num-intr-inputs\n", __func__); + pr_err("irq-xilinx: unable to read xlnx,num-intr-inputs\n"); return ret; } ret = of_property_read_u32(intc, "xlnx,kind-of-intr", &intr_mask); if (ret < 0) { - pr_err("%s: unable to read xlnx,kind-of-intr\n", __func__); + pr_err("irq-xilinx: unable to read xlnx,kind-of-intr\n"); return ret; } if (intr_mask >> nr_irq) - pr_warn("%s: mismatch in kind-of-intr param\n", __func__); + pr_warn("irq-xilinx: mismatch in kind-of-intr param\n"); - pr_info("%s: num_irq=%d, edge=0x%x\n", + pr_info("irq-xilinx: %s: num_irq=%d, edge=0x%x\n", intc->full_name, nr_irq, intr_mask); write_fn = intc_write32; From 591db74bfad475c2623f6e0d47307cf726a64375 Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Mon, 14 Nov 2016 12:13:47 +0000 Subject: [PATCH 21/25] irqchip/xilinx: Restructure and use jump label api Add a global structure to house various variables. And cleanup read/write handling by using jump label api. Tested-by; Michal Simek Signed-off-by: Zubair Lutfullah Kakakhel Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-xilinx-intc.c | 118 +++++++++++++++++------------- 1 file changed, 66 insertions(+), 52 deletions(-) diff --git a/drivers/irqchip/irq-xilinx-intc.c b/drivers/irqchip/irq-xilinx-intc.c index 096c1ed51d5a1..7331d8cb35f1a 100644 --- a/drivers/irqchip/irq-xilinx-intc.c +++ b/drivers/irqchip/irq-xilinx-intc.c @@ -14,10 +14,9 @@ #include #include #include +#include #include -static void __iomem *intc_baseaddr; - /* No one else should require these constants, so define them locally here. */ #define ISR 0x00 /* Interrupt Status Register */ #define IPR 0x04 /* Interrupt Pending Register */ @@ -31,27 +30,30 @@ static void __iomem *intc_baseaddr; #define MER_ME (1<<0) #define MER_HIE (1<<1) -static unsigned int (*read_fn)(void __iomem *); -static void (*write_fn)(u32, void __iomem *); +static DEFINE_STATIC_KEY_FALSE(xintc_is_be); -static void intc_write32(u32 val, void __iomem *addr) -{ - iowrite32(val, addr); -} +struct xintc_irq_chip { + void __iomem *base; + struct irq_domain *root_domain; + u32 intr_mask; +}; -static unsigned int intc_read32(void __iomem *addr) -{ - return ioread32(addr); -} +static struct xintc_irq_chip *xintc_irqc; -static void intc_write32_be(u32 val, void __iomem *addr) +static void xintc_write(int reg, u32 data) { - iowrite32be(val, addr); + if (static_branch_unlikely(&xintc_is_be)) + iowrite32be(data, xintc_irqc->base + reg); + else + iowrite32(data, xintc_irqc->base + reg); } -static unsigned int intc_read32_be(void __iomem *addr) +static unsigned int xintc_read(int reg) { - return ioread32be(addr); + if (static_branch_unlikely(&xintc_is_be)) + return ioread32be(xintc_irqc->base + reg); + else + return ioread32(xintc_irqc->base + reg); } static void intc_enable_or_unmask(struct irq_data *d) @@ -65,21 +67,21 @@ static void intc_enable_or_unmask(struct irq_data *d) * acks the irq before calling the interrupt handler */ if (irqd_is_level_type(d)) - write_fn(mask, intc_baseaddr + IAR); + xintc_write(IAR, mask); - write_fn(mask, intc_baseaddr + SIE); + xintc_write(SIE, mask); } static void intc_disable_or_mask(struct irq_data *d) { pr_debug("irq-xilinx: disable: %ld\n", d->hwirq); - write_fn(1 << d->hwirq, intc_baseaddr + CIE); + xintc_write(CIE, 1 << d->hwirq); } static void intc_ack(struct irq_data *d) { pr_debug("irq-xilinx: ack: %ld\n", d->hwirq); - write_fn(1 << d->hwirq, intc_baseaddr + IAR); + xintc_write(IAR, 1 << d->hwirq); } static void intc_mask_ack(struct irq_data *d) @@ -87,8 +89,8 @@ static void intc_mask_ack(struct irq_data *d) unsigned long mask = 1 << d->hwirq; pr_debug("irq-xilinx: disable_and_ack: %ld\n", d->hwirq); - write_fn(mask, intc_baseaddr + CIE); - write_fn(mask, intc_baseaddr + IAR); + xintc_write(CIE, mask); + xintc_write(IAR, mask); } static struct irq_chip intc_dev = { @@ -99,15 +101,13 @@ static struct irq_chip intc_dev = { .irq_mask_ack = intc_mask_ack, }; -static struct irq_domain *root_domain; - unsigned int get_irq(void) { unsigned int hwirq, irq = -1; - hwirq = read_fn(intc_baseaddr + IVR); + hwirq = xintc_read(IVR); if (hwirq != -1U) - irq = irq_find_mapping(root_domain, hwirq); + irq = irq_find_mapping(xintc_irqc->root_domain, hwirq); pr_debug("irq-xilinx: hwirq=%d, irq=%d\n", hwirq, irq); @@ -116,9 +116,7 @@ unsigned int get_irq(void) static int xintc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { - u32 intr_mask = (u32)d->host_data; - - if (intr_mask & (1 << hw)) { + if (xintc_irqc->intr_mask & (1 << hw)) { irq_set_chip_and_handler_name(irq, &intc_dev, handle_edge_irq, "edge"); irq_clear_status_flags(irq, IRQ_LEVEL); @@ -138,59 +136,75 @@ static const struct irq_domain_ops xintc_irq_domain_ops = { static int __init xilinx_intc_of_init(struct device_node *intc, struct device_node *parent) { - u32 nr_irq, intr_mask; + u32 nr_irq; int ret; + struct xintc_irq_chip *irqc; - intc_baseaddr = of_iomap(intc, 0); - BUG_ON(!intc_baseaddr); + if (xintc_irqc) { + pr_err("irq-xilinx: Multiple instances aren't supported\n"); + return -EINVAL; + } + + irqc = kzalloc(sizeof(*irqc), GFP_KERNEL); + if (!irqc) + return -ENOMEM; + + xintc_irqc = irqc; + + irqc->base = of_iomap(intc, 0); + BUG_ON(!irqc->base); ret = of_property_read_u32(intc, "xlnx,num-intr-inputs", &nr_irq); if (ret < 0) { pr_err("irq-xilinx: unable to read xlnx,num-intr-inputs\n"); - return ret; + goto err_alloc; } - ret = of_property_read_u32(intc, "xlnx,kind-of-intr", &intr_mask); + ret = of_property_read_u32(intc, "xlnx,kind-of-intr", &irqc->intr_mask); if (ret < 0) { pr_err("irq-xilinx: unable to read xlnx,kind-of-intr\n"); - return ret; + goto err_alloc; } - if (intr_mask >> nr_irq) + if (irqc->intr_mask >> nr_irq) pr_warn("irq-xilinx: mismatch in kind-of-intr param\n"); pr_info("irq-xilinx: %s: num_irq=%d, edge=0x%x\n", - intc->full_name, nr_irq, intr_mask); + intc->full_name, nr_irq, irqc->intr_mask); - write_fn = intc_write32; - read_fn = intc_read32; /* * Disable all external interrupts until they are * explicity requested. */ - write_fn(0, intc_baseaddr + IER); + xintc_write(IER, 0); /* Acknowledge any pending interrupts just in case. */ - write_fn(0xffffffff, intc_baseaddr + IAR); + xintc_write(IAR, 0xffffffff); /* Turn on the Master Enable. */ - write_fn(MER_HIE | MER_ME, intc_baseaddr + MER); - if (!(read_fn(intc_baseaddr + MER) & (MER_HIE | MER_ME))) { - write_fn = intc_write32_be; - read_fn = intc_read32_be; - write_fn(MER_HIE | MER_ME, intc_baseaddr + MER); + xintc_write(MER, MER_HIE | MER_ME); + if (!(xintc_read(MER) & (MER_HIE | MER_ME))) { + static_branch_enable(&xintc_is_be); + xintc_write(MER, MER_HIE | MER_ME); } - /* Yeah, okay, casting the intr_mask to a void* is butt-ugly, but I'm - * lazy and Michal can clean it up to something nicer when he tests - * and commits this patch. ~~gcl */ - root_domain = irq_domain_add_linear(intc, nr_irq, &xintc_irq_domain_ops, - (void *)intr_mask); + irqc->root_domain = irq_domain_add_linear(intc, nr_irq, + &xintc_irq_domain_ops, irqc); + if (!irqc->root_domain) { + pr_err("irq-xilinx: Unable to create IRQ domain\n"); + goto err_alloc; + } - irq_set_default_host(root_domain); + irq_set_default_host(irqc->root_domain); return 0; + +err_alloc: + xintc_irqc = NULL; + kfree(irqc); + return ret; + } IRQCHIP_DECLARE(xilinx_intc, "xlnx,xps-intc-1.00.a", xilinx_intc_of_init); From 2120a43527f3bd0efba0e893799acdd1486c2d1b Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Mon, 14 Nov 2016 12:13:48 +0000 Subject: [PATCH 22/25] irqchip/xilinx: Rename get_irq to xintc_get_irq Now that the driver is generic and used by multiple archs, get_irq is too generic. Rename get_irq to xintc_get_irq to avoid any conflicts Acked-by: Michal Simek Signed-off-by: Zubair Lutfullah Kakakhel Signed-off-by: Marc Zyngier --- arch/microblaze/include/asm/irq.h | 2 +- arch/microblaze/kernel/irq.c | 4 ++-- drivers/irqchip/irq-xilinx-intc.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/microblaze/include/asm/irq.h b/arch/microblaze/include/asm/irq.h index bab3b1393ad43..d785defeeed58 100644 --- a/arch/microblaze/include/asm/irq.h +++ b/arch/microblaze/include/asm/irq.h @@ -16,6 +16,6 @@ struct pt_regs; extern void do_IRQ(struct pt_regs *regs); /* should be defined in each interrupt controller driver */ -extern unsigned int get_irq(void); +extern unsigned int xintc_get_irq(void); #endif /* _ASM_MICROBLAZE_IRQ_H */ diff --git a/arch/microblaze/kernel/irq.c b/arch/microblaze/kernel/irq.c index 11e24de91aa42..903dad822fad9 100644 --- a/arch/microblaze/kernel/irq.c +++ b/arch/microblaze/kernel/irq.c @@ -29,12 +29,12 @@ void __irq_entry do_IRQ(struct pt_regs *regs) trace_hardirqs_off(); irq_enter(); - irq = get_irq(); + irq = xintc_get_irq(); next_irq: BUG_ON(!irq); generic_handle_irq(irq); - irq = get_irq(); + irq = xintc_get_irq(); if (irq != -1U) { pr_debug("next irq: %d\n", irq); ++concurrent_irq; diff --git a/drivers/irqchip/irq-xilinx-intc.c b/drivers/irqchip/irq-xilinx-intc.c index 7331d8cb35f1a..34ec609ea8983 100644 --- a/drivers/irqchip/irq-xilinx-intc.c +++ b/drivers/irqchip/irq-xilinx-intc.c @@ -101,7 +101,7 @@ static struct irq_chip intc_dev = { .irq_mask_ack = intc_mask_ack, }; -unsigned int get_irq(void) +unsigned int xintc_get_irq(void) { unsigned int hwirq, irq = -1; From 9689c99e49509f19abd4a6c040bfc7fc051ef65b Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Mon, 14 Nov 2016 12:13:49 +0000 Subject: [PATCH 23/25] irqchip/xilinx: Add support for parent intc The MIPS based xilfpga platform has the following IRQ structure Peripherals --> xilinx_intcontroller -> mips_cpu_int controller Add support for the driver to chain the irq handler Signed-off-by: Zubair Lutfullah Kakakhel Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-xilinx-intc.c | 34 +++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-xilinx-intc.c b/drivers/irqchip/irq-xilinx-intc.c index 34ec609ea8983..971c141d575da 100644 --- a/drivers/irqchip/irq-xilinx-intc.c +++ b/drivers/irqchip/irq-xilinx-intc.c @@ -12,10 +12,12 @@ #include #include #include +#include #include #include #include #include +#include /* No one else should require these constants, so define them locally here. */ #define ISR 0x00 /* Interrupt Status Register */ @@ -133,11 +135,26 @@ static const struct irq_domain_ops xintc_irq_domain_ops = { .map = xintc_map, }; +static void xil_intc_irq_handler(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + u32 pending; + + chained_irq_enter(chip, desc); + do { + pending = xintc_get_irq(); + if (pending == -1U) + break; + generic_handle_irq(pending); + } while (true); + chained_irq_exit(chip, desc); +} + static int __init xilinx_intc_of_init(struct device_node *intc, struct device_node *parent) { u32 nr_irq; - int ret; + int ret, irq; struct xintc_irq_chip *irqc; if (xintc_irqc) { @@ -196,7 +213,20 @@ static int __init xilinx_intc_of_init(struct device_node *intc, goto err_alloc; } - irq_set_default_host(irqc->root_domain); + if (parent) { + irq = irq_of_parse_and_map(intc, 0); + if (irq) { + irq_set_chained_handler_and_data(irq, + xil_intc_irq_handler, + irqc); + } else { + pr_err("irq-xilinx: interrupts property not in DT\n"); + ret = -EINVAL; + goto err_alloc; + } + } else { + irq_set_default_host(irqc->root_domain); + } return 0; From 8a11da598e2ff96050a99e24772e1568e3e31c4d Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Mon, 14 Nov 2016 12:13:50 +0000 Subject: [PATCH 24/25] irqchip/xilinx: Try to fall back if xlnx,kind-of-intr not provided The powerpc dts file does not have the xlnx,kind-of-intr property. Instead of erroring out, give a warning instead. And attempt to continue to probe the interrupt controller while assuming kind-of-intr is 0x0 as a fall back. Acked-by: Michal Simek Signed-off-by: Zubair Lutfullah Kakakhel Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-xilinx-intc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-xilinx-intc.c b/drivers/irqchip/irq-xilinx-intc.c index 971c141d575da..d330917840a4b 100644 --- a/drivers/irqchip/irq-xilinx-intc.c +++ b/drivers/irqchip/irq-xilinx-intc.c @@ -179,8 +179,8 @@ static int __init xilinx_intc_of_init(struct device_node *intc, ret = of_property_read_u32(intc, "xlnx,kind-of-intr", &irqc->intr_mask); if (ret < 0) { - pr_err("irq-xilinx: unable to read xlnx,kind-of-intr\n"); - goto err_alloc; + pr_warn("irq-xilinx: unable to read xlnx,kind-of-intr\n"); + irqc->intr_mask = 0; } if (irqc->intr_mask >> nr_irq) From 8328255ff81ed422847b443f81b689366e98ce95 Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Mon, 14 Nov 2016 12:13:51 +0000 Subject: [PATCH 25/25] powerpc/virtex: Use generic xilinx irqchip driver The Xilinx interrupt controller driver is now available in drivers/irqchip. Switch to using that driver. Acked-by: Michael Ellerman Acked-by: Michal Simek Signed-off-by: Zubair Lutfullah Kakakhel Signed-off-by: Marc Zyngier --- arch/powerpc/include/asm/xilinx_intc.h | 2 +- arch/powerpc/platforms/40x/Kconfig | 1 + arch/powerpc/platforms/40x/virtex.c | 2 +- arch/powerpc/platforms/44x/Kconfig | 1 + arch/powerpc/platforms/44x/virtex.c | 2 +- arch/powerpc/sysdev/xilinx_intc.c | 211 +------------------------ drivers/irqchip/irq-xilinx-intc.c | 3 +- 7 files changed, 9 insertions(+), 213 deletions(-) diff --git a/arch/powerpc/include/asm/xilinx_intc.h b/arch/powerpc/include/asm/xilinx_intc.h index 343612f8fece3..3192d7f0a05b6 100644 --- a/arch/powerpc/include/asm/xilinx_intc.h +++ b/arch/powerpc/include/asm/xilinx_intc.h @@ -14,7 +14,7 @@ #ifdef __KERNEL__ extern void __init xilinx_intc_init_tree(void); -extern unsigned int xilinx_intc_get_irq(void); +extern unsigned int xintc_get_irq(void); #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_XILINX_INTC_H */ diff --git a/arch/powerpc/platforms/40x/Kconfig b/arch/powerpc/platforms/40x/Kconfig index e3257f24a8a19..1d7c1b142bf45 100644 --- a/arch/powerpc/platforms/40x/Kconfig +++ b/arch/powerpc/platforms/40x/Kconfig @@ -64,6 +64,7 @@ config XILINX_VIRTEX_GENERIC_BOARD default n select XILINX_VIRTEX_II_PRO select XILINX_VIRTEX_4_FX + select XILINX_INTC help This option enables generic support for Xilinx Virtex based boards. diff --git a/arch/powerpc/platforms/40x/virtex.c b/arch/powerpc/platforms/40x/virtex.c index 91a08ea758a8a..e3d5e095846b9 100644 --- a/arch/powerpc/platforms/40x/virtex.c +++ b/arch/powerpc/platforms/40x/virtex.c @@ -48,7 +48,7 @@ define_machine(virtex) { .probe = virtex_probe, .setup_arch = xilinx_pci_init, .init_IRQ = xilinx_intc_init_tree, - .get_irq = xilinx_intc_get_irq, + .get_irq = xintc_get_irq, .restart = ppc4xx_reset_system, .calibrate_decr = generic_calibrate_decr, }; diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig index 48fc18041ff68..25b8d641ff9fa 100644 --- a/arch/powerpc/platforms/44x/Kconfig +++ b/arch/powerpc/platforms/44x/Kconfig @@ -241,6 +241,7 @@ config XILINX_VIRTEX440_GENERIC_BOARD depends on 44x default n select XILINX_VIRTEX_5_FXT + select XILINX_INTC help This option enables generic support for Xilinx Virtex based boards that use a 440 based processor in the Virtex 5 FXT FPGA architecture. diff --git a/arch/powerpc/platforms/44x/virtex.c b/arch/powerpc/platforms/44x/virtex.c index a7e08026097a5..3eb13ed926ee6 100644 --- a/arch/powerpc/platforms/44x/virtex.c +++ b/arch/powerpc/platforms/44x/virtex.c @@ -54,7 +54,7 @@ define_machine(virtex) { .probe = virtex_probe, .setup_arch = xilinx_pci_init, .init_IRQ = xilinx_intc_init_tree, - .get_irq = xilinx_intc_get_irq, + .get_irq = xintc_get_irq, .calibrate_decr = generic_calibrate_decr, .restart = ppc4xx_reset_system, }; diff --git a/arch/powerpc/sysdev/xilinx_intc.c b/arch/powerpc/sysdev/xilinx_intc.c index 0f52d79557967..4a86dcff3fcd5 100644 --- a/arch/powerpc/sysdev/xilinx_intc.c +++ b/arch/powerpc/sysdev/xilinx_intc.c @@ -29,194 +29,7 @@ #include #include #include - -/* - * INTC Registers - */ -#define XINTC_ISR 0 /* Interrupt Status */ -#define XINTC_IPR 4 /* Interrupt Pending */ -#define XINTC_IER 8 /* Interrupt Enable */ -#define XINTC_IAR 12 /* Interrupt Acknowledge */ -#define XINTC_SIE 16 /* Set Interrupt Enable bits */ -#define XINTC_CIE 20 /* Clear Interrupt Enable bits */ -#define XINTC_IVR 24 /* Interrupt Vector */ -#define XINTC_MER 28 /* Master Enable */ - -static struct irq_domain *master_irqhost; - -#define XILINX_INTC_MAXIRQS (32) - -/* The following table allows the interrupt type, edge or level, - * to be cached after being read from the device tree until the interrupt - * is mapped - */ -static int xilinx_intc_typetable[XILINX_INTC_MAXIRQS]; - -/* Map the interrupt type from the device tree to the interrupt types - * used by the interrupt subsystem - */ -static unsigned char xilinx_intc_map_senses[] = { - IRQ_TYPE_EDGE_RISING, - IRQ_TYPE_EDGE_FALLING, - IRQ_TYPE_LEVEL_HIGH, - IRQ_TYPE_LEVEL_LOW, -}; - -/* - * The interrupt controller is setup such that it doesn't work well with - * the level interrupt handler in the kernel because the handler acks the - * interrupt before calling the application interrupt handler. To deal with - * that, we use 2 different irq chips so that different functions can be - * used for level and edge type interrupts. - * - * IRQ Chip common (across level and edge) operations - */ -static void xilinx_intc_mask(struct irq_data *d) -{ - int irq = irqd_to_hwirq(d); - void * regs = irq_data_get_irq_chip_data(d); - pr_debug("mask: %d\n", irq); - out_be32(regs + XINTC_CIE, 1 << irq); -} - -static int xilinx_intc_set_type(struct irq_data *d, unsigned int flow_type) -{ - return 0; -} - -/* - * IRQ Chip level operations - */ -static void xilinx_intc_level_unmask(struct irq_data *d) -{ - int irq = irqd_to_hwirq(d); - void * regs = irq_data_get_irq_chip_data(d); - pr_debug("unmask: %d\n", irq); - out_be32(regs + XINTC_SIE, 1 << irq); - - /* ack level irqs because they can't be acked during - * ack function since the handle_level_irq function - * acks the irq before calling the inerrupt handler - */ - out_be32(regs + XINTC_IAR, 1 << irq); -} - -static struct irq_chip xilinx_intc_level_irqchip = { - .name = "Xilinx Level INTC", - .irq_mask = xilinx_intc_mask, - .irq_mask_ack = xilinx_intc_mask, - .irq_unmask = xilinx_intc_level_unmask, - .irq_set_type = xilinx_intc_set_type, -}; - -/* - * IRQ Chip edge operations - */ -static void xilinx_intc_edge_unmask(struct irq_data *d) -{ - int irq = irqd_to_hwirq(d); - void *regs = irq_data_get_irq_chip_data(d); - pr_debug("unmask: %d\n", irq); - out_be32(regs + XINTC_SIE, 1 << irq); -} - -static void xilinx_intc_edge_ack(struct irq_data *d) -{ - int irq = irqd_to_hwirq(d); - void * regs = irq_data_get_irq_chip_data(d); - pr_debug("ack: %d\n", irq); - out_be32(regs + XINTC_IAR, 1 << irq); -} - -static struct irq_chip xilinx_intc_edge_irqchip = { - .name = "Xilinx Edge INTC", - .irq_mask = xilinx_intc_mask, - .irq_unmask = xilinx_intc_edge_unmask, - .irq_ack = xilinx_intc_edge_ack, - .irq_set_type = xilinx_intc_set_type, -}; - -/* - * IRQ Host operations - */ - -/** - * xilinx_intc_xlate - translate virq# from device tree interrupts property - */ -static int xilinx_intc_xlate(struct irq_domain *h, struct device_node *ct, - const u32 *intspec, unsigned int intsize, - irq_hw_number_t *out_hwirq, - unsigned int *out_flags) -{ - if ((intsize < 2) || (intspec[0] >= XILINX_INTC_MAXIRQS)) - return -EINVAL; - - /* keep a copy of the interrupt type til the interrupt is mapped - */ - xilinx_intc_typetable[intspec[0]] = xilinx_intc_map_senses[intspec[1]]; - - /* Xilinx uses 2 interrupt entries, the 1st being the h/w - * interrupt number, the 2nd being the interrupt type, edge or level - */ - *out_hwirq = intspec[0]; - *out_flags = xilinx_intc_map_senses[intspec[1]]; - - return 0; -} -static int xilinx_intc_map(struct irq_domain *h, unsigned int virq, - irq_hw_number_t irq) -{ - irq_set_chip_data(virq, h->host_data); - - if (xilinx_intc_typetable[irq] == IRQ_TYPE_LEVEL_HIGH || - xilinx_intc_typetable[irq] == IRQ_TYPE_LEVEL_LOW) { - irq_set_chip_and_handler(virq, &xilinx_intc_level_irqchip, - handle_level_irq); - } else { - irq_set_chip_and_handler(virq, &xilinx_intc_edge_irqchip, - handle_edge_irq); - } - return 0; -} - -static const struct irq_domain_ops xilinx_intc_ops = { - .map = xilinx_intc_map, - .xlate = xilinx_intc_xlate, -}; - -struct irq_domain * __init -xilinx_intc_init(struct device_node *np) -{ - struct irq_domain * irq; - void * regs; - - /* Find and map the intc registers */ - regs = of_iomap(np, 0); - if (!regs) { - pr_err("xilinx_intc: could not map registers\n"); - return NULL; - } - - /* Setup interrupt controller */ - out_be32(regs + XINTC_IER, 0); /* disable all irqs */ - out_be32(regs + XINTC_IAR, ~(u32) 0); /* Acknowledge pending irqs */ - out_be32(regs + XINTC_MER, 0x3UL); /* Turn on the Master Enable. */ - - /* Allocate and initialize an irq_domain structure. */ - irq = irq_domain_add_linear(np, XILINX_INTC_MAXIRQS, &xilinx_intc_ops, - regs); - if (!irq) - panic(__FILE__ ": Cannot allocate IRQ host\n"); - - return irq; -} - -int xilinx_intc_get_irq(void) -{ - void * regs = master_irqhost->host_data; - pr_debug("get_irq:\n"); - return irq_linear_revmap(master_irqhost, in_be32(regs + XINTC_IVR)); -} +#include #if defined(CONFIG_PPC_I8259) /* @@ -265,31 +78,11 @@ static void __init xilinx_i8259_setup_cascade(void) static inline void xilinx_i8259_setup_cascade(void) { return; } #endif /* defined(CONFIG_PPC_I8259) */ -static const struct of_device_id xilinx_intc_match[] __initconst = { - { .compatible = "xlnx,opb-intc-1.00.c", }, - { .compatible = "xlnx,xps-intc-1.00.a", }, - {} -}; - /* * Initialize master Xilinx interrupt controller */ void __init xilinx_intc_init_tree(void) { - struct device_node *np; - - /* find top level interrupt controller */ - for_each_matching_node(np, xilinx_intc_match) { - if (!of_get_property(np, "interrupts", NULL)) - break; - } - BUG_ON(!np); - - master_irqhost = xilinx_intc_init(np); - BUG_ON(!master_irqhost); - - irq_set_default_host(master_irqhost); - of_node_put(np); - + irqchip_init(); xilinx_i8259_setup_cascade(); } diff --git a/drivers/irqchip/irq-xilinx-intc.c b/drivers/irqchip/irq-xilinx-intc.c index d330917840a4b..3db7ab1c97416 100644 --- a/drivers/irqchip/irq-xilinx-intc.c +++ b/drivers/irqchip/irq-xilinx-intc.c @@ -237,4 +237,5 @@ static int __init xilinx_intc_of_init(struct device_node *intc, } -IRQCHIP_DECLARE(xilinx_intc, "xlnx,xps-intc-1.00.a", xilinx_intc_of_init); +IRQCHIP_DECLARE(xilinx_intc_xps, "xlnx,xps-intc-1.00.a", xilinx_intc_of_init); +IRQCHIP_DECLARE(xilinx_intc_opb, "xlnx,opb-intc-1.00.c", xilinx_intc_of_init);