cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

irq.c (6106B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  linux/arch/arm/mach-pxa/irq.c
      4 *
      5 *  Generic PXA IRQ handling
      6 *
      7 *  Author:	Nicolas Pitre
      8 *  Created:	Jun 15, 2001
      9 *  Copyright:	MontaVista Software Inc.
     10 */
     11#include <linux/bitops.h>
     12#include <linux/init.h>
     13#include <linux/module.h>
     14#include <linux/interrupt.h>
     15#include <linux/syscore_ops.h>
     16#include <linux/io.h>
     17#include <linux/irq.h>
     18#include <linux/of_address.h>
     19#include <linux/of_irq.h>
     20#include <linux/soc/pxa/cpu.h>
     21
     22#include <asm/exception.h>
     23
     24#include "irqs.h"
     25
     26#include "generic.h"
     27#include "pxa-regs.h"
     28
     29#define ICIP			(0x000)
     30#define ICMR			(0x004)
     31#define ICLR			(0x008)
     32#define ICFR			(0x00c)
     33#define ICPR			(0x010)
     34#define ICCR			(0x014)
     35#define ICHP			(0x018)
     36#define IPR(i)			(((i) < 32) ? (0x01c + ((i) << 2)) :		\
     37				((i) < 64) ? (0x0b0 + (((i) - 32) << 2)) :	\
     38				      (0x144 + (((i) - 64) << 2)))
     39#define ICHP_VAL_IRQ		(1 << 31)
     40#define ICHP_IRQ(i)		(((i) >> 16) & 0x7fff)
     41#define IPR_VALID		(1 << 31)
     42
     43#define MAX_INTERNAL_IRQS	128
     44
     45/*
     46 * This is for peripheral IRQs internal to the PXA chip.
     47 */
     48
     49static void __iomem *pxa_irq_base;
     50static int pxa_internal_irq_nr;
     51static bool cpu_has_ipr;
     52static struct irq_domain *pxa_irq_domain;
     53
     54static inline void __iomem *irq_base(int i)
     55{
     56	static unsigned long phys_base_offset[] = {
     57		0x0,
     58		0x9c,
     59		0x130,
     60	};
     61
     62	return pxa_irq_base + phys_base_offset[i];
     63}
     64
     65void pxa_mask_irq(struct irq_data *d)
     66{
     67	void __iomem *base = irq_data_get_irq_chip_data(d);
     68	irq_hw_number_t irq = irqd_to_hwirq(d);
     69	uint32_t icmr = __raw_readl(base + ICMR);
     70
     71	icmr &= ~BIT(irq & 0x1f);
     72	__raw_writel(icmr, base + ICMR);
     73}
     74
     75void pxa_unmask_irq(struct irq_data *d)
     76{
     77	void __iomem *base = irq_data_get_irq_chip_data(d);
     78	irq_hw_number_t irq = irqd_to_hwirq(d);
     79	uint32_t icmr = __raw_readl(base + ICMR);
     80
     81	icmr |= BIT(irq & 0x1f);
     82	__raw_writel(icmr, base + ICMR);
     83}
     84
     85static struct irq_chip pxa_internal_irq_chip = {
     86	.name		= "SC",
     87	.irq_ack	= pxa_mask_irq,
     88	.irq_mask	= pxa_mask_irq,
     89	.irq_unmask	= pxa_unmask_irq,
     90};
     91
     92asmlinkage void __exception_irq_entry icip_handle_irq(struct pt_regs *regs)
     93{
     94	uint32_t icip, icmr, mask;
     95
     96	do {
     97		icip = __raw_readl(pxa_irq_base + ICIP);
     98		icmr = __raw_readl(pxa_irq_base + ICMR);
     99		mask = icip & icmr;
    100
    101		if (mask == 0)
    102			break;
    103
    104		handle_IRQ(PXA_IRQ(fls(mask) - 1), regs);
    105	} while (1);
    106}
    107
    108asmlinkage void __exception_irq_entry ichp_handle_irq(struct pt_regs *regs)
    109{
    110	uint32_t ichp;
    111
    112	do {
    113		__asm__ __volatile__("mrc p6, 0, %0, c5, c0, 0\n": "=r"(ichp));
    114
    115		if ((ichp & ICHP_VAL_IRQ) == 0)
    116			break;
    117
    118		handle_IRQ(PXA_IRQ(ICHP_IRQ(ichp)), regs);
    119	} while (1);
    120}
    121
    122static int pxa_irq_map(struct irq_domain *h, unsigned int virq,
    123		       irq_hw_number_t hw)
    124{
    125	void __iomem *base = irq_base(hw / 32);
    126
    127	/* initialize interrupt priority */
    128	if (cpu_has_ipr)
    129		__raw_writel(hw | IPR_VALID, pxa_irq_base + IPR(hw));
    130
    131	irq_set_chip_and_handler(virq, &pxa_internal_irq_chip,
    132				 handle_level_irq);
    133	irq_set_chip_data(virq, base);
    134
    135	return 0;
    136}
    137
    138static const struct irq_domain_ops pxa_irq_ops = {
    139	.map    = pxa_irq_map,
    140	.xlate  = irq_domain_xlate_onecell,
    141};
    142
    143static __init void
    144pxa_init_irq_common(struct device_node *node, int irq_nr,
    145		    int (*fn)(struct irq_data *, unsigned int))
    146{
    147	int n;
    148
    149	pxa_internal_irq_nr = irq_nr;
    150	pxa_irq_domain = irq_domain_add_legacy(node, irq_nr,
    151					       PXA_IRQ(0), 0,
    152					       &pxa_irq_ops, NULL);
    153	if (!pxa_irq_domain)
    154		panic("Unable to add PXA IRQ domain\n");
    155	irq_set_default_host(pxa_irq_domain);
    156
    157	for (n = 0; n < irq_nr; n += 32) {
    158		void __iomem *base = irq_base(n >> 5);
    159
    160		__raw_writel(0, base + ICMR);	/* disable all IRQs */
    161		__raw_writel(0, base + ICLR);	/* all IRQs are IRQ, not FIQ */
    162	}
    163	/* only unmasked interrupts kick us out of idle */
    164	__raw_writel(1, irq_base(0) + ICCR);
    165
    166	pxa_internal_irq_chip.irq_set_wake = fn;
    167}
    168
    169void __init pxa_init_irq(int irq_nr, int (*fn)(struct irq_data *, unsigned int))
    170{
    171	BUG_ON(irq_nr > MAX_INTERNAL_IRQS);
    172
    173	pxa_irq_base = io_p2v(0x40d00000);
    174	cpu_has_ipr = !cpu_is_pxa25x();
    175	pxa_init_irq_common(NULL, irq_nr, fn);
    176}
    177
    178#ifdef CONFIG_PM
    179static unsigned long saved_icmr[MAX_INTERNAL_IRQS/32];
    180static unsigned long saved_ipr[MAX_INTERNAL_IRQS];
    181
    182static int pxa_irq_suspend(void)
    183{
    184	int i;
    185
    186	for (i = 0; i < DIV_ROUND_UP(pxa_internal_irq_nr, 32); i++) {
    187		void __iomem *base = irq_base(i);
    188
    189		saved_icmr[i] = __raw_readl(base + ICMR);
    190		__raw_writel(0, base + ICMR);
    191	}
    192
    193	if (cpu_has_ipr) {
    194		for (i = 0; i < pxa_internal_irq_nr; i++)
    195			saved_ipr[i] = __raw_readl(pxa_irq_base + IPR(i));
    196	}
    197
    198	return 0;
    199}
    200
    201static void pxa_irq_resume(void)
    202{
    203	int i;
    204
    205	for (i = 0; i < DIV_ROUND_UP(pxa_internal_irq_nr, 32); i++) {
    206		void __iomem *base = irq_base(i);
    207
    208		__raw_writel(saved_icmr[i], base + ICMR);
    209		__raw_writel(0, base + ICLR);
    210	}
    211
    212	if (cpu_has_ipr)
    213		for (i = 0; i < pxa_internal_irq_nr; i++)
    214			__raw_writel(saved_ipr[i], pxa_irq_base + IPR(i));
    215
    216	__raw_writel(1, pxa_irq_base + ICCR);
    217}
    218#else
    219#define pxa_irq_suspend		NULL
    220#define pxa_irq_resume		NULL
    221#endif
    222
    223struct syscore_ops pxa_irq_syscore_ops = {
    224	.suspend	= pxa_irq_suspend,
    225	.resume		= pxa_irq_resume,
    226};
    227
    228#ifdef CONFIG_OF
    229static const struct of_device_id intc_ids[] __initconst = {
    230	{ .compatible = "marvell,pxa-intc", },
    231	{}
    232};
    233
    234void __init pxa_dt_irq_init(int (*fn)(struct irq_data *, unsigned int))
    235{
    236	struct device_node *node;
    237	struct resource res;
    238	int ret;
    239
    240	node = of_find_matching_node(NULL, intc_ids);
    241	if (!node) {
    242		pr_err("Failed to find interrupt controller in arch-pxa\n");
    243		return;
    244	}
    245
    246	ret = of_property_read_u32(node, "marvell,intc-nr-irqs",
    247				   &pxa_internal_irq_nr);
    248	if (ret) {
    249		pr_err("Not found marvell,intc-nr-irqs property\n");
    250		return;
    251	}
    252
    253	ret = of_address_to_resource(node, 0, &res);
    254	if (ret < 0) {
    255		pr_err("No registers defined for node\n");
    256		return;
    257	}
    258	pxa_irq_base = io_p2v(res.start);
    259
    260	if (of_find_property(node, "marvell,intc-priority", NULL))
    261		cpu_has_ipr = 1;
    262
    263	ret = irq_alloc_descs(-1, 0, pxa_internal_irq_nr, 0);
    264	if (ret < 0) {
    265		pr_err("Failed to allocate IRQ numbers\n");
    266		return;
    267	}
    268
    269	pxa_init_irq_common(node, pxa_internal_irq_nr, fn);
    270}
    271#endif /* CONFIG_OF */