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

avic.c (6550B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
      4 * Copyright 2008 Juergen Beisert, kernel@pengutronix.de
      5 */
      6
      7#include <linux/module.h>
      8#include <linux/irq.h>
      9#include <linux/irqdomain.h>
     10#include <linux/irqchip.h>
     11#include <linux/io.h>
     12#include <linux/of.h>
     13#include <linux/of_address.h>
     14#include <asm/mach/irq.h>
     15#include <asm/exception.h>
     16
     17#include "common.h"
     18#include "hardware.h"
     19#include "irq-common.h"
     20
     21#define AVIC_INTCNTL		0x00	/* int control reg */
     22#define AVIC_NIMASK		0x04	/* int mask reg */
     23#define AVIC_INTENNUM		0x08	/* int enable number reg */
     24#define AVIC_INTDISNUM		0x0C	/* int disable number reg */
     25#define AVIC_INTENABLEH		0x10	/* int enable reg high */
     26#define AVIC_INTENABLEL		0x14	/* int enable reg low */
     27#define AVIC_INTTYPEH		0x18	/* int type reg high */
     28#define AVIC_INTTYPEL		0x1C	/* int type reg low */
     29#define AVIC_NIPRIORITY(x)	(0x20 + 4 * (7 - (x))) /* int priority */
     30#define AVIC_NIVECSR		0x40	/* norm int vector/status */
     31#define AVIC_FIVECSR		0x44	/* fast int vector/status */
     32#define AVIC_INTSRCH		0x48	/* int source reg high */
     33#define AVIC_INTSRCL		0x4C	/* int source reg low */
     34#define AVIC_INTFRCH		0x50	/* int force reg high */
     35#define AVIC_INTFRCL		0x54	/* int force reg low */
     36#define AVIC_NIPNDH		0x58	/* norm int pending high */
     37#define AVIC_NIPNDL		0x5C	/* norm int pending low */
     38#define AVIC_FIPNDH		0x60	/* fast int pending high */
     39#define AVIC_FIPNDL		0x64	/* fast int pending low */
     40
     41#define AVIC_NUM_IRQS 64
     42
     43/* low power interrupt mask registers */
     44#define MX25_CCM_LPIMR0	0x68
     45#define MX25_CCM_LPIMR1	0x6C
     46
     47static void __iomem *avic_base;
     48static void __iomem *mx25_ccm_base;
     49static struct irq_domain *domain;
     50
     51#ifdef CONFIG_FIQ
     52static int avic_set_irq_fiq(unsigned int hwirq, unsigned int type)
     53{
     54	unsigned int irqt;
     55
     56	if (hwirq >= AVIC_NUM_IRQS)
     57		return -EINVAL;
     58
     59	if (hwirq < AVIC_NUM_IRQS / 2) {
     60		irqt = imx_readl(avic_base + AVIC_INTTYPEL) & ~(1 << hwirq);
     61		imx_writel(irqt | (!!type << hwirq), avic_base + AVIC_INTTYPEL);
     62	} else {
     63		hwirq -= AVIC_NUM_IRQS / 2;
     64		irqt = imx_readl(avic_base + AVIC_INTTYPEH) & ~(1 << hwirq);
     65		imx_writel(irqt | (!!type << hwirq), avic_base + AVIC_INTTYPEH);
     66	}
     67
     68	return 0;
     69}
     70#endif /* CONFIG_FIQ */
     71
     72
     73static struct mxc_extra_irq avic_extra_irq = {
     74#ifdef CONFIG_FIQ
     75	.set_irq_fiq = avic_set_irq_fiq,
     76#endif
     77};
     78
     79#ifdef CONFIG_PM
     80static u32 avic_saved_mask_reg[2];
     81
     82static void avic_irq_suspend(struct irq_data *d)
     83{
     84	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
     85	struct irq_chip_type *ct = gc->chip_types;
     86	int idx = d->hwirq >> 5;
     87
     88	avic_saved_mask_reg[idx] = imx_readl(avic_base + ct->regs.mask);
     89	imx_writel(gc->wake_active, avic_base + ct->regs.mask);
     90
     91	if (mx25_ccm_base) {
     92		u8 offs = d->hwirq < AVIC_NUM_IRQS / 2 ?
     93			MX25_CCM_LPIMR0 : MX25_CCM_LPIMR1;
     94		/*
     95		 * The interrupts which are still enabled will be used as wakeup
     96		 * sources. Allow those interrupts in low-power mode.
     97		 * The LPIMR registers use 0 to allow an interrupt, the AVIC
     98		 * registers use 1.
     99		 */
    100		imx_writel(~gc->wake_active, mx25_ccm_base + offs);
    101	}
    102}
    103
    104static void avic_irq_resume(struct irq_data *d)
    105{
    106	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
    107	struct irq_chip_type *ct = gc->chip_types;
    108	int idx = d->hwirq >> 5;
    109
    110	imx_writel(avic_saved_mask_reg[idx], avic_base + ct->regs.mask);
    111
    112	if (mx25_ccm_base) {
    113		u8 offs = d->hwirq < AVIC_NUM_IRQS / 2 ?
    114			MX25_CCM_LPIMR0 : MX25_CCM_LPIMR1;
    115
    116		imx_writel(0xffffffff, mx25_ccm_base + offs);
    117	}
    118}
    119
    120#else
    121#define avic_irq_suspend NULL
    122#define avic_irq_resume NULL
    123#endif
    124
    125static __init void avic_init_gc(int idx, unsigned int irq_start)
    126{
    127	struct irq_chip_generic *gc;
    128	struct irq_chip_type *ct;
    129
    130	gc = irq_alloc_generic_chip("mxc-avic", 1, irq_start, avic_base,
    131				    handle_level_irq);
    132	gc->private = &avic_extra_irq;
    133	gc->wake_enabled = IRQ_MSK(32);
    134
    135	ct = gc->chip_types;
    136	ct->chip.irq_mask = irq_gc_mask_clr_bit;
    137	ct->chip.irq_unmask = irq_gc_mask_set_bit;
    138	ct->chip.irq_ack = irq_gc_mask_clr_bit;
    139	ct->chip.irq_set_wake = irq_gc_set_wake;
    140	ct->chip.irq_suspend = avic_irq_suspend;
    141	ct->chip.irq_resume = avic_irq_resume;
    142	ct->regs.mask = !idx ? AVIC_INTENABLEL : AVIC_INTENABLEH;
    143	ct->regs.ack = ct->regs.mask;
    144
    145	irq_setup_generic_chip(gc, IRQ_MSK(32), 0, IRQ_NOREQUEST, 0);
    146}
    147
    148static void __exception_irq_entry avic_handle_irq(struct pt_regs *regs)
    149{
    150	u32 nivector;
    151
    152	do {
    153		nivector = imx_readl(avic_base + AVIC_NIVECSR) >> 16;
    154		if (nivector == 0xffff)
    155			break;
    156
    157		generic_handle_domain_irq(domain, nivector);
    158	} while (1);
    159}
    160
    161/*
    162 * This function initializes the AVIC hardware and disables all the
    163 * interrupts. It registers the interrupt enable and disable functions
    164 * to the kernel for each interrupt source.
    165 */
    166static void __init mxc_init_irq(void __iomem *irqbase)
    167{
    168	struct device_node *np;
    169	int irq_base;
    170	int i;
    171
    172	avic_base = irqbase;
    173
    174	np = of_find_compatible_node(NULL, NULL, "fsl,imx25-ccm");
    175	mx25_ccm_base = of_iomap(np, 0);
    176
    177	if (mx25_ccm_base) {
    178		/*
    179		 * By default, we mask all interrupts. We set the actual mask
    180		 * before we go into low-power mode.
    181		 */
    182		imx_writel(0xffffffff, mx25_ccm_base + MX25_CCM_LPIMR0);
    183		imx_writel(0xffffffff, mx25_ccm_base + MX25_CCM_LPIMR1);
    184	}
    185
    186	/* put the AVIC into the reset value with
    187	 * all interrupts disabled
    188	 */
    189	imx_writel(0, avic_base + AVIC_INTCNTL);
    190	imx_writel(0x1f, avic_base + AVIC_NIMASK);
    191
    192	/* disable all interrupts */
    193	imx_writel(0, avic_base + AVIC_INTENABLEH);
    194	imx_writel(0, avic_base + AVIC_INTENABLEL);
    195
    196	/* all IRQ no FIQ */
    197	imx_writel(0, avic_base + AVIC_INTTYPEH);
    198	imx_writel(0, avic_base + AVIC_INTTYPEL);
    199
    200	irq_base = irq_alloc_descs(-1, 0, AVIC_NUM_IRQS, numa_node_id());
    201	WARN_ON(irq_base < 0);
    202
    203	np = of_find_compatible_node(NULL, NULL, "fsl,avic");
    204	domain = irq_domain_add_legacy(np, AVIC_NUM_IRQS, irq_base, 0,
    205				       &irq_domain_simple_ops, NULL);
    206	WARN_ON(!domain);
    207
    208	for (i = 0; i < AVIC_NUM_IRQS / 32; i++, irq_base += 32)
    209		avic_init_gc(i, irq_base);
    210
    211	/* Set default priority value (0) for all IRQ's */
    212	for (i = 0; i < 8; i++)
    213		imx_writel(0, avic_base + AVIC_NIPRIORITY(i));
    214
    215	set_handle_irq(avic_handle_irq);
    216
    217#ifdef CONFIG_FIQ
    218	/* Initialize FIQ */
    219	init_FIQ(FIQ_START);
    220#endif
    221
    222	printk(KERN_INFO "MXC IRQ initialized\n");
    223}
    224
    225static int __init imx_avic_init(struct device_node *node,
    226			       struct device_node *parent)
    227{
    228	void __iomem *avic_base;
    229
    230	avic_base = of_iomap(node, 0);
    231	BUG_ON(!avic_base);
    232	mxc_init_irq(avic_base);
    233	return 0;
    234}
    235
    236IRQCHIP_DECLARE(imx_avic, "fsl,avic", imx_avic_init);