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-mxs.c (6165B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
      4 * Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.de>
      5 *	Add Alphascale ASM9260 support.
      6 */
      7
      8#include <linux/kernel.h>
      9#include <linux/init.h>
     10#include <linux/irq.h>
     11#include <linux/irqchip.h>
     12#include <linux/irqdomain.h>
     13#include <linux/io.h>
     14#include <linux/of.h>
     15#include <linux/of_address.h>
     16#include <linux/of_irq.h>
     17#include <linux/stmp_device.h>
     18#include <asm/exception.h>
     19
     20#include "alphascale_asm9260-icoll.h"
     21
     22/*
     23 * this device provide 4 offsets for each register:
     24 * 0x0 - plain read write mode
     25 * 0x4 - set mode, OR logic.
     26 * 0x8 - clr mode, XOR logic.
     27 * 0xc - togle mode.
     28 */
     29#define SET_REG 4
     30#define CLR_REG 8
     31
     32#define HW_ICOLL_VECTOR				0x0000
     33#define HW_ICOLL_LEVELACK			0x0010
     34#define HW_ICOLL_CTRL				0x0020
     35#define HW_ICOLL_STAT_OFFSET			0x0070
     36#define HW_ICOLL_INTERRUPT0			0x0120
     37#define HW_ICOLL_INTERRUPTn(n)			((n) * 0x10)
     38#define BM_ICOLL_INTR_ENABLE			BIT(2)
     39#define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0	0x1
     40
     41#define ICOLL_NUM_IRQS		128
     42
     43enum icoll_type {
     44	ICOLL,
     45	ASM9260_ICOLL,
     46};
     47
     48struct icoll_priv {
     49	void __iomem *vector;
     50	void __iomem *levelack;
     51	void __iomem *ctrl;
     52	void __iomem *stat;
     53	void __iomem *intr;
     54	void __iomem *clear;
     55	enum icoll_type type;
     56};
     57
     58static struct icoll_priv icoll_priv;
     59static struct irq_domain *icoll_domain;
     60
     61/* calculate bit offset depending on number of interrupt per register */
     62static u32 icoll_intr_bitshift(struct irq_data *d, u32 bit)
     63{
     64	/*
     65	 * mask lower part of hwirq to convert it
     66	 * in 0, 1, 2 or 3 and then multiply it by 8 (or shift by 3)
     67	 */
     68	return bit << ((d->hwirq & 3) << 3);
     69}
     70
     71/* calculate mem offset depending on number of interrupt per register */
     72static void __iomem *icoll_intr_reg(struct irq_data *d)
     73{
     74	/* offset = hwirq / intr_per_reg * 0x10 */
     75	return icoll_priv.intr + ((d->hwirq >> 2) * 0x10);
     76}
     77
     78static void icoll_ack_irq(struct irq_data *d)
     79{
     80	/*
     81	 * The Interrupt Collector is able to prioritize irqs.
     82	 * Currently only level 0 is used. So acking can use
     83	 * BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 unconditionally.
     84	 */
     85	__raw_writel(BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0,
     86			icoll_priv.levelack);
     87}
     88
     89static void icoll_mask_irq(struct irq_data *d)
     90{
     91	__raw_writel(BM_ICOLL_INTR_ENABLE,
     92			icoll_priv.intr + CLR_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
     93}
     94
     95static void icoll_unmask_irq(struct irq_data *d)
     96{
     97	__raw_writel(BM_ICOLL_INTR_ENABLE,
     98			icoll_priv.intr + SET_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
     99}
    100
    101static void asm9260_mask_irq(struct irq_data *d)
    102{
    103	__raw_writel(icoll_intr_bitshift(d, BM_ICOLL_INTR_ENABLE),
    104			icoll_intr_reg(d) + CLR_REG);
    105}
    106
    107static void asm9260_unmask_irq(struct irq_data *d)
    108{
    109	__raw_writel(ASM9260_BM_CLEAR_BIT(d->hwirq),
    110		     icoll_priv.clear +
    111		     ASM9260_HW_ICOLL_CLEARn(d->hwirq));
    112
    113	__raw_writel(icoll_intr_bitshift(d, BM_ICOLL_INTR_ENABLE),
    114			icoll_intr_reg(d) + SET_REG);
    115}
    116
    117static struct irq_chip mxs_icoll_chip = {
    118	.irq_ack = icoll_ack_irq,
    119	.irq_mask = icoll_mask_irq,
    120	.irq_unmask = icoll_unmask_irq,
    121	.flags = IRQCHIP_MASK_ON_SUSPEND |
    122		 IRQCHIP_SKIP_SET_WAKE,
    123};
    124
    125static struct irq_chip asm9260_icoll_chip = {
    126	.irq_ack = icoll_ack_irq,
    127	.irq_mask = asm9260_mask_irq,
    128	.irq_unmask = asm9260_unmask_irq,
    129	.flags = IRQCHIP_MASK_ON_SUSPEND |
    130		 IRQCHIP_SKIP_SET_WAKE,
    131};
    132
    133asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs)
    134{
    135	u32 irqnr;
    136
    137	irqnr = __raw_readl(icoll_priv.stat);
    138	__raw_writel(irqnr, icoll_priv.vector);
    139	generic_handle_domain_irq(icoll_domain, irqnr);
    140}
    141
    142static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq,
    143				irq_hw_number_t hw)
    144{
    145	struct irq_chip *chip;
    146
    147	if (icoll_priv.type == ICOLL)
    148		chip = &mxs_icoll_chip;
    149	else
    150		chip = &asm9260_icoll_chip;
    151
    152	irq_set_chip_and_handler(virq, chip, handle_level_irq);
    153
    154	return 0;
    155}
    156
    157static const struct irq_domain_ops icoll_irq_domain_ops = {
    158	.map = icoll_irq_domain_map,
    159	.xlate = irq_domain_xlate_onecell,
    160};
    161
    162static void __init icoll_add_domain(struct device_node *np,
    163			  int num)
    164{
    165	icoll_domain = irq_domain_add_linear(np, num,
    166					     &icoll_irq_domain_ops, NULL);
    167
    168	if (!icoll_domain)
    169		panic("%pOF: unable to create irq domain", np);
    170}
    171
    172static void __iomem * __init icoll_init_iobase(struct device_node *np)
    173{
    174	void __iomem *icoll_base;
    175
    176	icoll_base = of_io_request_and_map(np, 0, np->name);
    177	if (IS_ERR(icoll_base))
    178		panic("%pOF: unable to map resource", np);
    179	return icoll_base;
    180}
    181
    182static int __init icoll_of_init(struct device_node *np,
    183			  struct device_node *interrupt_parent)
    184{
    185	void __iomem *icoll_base;
    186
    187	icoll_priv.type = ICOLL;
    188
    189	icoll_base		= icoll_init_iobase(np);
    190	icoll_priv.vector	= icoll_base + HW_ICOLL_VECTOR;
    191	icoll_priv.levelack	= icoll_base + HW_ICOLL_LEVELACK;
    192	icoll_priv.ctrl		= icoll_base + HW_ICOLL_CTRL;
    193	icoll_priv.stat		= icoll_base + HW_ICOLL_STAT_OFFSET;
    194	icoll_priv.intr		= icoll_base + HW_ICOLL_INTERRUPT0;
    195	icoll_priv.clear	= NULL;
    196
    197	/*
    198	 * Interrupt Collector reset, which initializes the priority
    199	 * for each irq to level 0.
    200	 */
    201	stmp_reset_block(icoll_priv.ctrl);
    202
    203	icoll_add_domain(np, ICOLL_NUM_IRQS);
    204
    205	return 0;
    206}
    207IRQCHIP_DECLARE(mxs, "fsl,icoll", icoll_of_init);
    208
    209static int __init asm9260_of_init(struct device_node *np,
    210			  struct device_node *interrupt_parent)
    211{
    212	void __iomem *icoll_base;
    213	int i;
    214
    215	icoll_priv.type = ASM9260_ICOLL;
    216
    217	icoll_base = icoll_init_iobase(np);
    218	icoll_priv.vector	= icoll_base + ASM9260_HW_ICOLL_VECTOR;
    219	icoll_priv.levelack	= icoll_base + ASM9260_HW_ICOLL_LEVELACK;
    220	icoll_priv.ctrl		= icoll_base + ASM9260_HW_ICOLL_CTRL;
    221	icoll_priv.stat		= icoll_base + ASM9260_HW_ICOLL_STAT_OFFSET;
    222	icoll_priv.intr		= icoll_base + ASM9260_HW_ICOLL_INTERRUPT0;
    223	icoll_priv.clear	= icoll_base + ASM9260_HW_ICOLL_CLEAR0;
    224
    225	writel_relaxed(ASM9260_BM_CTRL_IRQ_ENABLE,
    226			icoll_priv.ctrl);
    227	/*
    228	 * ASM9260 don't provide reset bit. So, we need to set level 0
    229	 * manually.
    230	 */
    231	for (i = 0; i < 16 * 0x10; i += 0x10)
    232		writel(0, icoll_priv.intr + i);
    233
    234	icoll_add_domain(np, ASM9260_NUM_IRQS);
    235	set_handle_irq(icoll_handle_irq);
    236
    237	return 0;
    238}
    239IRQCHIP_DECLARE(asm9260, "alphascale,asm9260-icoll", asm9260_of_init);