mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	irqchip updates for Linux 5.5
- Qualcomm PDC wakeup interrupt support - Layerscape external IRQ support - Broadcom bcm7038 PM and wakeup support - Ingenic driver cleanup and modernization - GICv3 ITS preparation for GICv4.1 updates - GICv4 fixes - Various cleanups -----BEGIN PGP SIGNATURE----- iQJDBAABCgAtFiEEn9UcU+C1Yxj9lZw9I9DQutE9ekMFAl3VJasPHG1hekBrZXJu ZWwub3JnAAoJECPQ0LrRPXpDMLEP/2U55GLmPuNqW/na4YFmcOYAkoP0WpEKDzn9 u8lBi8CukKl1Z2JLAyP0E1e5iOVS0exvQ5V4OwxGKmeR9oWmB3Lym8UWRw7vcKEF HMKgtPCd3U3J5jW0P4Hr8hn6Q+B55fdMrrAaJcgsfBVB7bRB0lC0LZYGtN4VC4d8 rTQzup5CK8Mu9k4NztLCxxoBUKFoqM+ZKsrRB2eOXB9amcPQtFvwC+5ZL2tDr3wS 7d+pd6G4A+hsloIDUxoH9BrO/jd1jlfHyBRDFJIgpo/IQWVT6ciQECZomRR1pW30 bGFYBf/HPFqfyH+ZOrWprSAd0Yx33WtYaMokYaJ6vGu4wedyxh/1LTRLzL0tWuyZ tPFvEmiiP/Hoeq1JHFRFQUO/75ckqALLeAxCjACCN8+F2Z0armk1W/iwehZNQHHV JdDXegRNUlMipG2kk5D3L6AK28bi+3+axc1ERMN1RO40eLm8NLogWL2TJlxLbyUe lMZMe43ceC0McGnQpAY8qyuC7IycQtngKNBvzG+6ADucGpFez3gYxh39RR43XMVo 37Hsj+Ur7CFBJj6WTCzV2teC/WaXXQkJYxn6fsHNmUgdwPgGD3LppxhlWG49Ao9w x8ZnfyrrYmcFOJrKbT45ExMihioaGf8dyksKZNA/Z4dI0g/kf0LyYi5ujZaDDilI eDkMI/xI =uENR -----END PGP SIGNATURE----- Merge tag 'irqchip-5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/core Pull irqchip updates from Marc Zyngier: - Qualcomm PDC wakeup interrupt support - Layerscape external IRQ support - Broadcom bcm7038 PM and wakeup support - Ingenic driver cleanup and modernization - GICv3 ITS preparation for GICv4.1 updates - GICv4 fixes - Various cleanups
This commit is contained in:
		
						commit
						407e62f52a
					
				
					 24 changed files with 1041 additions and 160 deletions
				
			
		| 
						 | 
					@ -31,6 +31,17 @@ Required properties:
 | 
				
			||||||
- interrupts: specifies the interrupt line(s) in the interrupt-parent controller
 | 
					- interrupts: specifies the interrupt line(s) in the interrupt-parent controller
 | 
				
			||||||
  node; valid values depend on the type of parent interrupt controller
 | 
					  node; valid values depend on the type of parent interrupt controller
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Optional properties:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- brcm,irq-can-wake: If present, this means the L1 controller can be used as a
 | 
				
			||||||
 | 
					  wakeup source for system suspend/resume.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Optional properties:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- brcm,int-fwd-mask: if present, a bit mask to indicate which interrupts
 | 
				
			||||||
 | 
					  have already been configured by the firmware and should be left unmanaged.
 | 
				
			||||||
 | 
					  This should have one 32-bit word per status/set/clear/mask group.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If multiple reg ranges and interrupt-parent entries are present on an SMP
 | 
					If multiple reg ranges and interrupt-parent entries are present on an SMP
 | 
				
			||||||
system, the driver will allow IRQ SMP affinity to be set up through the
 | 
					system, the driver will allow IRQ SMP affinity to be set up through the
 | 
				
			||||||
/proc/irq/ interface.  In the simplest possible configuration, only one
 | 
					/proc/irq/ interface.  In the simplest possible configuration, only one
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,49 @@
 | 
				
			||||||
 | 
					* Freescale Layerscape external IRQs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Some Layerscape SOCs (LS1021A, LS1043A, LS1046A) support inverting
 | 
				
			||||||
 | 
					the polarity of certain external interrupt lines.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The device node must be a child of the node representing the
 | 
				
			||||||
 | 
					Supplemental Configuration Unit (SCFG).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Required properties:
 | 
				
			||||||
 | 
					- compatible: should be "fsl,<soc-name>-extirq", e.g. "fsl,ls1021a-extirq".
 | 
				
			||||||
 | 
					- #interrupt-cells: Must be 2. The first element is the index of the
 | 
				
			||||||
 | 
					  external interrupt line. The second element is the trigger type.
 | 
				
			||||||
 | 
					- #address-cells: Must be 0.
 | 
				
			||||||
 | 
					- interrupt-controller: Identifies the node as an interrupt controller
 | 
				
			||||||
 | 
					- reg: Specifies the Interrupt Polarity Control Register (INTPCR) in
 | 
				
			||||||
 | 
					  the SCFG.
 | 
				
			||||||
 | 
					- interrupt-map: Specifies the mapping from external interrupts to GIC
 | 
				
			||||||
 | 
					  interrupts.
 | 
				
			||||||
 | 
					- interrupt-map-mask: Must be <0xffffffff 0>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
						scfg: scfg@1570000 {
 | 
				
			||||||
 | 
							compatible = "fsl,ls1021a-scfg", "syscon";
 | 
				
			||||||
 | 
							reg = <0x0 0x1570000 0x0 0x10000>;
 | 
				
			||||||
 | 
							big-endian;
 | 
				
			||||||
 | 
							#address-cells = <1>;
 | 
				
			||||||
 | 
							#size-cells = <1>;
 | 
				
			||||||
 | 
							ranges = <0x0 0x0 0x1570000 0x10000>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							extirq: interrupt-controller@1ac {
 | 
				
			||||||
 | 
								compatible = "fsl,ls1021a-extirq";
 | 
				
			||||||
 | 
								#interrupt-cells = <2>;
 | 
				
			||||||
 | 
								#address-cells = <0>;
 | 
				
			||||||
 | 
								interrupt-controller;
 | 
				
			||||||
 | 
								reg = <0x1ac 4>;
 | 
				
			||||||
 | 
								interrupt-map =
 | 
				
			||||||
 | 
									<0 0 &gic GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>,
 | 
				
			||||||
 | 
									<1 0 &gic GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>,
 | 
				
			||||||
 | 
									<2 0 &gic GIC_SPI 165 IRQ_TYPE_LEVEL_HIGH>,
 | 
				
			||||||
 | 
									<3 0 &gic GIC_SPI 167 IRQ_TYPE_LEVEL_HIGH>,
 | 
				
			||||||
 | 
									<4 0 &gic GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>,
 | 
				
			||||||
 | 
									<5 0 &gic GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH>;
 | 
				
			||||||
 | 
								interrupt-map-mask = <0xffffffff 0x0>;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						interrupts-extended = <&gic GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>,
 | 
				
			||||||
 | 
								      <&extirq 1 IRQ_TYPE_LEVEL_LOW>;
 | 
				
			||||||
| 
						 | 
					@ -108,3 +108,15 @@ commonly used:
 | 
				
			||||||
			sensitivity = <7>;
 | 
								sensitivity = <7>;
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3) Interrupt wakeup parent
 | 
				
			||||||
 | 
					--------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Some interrupt controllers in a SoC, are always powered on and have a select
 | 
				
			||||||
 | 
					interrupts routed to them, so that they can wakeup the SoC from suspend. These
 | 
				
			||||||
 | 
					interrupt controllers do not fall into the category of a parent interrupt
 | 
				
			||||||
 | 
					controller and can be specified by the "wakeup-parent" property and contain a
 | 
				
			||||||
 | 
					single phandle referring to the wakeup capable interrupt controller.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Example:
 | 
				
			||||||
 | 
						wakeup-parent = <&pdc_intc>;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,8 @@ Properties:
 | 
				
			||||||
- compatible:
 | 
					- compatible:
 | 
				
			||||||
	Usage: required
 | 
						Usage: required
 | 
				
			||||||
	Value type: <string>
 | 
						Value type: <string>
 | 
				
			||||||
	Definition: Should contain "qcom,<soc>-pdc"
 | 
						Definition: Should contain "qcom,<soc>-pdc" and "qcom,pdc"
 | 
				
			||||||
 | 
							    - "qcom,sc7180-pdc": For SC7180
 | 
				
			||||||
		    - "qcom,sdm845-pdc": For SDM845
 | 
							    - "qcom,sdm845-pdc": For SDM845
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- reg:
 | 
					- reg:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -333,7 +333,7 @@ static inline u64 __gic_readq_nonatomic(const volatile void __iomem *addr)
 | 
				
			||||||
 * GITS_VPENDBASER - the Valid bit must be cleared before changing
 | 
					 * GITS_VPENDBASER - the Valid bit must be cleared before changing
 | 
				
			||||||
 * anything else.
 | 
					 * anything else.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static inline void gits_write_vpendbaser(u64 val, void * __iomem addr)
 | 
					static inline void gits_write_vpendbaser(u64 val, void __iomem *addr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	u32 tmp;
 | 
						u32 tmp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -370,6 +370,10 @@ config MVEBU_PIC
 | 
				
			||||||
config MVEBU_SEI
 | 
					config MVEBU_SEI
 | 
				
			||||||
        bool
 | 
					        bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config LS_EXTIRQ
 | 
				
			||||||
 | 
						def_bool y if SOC_LS1021A || ARCH_LAYERSCAPE
 | 
				
			||||||
 | 
						select MFD_SYSCON
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config LS_SCFG_MSI
 | 
					config LS_SCFG_MSI
 | 
				
			||||||
	def_bool y if SOC_LS1021A || ARCH_LAYERSCAPE
 | 
						def_bool y if SOC_LS1021A || ARCH_LAYERSCAPE
 | 
				
			||||||
	depends on PCI && PCI_MSI
 | 
						depends on PCI && PCI_MSI
 | 
				
			||||||
| 
						 | 
					@ -483,8 +487,6 @@ config TI_SCI_INTA_IRQCHIP
 | 
				
			||||||
	  If you wish to use interrupt aggregator irq resources managed by the
 | 
						  If you wish to use interrupt aggregator irq resources managed by the
 | 
				
			||||||
	  TI System Controller, say Y here. Otherwise, say N.
 | 
						  TI System Controller, say Y here. Otherwise, say N.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
endmenu
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
config SIFIVE_PLIC
 | 
					config SIFIVE_PLIC
 | 
				
			||||||
	bool "SiFive Platform-Level Interrupt Controller"
 | 
						bool "SiFive Platform-Level Interrupt Controller"
 | 
				
			||||||
	depends on RISCV
 | 
						depends on RISCV
 | 
				
			||||||
| 
						 | 
					@ -496,3 +498,5 @@ config SIFIVE_PLIC
 | 
				
			||||||
	   interrupt sources are subordinate to the PLIC.
 | 
						   interrupt sources are subordinate to the PLIC.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	   If you don't know what to do here, say Y.
 | 
						   If you don't know what to do here, say Y.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					endmenu
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,6 +84,7 @@ obj-$(CONFIG_MVEBU_ICU)			+= irq-mvebu-icu.o
 | 
				
			||||||
obj-$(CONFIG_MVEBU_ODMI)		+= irq-mvebu-odmi.o
 | 
					obj-$(CONFIG_MVEBU_ODMI)		+= irq-mvebu-odmi.o
 | 
				
			||||||
obj-$(CONFIG_MVEBU_PIC)			+= irq-mvebu-pic.o
 | 
					obj-$(CONFIG_MVEBU_PIC)			+= irq-mvebu-pic.o
 | 
				
			||||||
obj-$(CONFIG_MVEBU_SEI)			+= irq-mvebu-sei.o
 | 
					obj-$(CONFIG_MVEBU_SEI)			+= irq-mvebu-sei.o
 | 
				
			||||||
 | 
					obj-$(CONFIG_LS_EXTIRQ)			+= irq-ls-extirq.o
 | 
				
			||||||
obj-$(CONFIG_LS_SCFG_MSI)		+= irq-ls-scfg-msi.o
 | 
					obj-$(CONFIG_LS_SCFG_MSI)		+= irq-ls-scfg-msi.o
 | 
				
			||||||
obj-$(CONFIG_EZNPS_GIC)			+= irq-eznps.o
 | 
					obj-$(CONFIG_EZNPS_GIC)			+= irq-eznps.o
 | 
				
			||||||
obj-$(CONFIG_ARCH_ASPEED)		+= irq-aspeed-vic.o irq-aspeed-i2c-ic.o
 | 
					obj-$(CONFIG_ARCH_ASPEED)		+= irq-aspeed-vic.o irq-aspeed-i2c-ic.o
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,7 @@
 | 
				
			||||||
#include <linux/types.h>
 | 
					#include <linux/types.h>
 | 
				
			||||||
#include <linux/irqchip.h>
 | 
					#include <linux/irqchip.h>
 | 
				
			||||||
#include <linux/irqchip/chained_irq.h>
 | 
					#include <linux/irqchip/chained_irq.h>
 | 
				
			||||||
 | 
					#include <linux/syscore_ops.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define IRQS_PER_WORD		32
 | 
					#define IRQS_PER_WORD		32
 | 
				
			||||||
#define REG_BYTES_PER_IRQ_WORD	(sizeof(u32) * 4)
 | 
					#define REG_BYTES_PER_IRQ_WORD	(sizeof(u32) * 4)
 | 
				
			||||||
| 
						 | 
					@ -39,6 +40,11 @@ struct bcm7038_l1_chip {
 | 
				
			||||||
	unsigned int		n_words;
 | 
						unsigned int		n_words;
 | 
				
			||||||
	struct irq_domain	*domain;
 | 
						struct irq_domain	*domain;
 | 
				
			||||||
	struct bcm7038_l1_cpu	*cpus[NR_CPUS];
 | 
						struct bcm7038_l1_cpu	*cpus[NR_CPUS];
 | 
				
			||||||
 | 
					#ifdef CONFIG_PM_SLEEP
 | 
				
			||||||
 | 
						struct list_head	list;
 | 
				
			||||||
 | 
						u32			wake_mask[MAX_WORDS];
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						u32			irq_fwd_mask[MAX_WORDS];
 | 
				
			||||||
	u8			affinity[MAX_WORDS * IRQS_PER_WORD];
 | 
						u8			affinity[MAX_WORDS * IRQS_PER_WORD];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -249,6 +255,7 @@ static int __init bcm7038_l1_init_one(struct device_node *dn,
 | 
				
			||||||
	resource_size_t sz;
 | 
						resource_size_t sz;
 | 
				
			||||||
	struct bcm7038_l1_cpu *cpu;
 | 
						struct bcm7038_l1_cpu *cpu;
 | 
				
			||||||
	unsigned int i, n_words, parent_irq;
 | 
						unsigned int i, n_words, parent_irq;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (of_address_to_resource(dn, idx, &res))
 | 
						if (of_address_to_resource(dn, idx, &res))
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
| 
						 | 
					@ -262,6 +269,14 @@ static int __init bcm7038_l1_init_one(struct device_node *dn,
 | 
				
			||||||
	else if (intc->n_words != n_words)
 | 
						else if (intc->n_words != n_words)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = of_property_read_u32_array(dn , "brcm,int-fwd-mask",
 | 
				
			||||||
 | 
										 intc->irq_fwd_mask, n_words);
 | 
				
			||||||
 | 
						if (ret != 0 && ret != -EINVAL) {
 | 
				
			||||||
 | 
							/* property exists but has the wrong number of words */
 | 
				
			||||||
 | 
							pr_err("invalid brcm,int-fwd-mask property\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cpu = intc->cpus[idx] = kzalloc(sizeof(*cpu) + n_words * sizeof(u32),
 | 
						cpu = intc->cpus[idx] = kzalloc(sizeof(*cpu) + n_words * sizeof(u32),
 | 
				
			||||||
					GFP_KERNEL);
 | 
										GFP_KERNEL);
 | 
				
			||||||
	if (!cpu)
 | 
						if (!cpu)
 | 
				
			||||||
| 
						 | 
					@ -272,8 +287,11 @@ static int __init bcm7038_l1_init_one(struct device_node *dn,
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < n_words; i++) {
 | 
						for (i = 0; i < n_words; i++) {
 | 
				
			||||||
		l1_writel(0xffffffff, cpu->map_base + reg_mask_set(intc, i));
 | 
							l1_writel(~intc->irq_fwd_mask[i],
 | 
				
			||||||
		cpu->mask_cache[i] = 0xffffffff;
 | 
								  cpu->map_base + reg_mask_set(intc, i));
 | 
				
			||||||
 | 
							l1_writel(intc->irq_fwd_mask[i],
 | 
				
			||||||
 | 
								  cpu->map_base + reg_mask_clr(intc, i));
 | 
				
			||||||
 | 
							cpu->mask_cache[i] = ~intc->irq_fwd_mask[i];
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	parent_irq = irq_of_parse_and_map(dn, idx);
 | 
						parent_irq = irq_of_parse_and_map(dn, idx);
 | 
				
			||||||
| 
						 | 
					@ -281,12 +299,89 @@ static int __init bcm7038_l1_init_one(struct device_node *dn,
 | 
				
			||||||
		pr_err("failed to map parent interrupt %d\n", parent_irq);
 | 
							pr_err("failed to map parent interrupt %d\n", parent_irq);
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (of_property_read_bool(dn, "brcm,irq-can-wake"))
 | 
				
			||||||
 | 
							enable_irq_wake(parent_irq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	irq_set_chained_handler_and_data(parent_irq, bcm7038_l1_irq_handle,
 | 
						irq_set_chained_handler_and_data(parent_irq, bcm7038_l1_irq_handle,
 | 
				
			||||||
					 intc);
 | 
										 intc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_PM_SLEEP
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * We keep a list of bcm7038_l1_chip used for suspend/resume. This hack is
 | 
				
			||||||
 | 
					 * used because the struct chip_type suspend/resume hooks are not called
 | 
				
			||||||
 | 
					 * unless chip_type is hooked onto a generic_chip. Since this driver does
 | 
				
			||||||
 | 
					 * not use generic_chip, we need to manually hook our resume/suspend to
 | 
				
			||||||
 | 
					 * syscore_ops.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static LIST_HEAD(bcm7038_l1_intcs_list);
 | 
				
			||||||
 | 
					static DEFINE_RAW_SPINLOCK(bcm7038_l1_intcs_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int bcm7038_l1_suspend(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct bcm7038_l1_chip *intc;
 | 
				
			||||||
 | 
						int boot_cpu, word;
 | 
				
			||||||
 | 
						u32 val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Wakeup interrupt should only come from the boot cpu */
 | 
				
			||||||
 | 
						boot_cpu = cpu_logical_map(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						list_for_each_entry(intc, &bcm7038_l1_intcs_list, list) {
 | 
				
			||||||
 | 
							for (word = 0; word < intc->n_words; word++) {
 | 
				
			||||||
 | 
								val = intc->wake_mask[word] | intc->irq_fwd_mask[word];
 | 
				
			||||||
 | 
								l1_writel(~val,
 | 
				
			||||||
 | 
									intc->cpus[boot_cpu]->map_base + reg_mask_set(intc, word));
 | 
				
			||||||
 | 
								l1_writel(val,
 | 
				
			||||||
 | 
									intc->cpus[boot_cpu]->map_base + reg_mask_clr(intc, word));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bcm7038_l1_resume(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct bcm7038_l1_chip *intc;
 | 
				
			||||||
 | 
						int boot_cpu, word;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						boot_cpu = cpu_logical_map(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						list_for_each_entry(intc, &bcm7038_l1_intcs_list, list) {
 | 
				
			||||||
 | 
							for (word = 0; word < intc->n_words; word++) {
 | 
				
			||||||
 | 
								l1_writel(intc->cpus[boot_cpu]->mask_cache[word],
 | 
				
			||||||
 | 
									intc->cpus[boot_cpu]->map_base + reg_mask_set(intc, word));
 | 
				
			||||||
 | 
								l1_writel(~intc->cpus[boot_cpu]->mask_cache[word],
 | 
				
			||||||
 | 
									intc->cpus[boot_cpu]->map_base + reg_mask_clr(intc, word));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct syscore_ops bcm7038_l1_syscore_ops = {
 | 
				
			||||||
 | 
						.suspend	= bcm7038_l1_suspend,
 | 
				
			||||||
 | 
						.resume		= bcm7038_l1_resume,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int bcm7038_l1_set_wake(struct irq_data *d, unsigned int on)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d);
 | 
				
			||||||
 | 
						unsigned long flags;
 | 
				
			||||||
 | 
						u32 word = d->hwirq / IRQS_PER_WORD;
 | 
				
			||||||
 | 
						u32 mask = BIT(d->hwirq % IRQS_PER_WORD);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						raw_spin_lock_irqsave(&intc->lock, flags);
 | 
				
			||||||
 | 
						if (on)
 | 
				
			||||||
 | 
							intc->wake_mask[word] |= mask;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							intc->wake_mask[word] &= ~mask;
 | 
				
			||||||
 | 
						raw_spin_unlock_irqrestore(&intc->lock, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct irq_chip bcm7038_l1_irq_chip = {
 | 
					static struct irq_chip bcm7038_l1_irq_chip = {
 | 
				
			||||||
	.name			= "bcm7038-l1",
 | 
						.name			= "bcm7038-l1",
 | 
				
			||||||
	.irq_mask		= bcm7038_l1_mask,
 | 
						.irq_mask		= bcm7038_l1_mask,
 | 
				
			||||||
| 
						 | 
					@ -295,11 +390,21 @@ static struct irq_chip bcm7038_l1_irq_chip = {
 | 
				
			||||||
#ifdef CONFIG_SMP
 | 
					#ifdef CONFIG_SMP
 | 
				
			||||||
	.irq_cpu_offline	= bcm7038_l1_cpu_offline,
 | 
						.irq_cpu_offline	= bcm7038_l1_cpu_offline,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef CONFIG_PM_SLEEP
 | 
				
			||||||
 | 
						.irq_set_wake		= bcm7038_l1_set_wake,
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int bcm7038_l1_map(struct irq_domain *d, unsigned int virq,
 | 
					static int bcm7038_l1_map(struct irq_domain *d, unsigned int virq,
 | 
				
			||||||
			  irq_hw_number_t hw_irq)
 | 
								  irq_hw_number_t hw_irq)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct bcm7038_l1_chip *intc = d->host_data;
 | 
				
			||||||
 | 
						u32 mask = BIT(hw_irq % IRQS_PER_WORD);
 | 
				
			||||||
 | 
						u32 word = hw_irq / IRQS_PER_WORD;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (intc->irq_fwd_mask[word] & mask)
 | 
				
			||||||
 | 
							return -EPERM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	irq_set_chip_and_handler(virq, &bcm7038_l1_irq_chip, handle_level_irq);
 | 
						irq_set_chip_and_handler(virq, &bcm7038_l1_irq_chip, handle_level_irq);
 | 
				
			||||||
	irq_set_chip_data(virq, d->host_data);
 | 
						irq_set_chip_data(virq, d->host_data);
 | 
				
			||||||
	irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq)));
 | 
						irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq)));
 | 
				
			||||||
| 
						 | 
					@ -340,6 +445,16 @@ int __init bcm7038_l1_of_init(struct device_node *dn,
 | 
				
			||||||
		goto out_unmap;
 | 
							goto out_unmap;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_PM_SLEEP
 | 
				
			||||||
 | 
						/* Add bcm7038_l1_chip into a list */
 | 
				
			||||||
 | 
						raw_spin_lock(&bcm7038_l1_intcs_lock);
 | 
				
			||||||
 | 
						list_add_tail(&intc->list, &bcm7038_l1_intcs_list);
 | 
				
			||||||
 | 
						raw_spin_unlock(&bcm7038_l1_intcs_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (list_is_singular(&bcm7038_l1_intcs_list))
 | 
				
			||||||
 | 
							register_syscore_ops(&bcm7038_l1_syscore_ops);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pr_info("registered BCM7038 L1 intc (%pOF, IRQs: %d)\n",
 | 
						pr_info("registered BCM7038 L1 intc (%pOF, IRQs: %d)\n",
 | 
				
			||||||
		dn, IRQS_PER_WORD * intc->n_words);
 | 
							dn, IRQS_PER_WORD * intc->n_words);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/acpi.h>
 | 
					#include <linux/acpi.h>
 | 
				
			||||||
#include <linux/acpi_iort.h>
 | 
					#include <linux/acpi_iort.h>
 | 
				
			||||||
 | 
					#include <linux/bitfield.h>
 | 
				
			||||||
#include <linux/bitmap.h>
 | 
					#include <linux/bitmap.h>
 | 
				
			||||||
#include <linux/cpu.h>
 | 
					#include <linux/cpu.h>
 | 
				
			||||||
#include <linux/crash_dump.h>
 | 
					#include <linux/crash_dump.h>
 | 
				
			||||||
| 
						 | 
					@ -102,20 +103,21 @@ struct its_node {
 | 
				
			||||||
	struct its_collection	*collections;
 | 
						struct its_collection	*collections;
 | 
				
			||||||
	struct fwnode_handle	*fwnode_handle;
 | 
						struct fwnode_handle	*fwnode_handle;
 | 
				
			||||||
	u64			(*get_msi_base)(struct its_device *its_dev);
 | 
						u64			(*get_msi_base)(struct its_device *its_dev);
 | 
				
			||||||
 | 
						u64			typer;
 | 
				
			||||||
	u64			cbaser_save;
 | 
						u64			cbaser_save;
 | 
				
			||||||
	u32			ctlr_save;
 | 
						u32			ctlr_save;
 | 
				
			||||||
	struct list_head	its_device_list;
 | 
						struct list_head	its_device_list;
 | 
				
			||||||
	u64			flags;
 | 
						u64			flags;
 | 
				
			||||||
	unsigned long		list_nr;
 | 
						unsigned long		list_nr;
 | 
				
			||||||
	u32			ite_size;
 | 
					 | 
				
			||||||
	u32			device_ids;
 | 
					 | 
				
			||||||
	int			numa_node;
 | 
						int			numa_node;
 | 
				
			||||||
	unsigned int		msi_domain_flags;
 | 
						unsigned int		msi_domain_flags;
 | 
				
			||||||
	u32			pre_its_base; /* for Socionext Synquacer */
 | 
						u32			pre_its_base; /* for Socionext Synquacer */
 | 
				
			||||||
	bool			is_v4;
 | 
					 | 
				
			||||||
	int			vlpi_redist_offset;
 | 
						int			vlpi_redist_offset;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define is_v4(its)		(!!((its)->typer & GITS_TYPER_VLPIS))
 | 
				
			||||||
 | 
					#define device_ids(its)		(FIELD_GET(GITS_TYPER_DEVBITS, (its)->typer) + 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define ITS_ITT_ALIGN		SZ_256
 | 
					#define ITS_ITT_ALIGN		SZ_256
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* The maximum number of VPEID bits supported by VLPI commands */
 | 
					/* The maximum number of VPEID bits supported by VLPI commands */
 | 
				
			||||||
| 
						 | 
					@ -130,7 +132,7 @@ struct event_lpi_map {
 | 
				
			||||||
	u16			*col_map;
 | 
						u16			*col_map;
 | 
				
			||||||
	irq_hw_number_t		lpi_base;
 | 
						irq_hw_number_t		lpi_base;
 | 
				
			||||||
	int			nr_lpis;
 | 
						int			nr_lpis;
 | 
				
			||||||
	struct mutex		vlpi_lock;
 | 
						raw_spinlock_t		vlpi_lock;
 | 
				
			||||||
	struct its_vm		*vm;
 | 
						struct its_vm		*vm;
 | 
				
			||||||
	struct its_vlpi_map	*vlpi_maps;
 | 
						struct its_vlpi_map	*vlpi_maps;
 | 
				
			||||||
	int			nr_vlpis;
 | 
						int			nr_vlpis;
 | 
				
			||||||
| 
						 | 
					@ -181,7 +183,7 @@ static u16 get_its_list(struct its_vm *vm)
 | 
				
			||||||
	unsigned long its_list = 0;
 | 
						unsigned long its_list = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry(its, &its_nodes, entry) {
 | 
						list_for_each_entry(its, &its_nodes, entry) {
 | 
				
			||||||
		if (!its->is_v4)
 | 
							if (!is_v4(its))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (vm->vlpi_count[its->list_nr])
 | 
							if (vm->vlpi_count[its->list_nr])
 | 
				
			||||||
| 
						 | 
					@ -191,6 +193,12 @@ static u16 get_its_list(struct its_vm *vm)
 | 
				
			||||||
	return (u16)its_list;
 | 
						return (u16)its_list;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline u32 its_get_event_id(struct irq_data *d)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 | 
				
			||||||
 | 
						return d->hwirq - its_dev->event_map.lpi_base;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct its_collection *dev_event_to_col(struct its_device *its_dev,
 | 
					static struct its_collection *dev_event_to_col(struct its_device *its_dev,
 | 
				
			||||||
					       u32 event)
 | 
										       u32 event)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -199,6 +207,22 @@ static struct its_collection *dev_event_to_col(struct its_device *its_dev,
 | 
				
			||||||
	return its->collections + its_dev->event_map.col_map[event];
 | 
						return its->collections + its_dev->event_map.col_map[event];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct its_vlpi_map *dev_event_to_vlpi_map(struct its_device *its_dev,
 | 
				
			||||||
 | 
										       u32 event)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (WARN_ON_ONCE(event >= its_dev->event_map.nr_lpis))
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &its_dev->event_map.vlpi_maps[event];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct its_collection *irq_to_col(struct irq_data *d)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return dev_event_to_col(its_dev, its_get_event_id(d));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct its_collection *valid_col(struct its_collection *col)
 | 
					static struct its_collection *valid_col(struct its_collection *col)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (WARN_ON_ONCE(col->target_address & GENMASK_ULL(15, 0)))
 | 
						if (WARN_ON_ONCE(col->target_address & GENMASK_ULL(15, 0)))
 | 
				
			||||||
| 
						 | 
					@ -305,7 +329,10 @@ struct its_cmd_desc {
 | 
				
			||||||
 * The ITS command block, which is what the ITS actually parses.
 | 
					 * The ITS command block, which is what the ITS actually parses.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct its_cmd_block {
 | 
					struct its_cmd_block {
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
		u64	raw_cmd[4];
 | 
							u64	raw_cmd[4];
 | 
				
			||||||
 | 
							__le64	raw_cmd_le[4];
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define ITS_CMD_QUEUE_SZ		SZ_64K
 | 
					#define ITS_CMD_QUEUE_SZ		SZ_64K
 | 
				
			||||||
| 
						 | 
					@ -414,10 +441,10 @@ static void its_encode_vpt_size(struct its_cmd_block *cmd, u8 vpt_size)
 | 
				
			||||||
static inline void its_fixup_cmd(struct its_cmd_block *cmd)
 | 
					static inline void its_fixup_cmd(struct its_cmd_block *cmd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/* Let's fixup BE commands */
 | 
						/* Let's fixup BE commands */
 | 
				
			||||||
	cmd->raw_cmd[0] = cpu_to_le64(cmd->raw_cmd[0]);
 | 
						cmd->raw_cmd_le[0] = cpu_to_le64(cmd->raw_cmd[0]);
 | 
				
			||||||
	cmd->raw_cmd[1] = cpu_to_le64(cmd->raw_cmd[1]);
 | 
						cmd->raw_cmd_le[1] = cpu_to_le64(cmd->raw_cmd[1]);
 | 
				
			||||||
	cmd->raw_cmd[2] = cpu_to_le64(cmd->raw_cmd[2]);
 | 
						cmd->raw_cmd_le[2] = cpu_to_le64(cmd->raw_cmd[2]);
 | 
				
			||||||
	cmd->raw_cmd[3] = cpu_to_le64(cmd->raw_cmd[3]);
 | 
						cmd->raw_cmd_le[3] = cpu_to_le64(cmd->raw_cmd[3]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct its_collection *its_build_mapd_cmd(struct its_node *its,
 | 
					static struct its_collection *its_build_mapd_cmd(struct its_node *its,
 | 
				
			||||||
| 
						 | 
					@ -676,6 +703,60 @@ static struct its_vpe *its_build_vmovp_cmd(struct its_node *its,
 | 
				
			||||||
	return valid_vpe(its, desc->its_vmovp_cmd.vpe);
 | 
						return valid_vpe(its, desc->its_vmovp_cmd.vpe);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct its_vpe *its_build_vinv_cmd(struct its_node *its,
 | 
				
			||||||
 | 
										  struct its_cmd_block *cmd,
 | 
				
			||||||
 | 
										  struct its_cmd_desc *desc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct its_vlpi_map *map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						map = dev_event_to_vlpi_map(desc->its_inv_cmd.dev,
 | 
				
			||||||
 | 
									    desc->its_inv_cmd.event_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						its_encode_cmd(cmd, GITS_CMD_INV);
 | 
				
			||||||
 | 
						its_encode_devid(cmd, desc->its_inv_cmd.dev->device_id);
 | 
				
			||||||
 | 
						its_encode_event_id(cmd, desc->its_inv_cmd.event_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						its_fixup_cmd(cmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return valid_vpe(its, map->vpe);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct its_vpe *its_build_vint_cmd(struct its_node *its,
 | 
				
			||||||
 | 
										  struct its_cmd_block *cmd,
 | 
				
			||||||
 | 
										  struct its_cmd_desc *desc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct its_vlpi_map *map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						map = dev_event_to_vlpi_map(desc->its_int_cmd.dev,
 | 
				
			||||||
 | 
									    desc->its_int_cmd.event_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						its_encode_cmd(cmd, GITS_CMD_INT);
 | 
				
			||||||
 | 
						its_encode_devid(cmd, desc->its_int_cmd.dev->device_id);
 | 
				
			||||||
 | 
						its_encode_event_id(cmd, desc->its_int_cmd.event_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						its_fixup_cmd(cmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return valid_vpe(its, map->vpe);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct its_vpe *its_build_vclear_cmd(struct its_node *its,
 | 
				
			||||||
 | 
										    struct its_cmd_block *cmd,
 | 
				
			||||||
 | 
										    struct its_cmd_desc *desc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct its_vlpi_map *map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						map = dev_event_to_vlpi_map(desc->its_clear_cmd.dev,
 | 
				
			||||||
 | 
									    desc->its_clear_cmd.event_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						its_encode_cmd(cmd, GITS_CMD_CLEAR);
 | 
				
			||||||
 | 
						its_encode_devid(cmd, desc->its_clear_cmd.dev->device_id);
 | 
				
			||||||
 | 
						its_encode_event_id(cmd, desc->its_clear_cmd.event_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						its_fixup_cmd(cmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return valid_vpe(its, map->vpe);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static u64 its_cmd_ptr_to_offset(struct its_node *its,
 | 
					static u64 its_cmd_ptr_to_offset(struct its_node *its,
 | 
				
			||||||
				 struct its_cmd_block *ptr)
 | 
									 struct its_cmd_block *ptr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -953,7 +1034,7 @@ static void its_send_invall(struct its_node *its, struct its_collection *col)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void its_send_vmapti(struct its_device *dev, u32 id)
 | 
					static void its_send_vmapti(struct its_device *dev, u32 id)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct its_vlpi_map *map = &dev->event_map.vlpi_maps[id];
 | 
						struct its_vlpi_map *map = dev_event_to_vlpi_map(dev, id);
 | 
				
			||||||
	struct its_cmd_desc desc;
 | 
						struct its_cmd_desc desc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	desc.its_vmapti_cmd.vpe = map->vpe;
 | 
						desc.its_vmapti_cmd.vpe = map->vpe;
 | 
				
			||||||
| 
						 | 
					@ -967,7 +1048,7 @@ static void its_send_vmapti(struct its_device *dev, u32 id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void its_send_vmovi(struct its_device *dev, u32 id)
 | 
					static void its_send_vmovi(struct its_device *dev, u32 id)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct its_vlpi_map *map = &dev->event_map.vlpi_maps[id];
 | 
						struct its_vlpi_map *map = dev_event_to_vlpi_map(dev, id);
 | 
				
			||||||
	struct its_cmd_desc desc;
 | 
						struct its_cmd_desc desc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	desc.its_vmovi_cmd.vpe = map->vpe;
 | 
						desc.its_vmovi_cmd.vpe = map->vpe;
 | 
				
			||||||
| 
						 | 
					@ -1021,7 +1102,7 @@ static void its_send_vmovp(struct its_vpe *vpe)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Emit VMOVPs */
 | 
						/* Emit VMOVPs */
 | 
				
			||||||
	list_for_each_entry(its, &its_nodes, entry) {
 | 
						list_for_each_entry(its, &its_nodes, entry) {
 | 
				
			||||||
		if (!its->is_v4)
 | 
							if (!is_v4(its))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!vpe->its_vm->vlpi_count[its->list_nr])
 | 
							if (!vpe->its_vm->vlpi_count[its->list_nr])
 | 
				
			||||||
| 
						 | 
					@ -1042,29 +1123,71 @@ static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe)
 | 
				
			||||||
	its_send_single_vcommand(its, its_build_vinvall_cmd, &desc);
 | 
						its_send_single_vcommand(its, its_build_vinvall_cmd, &desc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void its_send_vinv(struct its_device *dev, u32 event_id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct its_cmd_desc desc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * There is no real VINV command. This is just a normal INV,
 | 
				
			||||||
 | 
						 * with a VSYNC instead of a SYNC.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						desc.its_inv_cmd.dev = dev;
 | 
				
			||||||
 | 
						desc.its_inv_cmd.event_id = event_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						its_send_single_vcommand(dev->its, its_build_vinv_cmd, &desc);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void its_send_vint(struct its_device *dev, u32 event_id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct its_cmd_desc desc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * There is no real VINT command. This is just a normal INT,
 | 
				
			||||||
 | 
						 * with a VSYNC instead of a SYNC.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						desc.its_int_cmd.dev = dev;
 | 
				
			||||||
 | 
						desc.its_int_cmd.event_id = event_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						its_send_single_vcommand(dev->its, its_build_vint_cmd, &desc);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void its_send_vclear(struct its_device *dev, u32 event_id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct its_cmd_desc desc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * There is no real VCLEAR command. This is just a normal CLEAR,
 | 
				
			||||||
 | 
						 * with a VSYNC instead of a SYNC.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						desc.its_clear_cmd.dev = dev;
 | 
				
			||||||
 | 
						desc.its_clear_cmd.event_id = event_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						its_send_single_vcommand(dev->its, its_build_vclear_cmd, &desc);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * irqchip functions - assumes MSI, mostly.
 | 
					 * irqchip functions - assumes MSI, mostly.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					static struct its_vlpi_map *get_vlpi_map(struct irq_data *d)
 | 
				
			||||||
static inline u32 its_get_event_id(struct irq_data *d)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 | 
						struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 | 
				
			||||||
	return d->hwirq - its_dev->event_map.lpi_base;
 | 
						u32 event = its_get_event_id(d);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!irqd_is_forwarded_to_vcpu(d))
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return dev_event_to_vlpi_map(its_dev, event);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void lpi_write_config(struct irq_data *d, u8 clr, u8 set)
 | 
					static void lpi_write_config(struct irq_data *d, u8 clr, u8 set)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct its_vlpi_map *map = get_vlpi_map(d);
 | 
				
			||||||
	irq_hw_number_t hwirq;
 | 
						irq_hw_number_t hwirq;
 | 
				
			||||||
	void *va;
 | 
						void *va;
 | 
				
			||||||
	u8 *cfg;
 | 
						u8 *cfg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (irqd_is_forwarded_to_vcpu(d)) {
 | 
						if (map) {
 | 
				
			||||||
		struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 | 
							va = page_address(map->vm->vprop_page);
 | 
				
			||||||
		u32 event = its_get_event_id(d);
 | 
					 | 
				
			||||||
		struct its_vlpi_map *map;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		va = page_address(its_dev->event_map.vm->vprop_page);
 | 
					 | 
				
			||||||
		map = &its_dev->event_map.vlpi_maps[event];
 | 
					 | 
				
			||||||
		hwirq = map->vintid;
 | 
							hwirq = map->vintid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Remember the updated property */
 | 
							/* Remember the updated property */
 | 
				
			||||||
| 
						 | 
					@ -1090,23 +1213,50 @@ static void lpi_write_config(struct irq_data *d, u8 clr, u8 set)
 | 
				
			||||||
		dsb(ishst);
 | 
							dsb(ishst);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void wait_for_syncr(void __iomem *rdbase)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						while (gic_read_lpir(rdbase + GICR_SYNCR) & 1)
 | 
				
			||||||
 | 
							cpu_relax();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void direct_lpi_inv(struct irq_data *d)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct its_collection *col;
 | 
				
			||||||
 | 
						void __iomem *rdbase;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Target the redistributor this LPI is currently routed to */
 | 
				
			||||||
 | 
						col = irq_to_col(d);
 | 
				
			||||||
 | 
						rdbase = per_cpu_ptr(gic_rdists->rdist, col->col_id)->rd_base;
 | 
				
			||||||
 | 
						gic_write_lpir(d->hwirq, rdbase + GICR_INVLPIR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wait_for_syncr(rdbase);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void lpi_update_config(struct irq_data *d, u8 clr, u8 set)
 | 
					static void lpi_update_config(struct irq_data *d, u8 clr, u8 set)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 | 
						struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	lpi_write_config(d, clr, set);
 | 
						lpi_write_config(d, clr, set);
 | 
				
			||||||
 | 
						if (gic_rdists->has_direct_lpi && !irqd_is_forwarded_to_vcpu(d))
 | 
				
			||||||
 | 
							direct_lpi_inv(d);
 | 
				
			||||||
 | 
						else if (!irqd_is_forwarded_to_vcpu(d))
 | 
				
			||||||
		its_send_inv(its_dev, its_get_event_id(d));
 | 
							its_send_inv(its_dev, its_get_event_id(d));
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							its_send_vinv(its_dev, its_get_event_id(d));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void its_vlpi_set_doorbell(struct irq_data *d, bool enable)
 | 
					static void its_vlpi_set_doorbell(struct irq_data *d, bool enable)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 | 
						struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 | 
				
			||||||
	u32 event = its_get_event_id(d);
 | 
						u32 event = its_get_event_id(d);
 | 
				
			||||||
 | 
						struct its_vlpi_map *map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (its_dev->event_map.vlpi_maps[event].db_enabled == enable)
 | 
						map = dev_event_to_vlpi_map(its_dev, event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (map->db_enabled == enable)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	its_dev->event_map.vlpi_maps[event].db_enabled = enable;
 | 
						map->db_enabled = enable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * More fun with the architecture:
 | 
						 * More fun with the architecture:
 | 
				
			||||||
| 
						 | 
					@ -1208,10 +1358,17 @@ static int its_irq_set_irqchip_state(struct irq_data *d,
 | 
				
			||||||
	if (which != IRQCHIP_STATE_PENDING)
 | 
						if (which != IRQCHIP_STATE_PENDING)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (irqd_is_forwarded_to_vcpu(d)) {
 | 
				
			||||||
 | 
							if (state)
 | 
				
			||||||
 | 
								its_send_vint(its_dev, event);
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								its_send_vclear(its_dev, event);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
		if (state)
 | 
							if (state)
 | 
				
			||||||
			its_send_int(its_dev, event);
 | 
								its_send_int(its_dev, event);
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			its_send_clear(its_dev, event);
 | 
								its_send_clear(its_dev, event);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1279,13 +1436,13 @@ static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info)
 | 
				
			||||||
	if (!info->map)
 | 
						if (!info->map)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_lock(&its_dev->event_map.vlpi_lock);
 | 
						raw_spin_lock(&its_dev->event_map.vlpi_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!its_dev->event_map.vm) {
 | 
						if (!its_dev->event_map.vm) {
 | 
				
			||||||
		struct its_vlpi_map *maps;
 | 
							struct its_vlpi_map *maps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		maps = kcalloc(its_dev->event_map.nr_lpis, sizeof(*maps),
 | 
							maps = kcalloc(its_dev->event_map.nr_lpis, sizeof(*maps),
 | 
				
			||||||
			       GFP_KERNEL);
 | 
								       GFP_ATOMIC);
 | 
				
			||||||
		if (!maps) {
 | 
							if (!maps) {
 | 
				
			||||||
			ret = -ENOMEM;
 | 
								ret = -ENOMEM;
 | 
				
			||||||
			goto out;
 | 
								goto out;
 | 
				
			||||||
| 
						 | 
					@ -1328,29 +1485,30 @@ static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	mutex_unlock(&its_dev->event_map.vlpi_lock);
 | 
						raw_spin_unlock(&its_dev->event_map.vlpi_lock);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int its_vlpi_get(struct irq_data *d, struct its_cmd_info *info)
 | 
					static int its_vlpi_get(struct irq_data *d, struct its_cmd_info *info)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 | 
						struct its_device *its_dev = irq_data_get_irq_chip_data(d);
 | 
				
			||||||
	u32 event = its_get_event_id(d);
 | 
						struct its_vlpi_map *map;
 | 
				
			||||||
	int ret = 0;
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_lock(&its_dev->event_map.vlpi_lock);
 | 
						raw_spin_lock(&its_dev->event_map.vlpi_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!its_dev->event_map.vm ||
 | 
						map = get_vlpi_map(d);
 | 
				
			||||||
	    !its_dev->event_map.vlpi_maps[event].vm) {
 | 
					
 | 
				
			||||||
 | 
						if (!its_dev->event_map.vm || !map) {
 | 
				
			||||||
		ret = -EINVAL;
 | 
							ret = -EINVAL;
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Copy our mapping information to the incoming request */
 | 
						/* Copy our mapping information to the incoming request */
 | 
				
			||||||
	*info->map = its_dev->event_map.vlpi_maps[event];
 | 
						*info->map = *map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	mutex_unlock(&its_dev->event_map.vlpi_lock);
 | 
						raw_spin_unlock(&its_dev->event_map.vlpi_lock);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1360,7 +1518,7 @@ static int its_vlpi_unmap(struct irq_data *d)
 | 
				
			||||||
	u32 event = its_get_event_id(d);
 | 
						u32 event = its_get_event_id(d);
 | 
				
			||||||
	int ret = 0;
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_lock(&its_dev->event_map.vlpi_lock);
 | 
						raw_spin_lock(&its_dev->event_map.vlpi_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!its_dev->event_map.vm || !irqd_is_forwarded_to_vcpu(d)) {
 | 
						if (!its_dev->event_map.vm || !irqd_is_forwarded_to_vcpu(d)) {
 | 
				
			||||||
		ret = -EINVAL;
 | 
							ret = -EINVAL;
 | 
				
			||||||
| 
						 | 
					@ -1390,7 +1548,7 @@ static int its_vlpi_unmap(struct irq_data *d)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	mutex_unlock(&its_dev->event_map.vlpi_lock);
 | 
						raw_spin_unlock(&its_dev->event_map.vlpi_lock);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1416,7 +1574,7 @@ static int its_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
 | 
				
			||||||
	struct its_cmd_info *info = vcpu_info;
 | 
						struct its_cmd_info *info = vcpu_info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Need a v4 ITS */
 | 
						/* Need a v4 ITS */
 | 
				
			||||||
	if (!its_dev->its->is_v4)
 | 
						if (!is_v4(its_dev->its))
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Unmap request? */
 | 
						/* Unmap request? */
 | 
				
			||||||
| 
						 | 
					@ -1922,9 +2080,9 @@ static bool its_parse_indirect_baser(struct its_node *its,
 | 
				
			||||||
	if (new_order >= MAX_ORDER) {
 | 
						if (new_order >= MAX_ORDER) {
 | 
				
			||||||
		new_order = MAX_ORDER - 1;
 | 
							new_order = MAX_ORDER - 1;
 | 
				
			||||||
		ids = ilog2(PAGE_ORDER_TO_SIZE(new_order) / (int)esz);
 | 
							ids = ilog2(PAGE_ORDER_TO_SIZE(new_order) / (int)esz);
 | 
				
			||||||
		pr_warn("ITS@%pa: %s Table too large, reduce ids %u->%u\n",
 | 
							pr_warn("ITS@%pa: %s Table too large, reduce ids %llu->%u\n",
 | 
				
			||||||
			&its->phys_base, its_base_type_string[type],
 | 
								&its->phys_base, its_base_type_string[type],
 | 
				
			||||||
			its->device_ids, ids);
 | 
								device_ids(its), ids);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*order = new_order;
 | 
						*order = new_order;
 | 
				
			||||||
| 
						 | 
					@ -1970,7 +2128,7 @@ static int its_alloc_tables(struct its_node *its)
 | 
				
			||||||
		case GITS_BASER_TYPE_DEVICE:
 | 
							case GITS_BASER_TYPE_DEVICE:
 | 
				
			||||||
			indirect = its_parse_indirect_baser(its, baser,
 | 
								indirect = its_parse_indirect_baser(its, baser,
 | 
				
			||||||
							    psz, &order,
 | 
												    psz, &order,
 | 
				
			||||||
							    its->device_ids);
 | 
												    device_ids(its));
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case GITS_BASER_TYPE_VCPU:
 | 
							case GITS_BASER_TYPE_VCPU:
 | 
				
			||||||
| 
						 | 
					@ -2361,7 +2519,7 @@ static bool its_alloc_device_table(struct its_node *its, u32 dev_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Don't allow device id that exceeds ITS hardware limit */
 | 
						/* Don't allow device id that exceeds ITS hardware limit */
 | 
				
			||||||
	if (!baser)
 | 
						if (!baser)
 | 
				
			||||||
		return (ilog2(dev_id) < its->device_ids);
 | 
							return (ilog2(dev_id) < device_ids(its));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return its_alloc_table_entry(its, baser, dev_id);
 | 
						return its_alloc_table_entry(its, baser, dev_id);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2380,7 +2538,7 @@ static bool its_alloc_vpe_table(u32 vpe_id)
 | 
				
			||||||
	list_for_each_entry(its, &its_nodes, entry) {
 | 
						list_for_each_entry(its, &its_nodes, entry) {
 | 
				
			||||||
		struct its_baser *baser;
 | 
							struct its_baser *baser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!its->is_v4)
 | 
							if (!is_v4(its))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		baser = its_get_baser(its, GITS_BASER_TYPE_VCPU);
 | 
							baser = its_get_baser(its, GITS_BASER_TYPE_VCPU);
 | 
				
			||||||
| 
						 | 
					@ -2419,7 +2577,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
 | 
				
			||||||
	 * sized as a power of two (and you need at least one bit...).
 | 
						 * sized as a power of two (and you need at least one bit...).
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	nr_ites = max(2, nvecs);
 | 
						nr_ites = max(2, nvecs);
 | 
				
			||||||
	sz = nr_ites * its->ite_size;
 | 
						sz = nr_ites * (FIELD_GET(GITS_TYPER_ITT_ENTRY_SIZE, its->typer) + 1);
 | 
				
			||||||
	sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1;
 | 
						sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1;
 | 
				
			||||||
	itt = kzalloc_node(sz, GFP_KERNEL, its->numa_node);
 | 
						itt = kzalloc_node(sz, GFP_KERNEL, its->numa_node);
 | 
				
			||||||
	if (alloc_lpis) {
 | 
						if (alloc_lpis) {
 | 
				
			||||||
| 
						 | 
					@ -2450,7 +2608,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
 | 
				
			||||||
	dev->event_map.col_map = col_map;
 | 
						dev->event_map.col_map = col_map;
 | 
				
			||||||
	dev->event_map.lpi_base = lpi_base;
 | 
						dev->event_map.lpi_base = lpi_base;
 | 
				
			||||||
	dev->event_map.nr_lpis = nr_lpis;
 | 
						dev->event_map.nr_lpis = nr_lpis;
 | 
				
			||||||
	mutex_init(&dev->event_map.vlpi_lock);
 | 
						raw_spin_lock_init(&dev->event_map.vlpi_lock);
 | 
				
			||||||
	dev->device_id = dev_id;
 | 
						dev->device_id = dev_id;
 | 
				
			||||||
	INIT_LIST_HEAD(&dev->entry);
 | 
						INIT_LIST_HEAD(&dev->entry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2471,6 +2629,7 @@ static void its_free_device(struct its_device *its_dev)
 | 
				
			||||||
	raw_spin_lock_irqsave(&its_dev->its->lock, flags);
 | 
						raw_spin_lock_irqsave(&its_dev->its->lock, flags);
 | 
				
			||||||
	list_del(&its_dev->entry);
 | 
						list_del(&its_dev->entry);
 | 
				
			||||||
	raw_spin_unlock_irqrestore(&its_dev->its->lock, flags);
 | 
						raw_spin_unlock_irqrestore(&its_dev->its->lock, flags);
 | 
				
			||||||
 | 
						kfree(its_dev->event_map.col_map);
 | 
				
			||||||
	kfree(its_dev->itt);
 | 
						kfree(its_dev->itt);
 | 
				
			||||||
	kfree(its_dev);
 | 
						kfree(its_dev);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2679,7 +2838,6 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
 | 
				
			||||||
		its_lpi_free(its_dev->event_map.lpi_map,
 | 
							its_lpi_free(its_dev->event_map.lpi_map,
 | 
				
			||||||
			     its_dev->event_map.lpi_base,
 | 
								     its_dev->event_map.lpi_base,
 | 
				
			||||||
			     its_dev->event_map.nr_lpis);
 | 
								     its_dev->event_map.nr_lpis);
 | 
				
			||||||
		kfree(its_dev->event_map.col_map);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Unmap device/itt */
 | 
							/* Unmap device/itt */
 | 
				
			||||||
		its_send_mapd(its_dev, 0);
 | 
							its_send_mapd(its_dev, 0);
 | 
				
			||||||
| 
						 | 
					@ -2772,8 +2930,7 @@ static void its_vpe_db_proxy_move(struct its_vpe *vpe, int from, int to)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		rdbase = per_cpu_ptr(gic_rdists->rdist, from)->rd_base;
 | 
							rdbase = per_cpu_ptr(gic_rdists->rdist, from)->rd_base;
 | 
				
			||||||
		gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_CLRLPIR);
 | 
							gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_CLRLPIR);
 | 
				
			||||||
		while (gic_read_lpir(rdbase + GICR_SYNCR) & 1)
 | 
							wait_for_syncr(rdbase);
 | 
				
			||||||
			cpu_relax();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -2869,7 +3026,7 @@ static void its_vpe_invall(struct its_vpe *vpe)
 | 
				
			||||||
	struct its_node *its;
 | 
						struct its_node *its;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry(its, &its_nodes, entry) {
 | 
						list_for_each_entry(its, &its_nodes, entry) {
 | 
				
			||||||
		if (!its->is_v4)
 | 
							if (!is_v4(its))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (its_list_map && !vpe->its_vm->vlpi_count[its->list_nr])
 | 
							if (its_list_map && !vpe->its_vm->vlpi_count[its->list_nr])
 | 
				
			||||||
| 
						 | 
					@ -2927,10 +3084,10 @@ static void its_vpe_send_inv(struct irq_data *d)
 | 
				
			||||||
	if (gic_rdists->has_direct_lpi) {
 | 
						if (gic_rdists->has_direct_lpi) {
 | 
				
			||||||
		void __iomem *rdbase;
 | 
							void __iomem *rdbase;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Target the redistributor this VPE is currently known on */
 | 
				
			||||||
		rdbase = per_cpu_ptr(gic_rdists->rdist, vpe->col_idx)->rd_base;
 | 
							rdbase = per_cpu_ptr(gic_rdists->rdist, vpe->col_idx)->rd_base;
 | 
				
			||||||
		gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_INVLPIR);
 | 
							gic_write_lpir(d->parent_data->hwirq, rdbase + GICR_INVLPIR);
 | 
				
			||||||
		while (gic_read_lpir(rdbase + GICR_SYNCR) & 1)
 | 
							wait_for_syncr(rdbase);
 | 
				
			||||||
			cpu_relax();
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		its_vpe_send_cmd(vpe, its_send_inv);
 | 
							its_vpe_send_cmd(vpe, its_send_inv);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -2972,8 +3129,7 @@ static int its_vpe_set_irqchip_state(struct irq_data *d,
 | 
				
			||||||
			gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_SETLPIR);
 | 
								gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_SETLPIR);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_CLRLPIR);
 | 
								gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_CLRLPIR);
 | 
				
			||||||
			while (gic_read_lpir(rdbase + GICR_SYNCR) & 1)
 | 
								wait_for_syncr(rdbase);
 | 
				
			||||||
				cpu_relax();
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if (state)
 | 
							if (state)
 | 
				
			||||||
| 
						 | 
					@ -3138,7 +3294,7 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain,
 | 
				
			||||||
	vpe->col_idx = cpumask_first(cpu_online_mask);
 | 
						vpe->col_idx = cpumask_first(cpu_online_mask);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry(its, &its_nodes, entry) {
 | 
						list_for_each_entry(its, &its_nodes, entry) {
 | 
				
			||||||
		if (!its->is_v4)
 | 
							if (!is_v4(its))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		its_send_vmapp(its, vpe, true);
 | 
							its_send_vmapp(its, vpe, true);
 | 
				
			||||||
| 
						 | 
					@ -3164,7 +3320,7 @@ static void its_vpe_irq_domain_deactivate(struct irq_domain *domain,
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry(its, &its_nodes, entry) {
 | 
						list_for_each_entry(its, &its_nodes, entry) {
 | 
				
			||||||
		if (!its->is_v4)
 | 
							if (!is_v4(its))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		its_send_vmapp(its, vpe, false);
 | 
							its_send_vmapp(its, vpe, false);
 | 
				
			||||||
| 
						 | 
					@ -3215,8 +3371,9 @@ static bool __maybe_unused its_enable_quirk_cavium_22375(void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct its_node *its = data;
 | 
						struct its_node *its = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* erratum 22375: only alloc 8MB table size */
 | 
						/* erratum 22375: only alloc 8MB table size (20 bits) */
 | 
				
			||||||
	its->device_ids = 0x14;		/* 20 bits, 8MB */
 | 
						its->typer &= ~GITS_TYPER_DEVBITS;
 | 
				
			||||||
 | 
						its->typer |= FIELD_PREP(GITS_TYPER_DEVBITS, 20 - 1);
 | 
				
			||||||
	its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375;
 | 
						its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
| 
						 | 
					@ -3236,7 +3393,8 @@ static bool __maybe_unused its_enable_quirk_qdf2400_e0065(void *data)
 | 
				
			||||||
	struct its_node *its = data;
 | 
						struct its_node *its = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* On QDF2400, the size of the ITE is 16Bytes */
 | 
						/* On QDF2400, the size of the ITE is 16Bytes */
 | 
				
			||||||
	its->ite_size = 16;
 | 
						its->typer &= ~GITS_TYPER_ITT_ENTRY_SIZE;
 | 
				
			||||||
 | 
						its->typer |= FIELD_PREP(GITS_TYPER_ITT_ENTRY_SIZE, 16 - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -3270,8 +3428,10 @@ static bool __maybe_unused its_enable_quirk_socionext_synquacer(void *data)
 | 
				
			||||||
		its->get_msi_base = its_irq_get_msi_base_pre_its;
 | 
							its->get_msi_base = its_irq_get_msi_base_pre_its;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ids = ilog2(pre_its_window[1]) - 2;
 | 
							ids = ilog2(pre_its_window[1]) - 2;
 | 
				
			||||||
		if (its->device_ids > ids)
 | 
							if (device_ids(its) > ids) {
 | 
				
			||||||
			its->device_ids = ids;
 | 
								its->typer &= ~GITS_TYPER_DEVBITS;
 | 
				
			||||||
 | 
								its->typer |= FIELD_PREP(GITS_TYPER_DEVBITS, ids - 1);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* the pre-ITS breaks isolation, so disable MSI remapping */
 | 
							/* the pre-ITS breaks isolation, so disable MSI remapping */
 | 
				
			||||||
		its->msi_domain_flags &= ~IRQ_DOMAIN_FLAG_MSI_REMAP;
 | 
							its->msi_domain_flags &= ~IRQ_DOMAIN_FLAG_MSI_REMAP;
 | 
				
			||||||
| 
						 | 
					@ -3504,7 +3664,7 @@ static int its_init_vpe_domain(void)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Use the last possible DevID */
 | 
						/* Use the last possible DevID */
 | 
				
			||||||
	devid = GENMASK(its->device_ids - 1, 0);
 | 
						devid = GENMASK(device_ids(its) - 1, 0);
 | 
				
			||||||
	vpe_proxy.dev = its_create_device(its, devid, entries, false);
 | 
						vpe_proxy.dev = its_create_device(its, devid, entries, false);
 | 
				
			||||||
	if (!vpe_proxy.dev) {
 | 
						if (!vpe_proxy.dev) {
 | 
				
			||||||
		kfree(vpe_proxy.vpes);
 | 
							kfree(vpe_proxy.vpes);
 | 
				
			||||||
| 
						 | 
					@ -3602,12 +3762,10 @@ static int __init its_probe_one(struct resource *res,
 | 
				
			||||||
	INIT_LIST_HEAD(&its->entry);
 | 
						INIT_LIST_HEAD(&its->entry);
 | 
				
			||||||
	INIT_LIST_HEAD(&its->its_device_list);
 | 
						INIT_LIST_HEAD(&its->its_device_list);
 | 
				
			||||||
	typer = gic_read_typer(its_base + GITS_TYPER);
 | 
						typer = gic_read_typer(its_base + GITS_TYPER);
 | 
				
			||||||
 | 
						its->typer = typer;
 | 
				
			||||||
	its->base = its_base;
 | 
						its->base = its_base;
 | 
				
			||||||
	its->phys_base = res->start;
 | 
						its->phys_base = res->start;
 | 
				
			||||||
	its->ite_size = GITS_TYPER_ITT_ENTRY_SIZE(typer);
 | 
						if (is_v4(its)) {
 | 
				
			||||||
	its->device_ids = GITS_TYPER_DEVBITS(typer);
 | 
					 | 
				
			||||||
	its->is_v4 = !!(typer & GITS_TYPER_VLPIS);
 | 
					 | 
				
			||||||
	if (its->is_v4) {
 | 
					 | 
				
			||||||
		if (!(typer & GITS_TYPER_VMOVP)) {
 | 
							if (!(typer & GITS_TYPER_VMOVP)) {
 | 
				
			||||||
			err = its_compute_its_list_map(res, its_base);
 | 
								err = its_compute_its_list_map(res, its_base);
 | 
				
			||||||
			if (err < 0)
 | 
								if (err < 0)
 | 
				
			||||||
| 
						 | 
					@ -3674,7 +3832,7 @@ static int __init its_probe_one(struct resource *res,
 | 
				
			||||||
	gits_write_cwriter(0, its->base + GITS_CWRITER);
 | 
						gits_write_cwriter(0, its->base + GITS_CWRITER);
 | 
				
			||||||
	ctlr = readl_relaxed(its->base + GITS_CTLR);
 | 
						ctlr = readl_relaxed(its->base + GITS_CTLR);
 | 
				
			||||||
	ctlr |= GITS_CTLR_ENABLE;
 | 
						ctlr |= GITS_CTLR_ENABLE;
 | 
				
			||||||
	if (its->is_v4)
 | 
						if (is_v4(its))
 | 
				
			||||||
		ctlr |= GITS_CTLR_ImDe;
 | 
							ctlr |= GITS_CTLR_ImDe;
 | 
				
			||||||
	writel_relaxed(ctlr, its->base + GITS_CTLR);
 | 
						writel_relaxed(ctlr, its->base + GITS_CTLR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3999,7 +4157,7 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
 | 
				
			||||||
		return err;
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry(its, &its_nodes, entry)
 | 
						list_for_each_entry(its, &its_nodes, entry)
 | 
				
			||||||
		has_v4 |= its->is_v4;
 | 
							has_v4 |= is_v4(its);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (has_v4 & rdists->has_vlpis) {
 | 
						if (has_v4 & rdists->has_vlpis) {
 | 
				
			||||||
		if (its_init_vpe_domain() ||
 | 
							if (its_init_vpe_domain() ||
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -174,7 +174,7 @@ static void gic_do_wait_for_rwp(void __iomem *base)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		cpu_relax();
 | 
							cpu_relax();
 | 
				
			||||||
		udelay(1);
 | 
							udelay(1);
 | 
				
			||||||
	};
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Wait for completion of a distributor change */
 | 
					/* Wait for completion of a distributor change */
 | 
				
			||||||
| 
						 | 
					@ -231,7 +231,7 @@ static void gic_enable_redist(bool enable)
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		cpu_relax();
 | 
							cpu_relax();
 | 
				
			||||||
		udelay(1);
 | 
							udelay(1);
 | 
				
			||||||
	};
 | 
						}
 | 
				
			||||||
	if (!count)
 | 
						if (!count)
 | 
				
			||||||
		pr_err_ratelimited("redistributor failed to %s...\n",
 | 
							pr_err_ratelimited("redistributor failed to %s...\n",
 | 
				
			||||||
				   enable ? "wakeup" : "sleep");
 | 
									   enable ? "wakeup" : "sleep");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 *  Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
 | 
					 *  Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
 | 
				
			||||||
 *  JZ4740 platform IRQ support
 | 
					 *  Ingenic XBurst platform IRQ support
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/errno.h>
 | 
					#include <linux/errno.h>
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,6 @@
 | 
				
			||||||
#include <linux/interrupt.h>
 | 
					#include <linux/interrupt.h>
 | 
				
			||||||
#include <linux/ioport.h>
 | 
					#include <linux/ioport.h>
 | 
				
			||||||
#include <linux/irqchip.h>
 | 
					#include <linux/irqchip.h>
 | 
				
			||||||
#include <linux/irqchip/ingenic.h>
 | 
					 | 
				
			||||||
#include <linux/of_address.h>
 | 
					#include <linux/of_address.h>
 | 
				
			||||||
#include <linux/of_irq.h>
 | 
					#include <linux/of_irq.h>
 | 
				
			||||||
#include <linux/timex.h>
 | 
					#include <linux/timex.h>
 | 
				
			||||||
| 
						 | 
					@ -22,6 +21,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ingenic_intc_data {
 | 
					struct ingenic_intc_data {
 | 
				
			||||||
	void __iomem *base;
 | 
						void __iomem *base;
 | 
				
			||||||
 | 
						struct irq_domain *domain;
 | 
				
			||||||
	unsigned num_chips;
 | 
						unsigned num_chips;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,41 +35,30 @@ struct ingenic_intc_data {
 | 
				
			||||||
static irqreturn_t intc_cascade(int irq, void *data)
 | 
					static irqreturn_t intc_cascade(int irq, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ingenic_intc_data *intc = irq_get_handler_data(irq);
 | 
						struct ingenic_intc_data *intc = irq_get_handler_data(irq);
 | 
				
			||||||
	uint32_t irq_reg;
 | 
						struct irq_domain *domain = intc->domain;
 | 
				
			||||||
 | 
						struct irq_chip_generic *gc;
 | 
				
			||||||
 | 
						uint32_t pending;
 | 
				
			||||||
	unsigned i;
 | 
						unsigned i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < intc->num_chips; i++) {
 | 
						for (i = 0; i < intc->num_chips; i++) {
 | 
				
			||||||
		irq_reg = readl(intc->base + (i * CHIP_SIZE) +
 | 
							gc = irq_get_domain_generic_chip(domain, i * 32);
 | 
				
			||||||
				JZ_REG_INTC_PENDING);
 | 
					
 | 
				
			||||||
		if (!irq_reg)
 | 
							pending = irq_reg_readl(gc, JZ_REG_INTC_PENDING);
 | 
				
			||||||
 | 
							if (!pending)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		generic_handle_irq(__fls(irq_reg) + (i * 32) + JZ4740_IRQ_BASE);
 | 
							while (pending) {
 | 
				
			||||||
 | 
								int bit = __fls(pending);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								irq = irq_find_mapping(domain, bit + (i * 32));
 | 
				
			||||||
 | 
								generic_handle_irq(irq);
 | 
				
			||||||
 | 
								pending &= ~BIT(bit);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return IRQ_HANDLED;
 | 
						return IRQ_HANDLED;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void intc_irq_set_mask(struct irq_chip_generic *gc, uint32_t mask)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct irq_chip_regs *regs = &gc->chip_types->regs;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	writel(mask, gc->reg_base + regs->enable);
 | 
					 | 
				
			||||||
	writel(~mask, gc->reg_base + regs->disable);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void ingenic_intc_irq_suspend(struct irq_data *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
 | 
					 | 
				
			||||||
	intc_irq_set_mask(gc, gc->wake_active);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void ingenic_intc_irq_resume(struct irq_data *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
 | 
					 | 
				
			||||||
	intc_irq_set_mask(gc, gc->mask_cache);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct irqaction intc_cascade_action = {
 | 
					static struct irqaction intc_cascade_action = {
 | 
				
			||||||
	.handler = intc_cascade,
 | 
						.handler = intc_cascade,
 | 
				
			||||||
	.name = "SoC intc cascade interrupt",
 | 
						.name = "SoC intc cascade interrupt",
 | 
				
			||||||
| 
						 | 
					@ -108,17 +97,27 @@ static int __init ingenic_intc_of_init(struct device_node *node,
 | 
				
			||||||
		goto out_unmap_irq;
 | 
							goto out_unmap_irq;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < num_chips; i++) {
 | 
						domain = irq_domain_add_legacy(node, num_chips * 32,
 | 
				
			||||||
		/* Mask all irqs */
 | 
									       JZ4740_IRQ_BASE, 0,
 | 
				
			||||||
		writel(0xffffffff, intc->base + (i * CHIP_SIZE) +
 | 
									       &irq_generic_chip_ops, NULL);
 | 
				
			||||||
		       JZ_REG_INTC_SET_MASK);
 | 
						if (!domain) {
 | 
				
			||||||
 | 
							err = -ENOMEM;
 | 
				
			||||||
 | 
							goto out_unmap_base;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		gc = irq_alloc_generic_chip("INTC", 1,
 | 
						intc->domain = domain;
 | 
				
			||||||
					    JZ4740_IRQ_BASE + (i * 32),
 | 
					
 | 
				
			||||||
					    intc->base + (i * CHIP_SIZE),
 | 
						err = irq_alloc_domain_generic_chips(domain, 32, 1, "INTC",
 | 
				
			||||||
					    handle_level_irq);
 | 
										     handle_level_irq, 0,
 | 
				
			||||||
 | 
										     IRQ_NOPROBE | IRQ_LEVEL, 0);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto out_domain_remove;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < num_chips; i++) {
 | 
				
			||||||
 | 
							gc = irq_get_domain_generic_chip(domain, i * 32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		gc->wake_enabled = IRQ_MSK(32);
 | 
							gc->wake_enabled = IRQ_MSK(32);
 | 
				
			||||||
 | 
							gc->reg_base = intc->base + (i * CHIP_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ct = gc->chip_types;
 | 
							ct = gc->chip_types;
 | 
				
			||||||
		ct->regs.enable = JZ_REG_INTC_CLEAR_MASK;
 | 
							ct->regs.enable = JZ_REG_INTC_CLEAR_MASK;
 | 
				
			||||||
| 
						 | 
					@ -127,21 +126,19 @@ static int __init ingenic_intc_of_init(struct device_node *node,
 | 
				
			||||||
		ct->chip.irq_mask = irq_gc_mask_disable_reg;
 | 
							ct->chip.irq_mask = irq_gc_mask_disable_reg;
 | 
				
			||||||
		ct->chip.irq_mask_ack = irq_gc_mask_disable_reg;
 | 
							ct->chip.irq_mask_ack = irq_gc_mask_disable_reg;
 | 
				
			||||||
		ct->chip.irq_set_wake = irq_gc_set_wake;
 | 
							ct->chip.irq_set_wake = irq_gc_set_wake;
 | 
				
			||||||
		ct->chip.irq_suspend = ingenic_intc_irq_suspend;
 | 
							ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND;
 | 
				
			||||||
		ct->chip.irq_resume = ingenic_intc_irq_resume;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0,
 | 
							/* Mask all irqs */
 | 
				
			||||||
				       IRQ_NOPROBE | IRQ_LEVEL);
 | 
							irq_reg_writel(gc, IRQ_MSK(32), JZ_REG_INTC_SET_MASK);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	domain = irq_domain_add_legacy(node, num_chips * 32, JZ4740_IRQ_BASE, 0,
 | 
					 | 
				
			||||||
				       &irq_domain_simple_ops, NULL);
 | 
					 | 
				
			||||||
	if (!domain)
 | 
					 | 
				
			||||||
		pr_warn("unable to register IRQ domain\n");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	setup_irq(parent_irq, &intc_cascade_action);
 | 
						setup_irq(parent_irq, &intc_cascade_action);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out_domain_remove:
 | 
				
			||||||
 | 
						irq_domain_remove(domain);
 | 
				
			||||||
 | 
					out_unmap_base:
 | 
				
			||||||
 | 
						iounmap(intc->base);
 | 
				
			||||||
out_unmap_irq:
 | 
					out_unmap_irq:
 | 
				
			||||||
	irq_dispose_mapping(parent_irq);
 | 
						irq_dispose_mapping(parent_irq);
 | 
				
			||||||
out_free:
 | 
					out_free:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										197
									
								
								drivers/irqchip/irq-ls-extirq.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								drivers/irqchip/irq-ls-extirq.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,197 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define pr_fmt(fmt) "irq-ls-extirq: " fmt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/irq.h>
 | 
				
			||||||
 | 
					#include <linux/irqchip.h>
 | 
				
			||||||
 | 
					#include <linux/irqdomain.h>
 | 
				
			||||||
 | 
					#include <linux/of.h>
 | 
				
			||||||
 | 
					#include <linux/mfd/syscon.h>
 | 
				
			||||||
 | 
					#include <linux/regmap.h>
 | 
				
			||||||
 | 
					#include <linux/slab.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <dt-bindings/interrupt-controller/arm-gic.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MAXIRQ 12
 | 
				
			||||||
 | 
					#define LS1021A_SCFGREVCR 0x200
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ls_extirq_data {
 | 
				
			||||||
 | 
						struct regmap		*syscon;
 | 
				
			||||||
 | 
						u32			intpcr;
 | 
				
			||||||
 | 
						bool			bit_reverse;
 | 
				
			||||||
 | 
						u32			nirq;
 | 
				
			||||||
 | 
						struct irq_fwspec	map[MAXIRQ];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					ls_extirq_set_type(struct irq_data *data, unsigned int type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ls_extirq_data *priv = data->chip_data;
 | 
				
			||||||
 | 
						irq_hw_number_t hwirq = data->hwirq;
 | 
				
			||||||
 | 
						u32 value, mask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (priv->bit_reverse)
 | 
				
			||||||
 | 
							mask = 1U << (31 - hwirq);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							mask = 1U << hwirq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (type) {
 | 
				
			||||||
 | 
						case IRQ_TYPE_LEVEL_LOW:
 | 
				
			||||||
 | 
							type = IRQ_TYPE_LEVEL_HIGH;
 | 
				
			||||||
 | 
							value = mask;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case IRQ_TYPE_EDGE_FALLING:
 | 
				
			||||||
 | 
							type = IRQ_TYPE_EDGE_RISING;
 | 
				
			||||||
 | 
							value = mask;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case IRQ_TYPE_LEVEL_HIGH:
 | 
				
			||||||
 | 
						case IRQ_TYPE_EDGE_RISING:
 | 
				
			||||||
 | 
							value = 0;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						regmap_update_bits(priv->syscon, priv->intpcr, mask, value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return irq_chip_set_type_parent(data, type);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct irq_chip ls_extirq_chip = {
 | 
				
			||||||
 | 
						.name			= "ls-extirq",
 | 
				
			||||||
 | 
						.irq_mask		= irq_chip_mask_parent,
 | 
				
			||||||
 | 
						.irq_unmask		= irq_chip_unmask_parent,
 | 
				
			||||||
 | 
						.irq_eoi		= irq_chip_eoi_parent,
 | 
				
			||||||
 | 
						.irq_set_type		= ls_extirq_set_type,
 | 
				
			||||||
 | 
						.irq_retrigger		= irq_chip_retrigger_hierarchy,
 | 
				
			||||||
 | 
						.irq_set_affinity	= irq_chip_set_affinity_parent,
 | 
				
			||||||
 | 
						.flags                  = IRQCHIP_SET_TYPE_MASKED,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					ls_extirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
 | 
				
			||||||
 | 
							       unsigned int nr_irqs, void *arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ls_extirq_data *priv = domain->host_data;
 | 
				
			||||||
 | 
						struct irq_fwspec *fwspec = arg;
 | 
				
			||||||
 | 
						irq_hw_number_t hwirq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fwspec->param_count != 2)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hwirq = fwspec->param[0];
 | 
				
			||||||
 | 
						if (hwirq >= priv->nirq)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &ls_extirq_chip,
 | 
				
			||||||
 | 
									      priv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return irq_domain_alloc_irqs_parent(domain, virq, 1, &priv->map[hwirq]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct irq_domain_ops extirq_domain_ops = {
 | 
				
			||||||
 | 
						.xlate		= irq_domain_xlate_twocell,
 | 
				
			||||||
 | 
						.alloc		= ls_extirq_domain_alloc,
 | 
				
			||||||
 | 
						.free		= irq_domain_free_irqs_common,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					ls_extirq_parse_map(struct ls_extirq_data *priv, struct device_node *node)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const __be32 *map;
 | 
				
			||||||
 | 
						u32 mapsize;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						map = of_get_property(node, "interrupt-map", &mapsize);
 | 
				
			||||||
 | 
						if (!map)
 | 
				
			||||||
 | 
							return -ENOENT;
 | 
				
			||||||
 | 
						if (mapsize % sizeof(*map))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						mapsize /= sizeof(*map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (mapsize) {
 | 
				
			||||||
 | 
							struct device_node *ipar;
 | 
				
			||||||
 | 
							u32 hwirq, intsize, j;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (mapsize < 3)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							hwirq = be32_to_cpup(map);
 | 
				
			||||||
 | 
							if (hwirq >= MAXIRQ)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							priv->nirq = max(priv->nirq, hwirq + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ipar = of_find_node_by_phandle(be32_to_cpup(map + 2));
 | 
				
			||||||
 | 
							map += 3;
 | 
				
			||||||
 | 
							mapsize -= 3;
 | 
				
			||||||
 | 
							if (!ipar)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							priv->map[hwirq].fwnode = &ipar->fwnode;
 | 
				
			||||||
 | 
							ret = of_property_read_u32(ipar, "#interrupt-cells", &intsize);
 | 
				
			||||||
 | 
							if (ret)
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (intsize > mapsize)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							priv->map[hwirq].param_count = intsize;
 | 
				
			||||||
 | 
							for (j = 0; j < intsize; ++j)
 | 
				
			||||||
 | 
								priv->map[hwirq].param[j] = be32_to_cpup(map++);
 | 
				
			||||||
 | 
							mapsize -= intsize;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int __init
 | 
				
			||||||
 | 
					ls_extirq_of_init(struct device_node *node, struct device_node *parent)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct irq_domain *domain, *parent_domain;
 | 
				
			||||||
 | 
						struct ls_extirq_data *priv;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						parent_domain = irq_find_host(parent);
 | 
				
			||||||
 | 
						if (!parent_domain) {
 | 
				
			||||||
 | 
							pr_err("Cannot find parent domain\n");
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!priv)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						priv->syscon = syscon_node_to_regmap(node->parent);
 | 
				
			||||||
 | 
						if (IS_ERR(priv->syscon)) {
 | 
				
			||||||
 | 
							ret = PTR_ERR(priv->syscon);
 | 
				
			||||||
 | 
							pr_err("Failed to lookup parent regmap\n");
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ret = of_property_read_u32(node, "reg", &priv->intpcr);
 | 
				
			||||||
 | 
						if (ret) {
 | 
				
			||||||
 | 
							pr_err("Missing INTPCR offset value\n");
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = ls_extirq_parse_map(priv, node);
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (of_device_is_compatible(node, "fsl,ls1021a-extirq")) {
 | 
				
			||||||
 | 
							u32 revcr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ret = regmap_read(priv->syscon, LS1021A_SCFGREVCR, &revcr);
 | 
				
			||||||
 | 
							if (ret)
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
 | 
							priv->bit_reverse = (revcr != 0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						domain = irq_domain_add_hierarchy(parent_domain, 0, priv->nirq, node,
 | 
				
			||||||
 | 
										  &extirq_domain_ops, priv);
 | 
				
			||||||
 | 
						if (!domain)
 | 
				
			||||||
 | 
							ret = -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							kfree(priv);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					IRQCHIP_DECLARE(ls1021a_extirq, "fsl,ls1021a-extirq", ls_extirq_of_init);
 | 
				
			||||||
| 
						 | 
					@ -246,8 +246,8 @@ static struct ti_sci_inta_event_desc *ti_sci_inta_alloc_irq(struct irq_domain *d
 | 
				
			||||||
	/* No free bits available. Allocate a new vint */
 | 
						/* No free bits available. Allocate a new vint */
 | 
				
			||||||
	vint_desc = ti_sci_inta_alloc_parent_irq(domain);
 | 
						vint_desc = ti_sci_inta_alloc_parent_irq(domain);
 | 
				
			||||||
	if (IS_ERR(vint_desc)) {
 | 
						if (IS_ERR(vint_desc)) {
 | 
				
			||||||
		mutex_unlock(&inta->vint_mutex);
 | 
							event_desc = ERR_CAST(vint_desc);
 | 
				
			||||||
		return ERR_PTR(PTR_ERR(vint_desc));
 | 
							goto unlock;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	free_bit = find_first_zero_bit(vint_desc->event_map,
 | 
						free_bit = find_first_zero_bit(vint_desc->event_map,
 | 
				
			||||||
| 
						 | 
					@ -259,6 +259,7 @@ static struct ti_sci_inta_event_desc *ti_sci_inta_alloc_irq(struct irq_domain *d
 | 
				
			||||||
	if (IS_ERR(event_desc))
 | 
						if (IS_ERR(event_desc))
 | 
				
			||||||
		clear_bit(free_bit, vint_desc->event_map);
 | 
							clear_bit(free_bit, vint_desc->event_map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unlock:
 | 
				
			||||||
	mutex_unlock(&inta->vint_mutex);
 | 
						mutex_unlock(&inta->vint_mutex);
 | 
				
			||||||
	return event_desc;
 | 
						return event_desc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,7 +51,7 @@ static void __exception_irq_entry zevio_handle_irq(struct pt_regs *regs)
 | 
				
			||||||
	while (readl(zevio_irq_io + IO_STATUS)) {
 | 
						while (readl(zevio_irq_io + IO_STATUS)) {
 | 
				
			||||||
		irqnr = readl(zevio_irq_io + IO_CURRENT);
 | 
							irqnr = readl(zevio_irq_io + IO_CURRENT);
 | 
				
			||||||
		handle_domain_irq(zevio_irq_domain, irqnr, regs);
 | 
							handle_domain_irq(zevio_irq_domain, irqnr, regs);
 | 
				
			||||||
	};
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void __init zevio_init_irq_base(void __iomem *base)
 | 
					static void __init zevio_init_irq_base(void __iomem *base)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,11 @@
 | 
				
			||||||
// SPDX-License-Identifier: GPL-2.0
 | 
					// SPDX-License-Identifier: GPL-2.0
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
 | 
					 * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/err.h>
 | 
					#include <linux/err.h>
 | 
				
			||||||
#include <linux/init.h>
 | 
					#include <linux/init.h>
 | 
				
			||||||
 | 
					#include <linux/interrupt.h>
 | 
				
			||||||
#include <linux/irq.h>
 | 
					#include <linux/irq.h>
 | 
				
			||||||
#include <linux/irqchip.h>
 | 
					#include <linux/irqchip.h>
 | 
				
			||||||
#include <linux/irqdomain.h>
 | 
					#include <linux/irqdomain.h>
 | 
				
			||||||
| 
						 | 
					@ -13,12 +14,13 @@
 | 
				
			||||||
#include <linux/of.h>
 | 
					#include <linux/of.h>
 | 
				
			||||||
#include <linux/of_address.h>
 | 
					#include <linux/of_address.h>
 | 
				
			||||||
#include <linux/of_device.h>
 | 
					#include <linux/of_device.h>
 | 
				
			||||||
 | 
					#include <linux/soc/qcom/irq.h>
 | 
				
			||||||
#include <linux/spinlock.h>
 | 
					#include <linux/spinlock.h>
 | 
				
			||||||
#include <linux/platform_device.h>
 | 
					 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
#include <linux/types.h>
 | 
					#include <linux/types.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define PDC_MAX_IRQS		126
 | 
					#define PDC_MAX_IRQS		168
 | 
				
			||||||
 | 
					#define PDC_MAX_GPIO_IRQS	256
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define CLEAR_INTR(reg, intr)	(reg & ~(1 << intr))
 | 
					#define CLEAR_INTR(reg, intr)	(reg & ~(1 << intr))
 | 
				
			||||||
#define ENABLE_INTR(reg, intr)	(reg | (1 << intr))
 | 
					#define ENABLE_INTR(reg, intr)	(reg | (1 << intr))
 | 
				
			||||||
| 
						 | 
					@ -26,6 +28,8 @@
 | 
				
			||||||
#define IRQ_ENABLE_BANK		0x10
 | 
					#define IRQ_ENABLE_BANK		0x10
 | 
				
			||||||
#define IRQ_i_CFG		0x110
 | 
					#define IRQ_i_CFG		0x110
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PDC_NO_PARENT_IRQ	~0UL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct pdc_pin_region {
 | 
					struct pdc_pin_region {
 | 
				
			||||||
	u32 pin_base;
 | 
						u32 pin_base;
 | 
				
			||||||
	u32 parent_base;
 | 
						u32 parent_base;
 | 
				
			||||||
| 
						 | 
					@ -47,6 +51,26 @@ 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 int qcom_pdc_gic_get_irqchip_state(struct irq_data *d,
 | 
				
			||||||
 | 
										  enum irqchip_irq_state which,
 | 
				
			||||||
 | 
										  bool *state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (d->hwirq == GPIO_NO_WAKE_IRQ)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return irq_chip_get_parent_state(d, which, state);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int qcom_pdc_gic_set_irqchip_state(struct irq_data *d,
 | 
				
			||||||
 | 
										  enum irqchip_irq_state which,
 | 
				
			||||||
 | 
										  bool value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (d->hwirq == GPIO_NO_WAKE_IRQ)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return irq_chip_set_parent_state(d, which, value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void pdc_enable_intr(struct irq_data *d, bool on)
 | 
					static void pdc_enable_intr(struct irq_data *d, bool on)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int pin_out = d->hwirq;
 | 
						int pin_out = d->hwirq;
 | 
				
			||||||
| 
						 | 
					@ -63,15 +87,37 @@ static void pdc_enable_intr(struct irq_data *d, bool on)
 | 
				
			||||||
	raw_spin_unlock(&pdc_lock);
 | 
						raw_spin_unlock(&pdc_lock);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void qcom_pdc_gic_disable(struct irq_data *d)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (d->hwirq == GPIO_NO_WAKE_IRQ)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pdc_enable_intr(d, false);
 | 
				
			||||||
 | 
						irq_chip_disable_parent(d);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void qcom_pdc_gic_enable(struct irq_data *d)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (d->hwirq == GPIO_NO_WAKE_IRQ)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pdc_enable_intr(d, true);
 | 
				
			||||||
 | 
						irq_chip_enable_parent(d);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void qcom_pdc_gic_mask(struct irq_data *d)
 | 
					static void qcom_pdc_gic_mask(struct irq_data *d)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	pdc_enable_intr(d, false);
 | 
						if (d->hwirq == GPIO_NO_WAKE_IRQ)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	irq_chip_mask_parent(d);
 | 
						irq_chip_mask_parent(d);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void qcom_pdc_gic_unmask(struct irq_data *d)
 | 
					static void qcom_pdc_gic_unmask(struct irq_data *d)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	pdc_enable_intr(d, true);
 | 
						if (d->hwirq == GPIO_NO_WAKE_IRQ)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	irq_chip_unmask_parent(d);
 | 
						irq_chip_unmask_parent(d);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -114,6 +160,9 @@ static int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type)
 | 
				
			||||||
	int pin_out = d->hwirq;
 | 
						int pin_out = d->hwirq;
 | 
				
			||||||
	enum pdc_irq_config_bits pdc_type;
 | 
						enum pdc_irq_config_bits pdc_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pin_out == GPIO_NO_WAKE_IRQ)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (type) {
 | 
						switch (type) {
 | 
				
			||||||
	case IRQ_TYPE_EDGE_RISING:
 | 
						case IRQ_TYPE_EDGE_RISING:
 | 
				
			||||||
		pdc_type = PDC_EDGE_RISING;
 | 
							pdc_type = PDC_EDGE_RISING;
 | 
				
			||||||
| 
						 | 
					@ -148,6 +197,10 @@ static struct irq_chip qcom_pdc_gic_chip = {
 | 
				
			||||||
	.irq_eoi		= irq_chip_eoi_parent,
 | 
						.irq_eoi		= irq_chip_eoi_parent,
 | 
				
			||||||
	.irq_mask		= qcom_pdc_gic_mask,
 | 
						.irq_mask		= qcom_pdc_gic_mask,
 | 
				
			||||||
	.irq_unmask		= qcom_pdc_gic_unmask,
 | 
						.irq_unmask		= qcom_pdc_gic_unmask,
 | 
				
			||||||
 | 
						.irq_disable		= qcom_pdc_gic_disable,
 | 
				
			||||||
 | 
						.irq_enable		= qcom_pdc_gic_enable,
 | 
				
			||||||
 | 
						.irq_get_irqchip_state	= qcom_pdc_gic_get_irqchip_state,
 | 
				
			||||||
 | 
						.irq_set_irqchip_state	= qcom_pdc_gic_set_irqchip_state,
 | 
				
			||||||
	.irq_retrigger		= irq_chip_retrigger_hierarchy,
 | 
						.irq_retrigger		= irq_chip_retrigger_hierarchy,
 | 
				
			||||||
	.irq_set_type		= qcom_pdc_gic_set_type,
 | 
						.irq_set_type		= qcom_pdc_gic_set_type,
 | 
				
			||||||
	.flags			= IRQCHIP_MASK_ON_SUSPEND |
 | 
						.flags			= IRQCHIP_MASK_ON_SUSPEND |
 | 
				
			||||||
| 
						 | 
					@ -169,8 +222,7 @@ static irq_hw_number_t get_parent_hwirq(int pin)
 | 
				
			||||||
			return (region->parent_base + pin - region->pin_base);
 | 
								return (region->parent_base + pin - region->pin_base);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	WARN_ON(1);
 | 
						return PDC_NO_PARENT_IRQ;
 | 
				
			||||||
	return ~0UL;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int qcom_pdc_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
 | 
					static int qcom_pdc_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
 | 
				
			||||||
| 
						 | 
					@ -199,17 +251,17 @@ static int qcom_pdc_alloc(struct irq_domain *domain, unsigned int virq,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = qcom_pdc_translate(domain, fwspec, &hwirq, &type);
 | 
						ret = qcom_pdc_translate(domain, fwspec, &hwirq, &type);
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		return -EINVAL;
 | 
							return ret;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	parent_hwirq = get_parent_hwirq(hwirq);
 | 
					 | 
				
			||||||
	if (parent_hwirq == ~0UL)
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret  = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
 | 
						ret  = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
 | 
				
			||||||
					     &qcom_pdc_gic_chip, NULL);
 | 
										     &qcom_pdc_gic_chip, NULL);
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						parent_hwirq = get_parent_hwirq(hwirq);
 | 
				
			||||||
 | 
						if (parent_hwirq == PDC_NO_PARENT_IRQ)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (type & IRQ_TYPE_EDGE_BOTH)
 | 
						if (type & IRQ_TYPE_EDGE_BOTH)
 | 
				
			||||||
		type = IRQ_TYPE_EDGE_RISING;
 | 
							type = IRQ_TYPE_EDGE_RISING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -232,6 +284,60 @@ static const struct irq_domain_ops qcom_pdc_ops = {
 | 
				
			||||||
	.free		= irq_domain_free_irqs_common,
 | 
						.free		= irq_domain_free_irqs_common,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int qcom_pdc_gpio_alloc(struct irq_domain *domain, unsigned int virq,
 | 
				
			||||||
 | 
								       unsigned int nr_irqs, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct irq_fwspec *fwspec = data;
 | 
				
			||||||
 | 
						struct irq_fwspec parent_fwspec;
 | 
				
			||||||
 | 
						irq_hw_number_t hwirq, parent_hwirq;
 | 
				
			||||||
 | 
						unsigned int type;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = qcom_pdc_translate(domain, fwspec, &hwirq, &type);
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
 | 
				
			||||||
 | 
										    &qcom_pdc_gic_chip, NULL);
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (hwirq == GPIO_NO_WAKE_IRQ)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						parent_hwirq = get_parent_hwirq(hwirq);
 | 
				
			||||||
 | 
						if (parent_hwirq == PDC_NO_PARENT_IRQ)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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]    = parent_hwirq;
 | 
				
			||||||
 | 
						parent_fwspec.param[2]    = type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
 | 
				
			||||||
 | 
										    &parent_fwspec);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int qcom_pdc_gpio_domain_select(struct irq_domain *d,
 | 
				
			||||||
 | 
									       struct irq_fwspec *fwspec,
 | 
				
			||||||
 | 
									       enum irq_domain_bus_token bus_token)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return bus_token == DOMAIN_BUS_WAKEUP;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct irq_domain_ops qcom_pdc_gpio_ops = {
 | 
				
			||||||
 | 
						.select		= qcom_pdc_gpio_domain_select,
 | 
				
			||||||
 | 
						.alloc		= qcom_pdc_gpio_alloc,
 | 
				
			||||||
 | 
						.free		= irq_domain_free_irqs_common,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int pdc_setup_pin_mapping(struct device_node *np)
 | 
					static int pdc_setup_pin_mapping(struct device_node *np)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret, n;
 | 
						int ret, n;
 | 
				
			||||||
| 
						 | 
					@ -270,7 +376,7 @@ static int pdc_setup_pin_mapping(struct device_node *np)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int qcom_pdc_init(struct device_node *node, struct device_node *parent)
 | 
					static int qcom_pdc_init(struct device_node *node, struct device_node *parent)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct irq_domain *parent_domain, *pdc_domain;
 | 
						struct irq_domain *parent_domain, *pdc_domain, *pdc_gpio_domain;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pdc_base = of_iomap(node, 0);
 | 
						pdc_base = of_iomap(node, 0);
 | 
				
			||||||
| 
						 | 
					@ -301,12 +407,27 @@ static int qcom_pdc_init(struct device_node *node, struct device_node *parent)
 | 
				
			||||||
		goto fail;
 | 
							goto fail;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pdc_gpio_domain = irq_domain_create_hierarchy(parent_domain,
 | 
				
			||||||
 | 
										IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP,
 | 
				
			||||||
 | 
										PDC_MAX_GPIO_IRQS,
 | 
				
			||||||
 | 
										of_fwnode_handle(node),
 | 
				
			||||||
 | 
										&qcom_pdc_gpio_ops, NULL);
 | 
				
			||||||
 | 
						if (!pdc_gpio_domain) {
 | 
				
			||||||
 | 
							pr_err("%pOF: PDC domain add failed for GPIO domain\n", node);
 | 
				
			||||||
 | 
							ret = -ENOMEM;
 | 
				
			||||||
 | 
							goto remove;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						irq_domain_update_bus_token(pdc_gpio_domain, DOMAIN_BUS_WAKEUP);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					remove:
 | 
				
			||||||
 | 
						irq_domain_remove(pdc_domain);
 | 
				
			||||||
fail:
 | 
					fail:
 | 
				
			||||||
	kfree(pdc_region);
 | 
						kfree(pdc_region);
 | 
				
			||||||
	iounmap(pdc_base);
 | 
						iounmap(pdc_base);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IRQCHIP_DECLARE(pdc_sdm845, "qcom,sdm845-pdc", qcom_pdc_init);
 | 
					IRQCHIP_DECLARE(qcom_pdc, "qcom,pdc", qcom_pdc_init);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,8 @@
 | 
				
			||||||
#include <linux/pm.h>
 | 
					#include <linux/pm.h>
 | 
				
			||||||
#include <linux/log2.h>
 | 
					#include <linux/log2.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/soc/qcom/irq.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "../core.h"
 | 
					#include "../core.h"
 | 
				
			||||||
#include "../pinconf.h"
 | 
					#include "../pinconf.h"
 | 
				
			||||||
#include "pinctrl-msm.h"
 | 
					#include "pinctrl-msm.h"
 | 
				
			||||||
| 
						 | 
					@ -44,6 +46,7 @@
 | 
				
			||||||
 * @enabled_irqs:   Bitmap of currently enabled irqs.
 | 
					 * @enabled_irqs:   Bitmap of currently enabled irqs.
 | 
				
			||||||
 * @dual_edge_irqs: Bitmap of irqs that need sw emulated dual edge
 | 
					 * @dual_edge_irqs: Bitmap of irqs that need sw emulated dual edge
 | 
				
			||||||
 *                  detection.
 | 
					 *                  detection.
 | 
				
			||||||
 | 
					 * @skip_wake_irqs: Skip IRQs that are handled by wakeup interrupt controller
 | 
				
			||||||
 * @soc;            Reference to soc_data of platform specific data.
 | 
					 * @soc;            Reference to soc_data of platform specific data.
 | 
				
			||||||
 * @regs:           Base addresses for the TLMM tiles.
 | 
					 * @regs:           Base addresses for the TLMM tiles.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -61,6 +64,7 @@ struct msm_pinctrl {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO);
 | 
						DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO);
 | 
				
			||||||
	DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO);
 | 
						DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO);
 | 
				
			||||||
 | 
						DECLARE_BITMAP(skip_wake_irqs, MAX_NR_GPIO);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const struct msm_pinctrl_soc_data *soc;
 | 
						const struct msm_pinctrl_soc_data *soc;
 | 
				
			||||||
	void __iomem *regs[MAX_NR_TILES];
 | 
						void __iomem *regs[MAX_NR_TILES];
 | 
				
			||||||
| 
						 | 
					@ -707,6 +711,12 @@ static void msm_gpio_irq_mask(struct irq_data *d)
 | 
				
			||||||
	unsigned long flags;
 | 
						unsigned long flags;
 | 
				
			||||||
	u32 val;
 | 
						u32 val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (d->parent_data)
 | 
				
			||||||
 | 
							irq_chip_mask_parent(d);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	g = &pctrl->soc->groups[d->hwirq];
 | 
						g = &pctrl->soc->groups[d->hwirq];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	raw_spin_lock_irqsave(&pctrl->lock, flags);
 | 
						raw_spin_lock_irqsave(&pctrl->lock, flags);
 | 
				
			||||||
| 
						 | 
					@ -751,6 +761,12 @@ static void msm_gpio_irq_clear_unmask(struct irq_data *d, bool status_clear)
 | 
				
			||||||
	unsigned long flags;
 | 
						unsigned long flags;
 | 
				
			||||||
	u32 val;
 | 
						u32 val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (d->parent_data)
 | 
				
			||||||
 | 
							irq_chip_unmask_parent(d);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	g = &pctrl->soc->groups[d->hwirq];
 | 
						g = &pctrl->soc->groups[d->hwirq];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	raw_spin_lock_irqsave(&pctrl->lock, flags);
 | 
						raw_spin_lock_irqsave(&pctrl->lock, flags);
 | 
				
			||||||
| 
						 | 
					@ -778,10 +794,35 @@ static void msm_gpio_irq_clear_unmask(struct irq_data *d, bool status_clear)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void msm_gpio_irq_enable(struct irq_data *d)
 | 
					static void msm_gpio_irq_enable(struct irq_data *d)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Clear the interrupt that may be pending before we enable
 | 
				
			||||||
 | 
						 * the line.
 | 
				
			||||||
 | 
						 * This is especially a problem with the GPIOs routed to the
 | 
				
			||||||
 | 
						 * PDC. These GPIOs are direct-connect interrupts to the GIC.
 | 
				
			||||||
 | 
						 * Disabling the interrupt line at the PDC does not prevent
 | 
				
			||||||
 | 
						 * the interrupt from being latched at the GIC. The state at
 | 
				
			||||||
 | 
						 * GIC needs to be cleared before enabling.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (d->parent_data) {
 | 
				
			||||||
 | 
							irq_chip_set_parent_state(d, IRQCHIP_STATE_PENDING, 0);
 | 
				
			||||||
 | 
							irq_chip_enable_parent(d);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	msm_gpio_irq_clear_unmask(d, true);
 | 
						msm_gpio_irq_clear_unmask(d, true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void msm_gpio_irq_disable(struct irq_data *d)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
 | 
				
			||||||
 | 
						struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (d->parent_data)
 | 
				
			||||||
 | 
							irq_chip_disable_parent(d);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!test_bit(d->hwirq, pctrl->skip_wake_irqs))
 | 
				
			||||||
 | 
							msm_gpio_irq_mask(d);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void msm_gpio_irq_unmask(struct irq_data *d)
 | 
					static void msm_gpio_irq_unmask(struct irq_data *d)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	msm_gpio_irq_clear_unmask(d, false);
 | 
						msm_gpio_irq_clear_unmask(d, false);
 | 
				
			||||||
| 
						 | 
					@ -795,6 +836,9 @@ static void msm_gpio_irq_ack(struct irq_data *d)
 | 
				
			||||||
	unsigned long flags;
 | 
						unsigned long flags;
 | 
				
			||||||
	u32 val;
 | 
						u32 val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	g = &pctrl->soc->groups[d->hwirq];
 | 
						g = &pctrl->soc->groups[d->hwirq];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	raw_spin_lock_irqsave(&pctrl->lock, flags);
 | 
						raw_spin_lock_irqsave(&pctrl->lock, flags);
 | 
				
			||||||
| 
						 | 
					@ -820,6 +864,12 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type)
 | 
				
			||||||
	unsigned long flags;
 | 
						unsigned long flags;
 | 
				
			||||||
	u32 val;
 | 
						u32 val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (d->parent_data)
 | 
				
			||||||
 | 
							irq_chip_set_type_parent(d, type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	g = &pctrl->soc->groups[d->hwirq];
 | 
						g = &pctrl->soc->groups[d->hwirq];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	raw_spin_lock_irqsave(&pctrl->lock, flags);
 | 
						raw_spin_lock_irqsave(&pctrl->lock, flags);
 | 
				
			||||||
| 
						 | 
					@ -912,6 +962,15 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
 | 
				
			||||||
	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
 | 
						struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
 | 
				
			||||||
	unsigned long flags;
 | 
						unsigned long flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * While they may not wake up when the TLMM is powered off,
 | 
				
			||||||
 | 
						 * some GPIOs would like to wakeup the system from suspend
 | 
				
			||||||
 | 
						 * when TLMM is powered on. To allow that, enable the GPIO
 | 
				
			||||||
 | 
						 * summary line to be wakeup capable at GIC.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (d->parent_data)
 | 
				
			||||||
 | 
							irq_chip_set_wake_parent(d, on);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	raw_spin_lock_irqsave(&pctrl->lock, flags);
 | 
						raw_spin_lock_irqsave(&pctrl->lock, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	irq_set_irq_wake(pctrl->irq, on);
 | 
						irq_set_irq_wake(pctrl->irq, on);
 | 
				
			||||||
| 
						 | 
					@ -990,6 +1049,30 @@ static void msm_gpio_irq_handler(struct irq_desc *desc)
 | 
				
			||||||
	chained_irq_exit(chip, desc);
 | 
						chained_irq_exit(chip, desc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int msm_gpio_wakeirq(struct gpio_chip *gc,
 | 
				
			||||||
 | 
								    unsigned int child,
 | 
				
			||||||
 | 
								    unsigned int child_type,
 | 
				
			||||||
 | 
								    unsigned int *parent,
 | 
				
			||||||
 | 
								    unsigned int *parent_type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
 | 
				
			||||||
 | 
						const struct msm_gpio_wakeirq_map *map;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*parent = GPIO_NO_WAKE_IRQ;
 | 
				
			||||||
 | 
						*parent_type = IRQ_TYPE_EDGE_RISING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < pctrl->soc->nwakeirq_map; i++) {
 | 
				
			||||||
 | 
							map = &pctrl->soc->wakeirq_map[i];
 | 
				
			||||||
 | 
							if (map->gpio == child) {
 | 
				
			||||||
 | 
								*parent = map->wakeirq;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool msm_gpio_needs_valid_mask(struct msm_pinctrl *pctrl)
 | 
					static bool msm_gpio_needs_valid_mask(struct msm_pinctrl *pctrl)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (pctrl->soc->reserved_gpios)
 | 
						if (pctrl->soc->reserved_gpios)
 | 
				
			||||||
| 
						 | 
					@ -1002,8 +1085,10 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gpio_chip *chip;
 | 
						struct gpio_chip *chip;
 | 
				
			||||||
	struct gpio_irq_chip *girq;
 | 
						struct gpio_irq_chip *girq;
 | 
				
			||||||
	int ret;
 | 
						int i, ret;
 | 
				
			||||||
	unsigned ngpio = pctrl->soc->ngpios;
 | 
						unsigned gpio, ngpio = pctrl->soc->ngpios;
 | 
				
			||||||
 | 
						struct device_node *np;
 | 
				
			||||||
 | 
						bool skip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (WARN_ON(ngpio > MAX_NR_GPIO))
 | 
						if (WARN_ON(ngpio > MAX_NR_GPIO))
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
| 
						 | 
					@ -1020,17 +1105,40 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pctrl->irq_chip.name = "msmgpio";
 | 
						pctrl->irq_chip.name = "msmgpio";
 | 
				
			||||||
	pctrl->irq_chip.irq_enable = msm_gpio_irq_enable;
 | 
						pctrl->irq_chip.irq_enable = msm_gpio_irq_enable;
 | 
				
			||||||
 | 
						pctrl->irq_chip.irq_disable = msm_gpio_irq_disable;
 | 
				
			||||||
	pctrl->irq_chip.irq_mask = msm_gpio_irq_mask;
 | 
						pctrl->irq_chip.irq_mask = msm_gpio_irq_mask;
 | 
				
			||||||
	pctrl->irq_chip.irq_unmask = msm_gpio_irq_unmask;
 | 
						pctrl->irq_chip.irq_unmask = msm_gpio_irq_unmask;
 | 
				
			||||||
	pctrl->irq_chip.irq_ack = msm_gpio_irq_ack;
 | 
						pctrl->irq_chip.irq_ack = msm_gpio_irq_ack;
 | 
				
			||||||
 | 
						pctrl->irq_chip.irq_eoi = irq_chip_eoi_parent;
 | 
				
			||||||
	pctrl->irq_chip.irq_set_type = msm_gpio_irq_set_type;
 | 
						pctrl->irq_chip.irq_set_type = msm_gpio_irq_set_type;
 | 
				
			||||||
	pctrl->irq_chip.irq_set_wake = msm_gpio_irq_set_wake;
 | 
						pctrl->irq_chip.irq_set_wake = msm_gpio_irq_set_wake;
 | 
				
			||||||
	pctrl->irq_chip.irq_request_resources = msm_gpio_irq_reqres;
 | 
						pctrl->irq_chip.irq_request_resources = msm_gpio_irq_reqres;
 | 
				
			||||||
	pctrl->irq_chip.irq_release_resources = msm_gpio_irq_relres;
 | 
						pctrl->irq_chip.irq_release_resources = msm_gpio_irq_relres;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						np = of_parse_phandle(pctrl->dev->of_node, "wakeup-parent", 0);
 | 
				
			||||||
 | 
						if (np) {
 | 
				
			||||||
 | 
							chip->irq.parent_domain = irq_find_matching_host(np,
 | 
				
			||||||
 | 
											 DOMAIN_BUS_WAKEUP);
 | 
				
			||||||
 | 
							of_node_put(np);
 | 
				
			||||||
 | 
							if (!chip->irq.parent_domain)
 | 
				
			||||||
 | 
								return -EPROBE_DEFER;
 | 
				
			||||||
 | 
							chip->irq.child_to_parent_hwirq = msm_gpio_wakeirq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Let's skip handling the GPIOs, if the parent irqchip
 | 
				
			||||||
 | 
							 * is handling the direct connect IRQ of the GPIO.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							skip = irq_domain_qcom_handle_wakeup(chip->irq.parent_domain);
 | 
				
			||||||
 | 
							for (i = 0; skip && i < pctrl->soc->nwakeirq_map; i++) {
 | 
				
			||||||
 | 
								gpio = pctrl->soc->wakeirq_map[i].gpio;
 | 
				
			||||||
 | 
								set_bit(gpio, pctrl->skip_wake_irqs);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	girq = &chip->irq;
 | 
						girq = &chip->irq;
 | 
				
			||||||
	girq->chip = &pctrl->irq_chip;
 | 
						girq->chip = &pctrl->irq_chip;
 | 
				
			||||||
	girq->parent_handler = msm_gpio_irq_handler;
 | 
						girq->parent_handler = msm_gpio_irq_handler;
 | 
				
			||||||
 | 
						girq->fwnode = pctrl->dev->fwnode;
 | 
				
			||||||
	girq->num_parents = 1;
 | 
						girq->num_parents = 1;
 | 
				
			||||||
	girq->parents = devm_kcalloc(pctrl->dev, 1, sizeof(*girq->parents),
 | 
						girq->parents = devm_kcalloc(pctrl->dev, 1, sizeof(*girq->parents),
 | 
				
			||||||
				     GFP_KERNEL);
 | 
									     GFP_KERNEL);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -91,6 +91,16 @@ struct msm_pingroup {
 | 
				
			||||||
	unsigned intr_detection_width:5;
 | 
						unsigned intr_detection_width:5;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct msm_gpio_wakeirq_map - Map of GPIOs and their wakeup pins
 | 
				
			||||||
 | 
					 * @gpio:          The GPIOs that are wakeup capable
 | 
				
			||||||
 | 
					 * @wakeirq:       The interrupt at the always-on interrupt controller
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct msm_gpio_wakeirq_map {
 | 
				
			||||||
 | 
						unsigned int gpio;
 | 
				
			||||||
 | 
						unsigned int wakeirq;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * struct msm_pinctrl_soc_data - Qualcomm pin controller driver configuration
 | 
					 * struct msm_pinctrl_soc_data - Qualcomm pin controller driver configuration
 | 
				
			||||||
 * @pins:	    An array describing all pins the pin controller affects.
 | 
					 * @pins:	    An array describing all pins the pin controller affects.
 | 
				
			||||||
| 
						 | 
					@ -101,6 +111,8 @@ struct msm_pingroup {
 | 
				
			||||||
 * @ngroups:	    The numbmer of entries in @groups.
 | 
					 * @ngroups:	    The numbmer of entries in @groups.
 | 
				
			||||||
 * @ngpio:	    The number of pingroups the driver should expose as GPIOs.
 | 
					 * @ngpio:	    The number of pingroups the driver should expose as GPIOs.
 | 
				
			||||||
 * @pull_no_keeper: The SoC does not support keeper bias.
 | 
					 * @pull_no_keeper: The SoC does not support keeper bias.
 | 
				
			||||||
 | 
					 * @wakeirq_map:    The map of wakeup capable GPIOs and the pin at PDC/MPM
 | 
				
			||||||
 | 
					 * @nwakeirq_map:   The number of entries in @wakeirq_map
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct msm_pinctrl_soc_data {
 | 
					struct msm_pinctrl_soc_data {
 | 
				
			||||||
	const struct pinctrl_pin_desc *pins;
 | 
						const struct pinctrl_pin_desc *pins;
 | 
				
			||||||
| 
						 | 
					@ -114,6 +126,8 @@ struct msm_pinctrl_soc_data {
 | 
				
			||||||
	const char *const *tiles;
 | 
						const char *const *tiles;
 | 
				
			||||||
	unsigned int ntiles;
 | 
						unsigned int ntiles;
 | 
				
			||||||
	const int *reserved_gpios;
 | 
						const int *reserved_gpios;
 | 
				
			||||||
 | 
						const struct msm_gpio_wakeirq_map *wakeirq_map;
 | 
				
			||||||
 | 
						unsigned int nwakeirq_map;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern const struct dev_pm_ops msm_pinctrl_dev_pm_ops;
 | 
					extern const struct dev_pm_ops msm_pinctrl_dev_pm_ops;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
// SPDX-License-Identifier: GPL-2.0
 | 
					// SPDX-License-Identifier: GPL-2.0
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
 | 
					 * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/acpi.h>
 | 
					#include <linux/acpi.h>
 | 
				
			||||||
| 
						 | 
					@ -1282,6 +1282,24 @@ static const int sdm845_acpi_reserved_gpios[] = {
 | 
				
			||||||
	0, 1, 2, 3, 81, 82, 83, 84, -1
 | 
						0, 1, 2, 3, 81, 82, 83, 84, -1
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct msm_gpio_wakeirq_map sdm845_pdc_map[] = {
 | 
				
			||||||
 | 
						{ 1, 30 }, { 3, 31 }, { 5, 32 }, { 10, 33 }, { 11, 34 },
 | 
				
			||||||
 | 
						{ 20, 35 }, { 22, 36 }, { 24, 37 }, { 26, 38 }, { 30, 39 },
 | 
				
			||||||
 | 
						{ 31, 117 }, { 32, 41 }, { 34, 42 }, { 36, 43 }, { 37, 44 },
 | 
				
			||||||
 | 
						{ 38, 45 }, { 39, 46 }, { 40, 47 }, { 41, 115 }, { 43, 49 },
 | 
				
			||||||
 | 
						{ 44, 50 }, { 46, 51 }, { 48, 52 }, { 49, 118 }, { 52, 54 },
 | 
				
			||||||
 | 
						{ 53, 55 }, { 54, 56 }, { 56, 57 }, { 57, 58 }, { 58, 59 },
 | 
				
			||||||
 | 
						{ 59, 60 }, { 60, 61 }, { 61, 62 }, { 62, 63 }, { 63, 64 },
 | 
				
			||||||
 | 
						{ 64, 65 }, { 66, 66 }, { 68, 67 }, { 71, 68 }, { 73, 69 },
 | 
				
			||||||
 | 
						{ 77, 70 }, { 78, 71 }, { 79, 72 }, { 80, 73 }, { 84, 74 },
 | 
				
			||||||
 | 
						{ 85, 75 }, { 86, 76 }, { 88, 77 }, { 89, 116 }, { 91, 79 },
 | 
				
			||||||
 | 
						{ 92, 80 }, { 95, 81 }, { 96, 82 }, { 97, 83 }, { 101, 84 },
 | 
				
			||||||
 | 
						{ 103, 85 }, { 104, 86 }, { 115, 90 }, { 116, 91 }, { 117, 92 },
 | 
				
			||||||
 | 
						{ 118, 93 }, { 119, 94 }, { 120, 95 }, { 121, 96 }, { 122, 97 },
 | 
				
			||||||
 | 
						{ 123, 98 }, { 124, 99 }, { 125, 100 }, { 127, 102 }, { 128, 103 },
 | 
				
			||||||
 | 
						{ 129, 104 }, { 130, 105 }, { 132, 106 }, { 133, 107 }, { 145, 108 },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct msm_pinctrl_soc_data sdm845_pinctrl = {
 | 
					static const struct msm_pinctrl_soc_data sdm845_pinctrl = {
 | 
				
			||||||
	.pins = sdm845_pins,
 | 
						.pins = sdm845_pins,
 | 
				
			||||||
	.npins = ARRAY_SIZE(sdm845_pins),
 | 
						.npins = ARRAY_SIZE(sdm845_pins),
 | 
				
			||||||
| 
						 | 
					@ -1290,6 +1308,9 @@ static const struct msm_pinctrl_soc_data sdm845_pinctrl = {
 | 
				
			||||||
	.groups = sdm845_groups,
 | 
						.groups = sdm845_groups,
 | 
				
			||||||
	.ngroups = ARRAY_SIZE(sdm845_groups),
 | 
						.ngroups = ARRAY_SIZE(sdm845_groups),
 | 
				
			||||||
	.ngpios = 151,
 | 
						.ngpios = 151,
 | 
				
			||||||
 | 
						.wakeirq_map = sdm845_pdc_map,
 | 
				
			||||||
 | 
						.nwakeirq_map = ARRAY_SIZE(sdm845_pdc_map),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct msm_pinctrl_soc_data sdm845_acpi_pinctrl = {
 | 
					static const struct msm_pinctrl_soc_data sdm845_acpi_pinctrl = {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -610,6 +610,12 @@ extern int irq_chip_pm_put(struct irq_data *data);
 | 
				
			||||||
#ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
 | 
					#ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
 | 
				
			||||||
extern void handle_fasteoi_ack_irq(struct irq_desc *desc);
 | 
					extern void handle_fasteoi_ack_irq(struct irq_desc *desc);
 | 
				
			||||||
extern void handle_fasteoi_mask_irq(struct irq_desc *desc);
 | 
					extern void handle_fasteoi_mask_irq(struct irq_desc *desc);
 | 
				
			||||||
 | 
					extern int irq_chip_set_parent_state(struct irq_data *data,
 | 
				
			||||||
 | 
									     enum irqchip_irq_state which,
 | 
				
			||||||
 | 
									     bool val);
 | 
				
			||||||
 | 
					extern int irq_chip_get_parent_state(struct irq_data *data,
 | 
				
			||||||
 | 
									     enum irqchip_irq_state which,
 | 
				
			||||||
 | 
									     bool *state);
 | 
				
			||||||
extern void irq_chip_enable_parent(struct irq_data *data);
 | 
					extern void irq_chip_enable_parent(struct irq_data *data);
 | 
				
			||||||
extern void irq_chip_disable_parent(struct irq_data *data);
 | 
					extern void irq_chip_disable_parent(struct irq_data *data);
 | 
				
			||||||
extern void irq_chip_ack_parent(struct irq_data *data);
 | 
					extern void irq_chip_ack_parent(struct irq_data *data);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -334,10 +334,10 @@
 | 
				
			||||||
#define GITS_TYPER_PLPIS		(1UL << 0)
 | 
					#define GITS_TYPER_PLPIS		(1UL << 0)
 | 
				
			||||||
#define GITS_TYPER_VLPIS		(1UL << 1)
 | 
					#define GITS_TYPER_VLPIS		(1UL << 1)
 | 
				
			||||||
#define GITS_TYPER_ITT_ENTRY_SIZE_SHIFT	4
 | 
					#define GITS_TYPER_ITT_ENTRY_SIZE_SHIFT	4
 | 
				
			||||||
#define GITS_TYPER_ITT_ENTRY_SIZE(r)	((((r) >> GITS_TYPER_ITT_ENTRY_SIZE_SHIFT) & 0xf) + 1)
 | 
					#define GITS_TYPER_ITT_ENTRY_SIZE	GENMASK_ULL(7, 4)
 | 
				
			||||||
#define GITS_TYPER_IDBITS_SHIFT		8
 | 
					#define GITS_TYPER_IDBITS_SHIFT		8
 | 
				
			||||||
#define GITS_TYPER_DEVBITS_SHIFT	13
 | 
					#define GITS_TYPER_DEVBITS_SHIFT	13
 | 
				
			||||||
#define GITS_TYPER_DEVBITS(r)		((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)
 | 
					#define GITS_TYPER_DEVBITS		GENMASK_ULL(17, 13)
 | 
				
			||||||
#define GITS_TYPER_PTA			(1UL << 19)
 | 
					#define GITS_TYPER_PTA			(1UL << 19)
 | 
				
			||||||
#define GITS_TYPER_HCC_SHIFT		24
 | 
					#define GITS_TYPER_HCC_SHIFT		24
 | 
				
			||||||
#define GITS_TYPER_HCC(r)		(((r) >> GITS_TYPER_HCC_SHIFT) & 0xff)
 | 
					#define GITS_TYPER_HCC(r)		(((r) >> GITS_TYPER_HCC_SHIFT) & 0xff)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +0,0 @@
 | 
				
			||||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef __LINUX_IRQCHIP_INGENIC_H__
 | 
					 | 
				
			||||||
#define __LINUX_IRQCHIP_INGENIC_H__
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <linux/irq.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern void ingenic_intc_irq_suspend(struct irq_data *data);
 | 
					 | 
				
			||||||
extern void ingenic_intc_irq_resume(struct irq_data *data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
| 
						 | 
					@ -83,6 +83,7 @@ enum irq_domain_bus_token {
 | 
				
			||||||
	DOMAIN_BUS_IPI,
 | 
						DOMAIN_BUS_IPI,
 | 
				
			||||||
	DOMAIN_BUS_FSL_MC_MSI,
 | 
						DOMAIN_BUS_FSL_MC_MSI,
 | 
				
			||||||
	DOMAIN_BUS_TI_SCI_INTA_MSI,
 | 
						DOMAIN_BUS_TI_SCI_INTA_MSI,
 | 
				
			||||||
 | 
						DOMAIN_BUS_WAKEUP,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										34
									
								
								include/linux/soc/qcom/irq.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								include/linux/soc/qcom/irq.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,34 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0-only */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef __QCOM_IRQ_H
 | 
				
			||||||
 | 
					#define __QCOM_IRQ_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/irqdomain.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define GPIO_NO_WAKE_IRQ	~0U
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * QCOM specific IRQ domain flags that distinguishes the handling of wakeup
 | 
				
			||||||
 | 
					 * capable interrupts by different interrupt controllers.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP: Line must be masked at TLMM and the
 | 
				
			||||||
 | 
					 *                                  interrupt configuration is done at PDC
 | 
				
			||||||
 | 
					 * IRQ_DOMAIN_FLAG_QCOM_MPM_WAKEUP: Interrupt configuration is handled at TLMM
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP		(IRQ_DOMAIN_FLAG_NONCORE << 0)
 | 
				
			||||||
 | 
					#define IRQ_DOMAIN_FLAG_QCOM_MPM_WAKEUP		(IRQ_DOMAIN_FLAG_NONCORE << 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * irq_domain_qcom_handle_wakeup: Return if the domain handles interrupt
 | 
				
			||||||
 | 
					 *                                configuration
 | 
				
			||||||
 | 
					 * @d: irq domain
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This QCOM specific irq domain call returns if the interrupt controller
 | 
				
			||||||
 | 
					 * requires the interrupt be masked at the child interrupt controller.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline bool irq_domain_qcom_handle_wakeup(const struct irq_domain *d)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return (d->flags & IRQ_DOMAIN_FLAG_QCOM_PDC_WAKEUP);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -1297,6 +1297,50 @@ EXPORT_SYMBOL_GPL(handle_fasteoi_mask_irq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS */
 | 
					#endif /* CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * irq_chip_set_parent_state - set the state of a parent interrupt.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @data: Pointer to interrupt specific data
 | 
				
			||||||
 | 
					 * @which: State to be restored (one of IRQCHIP_STATE_*)
 | 
				
			||||||
 | 
					 * @val: Value corresponding to @which
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Conditional success, if the underlying irqchip does not implement it.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int irq_chip_set_parent_state(struct irq_data *data,
 | 
				
			||||||
 | 
								      enum irqchip_irq_state which,
 | 
				
			||||||
 | 
								      bool val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						data = data->parent_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!data || !data->chip->irq_set_irqchip_state)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return data->chip->irq_set_irqchip_state(data, which, val);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(irq_chip_set_parent_state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * irq_chip_get_parent_state - get the state of a parent interrupt.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @data: Pointer to interrupt specific data
 | 
				
			||||||
 | 
					 * @which: one of IRQCHIP_STATE_* the caller wants to know
 | 
				
			||||||
 | 
					 * @state: a pointer to a boolean where the state is to be stored
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Conditional success, if the underlying irqchip does not implement it.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int irq_chip_get_parent_state(struct irq_data *data,
 | 
				
			||||||
 | 
								      enum irqchip_irq_state which,
 | 
				
			||||||
 | 
								      bool *state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						data = data->parent_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!data || !data->chip->irq_get_irqchip_state)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return data->chip->irq_get_irqchip_state(data, which, state);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(irq_chip_get_parent_state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * irq_chip_enable_parent - Enable the parent interrupt (defaults to unmask if
 | 
					 * irq_chip_enable_parent - Enable the parent interrupt (defaults to unmask if
 | 
				
			||||||
 * NULL)
 | 
					 * NULL)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue