summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/realtek,rtl-intc.yaml5
-rw-r--r--drivers/irqchip/Kconfig5
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-gic-its-msi-parent.c3
-rw-r--r--drivers/irqchip/irq-gic-v3-its.c30
-rw-r--r--drivers/irqchip/irq-gic-v5-iwb.c2
-rw-r--r--drivers/irqchip/irq-imgpdc.c495
-rw-r--r--drivers/irqchip/irq-qcom-mpm.c4
-rw-r--r--drivers/irqchip/irq-realtek-rtl.c152
-rw-r--r--drivers/irqchip/irq-renesas-irqc.c1
-rw-r--r--drivers/irqchip/irq-renesas-rzt2h.c2
-rw-r--r--drivers/irqchip/irq-renesas-rzv2h.c4
-rw-r--r--drivers/irqchip/qcom-pdc.c540
13 files changed, 568 insertions, 676 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/realtek,rtl-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/realtek,rtl-intc.yaml
index 833a01cdd1b1..5c52e57c6647 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/realtek,rtl-intc.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/realtek,rtl-intc.yaml
@@ -32,8 +32,9 @@ properties:
"#interrupt-cells":
description:
- SoC interrupt line index.
- const: 1
+ First cell is the SoC interrupt line index. Optional second cell
+ specifies the parent interrupt index to route to.
+ enum: [1, 2]
reg:
minItems: 1
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 42f2278a702d..20b77fbc51ee 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -212,11 +212,6 @@ config HISILICON_IRQ_MBIGEN
select ARM_GIC_V3
select ARM_GIC_V3_ITS
-config IMGPDC_IRQ
- bool
- select GENERIC_IRQ_CHIP
- select IRQ_DOMAIN
-
config IXP4XX_IRQ
bool
select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 74912429c59f..ab33cccd8471 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -44,7 +44,6 @@ obj-$(CONFIG_ARMADA_370_XP_IRQ) += irq-armada-370-xp.o
obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o
obj-$(CONFIG_ATMEL_AIC5_IRQ) += irq-atmel-aic-common.o irq-atmel-aic5.o
obj-$(CONFIG_I8259) += irq-i8259.o
-obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o
obj-$(CONFIG_IRQ_MIPS_CPU) += irq-mips-cpu.o
obj-$(CONFIG_IXP4XX_IRQ) += irq-ixp4xx.o
obj-$(CONFIG_JCORE_AIC) += irq-jcore-aic.o
diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c
index b9257103a999..b2b9d2068bb1 100644
--- a/drivers/irqchip/irq-gic-its-msi-parent.c
+++ b/drivers/irqchip/irq-gic-its-msi-parent.c
@@ -18,7 +18,8 @@
#define ITS_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \
MSI_FLAG_PCI_MSIX | \
- MSI_FLAG_MULTI_PCI_MSI)
+ MSI_FLAG_MULTI_PCI_MSI | \
+ MSI_FLAG_PCI_MSIX_ALLOC_DYN)
static int its_translate_frame_address(struct fwnode_handle *msi_node, phys_addr_t *pa)
{
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 6f5811aae59c..ac697e60a2f9 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -163,7 +163,7 @@ struct event_lpi_map {
/*
* The ITS view of a device - belongs to an ITS, owns an interrupt
- * translation table, and a list of interrupts. If it some of its
+ * translation table, and a list of interrupts. If some of its
* LPIs are injected into a guest (GICv4), the event_map.vm field
* indicates which one.
*/
@@ -2501,10 +2501,7 @@ static bool its_parse_indirect_baser(struct its_node *its,
/* No need to enable Indirection if memory requirement < (psz*2)bytes */
if ((esz << ids) > (psz * 2)) {
- /*
- * Find out whether hw supports a single or two-level table by
- * table by reading bit at offset '62' after writing '1' to it.
- */
+ /* Find out whether the hardware supports a single or two-level table */
its_write_baser(its, baser, val | GITS_BASER_INDIRECT);
indirect = !!(baser->val & GITS_BASER_INDIRECT);
@@ -4592,6 +4589,13 @@ static int its_vpe_init(struct its_vpe *vpe)
static void its_vpe_teardown(struct its_vpe *vpe)
{
+ /*
+ * If vpt_page is NULL, then its_vpe_init() has failed, and
+ * there is nothing to do as no resource has been allocated.
+ */
+ if (vpe->vpt_page == NULL)
+ return;
+
its_vpe_db_proxy_unmap(vpe);
its_vpe_id_free(vpe->vpe_id);
its_free_pending_table(vpe->vpt_page);
@@ -4672,8 +4676,10 @@ static int its_vpe_irq_domain_alloc(struct irq_domain *domain, unsigned int virq
irqd_set_resend_when_in_progress(irq_get_irq_data(virq + i));
}
- if (err)
+ if (err) {
+ its_vpe_teardown(vm->vpes[i]);
its_vpe_irq_domain_free(domain, virq, i);
+ }
return err;
}
@@ -5320,7 +5326,7 @@ static int __init its_probe_one(struct its_node *its)
err = its_init_domain(its);
if (err)
- goto out_free_tables;
+ goto out_free_collection;
raw_spin_lock(&its_lock);
list_add(&its->entry, &its_nodes);
@@ -5328,6 +5334,8 @@ static int __init its_probe_one(struct its_node *its)
return 0;
+out_free_collection:
+ kfree(its->collections);
out_free_tables:
its_free_tables(its);
out_free_cmd:
@@ -5739,9 +5747,13 @@ static int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header,
its->flags |= ITS_FLAGS_FORCE_NON_SHAREABLE;
err = its_probe_one(its);
- if (!err)
- return 0;
+ if (err)
+ goto probe_err;
+
+ return 0;
+probe_err:
+ its_node_destroy(its);
node_err:
iort_deregister_domain_token(its_entry->translation_id);
dom_err:
diff --git a/drivers/irqchip/irq-gic-v5-iwb.c b/drivers/irqchip/irq-gic-v5-iwb.c
index 9103feb70ce8..2cf0df53e9dd 100644
--- a/drivers/irqchip/irq-gic-v5-iwb.c
+++ b/drivers/irqchip/irq-gic-v5-iwb.c
@@ -143,7 +143,7 @@ static int gicv5_iwb_irq_domain_translate(struct irq_domain *d, struct irq_fwspe
return -EINVAL;
/*
- * param[0] is be the wire
+ * param[0] is the wire interrupt number
* param[1] is the interrupt type
*/
*hwirq = fwspec->param[0];
diff --git a/drivers/irqchip/irq-imgpdc.c b/drivers/irqchip/irq-imgpdc.c
deleted file mode 100644
index 4feef4ab5fec..000000000000
--- a/drivers/irqchip/irq-imgpdc.c
+++ /dev/null
@@ -1,495 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * IMG PowerDown Controller (PDC)
- *
- * Copyright 2010-2013 Imagination Technologies Ltd.
- *
- * Exposes the syswake and PDC peripheral wake interrupts to the system.
- *
- */
-
-#include <linux/bitops.h>
-#include <linux/interrupt.h>
-#include <linux/irqdomain.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-
-/* PDC interrupt register numbers */
-
-#define PDC_IRQ_STATUS 0x310
-#define PDC_IRQ_ENABLE 0x314
-#define PDC_IRQ_CLEAR 0x318
-#define PDC_IRQ_ROUTE 0x31c
-#define PDC_SYS_WAKE_BASE 0x330
-#define PDC_SYS_WAKE_STRIDE 0x8
-#define PDC_SYS_WAKE_CONFIG_BASE 0x334
-#define PDC_SYS_WAKE_CONFIG_STRIDE 0x8
-
-/* PDC interrupt register field masks */
-
-#define PDC_IRQ_SYS3 0x08
-#define PDC_IRQ_SYS2 0x04
-#define PDC_IRQ_SYS1 0x02
-#define PDC_IRQ_SYS0 0x01
-#define PDC_IRQ_ROUTE_WU_EN_SYS3 0x08000000
-#define PDC_IRQ_ROUTE_WU_EN_SYS2 0x04000000
-#define PDC_IRQ_ROUTE_WU_EN_SYS1 0x02000000
-#define PDC_IRQ_ROUTE_WU_EN_SYS0 0x01000000
-#define PDC_IRQ_ROUTE_WU_EN_WD 0x00040000
-#define PDC_IRQ_ROUTE_WU_EN_IR 0x00020000
-#define PDC_IRQ_ROUTE_WU_EN_RTC 0x00010000
-#define PDC_IRQ_ROUTE_EXT_EN_SYS3 0x00000800
-#define PDC_IRQ_ROUTE_EXT_EN_SYS2 0x00000400
-#define PDC_IRQ_ROUTE_EXT_EN_SYS1 0x00000200
-#define PDC_IRQ_ROUTE_EXT_EN_SYS0 0x00000100
-#define PDC_IRQ_ROUTE_EXT_EN_WD 0x00000004
-#define PDC_IRQ_ROUTE_EXT_EN_IR 0x00000002
-#define PDC_IRQ_ROUTE_EXT_EN_RTC 0x00000001
-#define PDC_SYS_WAKE_RESET 0x00000010
-#define PDC_SYS_WAKE_INT_MODE 0x0000000e
-#define PDC_SYS_WAKE_INT_MODE_SHIFT 1
-#define PDC_SYS_WAKE_PIN_VAL 0x00000001
-
-/* PDC interrupt constants */
-
-#define PDC_SYS_WAKE_INT_LOW 0x0
-#define PDC_SYS_WAKE_INT_HIGH 0x1
-#define PDC_SYS_WAKE_INT_DOWN 0x2
-#define PDC_SYS_WAKE_INT_UP 0x3
-#define PDC_SYS_WAKE_INT_CHANGE 0x6
-#define PDC_SYS_WAKE_INT_NONE 0x4
-
-/**
- * struct pdc_intc_priv - private pdc interrupt data.
- * @nr_perips: Number of peripheral interrupt signals.
- * @nr_syswakes: Number of syswake signals.
- * @perip_irqs: List of peripheral IRQ numbers handled.
- * @syswake_irq: Shared PDC syswake IRQ number.
- * @domain: IRQ domain for PDC peripheral and syswake IRQs.
- * @pdc_base: Base of PDC registers.
- * @irq_route: Cached version of PDC_IRQ_ROUTE register.
- * @lock: Lock to protect the PDC syswake registers and the cached
- * values of those registers in this struct.
- */
-struct pdc_intc_priv {
- unsigned int nr_perips;
- unsigned int nr_syswakes;
- unsigned int *perip_irqs;
- unsigned int syswake_irq;
- struct irq_domain *domain;
- void __iomem *pdc_base;
-
- u32 irq_route;
- raw_spinlock_t lock;
-};
-
-static void pdc_write(struct pdc_intc_priv *priv, unsigned int reg_offs,
- unsigned int data)
-{
- iowrite32(data, priv->pdc_base + reg_offs);
-}
-
-static unsigned int pdc_read(struct pdc_intc_priv *priv,
- unsigned int reg_offs)
-{
- return ioread32(priv->pdc_base + reg_offs);
-}
-
-/* Generic IRQ callbacks */
-
-#define SYS0_HWIRQ 8
-
-static unsigned int hwirq_is_syswake(irq_hw_number_t hw)
-{
- return hw >= SYS0_HWIRQ;
-}
-
-static unsigned int hwirq_to_syswake(irq_hw_number_t hw)
-{
- return hw - SYS0_HWIRQ;
-}
-
-static irq_hw_number_t syswake_to_hwirq(unsigned int syswake)
-{
- return SYS0_HWIRQ + syswake;
-}
-
-static struct pdc_intc_priv *irqd_to_priv(struct irq_data *data)
-{
- return (struct pdc_intc_priv *)data->domain->host_data;
-}
-
-/*
- * perip_irq_mask() and perip_irq_unmask() use IRQ_ROUTE which also contains
- * wake bits, therefore we cannot use the generic irqchip mask callbacks as they
- * cache the mask.
- */
-
-static void perip_irq_mask(struct irq_data *data)
-{
- struct pdc_intc_priv *priv = irqd_to_priv(data);
-
- raw_spin_lock(&priv->lock);
- priv->irq_route &= ~data->mask;
- pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
- raw_spin_unlock(&priv->lock);
-}
-
-static void perip_irq_unmask(struct irq_data *data)
-{
- struct pdc_intc_priv *priv = irqd_to_priv(data);
-
- raw_spin_lock(&priv->lock);
- priv->irq_route |= data->mask;
- pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
- raw_spin_unlock(&priv->lock);
-}
-
-static int syswake_irq_set_type(struct irq_data *data, unsigned int flow_type)
-{
- struct pdc_intc_priv *priv = irqd_to_priv(data);
- unsigned int syswake = hwirq_to_syswake(data->hwirq);
- unsigned int irq_mode;
- unsigned int soc_sys_wake_regoff, soc_sys_wake;
-
- /* translate to syswake IRQ mode */
- switch (flow_type) {
- case IRQ_TYPE_EDGE_BOTH:
- irq_mode = PDC_SYS_WAKE_INT_CHANGE;
- break;
- case IRQ_TYPE_EDGE_RISING:
- irq_mode = PDC_SYS_WAKE_INT_UP;
- break;
- case IRQ_TYPE_EDGE_FALLING:
- irq_mode = PDC_SYS_WAKE_INT_DOWN;
- break;
- case IRQ_TYPE_LEVEL_HIGH:
- irq_mode = PDC_SYS_WAKE_INT_HIGH;
- break;
- case IRQ_TYPE_LEVEL_LOW:
- irq_mode = PDC_SYS_WAKE_INT_LOW;
- break;
- default:
- return -EINVAL;
- }
-
- raw_spin_lock(&priv->lock);
-
- /* set the IRQ mode */
- soc_sys_wake_regoff = PDC_SYS_WAKE_BASE + syswake*PDC_SYS_WAKE_STRIDE;
- soc_sys_wake = pdc_read(priv, soc_sys_wake_regoff);
- soc_sys_wake &= ~PDC_SYS_WAKE_INT_MODE;
- soc_sys_wake |= irq_mode << PDC_SYS_WAKE_INT_MODE_SHIFT;
- pdc_write(priv, soc_sys_wake_regoff, soc_sys_wake);
-
- /* and update the handler */
- irq_setup_alt_chip(data, flow_type);
-
- raw_spin_unlock(&priv->lock);
-
- return 0;
-}
-
-/* applies to both peripheral and syswake interrupts */
-static int pdc_irq_set_wake(struct irq_data *data, unsigned int on)
-{
- struct pdc_intc_priv *priv = irqd_to_priv(data);
- irq_hw_number_t hw = data->hwirq;
- unsigned int mask = (1 << 16) << hw;
- unsigned int dst_irq;
-
- raw_spin_lock(&priv->lock);
- if (on)
- priv->irq_route |= mask;
- else
- priv->irq_route &= ~mask;
- pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
- raw_spin_unlock(&priv->lock);
-
- /* control the destination IRQ wakeup too for standby mode */
- if (hwirq_is_syswake(hw))
- dst_irq = priv->syswake_irq;
- else
- dst_irq = priv->perip_irqs[hw];
- irq_set_irq_wake(dst_irq, on);
-
- return 0;
-}
-
-static void pdc_intc_perip_isr(struct irq_desc *desc)
-{
- unsigned int irq = irq_desc_get_irq(desc);
- struct pdc_intc_priv *priv;
- unsigned int i;
-
- priv = (struct pdc_intc_priv *)irq_desc_get_handler_data(desc);
-
- /* find the peripheral number */
- for (i = 0; i < priv->nr_perips; ++i)
- if (irq == priv->perip_irqs[i])
- goto found;
-
- /* should never get here */
- return;
-found:
-
- /* pass on the interrupt */
- generic_handle_domain_irq(priv->domain, i);
-}
-
-static void pdc_intc_syswake_isr(struct irq_desc *desc)
-{
- struct pdc_intc_priv *priv;
- unsigned int syswake;
- unsigned int status;
-
- priv = (struct pdc_intc_priv *)irq_desc_get_handler_data(desc);
-
- status = pdc_read(priv, PDC_IRQ_STATUS) &
- pdc_read(priv, PDC_IRQ_ENABLE);
- status &= (1 << priv->nr_syswakes) - 1;
-
- for (syswake = 0; status; status >>= 1, ++syswake) {
- /* Has this sys_wake triggered? */
- if (!(status & 1))
- continue;
-
- generic_handle_domain_irq(priv->domain, syswake_to_hwirq(syswake));
- }
-}
-
-static void pdc_intc_setup(struct pdc_intc_priv *priv)
-{
- int i;
- unsigned int soc_sys_wake_regoff;
- unsigned int soc_sys_wake;
-
- /*
- * Mask all syswake interrupts before routing, or we could receive an
- * interrupt before we're ready to handle it.
- */
- pdc_write(priv, PDC_IRQ_ENABLE, 0);
-
- /*
- * Enable routing of all syswakes
- * Disable all wake sources
- */
- priv->irq_route = ((PDC_IRQ_ROUTE_EXT_EN_SYS0 << priv->nr_syswakes) -
- PDC_IRQ_ROUTE_EXT_EN_SYS0);
- pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
-
- /* Initialise syswake IRQ */
- for (i = 0; i < priv->nr_syswakes; ++i) {
- /* set the IRQ mode to none */
- soc_sys_wake_regoff = PDC_SYS_WAKE_BASE + i*PDC_SYS_WAKE_STRIDE;
- soc_sys_wake = PDC_SYS_WAKE_INT_NONE
- << PDC_SYS_WAKE_INT_MODE_SHIFT;
- pdc_write(priv, soc_sys_wake_regoff, soc_sys_wake);
- }
-}
-
-static int pdc_intc_probe(struct platform_device *pdev)
-{
- struct pdc_intc_priv *priv;
- struct device_node *node = pdev->dev.of_node;
- struct resource *res_regs;
- struct irq_chip_generic *gc;
- unsigned int i;
- int irq, ret;
- u32 val;
-
- if (!node)
- return -ENOENT;
-
- /* Get registers */
- res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res_regs == NULL) {
- dev_err(&pdev->dev, "cannot find registers resource\n");
- return -ENOENT;
- }
-
- /* Allocate driver data */
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- raw_spin_lock_init(&priv->lock);
- platform_set_drvdata(pdev, priv);
-
- /* Ioremap the registers */
- priv->pdc_base = devm_ioremap(&pdev->dev, res_regs->start,
- resource_size(res_regs));
- if (!priv->pdc_base)
- return -EIO;
-
- /* Get number of peripherals */
- ret = of_property_read_u32(node, "num-perips", &val);
- if (ret) {
- dev_err(&pdev->dev, "No num-perips node property found\n");
- return -EINVAL;
- }
- if (val > SYS0_HWIRQ) {
- dev_err(&pdev->dev, "num-perips (%u) out of range\n", val);
- return -EINVAL;
- }
- priv->nr_perips = val;
-
- /* Get number of syswakes */
- ret = of_property_read_u32(node, "num-syswakes", &val);
- if (ret) {
- dev_err(&pdev->dev, "No num-syswakes node property found\n");
- return -EINVAL;
- }
- if (val > SYS0_HWIRQ) {
- dev_err(&pdev->dev, "num-syswakes (%u) out of range\n", val);
- return -EINVAL;
- }
- priv->nr_syswakes = val;
-
- /* Get peripheral IRQ numbers */
- priv->perip_irqs = devm_kcalloc(&pdev->dev, 4, priv->nr_perips,
- GFP_KERNEL);
- if (!priv->perip_irqs)
- return -ENOMEM;
- for (i = 0; i < priv->nr_perips; ++i) {
- irq = platform_get_irq(pdev, 1 + i);
- if (irq < 0)
- return irq;
- priv->perip_irqs[i] = irq;
- }
- /* check if too many were provided */
- if (platform_get_irq(pdev, 1 + i) >= 0) {
- dev_err(&pdev->dev, "surplus perip IRQs detected\n");
- return -EINVAL;
- }
-
- /* Get syswake IRQ number */
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
- priv->syswake_irq = irq;
-
- /* Set up an IRQ domain */
- priv->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), 16, &irq_generic_chip_ops,
- priv);
- if (unlikely(!priv->domain)) {
- dev_err(&pdev->dev, "cannot add IRQ domain\n");
- return -ENOMEM;
- }
- priv->domain->flags |= IRQ_DOMAIN_FLAG_DESTROY_GC;
-
- /*
- * Set up 2 generic irq chips with 2 chip types.
- * The first one for peripheral irqs (only 1 chip type used)
- * The second one for syswake irqs (edge and level chip types)
- */
- ret = irq_alloc_domain_generic_chips(priv->domain, 8, 2, "pdc",
- handle_level_irq, 0, 0,
- IRQ_GC_INIT_NESTED_LOCK);
- if (ret)
- goto err_generic;
-
- /* peripheral interrupt chip */
-
- gc = irq_get_domain_generic_chip(priv->domain, 0);
- gc->unused = ~(BIT(priv->nr_perips) - 1);
- gc->reg_base = priv->pdc_base;
- /*
- * IRQ_ROUTE contains wake bits, so we can't use the generic versions as
- * they cache the mask
- */
- gc->chip_types[0].regs.mask = PDC_IRQ_ROUTE;
- gc->chip_types[0].chip.irq_mask = perip_irq_mask;
- gc->chip_types[0].chip.irq_unmask = perip_irq_unmask;
- gc->chip_types[0].chip.irq_set_wake = pdc_irq_set_wake;
-
- /* syswake interrupt chip */
-
- gc = irq_get_domain_generic_chip(priv->domain, 8);
- gc->unused = ~(BIT(priv->nr_syswakes) - 1);
- gc->reg_base = priv->pdc_base;
-
- /* edge interrupts */
- gc->chip_types[0].type = IRQ_TYPE_EDGE_BOTH;
- gc->chip_types[0].handler = handle_edge_irq;
- gc->chip_types[0].regs.ack = PDC_IRQ_CLEAR;
- gc->chip_types[0].regs.mask = PDC_IRQ_ENABLE;
- gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
- gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
- gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
- gc->chip_types[0].chip.irq_set_type = syswake_irq_set_type;
- gc->chip_types[0].chip.irq_set_wake = pdc_irq_set_wake;
- /* for standby we pass on to the shared syswake IRQ */
- gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
-
- /* level interrupts */
- gc->chip_types[1].type = IRQ_TYPE_LEVEL_MASK;
- gc->chip_types[1].handler = handle_level_irq;
- gc->chip_types[1].regs.ack = PDC_IRQ_CLEAR;
- gc->chip_types[1].regs.mask = PDC_IRQ_ENABLE;
- gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit;
- gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit;
- gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit;
- gc->chip_types[1].chip.irq_set_type = syswake_irq_set_type;
- gc->chip_types[1].chip.irq_set_wake = pdc_irq_set_wake;
- /* for standby we pass on to the shared syswake IRQ */
- gc->chip_types[1].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
-
- /* Set up the hardware to enable interrupt routing */
- pdc_intc_setup(priv);
-
- /* Setup chained handlers for the peripheral IRQs */
- for (i = 0; i < priv->nr_perips; ++i) {
- irq = priv->perip_irqs[i];
- irq_set_chained_handler_and_data(irq, pdc_intc_perip_isr,
- priv);
- }
-
- /* Setup chained handler for the syswake IRQ */
- irq_set_chained_handler_and_data(priv->syswake_irq,
- pdc_intc_syswake_isr, priv);
-
- dev_info(&pdev->dev,
- "PDC IRQ controller initialised (%u perip IRQs, %u syswake IRQs)\n",
- priv->nr_perips,
- priv->nr_syswakes);
-
- return 0;
-err_generic:
- irq_domain_remove(priv->domain);
- return ret;
-}
-
-static void pdc_intc_remove(struct platform_device *pdev)
-{
- struct pdc_intc_priv *priv = platform_get_drvdata(pdev);
-
- for (unsigned int i = 0; i < priv->nr_perips; ++i)
- irq_set_chained_handler_and_data(priv->perip_irqs[i], NULL, NULL);
-
- irq_set_chained_handler_and_data(priv->syswake_irq, NULL, NULL);
-
- irq_domain_remove(priv->domain);
-}
-
-static const struct of_device_id pdc_intc_match[] = {
- { .compatible = "img,pdc-intc" },
- {}
-};
-
-static struct platform_driver pdc_intc_driver = {
- .driver = {
- .name = "pdc-intc",
- .of_match_table = pdc_intc_match,
- },
- .probe = pdc_intc_probe,
- .remove = pdc_intc_remove,
-};
-
-static int __init pdc_intc_init(void)
-{
- return platform_driver_register(&pdc_intc_driver);
-}
-core_initcall(pdc_intc_init);
diff --git a/drivers/irqchip/irq-qcom-mpm.c b/drivers/irqchip/irq-qcom-mpm.c
index 181320528a47..8c575d7ce8a1 100644
--- a/drivers/irqchip/irq-qcom-mpm.c
+++ b/drivers/irqchip/irq-qcom-mpm.c
@@ -464,10 +464,8 @@ static int qcom_mpm_probe(struct platform_device *pdev, struct device_node *pare
ret = devm_request_irq(dev, irq, qcom_mpm_handler, IRQF_NO_SUSPEND,
"qcom_mpm", priv);
- if (ret) {
- dev_err(dev, "failed to request irq: %d\n", ret);
+ if (ret)
goto remove_domain;
- }
return 0;
diff --git a/drivers/irqchip/irq-realtek-rtl.c b/drivers/irqchip/irq-realtek-rtl.c
index 2ae3be7fa633..c8becb458da2 100644
--- a/drivers/irqchip/irq-realtek-rtl.c
+++ b/drivers/irqchip/irq-realtek-rtl.c
@@ -25,6 +25,15 @@
#define REG(cpu, x) (realtek_ictl_base[cpu] + x)
+struct realtek_ictl_output {
+ struct fwnode_handle *fwnode;
+ struct irq_domain *domain;
+ unsigned int parent_irq;
+ unsigned int parent_hwirq;
+ unsigned int index;
+ u32 mask;
+};
+
static DEFINE_RAW_SPINLOCK(irq_lock);
static void __iomem *realtek_ictl_base[NR_CPUS];
@@ -105,53 +114,141 @@ static struct irq_chip realtek_ictl_irq = {
.irq_set_affinity = realtek_ictl_irq_affinity,
};
-static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
+static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw_irq)
{
+ struct realtek_ictl_output *output = d->host_data;
unsigned int cpu;
irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq);
guard(raw_spinlock_irqsave)(&irq_lock);
+ output->mask |= BIT(hw_irq);
for_each_present_cpu(cpu)
- write_irr(cpu, hw, 1);
+ write_irr(cpu, hw_irq, output->parent_hwirq - 1);
return 0;
}
+static int intc_select(struct irq_domain *d, struct irq_fwspec *fwspec,
+ enum irq_domain_bus_token bus_token)
+{
+ struct realtek_ictl_output *output = d->host_data;
+ unsigned int index = 0;
+
+ if (fwspec->fwnode != output->fwnode)
+ return false;
+
+ if (fwspec->param_count == 2)
+ index = fwspec->param[1];
+
+ return index == output->index;
+}
+
static const struct irq_domain_ops irq_domain_ops = {
- .map = intc_map,
- .xlate = irq_domain_xlate_onecell,
+ .map = intc_map,
+ .select = intc_select,
+ .xlate = irq_domain_xlate_onecell,
};
static void realtek_irq_dispatch(struct irq_desc *desc)
{
+ struct realtek_ictl_output *output = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned int cpu = smp_processor_id();
- struct irq_domain *domain;
unsigned long pending;
- unsigned int soc_int;
+ unsigned int hw_irq;
chained_irq_enter(chip, desc);
- pending = readl(REG(cpu, RTL_ICTL_GIMR)) & readl(REG(cpu, RTL_ICTL_GISR));
+ pending = readl(REG(cpu, RTL_ICTL_GIMR)) & readl(REG(cpu, RTL_ICTL_GISR)) & output->mask;
if (unlikely(!pending)) {
spurious_interrupt();
goto out;
}
- domain = irq_desc_get_handler_data(desc);
- for_each_set_bit(soc_int, &pending, RTL_ICTL_NUM_INPUTS)
- generic_handle_domain_irq(domain, soc_int);
+ for_each_set_bit(hw_irq, &pending, RTL_ICTL_NUM_INPUTS)
+ generic_handle_domain_irq(output->domain, hw_irq);
out:
chained_irq_exit(chip, desc);
}
-static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent)
+static int __init realtek_setup_parents(struct device_node *node)
{
+ int p, cnt, err, parent_irq, num_parents = of_irq_count(node);
+ struct realtek_ictl_output *output;
+ struct irq_data *parent_data;
struct of_phandle_args oirq;
struct irq_domain *domain;
- int cpu, parent_irq;
+
+ cnt = max(1, num_parents);
+ output = kcalloc(cnt, sizeof(*output), GFP_KERNEL);
+ if (!output)
+ return -ENOMEM;
+
+ for (p = 0; p < cnt; p++) {
+ if (WARN_ON(!num_parents)) {
+ /*
+ * If DT contains no parent interrupts, assume MIPS IRQ 2 (HW0) is
+ * connected to the first output. This is the case for all known hardware.
+ */
+ oirq.np = of_find_compatible_node(NULL, NULL,
+ "mti,cpu-interrupt-controller");
+ if (!oirq.np) {
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ oirq.args_count = 1;
+ oirq.args[0] = 2;
+ parent_irq = irq_create_of_mapping(&oirq);
+ of_node_put(oirq.np);
+ } else {
+ parent_irq = of_irq_get(node, p);
+ }
+
+ if (parent_irq <= 0) {
+ err = parent_irq ? parent_irq : -ENODEV;
+ goto err_out;
+ }
+
+ parent_data = irq_get_irq_data(parent_irq);
+ if (!parent_data) {
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ domain = irq_domain_create_linear(of_fwnode_handle(node), RTL_ICTL_NUM_INPUTS,
+ &irq_domain_ops, &output[p]);
+ if (!domain) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ output[p].domain = domain;
+ output[p].fwnode = of_fwnode_handle(node);
+ output[p].index = p;
+ output[p].parent_irq = parent_irq;
+ output[p].parent_hwirq = irqd_to_hwirq(parent_data);
+ irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, &output[p]);
+ }
+
+ return 0;
+
+err_out:
+ while (p--) {
+ irq_set_chained_handler_and_data(output[p].parent_irq, NULL, NULL);
+ irq_domain_remove(output[p].domain);
+ }
+
+ kfree(output);
+
+ return err;
+}
+
+static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent)
+{
+ unsigned int cpu;
for_each_present_cpu(cpu) {
realtek_ictl_base[cpu] = of_iomap(node, cpu);
@@ -165,36 +262,7 @@ static int __init realtek_rtl_of_init(struct device_node *node, struct device_no
}
}
- if (WARN_ON(!of_irq_count(node))) {
- /*
- * If DT contains no parent interrupts, assume MIPS CPU IRQ 2
- * (HW0) is connected to the first output. This is the case for
- * all known hardware anyway. "interrupt-map" is deprecated, so
- * don't bother trying to parse that.
- */
- oirq.np = of_find_compatible_node(NULL, NULL, "mti,cpu-interrupt-controller");
- oirq.args_count = 1;
- oirq.args[0] = 2;
-
- parent_irq = irq_create_of_mapping(&oirq);
-
- of_node_put(oirq.np);
- } else {
- parent_irq = of_irq_get(node, 0);
- }
-
- if (parent_irq < 0)
- return parent_irq;
- else if (!parent_irq)
- return -ENODEV;
-
- domain = irq_domain_create_linear(of_fwnode_handle(node), RTL_ICTL_NUM_INPUTS, &irq_domain_ops, NULL);
- if (!domain)
- return -ENOMEM;
-
- irq_set_chained_handler_and_data(parent_irq, realtek_irq_dispatch, domain);
-
- return 0;
+ return realtek_setup_parents(node);
}
IRQCHIP_DECLARE(realtek_rtl_intc, "realtek,rtl-intc", realtek_rtl_of_init);
diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c
index a20a6471b0e4..1ff3535a4617 100644
--- a/drivers/irqchip/irq-renesas-irqc.c
+++ b/drivers/irqchip/irq-renesas-irqc.c
@@ -176,6 +176,7 @@ static int irqc_probe(struct platform_device *pdev)
goto err_runtime_pm_disable;
}
+ p->irq_domain->flags |= IRQ_DOMAIN_FLAG_DESTROY_GC;
ret = irq_alloc_domain_generic_chips(p->irq_domain, p->number_of_irqs,
1, "irqc", handle_level_irq,
0, 0, IRQ_GC_INIT_NESTED_LOCK);
diff --git a/drivers/irqchip/irq-renesas-rzt2h.c b/drivers/irqchip/irq-renesas-rzt2h.c
index e06264add3cc..9086fc3a2043 100644
--- a/drivers/irqchip/irq-renesas-rzt2h.c
+++ b/drivers/irqchip/irq-renesas-rzt2h.c
@@ -398,7 +398,7 @@ static int rzt2h_icu_request_irqs(struct platform_device *pdev, struct irq_domai
ret = devm_request_irq(dev, virq, handler, 0, dev_name(dev),
data ?: (void *)(uintptr_t)offset);
if (ret)
- return dev_err_probe(dev, ret, "Failed to request IRQ %u\n", offset);
+ return ret;
}
return 0;
diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c
index 971ac83eee90..1a4703a353e9 100644
--- a/drivers/irqchip/irq-renesas-rzv2h.c
+++ b/drivers/irqchip/irq-renesas-rzv2h.c
@@ -816,7 +816,7 @@ static int rzv2h_icu_setup_irqs(struct platform_device *pdev, struct irq_domain
ret = devm_request_irq(dev, virq, rzv2h_icu_swint_irq, 0, dev_name(dev),
(void *)(uintptr_t)i);
if (ret)
- return dev_err_probe(dev, ret, "Failed to request int-ca55-%u IRQ\n", i);
+ return ret;
}
/* Unmask and clear all IP/CA55 error interrupts */
@@ -844,7 +844,7 @@ static int rzv2h_icu_setup_irqs(struct platform_device *pdev, struct irq_domain
ret = devm_request_irq(dev, virq, rzv2h_icu_error_irq, 0, dev_name(dev), rzv2h_icu_data);
if (ret)
- return dev_err_probe(dev, ret, "Failed to request icu-error-ca55 IRQ\n");
+ return ret;
return 0;
}
diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c
index 2014dbb0bc43..ce6d80c7f17a 100644
--- a/drivers/irqchip/qcom-pdc.c
+++ b/drivers/irqchip/qcom-pdc.c
@@ -20,22 +20,19 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/types.h>
+#include <linux/firmware/qcom/qcom_scm.h>
-#define PDC_MAX_GPIO_IRQS 256
-#define PDC_DRV_SIZE 0x10000
-
-/* Valid only on HW version < 3.2 */
-#define IRQ_ENABLE_BANK 0x10
-#define IRQ_ENABLE_BANK_MAX (IRQ_ENABLE_BANK + BITS_TO_BYTES(PDC_MAX_GPIO_IRQS))
+#define PDC_MAX_IRQS 256
+#define IRQ_ENABLE_BANK_MAX BITS_TO_BYTES(PDC_MAX_IRQS)
#define IRQ_ENABLE_BANK_INDEX_MASK GENMASK(31, 5)
#define IRQ_ENABLE_BANK_BIT_MASK GENMASK(4, 0)
-#define IRQ_i_CFG 0x110
-
-/* Valid only on HW version >= 3.2 */
-#define IRQ_i_CFG_IRQ_ENABLE 3
-#define IRQ_i_CFG_TYPE_MASK GENMASK(2, 0)
+/* Secure DRV register to configure the PDC mode via qcom_scm_io_writel() */
+#define PDC_GPIO_INT_CTL_ENABLE 0xb2045e8
+#define PDC_PASS_THROUGH_MODE 0x0
+#define PDC_SECONDARY_MODE 0x1
+#define PDC_DRV_SIZE 0x10000
#define PDC_VERSION_REG 0x1000
#define PDC_VERSION_MAJOR GENMASK(23, 16)
#define PDC_VERSION_MINOR GENMASK(15, 8)
@@ -46,22 +43,149 @@
/* Notable PDC versions */
#define PDC_VERSION_3_2 PDC_VERSION(3, 2, 0)
+#define PDC_VERSION_3_0 PDC_VERSION(3, 0, 0)
+#define PDC_VERSION_2_7 PDC_VERSION(2, 7, 0)
+
+/*
+ * PDC Hardware registers layout per version:
+ *
+ * IRQ_ENABLE_BANK[b], b = 0....BITS_TO_BYTES(PDC_MAX_IRQS)
+ * IRQ_CFG[n], n = 0....PDC_MAX_IRQS
+ *
+ * +---------------------------------------------------------------+
+ * | v2.7 | v3.0 | v3.2 |
+ * |---------------------------------------------------------------|
+ * | BASE | BASE | BASE |
+ * |---------------------------------------------------------------|
+ * | |
+ * | IRQ_ENABLE_BANK | IRQ_ENABLE_BANK | NA |
+ * |---------------------------------------------------------------|
+ * | IRQ_CFG | IRQ_CFG | IRQ_CFG |
+ * | | | |
+ * | | | [31:6] Unused |
+ * | | [31:5] Unused | [5] GPIO_STATUS |
+ * | | [4] GPIO_STATUS| [4] GPIO_MASK |
+ * | [31:3] Unused | [3] GPIO_MASK | [3] IRQ_ENABLE |
+ * | [0:2] Type | [0:2] Type | [0:2] Type |
+ * |---------------------------------------------------------------|
+ * | IRQ_PARAM | IRQ_PARAM | IRQ_PARAM |
+ * | | |
+ * | [15:8] NUM_GPIO | [15:8] NUM_GPIO | [15:8] NUM_GPIO |
+ * | [7:0] NUM_SPI | [7:0] NUM_SPI | [7:0] NUM_SPI |
+ * +---------------------------------------------------------------+
+ */
+
+/**
+ * struct pdc_regs: PDC registers location
+ *
+ * @irq_en_reg: IRQ_ENABLE_BANK register location
+ * @irq_cfg_reg: IRQ_CFG register location
+ * @irq_param_reg: IRQ_PARAM register location
+ */
+struct pdc_regs {
+ u32 irq_en_reg;
+ u32 irq_cfg_reg;
+ u32 irq_param_reg;
+};
+
+/**
+ * struct pdc_irq_cfg: bit fields for PDC IRQ_CFG register
+ *
+ * @gpio_irq_sts: bit number for GPIO_STATUS field
+ * @gpio_irq_mask: bit number for GPIO_MASK field
+ * @irq_enable: bit number for IRQ_ENABLE field
+ * @irq_type: GENMASK for IRQ_TYPE field
+ */
+struct pdc_irq_cfg {
+ u32 gpio_irq_sts;
+ u32 gpio_irq_mask;
+ u32 irq_enable;
+ u32 irq_type;
+};
+
+/**
+ * struct pdc_desc: PDC driver state
+ *
+ * @base: PDC base register for DRV2 / HLOS
+ * @prev_base: PDC DRV1 base, applicable only for x1e RTL bug.
+ * @version: PDC version
+ * @num_spis: Total number of direct SPI interrupts
+ * @region: PDC interrupt continuous range
+ * @region_cnt: Total PDC ranges
+ * @mode: PDC_PASS_THROUGH_MODE or PDC_SECONDARY_MODE
+ * @x1e_quirk: x1e H/W Bug handling
+ * @lock: lock for IRQ_ENABLE_BANK protection
+ * @regs: PDC regs (IRQ_ENABLE_BANK and IRQ_CFG)
+ * @cfg_fields: Fields of IRQ_CFG reg
+ * @enable_intr: pointer to enable function based on PDC version
+ * @unmask_gpio: pointer to GPIO irq unmask function
+ * @clear_gpio: pointer to GPIO irq clear function
+ */
+struct pdc_desc {
+ void __iomem *base;
+ void __iomem *prev_base;
+ u32 version;
+ u32 num_spis;
+
+ struct pdc_pin_region *region;
+ int region_cnt;
+
+ bool x1e_quirk;
+
+ u8 mode;
+ raw_spinlock_t lock;
+
+ const struct pdc_regs *regs;
+ const struct pdc_irq_cfg *cfg_fields;
+
+ void (*enable_intr)(int pin_out, bool on);
+ void (*unmask_gpio)(int pin_out, bool on);
+ void (*clear_gpio)(int pin_out);
+};
+
+static const struct pdc_regs pdc_v3_2 = {
+ .irq_cfg_reg = 0x110,
+ .irq_param_reg = 0x100c,
+};
+
+static const struct pdc_irq_cfg pdc_cfg_v3_2 = {
+ .gpio_irq_sts = 5,
+ .gpio_irq_mask = 4,
+ .irq_enable = 3,
+ .irq_type = GENMASK(2, 0),
+};
+
+static const struct pdc_regs pdc_v3_0 = {
+ .irq_en_reg = 0x10,
+ .irq_cfg_reg = 0x110,
+ .irq_param_reg = 0x100c,
+};
+
+static const struct pdc_irq_cfg pdc_cfg_v3_0 = {
+ .gpio_irq_sts = 4,
+ .gpio_irq_mask = 3,
+ .irq_type = GENMASK(2, 0),
+};
+
+static const struct pdc_regs pdc_v2_7 = {
+ .irq_en_reg = 0x10,
+ .irq_cfg_reg = 0x110,
+ .irq_param_reg = 0x100c,
+};
+
+static const struct pdc_irq_cfg pdc_cfg_v2_7 = {
+ .irq_type = GENMASK(2, 0),
+};
struct pdc_pin_region {
- u32 pin_base;
- u32 parent_base;
- u32 cnt;
+ u32 pin_base;
+ u32 parent_base;
+ u32 cnt;
};
#define pin_to_hwirq(r, p) ((r)->parent_base + (p) - (r)->pin_base)
-static DEFINE_RAW_SPINLOCK(pdc_lock);
-static void __iomem *pdc_base;
-static void __iomem *pdc_prev_base;
-static struct pdc_pin_region *pdc_region;
-static int pdc_region_cnt;
-static unsigned int pdc_version;
-static bool pdc_x1e_quirk;
+static struct pdc_desc *pdc;
static void pdc_base_reg_write(void __iomem *base, int reg, u32 i, u32 val)
{
@@ -70,12 +194,21 @@ static void pdc_base_reg_write(void __iomem *base, int reg, u32 i, u32 val)
static void pdc_reg_write(int reg, u32 i, u32 val)
{
- pdc_base_reg_write(pdc_base, reg, i, val);
+ pdc_base_reg_write(pdc->base, reg, i, val);
}
static u32 pdc_reg_read(int reg, u32 i)
{
- return readl_relaxed(pdc_base + reg + i * sizeof(u32));
+ return readl_relaxed(pdc->base + reg + i * sizeof(u32));
+}
+
+static inline bool pdc_pin_is_gpio(int pin_out)
+{
+ /*
+ * PDC allocates direct SPIs at the beginning and
+ * all GPIOs as SPIs are allocated after direct SPIs.
+ */
+ return pin_out >= pdc->num_spis;
}
static void pdc_x1e_irq_enable_write(u32 bank, u32 enable)
@@ -86,24 +219,24 @@ static void pdc_x1e_irq_enable_write(u32 bank, u32 enable)
switch (bank) {
case 0 ... 1:
/* Use previous DRV (client) region and shift to bank 3-4 */
- base = pdc_prev_base;
+ base = pdc->prev_base;
bank += 3;
break;
case 2 ... 4:
/* Use our own region and shift to bank 0-2 */
- base = pdc_base;
+ base = pdc->base;
bank -= 2;
break;
case 5:
/* No fixup required for bank 5 */
- base = pdc_base;
+ base = pdc->base;
break;
default:
WARN_ON(1);
return;
}
- pdc_base_reg_write(base, IRQ_ENABLE_BANK, bank, enable);
+ pdc_base_reg_write(base, pdc->regs->irq_en_reg, bank, enable);
}
static void pdc_enable_intr_bank(int pin_out, bool on)
@@ -114,52 +247,97 @@ static void pdc_enable_intr_bank(int pin_out, bool on)
index = FIELD_GET(IRQ_ENABLE_BANK_INDEX_MASK, pin_out);
mask = FIELD_GET(IRQ_ENABLE_BANK_BIT_MASK, pin_out);
- enable = pdc_reg_read(IRQ_ENABLE_BANK, index);
+ guard(raw_spinlock_irqsave)(&pdc->lock);
+
+ enable = pdc_reg_read(pdc->regs->irq_en_reg, index);
__assign_bit(mask, &enable, on);
- if (pdc_x1e_quirk)
+ if (pdc->x1e_quirk)
pdc_x1e_irq_enable_write(index, enable);
else
- pdc_reg_write(IRQ_ENABLE_BANK, index, enable);
+ pdc_reg_write(pdc->regs->irq_en_reg, index, enable);
}
-static void pdc_enable_intr_cfg(int pin_out, bool on)
+static void pdc_clear_gpio_cfg(int pin_out)
{
- unsigned long enable = pdc_reg_read(IRQ_i_CFG, pin_out);
+ unsigned long gpio_sts;
- __assign_bit(IRQ_i_CFG_IRQ_ENABLE, &enable, on);
- pdc_reg_write(IRQ_i_CFG, pin_out, enable);
+ gpio_sts = pdc_reg_read(pdc->regs->irq_cfg_reg, pin_out);
+ __clear_bit(pdc->cfg_fields->gpio_irq_sts, &gpio_sts);
+ pdc_reg_write(pdc->regs->irq_cfg_reg, pin_out, gpio_sts);
}
-static void __pdc_enable_intr(int pin_out, bool on)
+static void pdc_unmask_gpio_cfg(int pin_out, bool unmask)
{
- if (pdc_version < PDC_VERSION_3_2)
- pdc_enable_intr_bank(pin_out, on);
- else
- pdc_enable_intr_cfg(pin_out, on);
+ unsigned long gpio_mask;
+
+ gpio_mask = pdc_reg_read(pdc->regs->irq_cfg_reg, pin_out);
+ __assign_bit(pdc->cfg_fields->gpio_irq_mask, &gpio_mask, !unmask);
+ pdc_reg_write(pdc->regs->irq_cfg_reg, pin_out, gpio_mask);
}
-static void pdc_enable_intr(struct irq_data *d, bool on)
+static void pdc_enable_intr_cfg(int pin_out, bool on)
{
- unsigned long flags;
+ unsigned long enable = pdc_reg_read(pdc->regs->irq_cfg_reg, pin_out);
- raw_spin_lock_irqsave(&pdc_lock, flags);
- __pdc_enable_intr(d->hwirq, on);
- raw_spin_unlock_irqrestore(&pdc_lock, flags);
+ __assign_bit(pdc->cfg_fields->irq_enable, &enable, on);
+ pdc_reg_write(pdc->regs->irq_cfg_reg, pin_out, enable);
+}
+
+static void qcom_pdc_gic_secondary_disable(struct irq_data *d)
+{
+ pdc->enable_intr(d->hwirq, false);
+ pdc->unmask_gpio(d->hwirq, false);
+ irq_chip_disable_parent(d);
}
static void qcom_pdc_gic_disable(struct irq_data *d)
{
- pdc_enable_intr(d, false);
+ pdc->enable_intr(d->hwirq, false);
irq_chip_disable_parent(d);
}
static void qcom_pdc_gic_enable(struct irq_data *d)
{
- pdc_enable_intr(d, true);
+ pdc->enable_intr(d->hwirq, true);
+ irq_chip_enable_parent(d);
+}
+
+static void qcom_pdc_gic_secondary_enable(struct irq_data *d)
+{
+ pdc->enable_intr(d->hwirq, true);
+ pdc->unmask_gpio(d->hwirq, true);
irq_chip_enable_parent(d);
}
+static void qcom_pdc_secondary_ack(struct irq_data *d)
+{
+ if (!irqd_is_level_type(d))
+ pdc->clear_gpio(d->hwirq);
+}
+
+static void qcom_pdc_gic_secondary_eoi(struct irq_data *d)
+{
+ if (irqd_is_level_type(d))
+ pdc->clear_gpio(d->hwirq);
+
+ irq_chip_eoi_parent(d);
+}
+
+static void qcom_pdc_secondary_mask(struct irq_data *d)
+{
+ pdc->enable_intr(d->hwirq, false);
+ pdc->unmask_gpio(d->hwirq, false);
+ irq_chip_mask_parent(d);
+}
+
+static void qcom_pdc_secondary_unmask(struct irq_data *d)
+{
+ pdc->enable_intr(d->hwirq, true);
+ pdc->unmask_gpio(d->hwirq, true);
+ irq_chip_unmask_parent(d);
+}
+
/*
* GIC does not handle falling edge or active low. To allow falling edge and
* active low interrupts to be handled at GIC, PDC has an inverter that inverts
@@ -186,18 +364,18 @@ enum pdc_irq_config_bits {
/**
* qcom_pdc_gic_set_type: Configure PDC for the interrupt
*
- * @d: the interrupt data
+ * @d: the interrupt data
* @type: the interrupt type
*
- * If @type is edge triggered, forward that as Rising edge as PDC
- * takes care of converting falling edge to rising edge signal
+ * If @type is edge triggered, forward that as rising edge as PDC
+ * takes care of converting all edge types to rising edge signal
* If @type is level, then forward that as level high as PDC
- * takes care of converting falling edge to rising edge signal
+ * takes care of converting all level types to level high signal
*/
static int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type)
{
- enum pdc_irq_config_bits pdc_type;
enum pdc_irq_config_bits old_pdc_type;
+ enum pdc_irq_config_bits pdc_type;
int ret;
switch (type) {
@@ -224,9 +402,75 @@ static int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type)
return -EINVAL;
}
- old_pdc_type = pdc_reg_read(IRQ_i_CFG, d->hwirq);
- pdc_type |= (old_pdc_type & ~IRQ_i_CFG_TYPE_MASK);
- pdc_reg_write(IRQ_i_CFG, d->hwirq, pdc_type);
+ old_pdc_type = pdc_reg_read(pdc->regs->irq_cfg_reg, d->hwirq);
+ pdc_type |= (old_pdc_type & ~pdc->cfg_fields->irq_type);
+ pdc_reg_write(pdc->regs->irq_cfg_reg, d->hwirq, pdc_type);
+
+ ret = irq_chip_set_type_parent(d, type);
+ if (ret)
+ return ret;
+
+ /*
+ * When we change types the PDC can give a phantom interrupt.
+ * Clear it. Specifically the phantom shows up when reconfiguring
+ * polarity of interrupt without changing the state of the signal
+ * but let's be consistent and clear it always.
+ *
+ * Doing this works because we have IRQCHIP_SET_TYPE_MASKED so the
+ * interrupt will be cleared before the rest of the system sees it.
+ */
+ if (old_pdc_type != pdc_type)
+ irq_chip_set_parent_state(d, IRQCHIP_STATE_PENDING, false);
+
+ return 0;
+}
+
+/**
+ * qcom_pdc_gic_secondary_set_type: Configure PDC for the interrupt in secondary mode
+ *
+ * @d: the interrupt data
+ * @type: the interrupt type
+ *
+ * All @type are forwarded as level high type to parent GIC
+ */
+static int qcom_pdc_gic_secondary_set_type(struct irq_data *d, unsigned int type)
+{
+ enum pdc_irq_config_bits old_pdc_type;
+ enum pdc_irq_config_bits pdc_type;
+ int ret;
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ pdc_type = PDC_EDGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ pdc_type = PDC_EDGE_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ pdc_type = PDC_EDGE_DUAL;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ pdc_type = PDC_LEVEL_HIGH;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ pdc_type = PDC_LEVEL_LOW;
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ old_pdc_type = pdc_reg_read(pdc->regs->irq_cfg_reg, d->hwirq);
+ pdc_type |= (old_pdc_type & ~pdc->cfg_fields->irq_type);
+ pdc_reg_write(pdc->regs->irq_cfg_reg, d->hwirq, pdc_type);
+
+ /*
+ * PDC forwards GPIOs as level high to GIC in secondary
+ * mode. Update the type and clear any previously latched
+ * phantom interrupt at PDC.
+ */
+ type = IRQ_TYPE_LEVEL_HIGH;
+ pdc->clear_gpio(d->hwirq);
ret = irq_chip_set_type_parent(d, type);
if (ret)
@@ -266,14 +510,32 @@ static struct irq_chip qcom_pdc_gic_chip = {
.irq_set_affinity = irq_chip_set_affinity_parent,
};
+static struct irq_chip qcom_pdc_gic_secondary_chip = {
+ .name = "PDC",
+ .irq_ack = qcom_pdc_secondary_ack,
+ .irq_eoi = qcom_pdc_gic_secondary_eoi,
+ .irq_mask = qcom_pdc_secondary_mask,
+ .irq_unmask = qcom_pdc_secondary_unmask,
+ .irq_disable = qcom_pdc_gic_secondary_disable,
+ .irq_enable = qcom_pdc_gic_secondary_enable,
+ .irq_get_irqchip_state = irq_chip_get_parent_state,
+ .irq_set_irqchip_state = irq_chip_set_parent_state,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_set_type = qcom_pdc_gic_secondary_set_type,
+ .flags = IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SET_TYPE_MASKED |
+ IRQCHIP_SKIP_SET_WAKE |
+ IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND,
+ .irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+};
+
static struct pdc_pin_region *get_pin_region(int pin)
{
- int i;
-
- for (i = 0; i < pdc_region_cnt; i++) {
- if (pin >= pdc_region[i].pin_base &&
- pin < pdc_region[i].pin_base + pdc_region[i].cnt)
- return &pdc_region[i];
+ for (int i = 0; i < pdc->region_cnt; i++) {
+ if (pin >= pdc->region[i].pin_base &&
+ pin < pdc->region[i].pin_base + pdc->region[i].cnt)
+ return &pdc->region[i];
}
return NULL;
@@ -301,24 +563,45 @@ static int qcom_pdc_alloc(struct irq_domain *domain, unsigned int virq,
if (ret)
return ret;
+ /*
+ * PDC secondary chip is only set for the GPIO interrupts as SPIs.
+ * Direct SPI interrupts are still in pass through mode (no latching
+ * at PDC).
+ */
+ if (pdc->mode == PDC_SECONDARY_MODE && pdc_pin_is_gpio(hwirq)) {
+ ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+ &qcom_pdc_gic_secondary_chip,
+ NULL);
+ if (ret)
+ return ret;
+
+ /* Secondary mode converts all interrupts to LEVEL HIGH type */
+ type = IRQ_TYPE_LEVEL_HIGH;
+ } else {
+ ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+ &qcom_pdc_gic_chip,
+ NULL);
+ if (ret)
+ return ret;
+
+ if (type & IRQ_TYPE_EDGE_BOTH)
+ type = IRQ_TYPE_EDGE_RISING;
+
+ if (type & IRQ_TYPE_LEVEL_MASK)
+ type = IRQ_TYPE_LEVEL_HIGH;
+ }
+
region = get_pin_region(hwirq);
if (!region)
return irq_domain_disconnect_hierarchy(domain->parent, virq);
- if (type & IRQ_TYPE_EDGE_BOTH)
- type = IRQ_TYPE_EDGE_RISING;
-
- if (type & IRQ_TYPE_LEVEL_MASK)
- type = IRQ_TYPE_LEVEL_HIGH;
-
parent_fwspec.fwnode = domain->parent->fwnode;
parent_fwspec.param_count = 3;
parent_fwspec.param[0] = 0;
parent_fwspec.param[1] = pin_to_hwirq(region, hwirq);
parent_fwspec.param[2] = type;
- return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
- &parent_fwspec);
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_fwspec);
}
static const struct irq_domain_ops qcom_pdc_ops = {
@@ -327,52 +610,56 @@ static const struct irq_domain_ops qcom_pdc_ops = {
.free = irq_domain_free_irqs_common,
};
-static int pdc_setup_pin_mapping(struct device_node *np)
+static int pdc_setup_pin_mapping(struct device *dev)
{
- int ret, n, i;
+ struct device_node *np = dev->of_node;
+ int ret, n;
n = of_property_count_elems_of_size(np, "qcom,pdc-ranges", sizeof(u32));
if (n <= 0 || n % 3)
return -EINVAL;
- pdc_region_cnt = n / 3;
- pdc_region = kzalloc_objs(*pdc_region, pdc_region_cnt);
- if (!pdc_region) {
- pdc_region_cnt = 0;
+ pdc->region_cnt = n / 3;
+ pdc->region = devm_kcalloc(dev, pdc->region_cnt, sizeof(*pdc->region), GFP_KERNEL);
+ if (!pdc->region) {
+ pdc->region_cnt = 0;
return -ENOMEM;
}
- for (n = 0; n < pdc_region_cnt; n++) {
- ret = of_property_read_u32_index(np, "qcom,pdc-ranges",
- n * 3 + 0,
- &pdc_region[n].pin_base);
+ for (n = 0; n < pdc->region_cnt; n++) {
+ ret = of_property_read_u32_index(np, "qcom,pdc-ranges", n * 3 + 0,
+ &pdc->region[n].pin_base);
if (ret)
return ret;
- ret = of_property_read_u32_index(np, "qcom,pdc-ranges",
- n * 3 + 1,
- &pdc_region[n].parent_base);
+ ret = of_property_read_u32_index(np, "qcom,pdc-ranges", n * 3 + 1,
+ &pdc->region[n].parent_base);
if (ret)
return ret;
- ret = of_property_read_u32_index(np, "qcom,pdc-ranges",
- n * 3 + 2,
- &pdc_region[n].cnt);
+ ret = of_property_read_u32_index(np, "qcom,pdc-ranges", n * 3 + 2,
+ &pdc->region[n].cnt);
if (ret)
return ret;
- for (i = 0; i < pdc_region[n].cnt; i++)
- __pdc_enable_intr(i + pdc_region[n].pin_base, 0);
+ for (int i = 0; i < pdc->region[n].cnt; i++) {
+ if (pdc_pin_is_gpio(i + pdc->region[n].pin_base) &&
+ pdc->mode == PDC_SECONDARY_MODE)
+ pdc->clear_gpio(i + pdc->region[n].pin_base);
+
+ pdc->enable_intr(i + pdc->region[n].pin_base, false);
+ }
}
return 0;
}
-
static int qcom_pdc_probe(struct platform_device *pdev, struct device_node *parent)
{
struct irq_domain *parent_domain, *pdc_domain;
struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
resource_size_t res_size;
struct resource res;
+ u32 irq_param;
int ret;
/* compat with old sm8150 DT which had very small region for PDC */
@@ -383,6 +670,34 @@ static int qcom_pdc_probe(struct platform_device *pdev, struct device_node *pare
if (res_size > resource_size(&res))
pr_warn("%pOF: invalid reg size, please fix DT\n", node);
+ pdc = devm_kzalloc(dev, sizeof(*pdc), GFP_KERNEL);
+ if (!pdc)
+ return -ENOMEM;
+
+ pdc->base = devm_ioremap(dev, res.start, res_size);
+ if (!pdc->base) {
+ pr_err("%pOF: unable to map PDC registers\n", node);
+ return -ENXIO;
+ }
+
+ pdc->version = pdc_reg_read(PDC_VERSION_REG, 0);
+
+ if (pdc->version >= PDC_VERSION_3_2) {
+ pdc->cfg_fields = &pdc_cfg_v3_2;
+ pdc->regs = &pdc_v3_2;
+ pdc->enable_intr = pdc_enable_intr_cfg;
+ } else if (pdc->version >= PDC_VERSION_3_0) {
+ pdc->cfg_fields = &pdc_cfg_v3_0;
+ pdc->regs = &pdc_v3_0;
+ pdc->enable_intr = pdc_enable_intr_bank;
+ } else {
+ pdc->cfg_fields = &pdc_cfg_v2_7;
+ pdc->regs = &pdc_v2_7;
+ pdc->enable_intr = pdc_enable_intr_bank;
+ }
+
+ pdc->mode = PDC_PASS_THROUGH_MODE;
+
/*
* PDC has multiple DRV regions, each one provides the same set of
* registers for a particular client in the system. Due to a hardware
@@ -392,57 +707,54 @@ static int qcom_pdc_probe(struct platform_device *pdev, struct device_node *pare
* region with the expected offset to preserve support for old DTs.
*/
if (of_device_is_compatible(node, "qcom,x1e80100-pdc")) {
- pdc_prev_base = ioremap(res.start - PDC_DRV_SIZE, IRQ_ENABLE_BANK_MAX);
- if (!pdc_prev_base) {
+ pdc->prev_base = devm_ioremap(dev, res.start - PDC_DRV_SIZE,
+ pdc->regs->irq_en_reg + IRQ_ENABLE_BANK_MAX);
+ if (!pdc->prev_base) {
pr_err("%pOF: unable to map previous PDC DRV region\n", node);
return -ENXIO;
}
- pdc_x1e_quirk = true;
- }
+ pdc->x1e_quirk = true;
- pdc_base = ioremap(res.start, res_size);
- if (!pdc_base) {
- pr_err("%pOF: unable to map PDC registers\n", node);
- ret = -ENXIO;
- goto fail;
+ if (!qcom_scm_is_available())
+ return -EPROBE_DEFER;
+
+ ret = qcom_scm_io_writel(PDC_GPIO_INT_CTL_ENABLE, PDC_PASS_THROUGH_MODE);
+ if (ret) {
+ pdc->mode = PDC_SECONDARY_MODE;
+ pdc->unmask_gpio = pdc_unmask_gpio_cfg;
+ pdc->clear_gpio = pdc_clear_gpio_cfg;
+ }
}
- pdc_version = pdc_reg_read(PDC_VERSION_REG, 0);
+ irq_param = pdc_reg_read(pdc->regs->irq_param_reg, 0);
+ pdc->num_spis = FIELD_GET(GENMASK(7, 0), irq_param);
parent_domain = irq_find_host(parent);
if (!parent_domain) {
pr_err("%pOF: unable to find PDC's parent domain\n", node);
- ret = -ENXIO;
- goto fail;
+ return -ENXIO;
}
- ret = pdc_setup_pin_mapping(node);
+ raw_spin_lock_init(&pdc->lock);
+
+ ret = pdc_setup_pin_mapping(dev);
if (ret) {
pr_err("%pOF: failed to init PDC pin-hwirq mapping\n", node);
- goto fail;
+ return ret;
}
- pdc_domain = irq_domain_create_hierarchy(parent_domain,
- IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP,
- PDC_MAX_GPIO_IRQS,
- of_fwnode_handle(node),
- &qcom_pdc_ops, NULL);
+ pdc_domain = irq_domain_create_hierarchy(parent_domain, IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP,
+ PDC_MAX_IRQS, of_fwnode_handle(node),
+ &qcom_pdc_ops, NULL);
if (!pdc_domain) {
pr_err("%pOF: PDC domain add failed\n", node);
- ret = -ENOMEM;
- goto fail;
+ return -ENOMEM;
}
irq_domain_update_bus_token(pdc_domain, DOMAIN_BUS_WAKEUP);
return 0;
-
-fail:
- kfree(pdc_region);
- iounmap(pdc_base);
- iounmap(pdc_prev_base);
- return ret;
}
IRQCHIP_PLATFORM_DRIVER_BEGIN(qcom_pdc)