Merge branch 'irq/for-block' into irq/core
authorThomas Gleixner <tglx@linutronix.de>
Thu, 15 Sep 2016 18:54:40 +0000 (20:54 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Thu, 15 Sep 2016 18:54:40 +0000 (20:54 +0200)
Add the new irq spreading infrastructure.

25 files changed:
Documentation/ABI/testing/sysfs-kernel-irq [new file with mode: 0644]
arch/arm/include/asm/arch_gicv3.h
arch/arm64/include/asm/arch_gicv3.h
drivers/acpi/Kconfig
drivers/acpi/Makefile
drivers/acpi/arm64/Kconfig [new file with mode: 0644]
drivers/acpi/arm64/Makefile [new file with mode: 0644]
drivers/acpi/arm64/iort.c [new file with mode: 0644]
drivers/acpi/bus.c
drivers/irqchip/Kconfig
drivers/irqchip/irq-gic-pm.c
drivers/irqchip/irq-gic-v3-its-pci-msi.c
drivers/irqchip/irq-gic-v3-its.c
drivers/irqchip/irq-gic-v3.c
drivers/irqchip/irq-gic.c
drivers/pci/msi.c
include/linux/acpi_iort.h [new file with mode: 0644]
include/linux/irq.h
include/linux/irqchip/arm-gic-v3.h
include/linux/irqdesc.h
kernel/irq/chip.c
kernel/irq/generic-chip.c
kernel/irq/irqdesc.c
kernel/irq/irqdomain.c
kernel/irq/manage.c

diff --git a/Documentation/ABI/testing/sysfs-kernel-irq b/Documentation/ABI/testing/sysfs-kernel-irq
new file mode 100644 (file)
index 0000000..eb074b1
--- /dev/null
@@ -0,0 +1,53 @@
+What:          /sys/kernel/irq
+Date:          September 2016
+KernelVersion: 4.9
+Contact:       Craig Gallek <kraig@google.com>
+Description:   Directory containing information about the system's IRQs.
+               Specifically, data from the associated struct irq_desc.
+               The information here is similar to that in /proc/interrupts
+               but in a more machine-friendly format.  This directory contains
+               one subdirectory for each Linux IRQ number.
+
+What:          /sys/kernel/irq/<irq>/actions
+Date:          September 2016
+KernelVersion: 4.9
+Contact:       Craig Gallek <kraig@google.com>
+Description:   The IRQ action chain.  A comma-separated list of zero or more
+               device names associated with this interrupt.
+
+What:          /sys/kernel/irq/<irq>/chip_name
+Date:          September 2016
+KernelVersion: 4.9
+Contact:       Craig Gallek <kraig@google.com>
+Description:   Human-readable chip name supplied by the associated device
+               driver.
+
+What:          /sys/kernel/irq/<irq>/hwirq
+Date:          September 2016
+KernelVersion: 4.9
+Contact:       Craig Gallek <kraig@google.com>
+Description:   When interrupt translation domains are used, this file contains
+               the underlying hardware IRQ number used for this Linux IRQ.
+
+What:          /sys/kernel/irq/<irq>/name
+Date:          September 2016
+KernelVersion: 4.9
+Contact:       Craig Gallek <kraig@google.com>
+Description:   Human-readable flow handler name as defined by the irq chip
+               driver.
+
+What:          /sys/kernel/irq/<irq>/per_cpu_count
+Date:          September 2016
+KernelVersion: 4.9
+Contact:       Craig Gallek <kraig@google.com>
+Description:   The number of times the interrupt has fired since boot.  This
+               is a comma-separated list of counters; one per CPU in CPU id
+               order.  NOTE: This file consistently shows counters for all
+               CPU ids.  This differs from the behavior of /proc/interrupts
+               which only shows counters for online CPUs.
+
+What:          /sys/kernel/irq/<irq>/type
+Date:          September 2016
+KernelVersion: 4.9
+Contact:       Craig Gallek <kraig@google.com>
+Description:   The type of the interrupt.  Either the string 'level' or 'edge'.
index e08d151..dfe4002 100644 (file)
@@ -34,6 +34,7 @@
 #define ICC_CTLR                       __ACCESS_CP15(c12, 0, c12, 4)
 #define ICC_SRE                                __ACCESS_CP15(c12, 0, c12, 5)
 #define ICC_IGRPEN1                    __ACCESS_CP15(c12, 0, c12, 7)
+#define ICC_BPR1                       __ACCESS_CP15(c12, 0, c12, 3)
 
 #define ICC_HSRE                       __ACCESS_CP15(c12, 4, c9, 5)
 
@@ -157,6 +158,11 @@ static inline void gic_write_sre(u32 val)
        isb();
 }
 
+static inline void gic_write_bpr1(u32 val)
+{
+       asm volatile("mcr " __stringify(ICC_BPR1) : : "r" (val));
+}
+
 /*
  * Even in 32bit systems that use LPAE, there is no guarantee that the I/O
  * interface provides true 64bit atomic accesses, so using strd/ldrd doesn't
index 8ec88e5..fc2a0cb 100644 (file)
@@ -28,6 +28,7 @@
 #define ICC_CTLR_EL1                   sys_reg(3, 0, 12, 12, 4)
 #define ICC_SRE_EL1                    sys_reg(3, 0, 12, 12, 5)
 #define ICC_GRPEN1_EL1                 sys_reg(3, 0, 12, 12, 7)
+#define ICC_BPR1_EL1                   sys_reg(3, 0, 12, 12, 3)
 
 #define ICC_SRE_EL2                    sys_reg(3, 4, 12, 9, 5)
 
@@ -165,6 +166,11 @@ static inline void gic_write_sre(u32 val)
        isb();
 }
 
+static inline void gic_write_bpr1(u32 val)
+{
+       asm volatile("msr_s " __stringify(ICC_BPR1_EL1) ", %0" : : "r" (val));
+}
+
 #define gic_read_typer(c)              readq_relaxed(c)
 #define gic_write_irouter(v, c)                writeq_relaxed(v, c)
 
index 445ce28..d5c0614 100644 (file)
@@ -521,4 +521,8 @@ config ACPI_CONFIGFS
          userspace. The configurable ACPI groups will be visible under
          /config/acpi, assuming configfs is mounted under /config.
 
+if ARM64
+source "drivers/acpi/arm64/Kconfig"
+endif
+
 endif  # ACPI
index 5ae9d85..e5ada78 100644 (file)
@@ -105,3 +105,5 @@ obj-$(CONFIG_ACPI_CONFIGFS) += acpi_configfs.o
 
 video-objs                     += acpi_video.o video_detect.o
 obj-y                          += dptf/
+
+obj-$(CONFIG_ARM64)            += arm64/
diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig
new file mode 100644 (file)
index 0000000..4616da4
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# ACPI Configuration for ARM64
+#
+
+config ACPI_IORT
+       bool
diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
new file mode 100644 (file)
index 0000000..72331f2
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_ACPI_IORT)        += iort.o
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
new file mode 100644 (file)
index 0000000..6b81746
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2016, Semihalf
+ *     Author: Tomasz Nowicki <tn@semihalf.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * This file implements early detection/parsing of I/O mapping
+ * reported to OS through firmware via I/O Remapping Table (IORT)
+ * IORT document number: ARM DEN 0049A
+ */
+
+#define pr_fmt(fmt)    "ACPI: IORT: " fmt
+
+#include <linux/acpi_iort.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+struct iort_its_msi_chip {
+       struct list_head        list;
+       struct fwnode_handle    *fw_node;
+       u32                     translation_id;
+};
+
+typedef acpi_status (*iort_find_node_callback)
+       (struct acpi_iort_node *node, void *context);
+
+/* Root pointer to the mapped IORT table */
+static struct acpi_table_header *iort_table;
+
+static LIST_HEAD(iort_msi_chip_list);
+static DEFINE_SPINLOCK(iort_msi_chip_lock);
+
+/**
+ * iort_register_domain_token() - register domain token and related ITS ID
+ * to the list from where we can get it back later on.
+ * @trans_id: ITS ID.
+ * @fw_node: Domain token.
+ *
+ * Returns: 0 on success, -ENOMEM if no memory when allocating list element
+ */
+int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node)
+{
+       struct iort_its_msi_chip *its_msi_chip;
+
+       its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL);
+       if (!its_msi_chip)
+               return -ENOMEM;
+
+       its_msi_chip->fw_node = fw_node;
+       its_msi_chip->translation_id = trans_id;
+
+       spin_lock(&iort_msi_chip_lock);
+       list_add(&its_msi_chip->list, &iort_msi_chip_list);
+       spin_unlock(&iort_msi_chip_lock);
+
+       return 0;
+}
+
+/**
+ * iort_deregister_domain_token() - Deregister domain token based on ITS ID
+ * @trans_id: ITS ID.
+ *
+ * Returns: none.
+ */
+void iort_deregister_domain_token(int trans_id)
+{
+       struct iort_its_msi_chip *its_msi_chip, *t;
+
+       spin_lock(&iort_msi_chip_lock);
+       list_for_each_entry_safe(its_msi_chip, t, &iort_msi_chip_list, list) {
+               if (its_msi_chip->translation_id == trans_id) {
+                       list_del(&its_msi_chip->list);
+                       kfree(its_msi_chip);
+                       break;
+               }
+       }
+       spin_unlock(&iort_msi_chip_lock);
+}
+
+/**
+ * iort_find_domain_token() - Find domain token based on given ITS ID
+ * @trans_id: ITS ID.
+ *
+ * Returns: domain token when find on the list, NULL otherwise
+ */
+struct fwnode_handle *iort_find_domain_token(int trans_id)
+{
+       struct fwnode_handle *fw_node = NULL;
+       struct iort_its_msi_chip *its_msi_chip;
+
+       spin_lock(&iort_msi_chip_lock);
+       list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) {
+               if (its_msi_chip->translation_id == trans_id) {
+                       fw_node = its_msi_chip->fw_node;
+                       break;
+               }
+       }
+       spin_unlock(&iort_msi_chip_lock);
+
+       return fw_node;
+}
+
+static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type,
+                                            iort_find_node_callback callback,
+                                            void *context)
+{
+       struct acpi_iort_node *iort_node, *iort_end;
+       struct acpi_table_iort *iort;
+       int i;
+
+       if (!iort_table)
+               return NULL;
+
+       /* Get the first IORT node */
+       iort = (struct acpi_table_iort *)iort_table;
+       iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort,
+                                iort->node_offset);
+       iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
+                               iort_table->length);
+
+       for (i = 0; i < iort->node_count; i++) {
+               if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND,
+                              "IORT node pointer overflows, bad table!\n"))
+                       return NULL;
+
+               if (iort_node->type == type &&
+                   ACPI_SUCCESS(callback(iort_node, context)))
+                               return iort_node;
+
+               iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
+                                        iort_node->length);
+       }
+
+       return NULL;
+}
+
+static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
+                                           void *context)
+{
+       struct device *dev = context;
+       acpi_status status;
+
+       if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
+               struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
+               struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
+               struct acpi_iort_named_component *ncomp;
+
+               if (!adev) {
+                       status = AE_NOT_FOUND;
+                       goto out;
+               }
+
+               status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf);
+               if (ACPI_FAILURE(status)) {
+                       dev_warn(dev, "Can't get device full path name\n");
+                       goto out;
+               }
+
+               ncomp = (struct acpi_iort_named_component *)node->node_data;
+               status = !strcmp(ncomp->device_name, buf.pointer) ?
+                                                       AE_OK : AE_NOT_FOUND;
+               acpi_os_free(buf.pointer);
+       } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
+               struct acpi_iort_root_complex *pci_rc;
+               struct pci_bus *bus;
+
+               bus = to_pci_bus(dev);
+               pci_rc = (struct acpi_iort_root_complex *)node->node_data;
+
+               /*
+                * It is assumed that PCI segment numbers maps one-to-one
+                * with root complexes. Each segment number can represent only
+                * one root complex.
+                */
+               status = pci_rc->pci_segment_number == pci_domain_nr(bus) ?
+                                                       AE_OK : AE_NOT_FOUND;
+       } else {
+               status = AE_NOT_FOUND;
+       }
+out:
+       return status;
+}
+
+static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
+                      u32 *rid_out)
+{
+       /* Single mapping does not care for input id */
+       if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) {
+               if (type == ACPI_IORT_NODE_NAMED_COMPONENT ||
+                   type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
+                       *rid_out = map->output_base;
+                       return 0;
+               }
+
+               pr_warn(FW_BUG "[map %p] SINGLE MAPPING flag not allowed for node type %d, skipping ID map\n",
+                       map, type);
+               return -ENXIO;
+       }
+
+       if (rid_in < map->input_base ||
+           (rid_in >= map->input_base + map->id_count))
+               return -ENXIO;
+
+       *rid_out = map->output_base + (rid_in - map->input_base);
+       return 0;
+}
+
+static struct acpi_iort_node *iort_node_map_rid(struct acpi_iort_node *node,
+                                               u32 rid_in, u32 *rid_out,
+                                               u8 type)
+{
+       u32 rid = rid_in;
+
+       /* Parse the ID mapping tree to find specified node type */
+       while (node) {
+               struct acpi_iort_id_mapping *map;
+               int i;
+
+               if (node->type == type) {
+                       if (rid_out)
+                               *rid_out = rid;
+                       return node;
+               }
+
+               if (!node->mapping_offset || !node->mapping_count)
+                       goto fail_map;
+
+               map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node,
+                                  node->mapping_offset);
+
+               /* Firmware bug! */
+               if (!map->output_reference) {
+                       pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n",
+                              node, node->type);
+                       goto fail_map;
+               }
+
+               /* Do the RID translation */
+               for (i = 0; i < node->mapping_count; i++, map++) {
+                       if (!iort_id_map(map, node->type, rid, &rid))
+                               break;
+               }
+
+               if (i == node->mapping_count)
+                       goto fail_map;
+
+               node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
+                                   map->output_reference);
+       }
+
+fail_map:
+       /* Map input RID to output RID unchanged on mapping failure*/
+       if (rid_out)
+               *rid_out = rid_in;
+
+       return NULL;
+}
+
+static struct acpi_iort_node *iort_find_dev_node(struct device *dev)
+{
+       struct pci_bus *pbus;
+
+       if (!dev_is_pci(dev))
+               return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+                                     iort_match_node_callback, dev);
+
+       /* Find a PCI root bus */
+       pbus = to_pci_dev(dev)->bus;
+       while (!pci_is_root_bus(pbus))
+               pbus = pbus->parent;
+
+       return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
+                             iort_match_node_callback, &pbus->dev);
+}
+
+/**
+ * iort_msi_map_rid() - Map a MSI requester ID for a device
+ * @dev: The device for which the mapping is to be done.
+ * @req_id: The device requester ID.
+ *
+ * Returns: mapped MSI RID on success, input requester ID otherwise
+ */
+u32 iort_msi_map_rid(struct device *dev, u32 req_id)
+{
+       struct acpi_iort_node *node;
+       u32 dev_id;
+
+       node = iort_find_dev_node(dev);
+       if (!node)
+               return req_id;
+
+       iort_node_map_rid(node, req_id, &dev_id, ACPI_IORT_NODE_ITS_GROUP);
+       return dev_id;
+}
+
+/**
+ * iort_dev_find_its_id() - Find the ITS identifier for a device
+ * @dev: The device.
+ * @idx: Index of the ITS identifier list.
+ * @its_id: ITS identifier.
+ *
+ * Returns: 0 on success, appropriate error value otherwise
+ */
+static int iort_dev_find_its_id(struct device *dev, u32 req_id,
+                               unsigned int idx, int *its_id)
+{
+       struct acpi_iort_its_group *its;
+       struct acpi_iort_node *node;
+
+       node = iort_find_dev_node(dev);
+       if (!node)
+               return -ENXIO;
+
+       node = iort_node_map_rid(node, req_id, NULL, ACPI_IORT_NODE_ITS_GROUP);
+       if (!node)
+               return -ENXIO;
+
+       /* Move to ITS specific data */
+       its = (struct acpi_iort_its_group *)node->node_data;
+       if (idx > its->its_count) {
+               dev_err(dev, "requested ITS ID index [%d] is greater than available [%d]\n",
+                       idx, its->its_count);
+               return -ENXIO;
+       }
+
+       *its_id = its->identifiers[idx];
+       return 0;
+}
+
+/**
+ * iort_get_device_domain() - Find MSI domain related to a device
+ * @dev: The device.
+ * @req_id: Requester ID for the device.
+ *
+ * Returns: the MSI domain for this device, NULL otherwise
+ */
+struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id)
+{
+       struct fwnode_handle *handle;
+       int its_id;
+
+       if (iort_dev_find_its_id(dev, req_id, 0, &its_id))
+               return NULL;
+
+       handle = iort_find_domain_token(its_id);
+       if (!handle)
+               return NULL;
+
+       return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI);
+}
+
+void __init acpi_iort_init(void)
+{
+       acpi_status status;
+
+       status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table);
+       if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+               const char *msg = acpi_format_exception(status);
+               pr_err("Failed to get table, %s\n", msg);
+       }
+}
index 85b7d07..e56e643 100644 (file)
@@ -36,6 +36,7 @@
 #ifdef CONFIG_X86
 #include <asm/mpspec.h>
 #endif
+#include <linux/acpi_iort.h>
 #include <linux/pci.h>
 #include <acpi/apei.h>
 #include <linux/dmi.h>
@@ -1186,6 +1187,7 @@ static int __init acpi_init(void)
        }
 
        pci_mmcfg_late_init();
+       acpi_iort_init();
        acpi_scan_init();
        acpi_ec_init();
        acpi_debugfs_init();
index 7f87289..9aeea1d 100644 (file)
@@ -39,6 +39,7 @@ config ARM_GIC_V3_ITS
        bool
        depends on PCI
        depends on PCI_MSI
+       select ACPI_IORT if ACPI
 
 config ARM_NVIC
        bool
index 4cbffba..ecafd29 100644 (file)
@@ -64,7 +64,6 @@ static int gic_runtime_suspend(struct device *dev)
 
 static int gic_get_clocks(struct device *dev, const struct gic_clk_data *data)
 {
-       struct clk *clk;
        unsigned int i;
        int ret;
 
@@ -76,28 +75,16 @@ static int gic_get_clocks(struct device *dev, const struct gic_clk_data *data)
                return ret;
 
        for (i = 0; i < data->num_clocks; i++) {
-               clk = of_clk_get_by_name(dev->of_node, data->clocks[i]);
-               if (IS_ERR(clk)) {
-                       dev_err(dev, "failed to get clock %s\n",
-                               data->clocks[i]);
-                       ret = PTR_ERR(clk);
-                       goto error;
-               }
-
-               ret = pm_clk_add_clk(dev, clk);
+               ret = of_pm_clk_add_clk(dev, data->clocks[i]);
                if (ret) {
-                       dev_err(dev, "failed to add clock at index %d\n", i);
-                       clk_put(clk);
-                       goto error;
+                       dev_err(dev, "failed to add clock %s\n",
+                               data->clocks[i]);
+                       pm_clk_destroy(dev);
+                       return ret;
                }
        }
 
        return 0;
-
-error:
-       pm_clk_destroy(dev);
-
-       return ret;
 }
 
 static int gic_probe(struct platform_device *pdev)
index aee60ed..aee1c60 100644 (file)
@@ -15,6 +15,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/acpi_iort.h>
 #include <linux/msi.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
@@ -106,34 +107,91 @@ static struct of_device_id its_device_id[] = {
        {},
 };
 
-static int __init its_pci_msi_init(void)
+static int __init its_pci_msi_init_one(struct fwnode_handle *handle,
+                                      const char *name)
 {
-       struct device_node *np;
        struct irq_domain *parent;
 
+       parent = irq_find_matching_fwnode(handle, DOMAIN_BUS_NEXUS);
+       if (!parent || !msi_get_domain_info(parent)) {
+               pr_err("%s: Unable to locate ITS domain\n", name);
+               return -ENXIO;
+       }
+
+       if (!pci_msi_create_irq_domain(handle, &its_pci_msi_domain_info,
+                                      parent)) {
+               pr_err("%s: Unable to create PCI domain\n", name);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static int __init its_pci_of_msi_init(void)
+{
+       struct device_node *np;
+
        for (np = of_find_matching_node(NULL, its_device_id); np;
             np = of_find_matching_node(np, its_device_id)) {
                if (!of_property_read_bool(np, "msi-controller"))
                        continue;
 
-               parent = irq_find_matching_host(np, DOMAIN_BUS_NEXUS);
-               if (!parent || !msi_get_domain_info(parent)) {
-                       pr_err("%s: unable to locate ITS domain\n",
-                              np->full_name);
-                       continue;
-               }
-
-               if (!pci_msi_create_irq_domain(of_node_to_fwnode(np),
-                                              &its_pci_msi_domain_info,
-                                              parent)) {
-                       pr_err("%s: unable to create PCI domain\n",
-                              np->full_name);
+               if (its_pci_msi_init_one(of_node_to_fwnode(np), np->full_name))
                        continue;
-               }
 
                pr_info("PCI/MSI: %s domain created\n", np->full_name);
        }
 
        return 0;
 }
+
+#ifdef CONFIG_ACPI
+
+static int __init
+its_pci_msi_parse_madt(struct acpi_subtable_header *header,
+                      const unsigned long end)
+{
+       struct acpi_madt_generic_translator *its_entry;
+       struct fwnode_handle *dom_handle;
+       const char *node_name;
+       int err = -ENXIO;
+
+       its_entry = (struct acpi_madt_generic_translator *)header;
+       node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx",
+                             (long)its_entry->base_address);
+       dom_handle = iort_find_domain_token(its_entry->translation_id);
+       if (!dom_handle) {
+               pr_err("%s: Unable to locate ITS domain handle\n", node_name);
+               goto out;
+       }
+
+       err = its_pci_msi_init_one(dom_handle, node_name);
+       if (!err)
+               pr_info("PCI/MSI: %s domain created\n", node_name);
+
+out:
+       kfree(node_name);
+       return err;
+}
+
+static int __init its_pci_acpi_msi_init(void)
+{
+       acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
+                             its_pci_msi_parse_madt, 0);
+       return 0;
+}
+#else
+static int __init its_pci_acpi_msi_init(void)
+{
+       return 0;
+}
+#endif
+
+static int __init its_pci_msi_init(void)
+{
+       its_pci_of_msi_init();
+       its_pci_acpi_msi_init();
+
+       return 0;
+}
 early_initcall(its_pci_msi_init);
index 36b9c28..35c851c 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/acpi.h>
 #include <linux/bitmap.h>
 #include <linux/cpu.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/acpi_iort.h>
 #include <linux/log2.h>
 #include <linux/mm.h>
 #include <linux/msi.h>
@@ -75,7 +78,7 @@ struct its_node {
        raw_spinlock_t          lock;
        struct list_head        entry;
        void __iomem            *base;
-       unsigned long           phys_base;
+       phys_addr_t             phys_base;
        struct its_cmd_block    *cmd_base;
        struct its_cmd_block    *cmd_write;
        struct its_baser        tables[GITS_BASER_NR_REGS];
@@ -115,6 +118,7 @@ struct its_device {
 static LIST_HEAD(its_nodes);
 static DEFINE_SPINLOCK(its_lock);
 static struct rdists *gic_rdists;
+static struct irq_domain *its_parent;
 
 #define gic_data_rdist()               (raw_cpu_ptr(gic_rdists->rdist))
 #define gic_data_rdist_rd_base()       (gic_data_rdist()->rd_base)
@@ -1437,6 +1441,11 @@ static int its_irq_gic_domain_alloc(struct irq_domain *domain,
                fwspec.param[0] = GIC_IRQ_TYPE_LPI;
                fwspec.param[1] = hwirq;
                fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
+       } else if (is_fwnode_irqchip(domain->parent->fwnode)) {
+               fwspec.fwnode = domain->parent->fwnode;
+               fwspec.param_count = 2;
+               fwspec.param[0] = hwirq;
+               fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
        } else {
                return -EINVAL;
        }
@@ -1614,44 +1623,59 @@ static void its_enable_quirks(struct its_node *its)
        gic_enable_quirks(iidr, its_quirks, its);
 }
 
-static int __init its_probe(struct device_node *node,
-                           struct irq_domain *parent)
+static int its_init_domain(struct fwnode_handle *handle, struct its_node *its)
+{
+       struct irq_domain *inner_domain;
+       struct msi_domain_info *info;
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       inner_domain = irq_domain_create_tree(handle, &its_domain_ops, its);
+       if (!inner_domain) {
+               kfree(info);
+               return -ENOMEM;
+       }
+
+       inner_domain->parent = its_parent;
+       inner_domain->bus_token = DOMAIN_BUS_NEXUS;
+       info->ops = &its_msi_domain_ops;
+       info->data = its;
+       inner_domain->host_data = info;
+
+       return 0;
+}
+
+static int __init its_probe_one(struct resource *res,
+                               struct fwnode_handle *handle, int numa_node)
 {
-       struct resource res;
        struct its_node *its;
        void __iomem *its_base;
-       struct irq_domain *inner_domain;
        u32 val;
        u64 baser, tmp;
        int err;
 
-       err = of_address_to_resource(node, 0, &res);
-       if (err) {
-               pr_warn("%s: no regs?\n", node->full_name);
-               return -ENXIO;
-       }
-
-       its_base = ioremap(res.start, resource_size(&res));
+       its_base = ioremap(res->start, resource_size(res));
        if (!its_base) {
-               pr_warn("%s: unable to map registers\n", node->full_name);
+               pr_warn("ITS@%pa: Unable to map ITS registers\n", &res->start);
                return -ENOMEM;
        }
 
        val = readl_relaxed(its_base + GITS_PIDR2) & GIC_PIDR2_ARCH_MASK;
        if (val != 0x30 && val != 0x40) {
-               pr_warn("%s: no ITS detected, giving up\n", node->full_name);
+               pr_warn("ITS@%pa: No ITS detected, giving up\n", &res->start);
                err = -ENODEV;
                goto out_unmap;
        }
 
        err = its_force_quiescent(its_base);
        if (err) {
-               pr_warn("%s: failed to quiesce, giving up\n",
-                       node->full_name);
+               pr_warn("ITS@%pa: Failed to quiesce, giving up\n", &res->start);
                goto out_unmap;
        }
 
-       pr_info("ITS: %s\n", node->full_name);
+       pr_info("ITS %pR\n", res);
 
        its = kzalloc(sizeof(*its), GFP_KERNEL);
        if (!its) {
@@ -1663,9 +1687,9 @@ static int __init its_probe(struct device_node *node,
        INIT_LIST_HEAD(&its->entry);
        INIT_LIST_HEAD(&its->its_device_list);
        its->base = its_base;
-       its->phys_base = res.start;
+       its->phys_base = res->start;
        its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1;
-       its->numa_node = of_node_to_nid(node);
+       its->numa_node = numa_node;
 
        its->cmd_base = kzalloc(ITS_CMD_QUEUE_SZ, GFP_KERNEL);
        if (!its->cmd_base) {
@@ -1712,28 +1736,9 @@ static int __init its_probe(struct device_node *node,
        writeq_relaxed(0, its->base + GITS_CWRITER);
        writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR);
 
-       if (of_property_read_bool(node, "msi-controller")) {
-               struct msi_domain_info *info;
-
-               info = kzalloc(sizeof(*info), GFP_KERNEL);
-               if (!info) {
-                       err = -ENOMEM;
-                       goto out_free_tables;
-               }
-
-               inner_domain = irq_domain_add_tree(node, &its_domain_ops, its);
-               if (!inner_domain) {
-                       err = -ENOMEM;
-                       kfree(info);
-                       goto out_free_tables;
-               }
-
-               inner_domain->parent = parent;
-               inner_domain->bus_token = DOMAIN_BUS_NEXUS;
-               info->ops = &its_msi_domain_ops;
-               info->data = its;
-               inner_domain->host_data = info;
-       }
+       err = its_init_domain(handle, its);
+       if (err)
+               goto out_free_tables;
 
        spin_lock(&its_lock);
        list_add(&its->entry, &its_nodes);
@@ -1749,7 +1754,7 @@ out_free_its:
        kfree(its);
 out_unmap:
        iounmap(its_base);
-       pr_err("ITS: failed probing %s (%d)\n", node->full_name, err);
+       pr_err("ITS@%pa: failed probing (%d)\n", &res->start, err);
        return err;
 }
 
@@ -1777,16 +1782,92 @@ static struct of_device_id its_device_id[] = {
        {},
 };
 
-int __init its_init(struct device_node *node, struct rdists *rdists,
-            struct irq_domain *parent_domain)
+static int __init its_of_probe(struct device_node *node)
 {
        struct device_node *np;
+       struct resource res;
 
        for (np = of_find_matching_node(node, its_device_id); np;
             np = of_find_matching_node(np, its_device_id)) {
-               its_probe(np, parent_domain);
+               if (!of_property_read_bool(np, "msi-controller")) {
+                       pr_warn("%s: no msi-controller property, ITS ignored\n",
+                               np->full_name);
+                       continue;
+               }
+
+               if (of_address_to_resource(np, 0, &res)) {
+                       pr_warn("%s: no regs?\n", np->full_name);
+                       continue;
+               }
+
+               its_probe_one(&res, &np->fwnode, of_node_to_nid(np));
+       }
+       return 0;
+}
+
+#ifdef CONFIG_ACPI
+
+#define ACPI_GICV3_ITS_MEM_SIZE (SZ_128K)
+
+static int __init gic_acpi_parse_madt_its(struct acpi_subtable_header *header,
+                                         const unsigned long end)
+{
+       struct acpi_madt_generic_translator *its_entry;
+       struct fwnode_handle *dom_handle;
+       struct resource res;
+       int err;
+
+       its_entry = (struct acpi_madt_generic_translator *)header;
+       memset(&res, 0, sizeof(res));
+       res.start = its_entry->base_address;
+       res.end = its_entry->base_address + ACPI_GICV3_ITS_MEM_SIZE - 1;
+       res.flags = IORESOURCE_MEM;
+
+       dom_handle = irq_domain_alloc_fwnode((void *)its_entry->base_address);
+       if (!dom_handle) {
+               pr_err("ITS@%pa: Unable to allocate GICv3 ITS domain token\n",
+                      &res.start);
+               return -ENOMEM;
        }
 
+       err = iort_register_domain_token(its_entry->translation_id, dom_handle);
+       if (err) {
+               pr_err("ITS@%pa: Unable to register GICv3 ITS domain token (ITS ID %d) to IORT\n",
+                      &res.start, its_entry->translation_id);
+               goto dom_err;
+       }
+
+       err = its_probe_one(&res, dom_handle, NUMA_NO_NODE);
+       if (!err)
+               return 0;
+
+       iort_deregister_domain_token(its_entry->translation_id);
+dom_err:
+       irq_domain_free_fwnode(dom_handle);
+       return err;
+}
+
+static void __init its_acpi_probe(void)
+{
+       acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
+                             gic_acpi_parse_madt_its, 0);
+}
+#else
+static void __init its_acpi_probe(void) { }
+#endif
+
+int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
+                   struct irq_domain *parent_domain)
+{
+       struct device_node *of_node;
+
+       its_parent = parent_domain;
+       of_node = to_of_node(handle);
+       if (of_node)
+               its_of_probe(of_node);
+       else
+               its_acpi_probe();
+
        if (list_empty(&its_nodes)) {
                pr_warn("ITS: No ITS available, not enabling LPIs\n");
                return -ENXIO;
index ede5672..850f9c4 100644 (file)
@@ -495,6 +495,14 @@ static void gic_cpu_sys_reg_init(void)
        /* Set priority mask register */
        gic_write_pmr(DEFAULT_PMR_VALUE);
 
+       /*
+        * Some firmwares hand over to the kernel with the BPR changed from
+        * its reset value (and with a value large enough to prevent
+        * any pre-emptive interrupts from working at all). Writing a zero
+        * to BPR restores is reset value.
+        */
+       gic_write_bpr1(0);
+
        if (static_key_true(&supports_deactivate)) {
                /* EOI drops priority only (mode 1) */
                gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop);
@@ -910,7 +918,6 @@ static int __init gic_init_bases(void __iomem *dist_base,
                                 u64 redist_stride,
                                 struct fwnode_handle *handle)
 {
-       struct device_node *node;
        u32 typer;
        int gic_irqs;
        int err;
@@ -951,10 +958,8 @@ static int __init gic_init_bases(void __iomem *dist_base,
 
        set_handle_irq(gic_handle_irq);
 
-       node = to_of_node(handle);
-       if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis() &&
-           node) /* Temp hack to prevent ITS init for ACPI */
-               its_init(node, &gic_data.rdists, gic_data.domain);
+       if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis())
+               its_init(handle, &gic_data.rdists, gic_data.domain);
 
        gic_smp_init();
        gic_dist_init();
index 390fac5..58e5b4e 100644 (file)
@@ -91,7 +91,27 @@ struct gic_chip_data {
 #endif
 };
 
-static DEFINE_RAW_SPINLOCK(irq_controller_lock);
+#ifdef CONFIG_BL_SWITCHER
+
+static DEFINE_RAW_SPINLOCK(cpu_map_lock);
+
+#define gic_lock_irqsave(f)            \
+       raw_spin_lock_irqsave(&cpu_map_lock, (f))
+#define gic_unlock_irqrestore(f)       \
+       raw_spin_unlock_irqrestore(&cpu_map_lock, (f))
+
+#define gic_lock()                     raw_spin_lock(&cpu_map_lock)
+#define gic_unlock()                   raw_spin_unlock(&cpu_map_lock)
+
+#else
+
+#define gic_lock_irqsave(f)            do { (void)(f); } while(0)
+#define gic_unlock_irqrestore(f)       do { (void)(f); } while(0)
+
+#define gic_lock()                     do { } while(0)
+#define gic_unlock()                   do { } while(0)
+
+#endif
 
 /*
  * The GIC mapping of CPU interfaces does not necessarily match
@@ -317,12 +337,12 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
        if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids)
                return -EINVAL;
 
-       raw_spin_lock_irqsave(&irq_controller_lock, flags);
+       gic_lock_irqsave(flags);
        mask = 0xff << shift;
        bit = gic_cpu_map[cpu] << shift;
        val = readl_relaxed(reg) & ~mask;
        writel_relaxed(val | bit, reg);
-       raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
+       gic_unlock_irqrestore(flags);
 
        return IRQ_SET_MASK_OK_DONE;
 }
@@ -374,9 +394,7 @@ static void gic_handle_cascade_irq(struct irq_desc *desc)
 
        chained_irq_enter(chip, desc);
 
-       raw_spin_lock(&irq_controller_lock);
        status = readl_relaxed(gic_data_cpu_base(chip_data) + GIC_CPU_INTACK);
-       raw_spin_unlock(&irq_controller_lock);
 
        gic_irq = (status & GICC_IAR_INT_ID_MASK);
        if (gic_irq == GICC_INT_SPURIOUS)
@@ -776,7 +794,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
                return;
        }
 
-       raw_spin_lock_irqsave(&irq_controller_lock, flags);
+       gic_lock_irqsave(flags);
 
        /* Convert our logical CPU mask into a physical one. */
        for_each_cpu(cpu, mask)
@@ -791,7 +809,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
        /* this always happens on GIC0 */
        writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
 
-       raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
+       gic_unlock_irqrestore(flags);
 }
 #endif
 
@@ -859,7 +877,7 @@ void gic_migrate_target(unsigned int new_cpu_id)
        cur_target_mask = 0x01010101 << cur_cpu_id;
        ror_val = (cur_cpu_id - new_cpu_id) & 31;
 
-       raw_spin_lock(&irq_controller_lock);
+       gic_lock();
 
        /* Update the target interface for this logical CPU */
        gic_cpu_map[cpu] = 1 << new_cpu_id;
@@ -879,7 +897,7 @@ void gic_migrate_target(unsigned int new_cpu_id)
                }
        }
 
-       raw_spin_unlock(&irq_controller_lock);
+       gic_unlock();
 
        /*
         * Now let's migrate and clear any potential SGIs that might be
@@ -921,7 +939,7 @@ unsigned long gic_get_sgir_physaddr(void)
        return gic_dist_physaddr + GIC_DIST_SOFTINT;
 }
 
-void __init gic_init_physaddr(struct device_node *node)
+static void __init gic_init_physaddr(struct device_node *node)
 {
        struct resource res;
        if (of_address_to_resource(node, 0, &res) == 0) {
index 9da5ecb..bfdd074 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/smp.h>
 #include <linux/errno.h>
 #include <linux/io.h>
+#include <linux/acpi_iort.h>
 #include <linux/slab.h>
 #include <linux/irqdomain.h>
 #include <linux/of_irq.h>
@@ -1547,8 +1548,8 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev)
        pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
 
        of_node = irq_domain_get_of_node(domain);
-       if (of_node)
-               rid = of_msi_map_rid(&pdev->dev, of_node, rid);
+       rid = of_node ? of_msi_map_rid(&pdev->dev, of_node, rid) :
+                       iort_msi_map_rid(&pdev->dev, rid);
 
        return rid;
 }
@@ -1564,9 +1565,13 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev)
  */
 struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev)
 {
+       struct irq_domain *dom;
        u32 rid = 0;
 
        pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
-       return of_msi_map_get_device_domain(&pdev->dev, rid);
+       dom = of_msi_map_get_device_domain(&pdev->dev, rid);
+       if (!dom)
+               dom = iort_get_device_domain(&pdev->dev, rid);
+       return dom;
 }
 #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h
new file mode 100644 (file)
index 0000000..0e32dac
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016, Semihalf
+ *     Author: Tomasz Nowicki <tn@semihalf.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef __ACPI_IORT_H__
+#define __ACPI_IORT_H__
+
+#include <linux/acpi.h>
+#include <linux/fwnode.h>
+#include <linux/irqdomain.h>
+
+int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node);
+void iort_deregister_domain_token(int trans_id);
+struct fwnode_handle *iort_find_domain_token(int trans_id);
+#ifdef CONFIG_ACPI_IORT
+void acpi_iort_init(void);
+u32 iort_msi_map_rid(struct device *dev, u32 req_id);
+struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id);
+#else
+static inline void acpi_iort_init(void) { }
+static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id)
+{ return req_id; }
+static inline struct irq_domain *iort_get_device_domain(struct device *dev,
+                                                       u32 req_id)
+{ return NULL; }
+#endif
+
+#endif /* __ACPI_IORT_H__ */
index b52424e..6039867 100644 (file)
@@ -916,12 +916,20 @@ void irq_remove_generic_chip(struct irq_chip_generic *gc, u32 msk,
                             unsigned int clr, unsigned int set);
 
 struct irq_chip_generic *irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq);
-int irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
-                                  int num_ct, const char *name,
-                                  irq_flow_handler_t handler,
-                                  unsigned int clr, unsigned int set,
-                                  enum irq_gc_flags flags);
 
+int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
+                                    int num_ct, const char *name,
+                                    irq_flow_handler_t handler,
+                                    unsigned int clr, unsigned int set,
+                                    enum irq_gc_flags flags);
+
+#define irq_alloc_domain_generic_chips(d, irqs_per_chip, num_ct, name, \
+                                      handler, clr, set, flags)        \
+({                                                                     \
+       MAYBE_BUILD_BUG_ON(irqs_per_chip > 32);                         \
+       __irq_alloc_domain_generic_chips(d, irqs_per_chip, num_ct, name,\
+                                        handler, clr, set, flags);     \
+})
 
 static inline struct irq_chip_type *irq_data_get_chip_type(struct irq_data *d)
 {
index 99ac022..8361c8d 100644 (file)
@@ -430,9 +430,9 @@ struct rdists {
 };
 
 struct irq_domain;
-struct device_node;
+struct fwnode_handle;
 int its_cpu_init(void);
-int its_init(struct device_node *node, struct rdists *rdists,
+int its_init(struct fwnode_handle *handle, struct rdists *rdists,
             struct irq_domain *domain);
 
 static inline bool gic_enable_sre(void)
index b51beeb..c9be579 100644 (file)
@@ -2,6 +2,7 @@
 #define _LINUX_IRQDESC_H
 
 #include <linux/rcupdate.h>
+#include <linux/kobject.h>
 
 /*
  * Core internal functions to deal with irq descriptors
@@ -43,6 +44,7 @@ struct pt_regs;
  * @force_resume_depth:        number of irqactions on a irq descriptor with
  *                     IRQF_FORCE_RESUME set
  * @rcu:               rcu head for delayed free
+ * @kobj:              kobject used to represent this struct in sysfs
  * @dir:               /proc/irq/ procfs entry
  * @name:              flow handler name for /proc/interrupts output
  */
@@ -88,6 +90,7 @@ struct irq_desc {
 #endif
 #ifdef CONFIG_SPARSE_IRQ
        struct rcu_head         rcu;
+       struct kobject          kobj;
 #endif
        int                     parent_irq;
        struct module           *owner;
index 6373890..18f2958 100644 (file)
@@ -76,7 +76,6 @@ int irq_set_irq_type(unsigned int irq, unsigned int type)
        if (!desc)
                return -EINVAL;
 
-       type &= IRQ_TYPE_SENSE_MASK;
        ret = __irq_set_trigger(desc, type);
        irq_put_desc_busunlock(desc, flags);
        return ret;
@@ -756,7 +755,6 @@ void handle_percpu_devid_irq(struct irq_desc *desc)
 {
        struct irq_chip *chip = irq_desc_get_chip(desc);
        struct irqaction *action = desc->action;
-       void *dev_id = raw_cpu_ptr(action->percpu_dev_id);
        unsigned int irq = irq_desc_get_irq(desc);
        irqreturn_t res;
 
@@ -765,9 +763,20 @@ void handle_percpu_devid_irq(struct irq_desc *desc)
        if (chip->irq_ack)
                chip->irq_ack(&desc->irq_data);
 
-       trace_irq_handler_entry(irq, action);
-       res = action->handler(irq, dev_id);
-       trace_irq_handler_exit(irq, action, res);
+       if (likely(action)) {
+               trace_irq_handler_entry(irq, action);
+               res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id));
+               trace_irq_handler_exit(irq, action, res);
+       } else {
+               unsigned int cpu = smp_processor_id();
+               bool enabled = cpumask_test_cpu(cpu, desc->percpu_enabled);
+
+               if (enabled)
+                       irq_percpu_disable(desc, cpu);
+
+               pr_err_once("Spurious%s percpu IRQ%u on CPU%u\n",
+                           enabled ? " and unmasked" : "", irq, cpu);
+       }
 
        if (chip->irq_eoi)
                chip->irq_eoi(&desc->irq_data);
index abd286a..ee32870 100644 (file)
@@ -260,9 +260,9 @@ irq_gc_init_mask_cache(struct irq_chip_generic *gc, enum irq_gc_flags flags)
 }
 
 /**
- * irq_alloc_domain_generic_chip - Allocate generic chips for an irq domain
+ * __irq_alloc_domain_generic_chip - Allocate generic chips for an irq domain
  * @d:                 irq domain for which to allocate chips
- * @irqs_per_chip:     Number of interrupts each chip handles
+ * @irqs_per_chip:     Number of interrupts each chip handles (max 32)
  * @num_ct:            Number of irq_chip_type instances associated with this
  * @name:              Name of the irq chip
  * @handler:           Default flow handler associated with these chips
@@ -270,11 +270,11 @@ irq_gc_init_mask_cache(struct irq_chip_generic *gc, enum irq_gc_flags flags)
  * @set:               IRQ_* bits to set in the mapping function
  * @gcflags:           Generic chip specific setup flags
  */
-int irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
-                                  int num_ct, const char *name,
-                                  irq_flow_handler_t handler,
-                                  unsigned int clr, unsigned int set,
-                                  enum irq_gc_flags gcflags)
+int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
+                                    int num_ct, const char *name,
+                                    irq_flow_handler_t handler,
+                                    unsigned int clr, unsigned int set,
+                                    enum irq_gc_flags gcflags)
 {
        struct irq_domain_chip_generic *dgc;
        struct irq_chip_generic *gc;
@@ -326,7 +326,21 @@ int irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
        d->name = name;
        return 0;
 }
-EXPORT_SYMBOL_GPL(irq_alloc_domain_generic_chips);
+EXPORT_SYMBOL_GPL(__irq_alloc_domain_generic_chips);
+
+static struct irq_chip_generic *
+__irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq)
+{
+       struct irq_domain_chip_generic *dgc = d->gc;
+       int idx;
+
+       if (!dgc)
+               return ERR_PTR(-ENODEV);
+       idx = hw_irq / dgc->irqs_per_chip;
+       if (idx >= dgc->num_chips)
+               return ERR_PTR(-EINVAL);
+       return dgc->gc[idx];
+}
 
 /**
  * irq_get_domain_generic_chip - Get a pointer to the generic chip of a hw_irq
@@ -336,15 +350,9 @@ EXPORT_SYMBOL_GPL(irq_alloc_domain_generic_chips);
 struct irq_chip_generic *
 irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq)
 {
-       struct irq_domain_chip_generic *dgc = d->gc;
-       int idx;
+       struct irq_chip_generic *gc = __irq_get_domain_generic_chip(d, hw_irq);
 
-       if (!dgc)
-               return NULL;
-       idx = hw_irq / dgc->irqs_per_chip;
-       if (idx >= dgc->num_chips)
-               return NULL;
-       return dgc->gc[idx];
+       return !IS_ERR(gc) ? gc : NULL;
 }
 EXPORT_SYMBOL_GPL(irq_get_domain_generic_chip);
 
@@ -368,13 +376,9 @@ int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
        unsigned long flags;
        int idx;
 
-       if (!d->gc)
-               return -ENODEV;
-
-       idx = hw_irq / dgc->irqs_per_chip;
-       if (idx >= dgc->num_chips)
-               return -EINVAL;
-       gc = dgc->gc[idx];
+       gc = __irq_get_domain_generic_chip(d, hw_irq);
+       if (IS_ERR(gc))
+               return PTR_ERR(gc);
 
        idx = hw_irq % dgc->irqs_per_chip;
 
@@ -409,10 +413,30 @@ int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
        irq_modify_status(virq, dgc->irq_flags_to_clear, dgc->irq_flags_to_set);
        return 0;
 }
-EXPORT_SYMBOL_GPL(irq_map_generic_chip);
+
+static void irq_unmap_generic_chip(struct irq_domain *d, unsigned int virq)
+{
+       struct irq_data *data = irq_domain_get_irq_data(d, virq);
+       struct irq_domain_chip_generic *dgc = d->gc;
+       unsigned int hw_irq = data->hwirq;
+       struct irq_chip_generic *gc;
+       int irq_idx;
+
+       gc = irq_get_domain_generic_chip(d, hw_irq);
+       if (!gc)
+               return;
+
+       irq_idx = hw_irq % dgc->irqs_per_chip;
+
+       clear_bit(irq_idx, &gc->installed);
+       irq_domain_set_info(d, virq, hw_irq, &no_irq_chip, NULL, NULL, NULL,
+                           NULL);
+
+}
 
 struct irq_domain_ops irq_generic_chip_ops = {
        .map    = irq_map_generic_chip,
+       .unmap  = irq_unmap_generic_chip,
        .xlate  = irq_domain_xlate_onetwocell,
 };
 EXPORT_SYMBOL_GPL(irq_generic_chip_ops);
index 5a5a685..00bb0ae 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/radix-tree.h>
 #include <linux/bitmap.h>
 #include <linux/irqdomain.h>
+#include <linux/sysfs.h>
 
 #include "internals.h"
 
@@ -123,6 +124,181 @@ static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS);
 
 #ifdef CONFIG_SPARSE_IRQ
 
+static void irq_kobj_release(struct kobject *kobj);
+
+#ifdef CONFIG_SYSFS
+static struct kobject *irq_kobj_base;
+
+#define IRQ_ATTR_RO(_name) \
+static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
+
+static ssize_t per_cpu_count_show(struct kobject *kobj,
+                                 struct kobj_attribute *attr, char *buf)
+{
+       struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+       int cpu, irq = desc->irq_data.irq;
+       ssize_t ret = 0;
+       char *p = "";
+
+       for_each_possible_cpu(cpu) {
+               unsigned int c = kstat_irqs_cpu(irq, cpu);
+
+               ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%u", p, c);
+               p = ",";
+       }
+
+       ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
+       return ret;
+}
+IRQ_ATTR_RO(per_cpu_count);
+
+static ssize_t chip_name_show(struct kobject *kobj,
+                             struct kobj_attribute *attr, char *buf)
+{
+       struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+       ssize_t ret = 0;
+
+       raw_spin_lock_irq(&desc->lock);
+       if (desc->irq_data.chip && desc->irq_data.chip->name) {
+               ret = scnprintf(buf, PAGE_SIZE, "%s\n",
+                               desc->irq_data.chip->name);
+       }
+       raw_spin_unlock_irq(&desc->lock);
+
+       return ret;
+}
+IRQ_ATTR_RO(chip_name);
+
+static ssize_t hwirq_show(struct kobject *kobj,
+                         struct kobj_attribute *attr, char *buf)
+{
+       struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+       ssize_t ret = 0;
+
+       raw_spin_lock_irq(&desc->lock);
+       if (desc->irq_data.domain)
+               ret = sprintf(buf, "%d\n", (int)desc->irq_data.hwirq);
+       raw_spin_unlock_irq(&desc->lock);
+
+       return ret;
+}
+IRQ_ATTR_RO(hwirq);
+
+static ssize_t type_show(struct kobject *kobj,
+                        struct kobj_attribute *attr, char *buf)
+{
+       struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+       ssize_t ret = 0;
+
+       raw_spin_lock_irq(&desc->lock);
+       ret = sprintf(buf, "%s\n",
+                     irqd_is_level_type(&desc->irq_data) ? "level" : "edge");
+       raw_spin_unlock_irq(&desc->lock);
+
+       return ret;
+
+}
+IRQ_ATTR_RO(type);
+
+static ssize_t name_show(struct kobject *kobj,
+                        struct kobj_attribute *attr, char *buf)
+{
+       struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+       ssize_t ret = 0;
+
+       raw_spin_lock_irq(&desc->lock);
+       if (desc->name)
+               ret = scnprintf(buf, PAGE_SIZE, "%s\n", desc->name);
+       raw_spin_unlock_irq(&desc->lock);
+
+       return ret;
+}
+IRQ_ATTR_RO(name);
+
+static ssize_t actions_show(struct kobject *kobj,
+                           struct kobj_attribute *attr, char *buf)
+{
+       struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+       struct irqaction *action;
+       ssize_t ret = 0;
+       char *p = "";
+
+       raw_spin_lock_irq(&desc->lock);
+       for (action = desc->action; action != NULL; action = action->next) {
+               ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%s",
+                                p, action->name);
+               p = ",";
+       }
+       raw_spin_unlock_irq(&desc->lock);
+
+       if (ret)
+               ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+       return ret;
+}
+IRQ_ATTR_RO(actions);
+
+static struct attribute *irq_attrs[] = {
+       &per_cpu_count_attr.attr,
+       &chip_name_attr.attr,
+       &hwirq_attr.attr,
+       &type_attr.attr,
+       &name_attr.attr,
+       &actions_attr.attr,
+       NULL
+};
+
+static struct kobj_type irq_kobj_type = {
+       .release        = irq_kobj_release,
+       .sysfs_ops      = &kobj_sysfs_ops,
+       .default_attrs  = irq_attrs,
+};
+
+static void irq_sysfs_add(int irq, struct irq_desc *desc)
+{
+       if (irq_kobj_base) {
+               /*
+                * Continue even in case of failure as this is nothing
+                * crucial.
+                */
+               if (kobject_add(&desc->kobj, irq_kobj_base, "%d", irq))
+                       pr_warn("Failed to add kobject for irq %d\n", irq);
+       }
+}
+
+static int __init irq_sysfs_init(void)
+{
+       struct irq_desc *desc;
+       int irq;
+
+       /* Prevent concurrent irq alloc/free */
+       irq_lock_sparse();
+
+       irq_kobj_base = kobject_create_and_add("irq", kernel_kobj);
+       if (!irq_kobj_base) {
+               irq_unlock_sparse();
+               return -ENOMEM;
+       }
+
+       /* Add the already allocated interrupts */
+       for_each_irq_desc(irq, desc)
+               irq_sysfs_add(irq, desc);
+       irq_unlock_sparse();
+
+       return 0;
+}
+postcore_initcall(irq_sysfs_init);
+
+#else /* !CONFIG_SYSFS */
+
+static struct kobj_type irq_kobj_type = {
+       .release        = irq_kobj_release,
+};
+
+static void irq_sysfs_add(int irq, struct irq_desc *desc) {}
+
+#endif /* CONFIG_SYSFS */
+
 static RADIX_TREE(irq_desc_tree, GFP_KERNEL);
 
 static void irq_insert_desc(unsigned int irq, struct irq_desc *desc)
@@ -187,6 +363,7 @@ static struct irq_desc *alloc_desc(int irq, int node, unsigned int flags,
 
        desc_set_defaults(irq, desc, node, affinity, owner);
        irqd_set(&desc->irq_data, flags);
+       kobject_init(&desc->kobj, &irq_kobj_type);
 
        return desc;
 
@@ -197,15 +374,22 @@ err_desc:
        return NULL;
 }
 
-static void delayed_free_desc(struct rcu_head *rhp)
+static void irq_kobj_release(struct kobject *kobj)
 {
-       struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+       struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
 
        free_masks(desc);
        free_percpu(desc->kstat_irqs);
        kfree(desc);
 }
 
+static void delayed_free_desc(struct rcu_head *rhp)
+{
+       struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+
+       kobject_put(&desc->kobj);
+}
+
 static void free_desc(unsigned int irq)
 {
        struct irq_desc *desc = irq_to_desc(irq);
@@ -217,8 +401,12 @@ static void free_desc(unsigned int irq)
         * kstat_irq_usr(). Once we deleted the descriptor from the
         * sparse tree we can free it. Access in proc will fail to
         * lookup the descriptor.
+        *
+        * The sysfs entry must be serialized against a concurrent
+        * irq_sysfs_init() as well.
         */
        mutex_lock(&sparse_irq_lock);
+       kobject_del(&desc->kobj);
        delete_irq_desc(irq);
        mutex_unlock(&sparse_irq_lock);
 
@@ -260,6 +448,7 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node,
                        goto err;
                mutex_lock(&sparse_irq_lock);
                irq_insert_desc(start + i, desc);
+               irq_sysfs_add(start + i, desc);
                mutex_unlock(&sparse_irq_lock);
        }
        return start;
index 4752b43..8c0a0ae 100644 (file)
@@ -80,7 +80,7 @@ EXPORT_SYMBOL_GPL(irq_domain_free_fwnode);
 
 /**
  * __irq_domain_add() - Allocate a new irq_domain data structure
- * @of_node: optional device-tree node of the interrupt controller
+ * @fwnode: firmware node for the interrupt controller
  * @size: Size of linear map; 0 for radix mapping only
  * @hwirq_max: Maximum number of interrupts supported by controller
  * @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no
@@ -96,10 +96,8 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
                                    const struct irq_domain_ops *ops,
                                    void *host_data)
 {
+       struct device_node *of_node = to_of_node(fwnode);
        struct irq_domain *domain;
-       struct device_node *of_node;
-
-       of_node = to_of_node(fwnode);
 
        domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
                              GFP_KERNEL, of_node_to_nid(of_node));
@@ -868,7 +866,10 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d,
        if (WARN_ON(intsize < 1))
                return -EINVAL;
        *out_hwirq = intspec[0];
-       *out_type = (intsize > 1) ? intspec[1] : IRQ_TYPE_NONE;
+       if (intsize > 1)
+               *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
+       else
+               *out_type = IRQ_TYPE_NONE;
        return 0;
 }
 EXPORT_SYMBOL_GPL(irq_domain_xlate_onetwocell);
index 9530fcd..0c5f1a5 100644 (file)
@@ -669,8 +669,6 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned long flags)
                return 0;
        }
 
-       flags &= IRQ_TYPE_SENSE_MASK;
-
        if (chip->flags & IRQCHIP_SET_TYPE_MASKED) {
                if (!irqd_irq_masked(&desc->irq_data))
                        mask_irq(desc);
@@ -678,7 +676,8 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned long flags)
                        unmask = 1;
        }
 
-       /* caller masked out all except trigger mode flags */
+       /* Mask all flags except trigger mode */
+       flags &= IRQ_TYPE_SENSE_MASK;
        ret = chip->irq_set_type(&desc->irq_data, flags);
 
        switch (ret) {