diff options
| -rw-r--r-- | Documentation/devicetree/bindings/interrupt-controller/realtek,rtl-intc.yaml | 5 | ||||
| -rw-r--r-- | drivers/irqchip/Kconfig | 5 | ||||
| -rw-r--r-- | drivers/irqchip/Makefile | 1 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-its-msi-parent.c | 3 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-v3-its.c | 30 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-v5-iwb.c | 2 | ||||
| -rw-r--r-- | drivers/irqchip/irq-imgpdc.c | 495 | ||||
| -rw-r--r-- | drivers/irqchip/irq-qcom-mpm.c | 4 | ||||
| -rw-r--r-- | drivers/irqchip/irq-realtek-rtl.c | 152 | ||||
| -rw-r--r-- | drivers/irqchip/irq-renesas-irqc.c | 1 | ||||
| -rw-r--r-- | drivers/irqchip/irq-renesas-rzt2h.c | 2 | ||||
| -rw-r--r-- | drivers/irqchip/irq-renesas-rzv2h.c | 4 | ||||
| -rw-r--r-- | drivers/irqchip/qcom-pdc.c | 540 |
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) |
