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-al-fic.c (7139B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
      4 */
      5
      6#include <linux/bitfield.h>
      7#include <linux/irq.h>
      8#include <linux/irqchip.h>
      9#include <linux/irqchip/chained_irq.h>
     10#include <linux/irqdomain.h>
     11#include <linux/module.h>
     12#include <linux/of.h>
     13#include <linux/of_address.h>
     14#include <linux/of_irq.h>
     15
     16/* FIC Registers */
     17#define AL_FIC_CAUSE		0x00
     18#define AL_FIC_SET_CAUSE	0x08
     19#define AL_FIC_MASK		0x10
     20#define AL_FIC_CONTROL		0x28
     21
     22#define CONTROL_TRIGGER_RISING	BIT(3)
     23#define CONTROL_MASK_MSI_X	BIT(5)
     24
     25#define NR_FIC_IRQS 32
     26
     27MODULE_AUTHOR("Talel Shenhar");
     28MODULE_DESCRIPTION("Amazon's Annapurna Labs Interrupt Controller Driver");
     29MODULE_LICENSE("GPL v2");
     30
     31enum al_fic_state {
     32	AL_FIC_UNCONFIGURED = 0,
     33	AL_FIC_CONFIGURED_LEVEL,
     34	AL_FIC_CONFIGURED_RISING_EDGE,
     35};
     36
     37struct al_fic {
     38	void __iomem *base;
     39	struct irq_domain *domain;
     40	const char *name;
     41	unsigned int parent_irq;
     42	enum al_fic_state state;
     43};
     44
     45static void al_fic_set_trigger(struct al_fic *fic,
     46			       struct irq_chip_generic *gc,
     47			       enum al_fic_state new_state)
     48{
     49	irq_flow_handler_t handler;
     50	u32 control = readl_relaxed(fic->base + AL_FIC_CONTROL);
     51
     52	if (new_state == AL_FIC_CONFIGURED_LEVEL) {
     53		handler = handle_level_irq;
     54		control &= ~CONTROL_TRIGGER_RISING;
     55	} else {
     56		handler = handle_edge_irq;
     57		control |= CONTROL_TRIGGER_RISING;
     58	}
     59	gc->chip_types->handler = handler;
     60	fic->state = new_state;
     61	writel_relaxed(control, fic->base + AL_FIC_CONTROL);
     62}
     63
     64static int al_fic_irq_set_type(struct irq_data *data, unsigned int flow_type)
     65{
     66	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
     67	struct al_fic *fic = gc->private;
     68	enum al_fic_state new_state;
     69	int ret = 0;
     70
     71	irq_gc_lock(gc);
     72
     73	if (((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_HIGH) &&
     74	    ((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_EDGE_RISING)) {
     75		pr_debug("fic doesn't support flow type %d\n", flow_type);
     76		ret = -EINVAL;
     77		goto err;
     78	}
     79
     80	new_state = (flow_type & IRQ_TYPE_LEVEL_HIGH) ?
     81		AL_FIC_CONFIGURED_LEVEL : AL_FIC_CONFIGURED_RISING_EDGE;
     82
     83	/*
     84	 * A given FIC instance can be either all level or all edge triggered.
     85	 * This is generally fixed depending on what pieces of HW it's wired up
     86	 * to.
     87	 *
     88	 * We configure it based on the sensitivity of the first source
     89	 * being setup, and reject any subsequent attempt at configuring it in a
     90	 * different way.
     91	 */
     92	if (fic->state == AL_FIC_UNCONFIGURED) {
     93		al_fic_set_trigger(fic, gc, new_state);
     94	} else if (fic->state != new_state) {
     95		pr_debug("fic %s state already configured to %d\n",
     96			 fic->name, fic->state);
     97		ret = -EINVAL;
     98		goto err;
     99	}
    100
    101err:
    102	irq_gc_unlock(gc);
    103
    104	return ret;
    105}
    106
    107static void al_fic_irq_handler(struct irq_desc *desc)
    108{
    109	struct al_fic *fic = irq_desc_get_handler_data(desc);
    110	struct irq_domain *domain = fic->domain;
    111	struct irq_chip *irqchip = irq_desc_get_chip(desc);
    112	struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0);
    113	unsigned long pending;
    114	u32 hwirq;
    115
    116	chained_irq_enter(irqchip, desc);
    117
    118	pending = readl_relaxed(fic->base + AL_FIC_CAUSE);
    119	pending &= ~gc->mask_cache;
    120
    121	for_each_set_bit(hwirq, &pending, NR_FIC_IRQS)
    122		generic_handle_domain_irq(domain, hwirq);
    123
    124	chained_irq_exit(irqchip, desc);
    125}
    126
    127static int al_fic_irq_retrigger(struct irq_data *data)
    128{
    129	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
    130	struct al_fic *fic = gc->private;
    131
    132	writel_relaxed(BIT(data->hwirq), fic->base + AL_FIC_SET_CAUSE);
    133
    134	return 1;
    135}
    136
    137static int al_fic_register(struct device_node *node,
    138			   struct al_fic *fic)
    139{
    140	struct irq_chip_generic *gc;
    141	int ret;
    142
    143	fic->domain = irq_domain_add_linear(node,
    144					    NR_FIC_IRQS,
    145					    &irq_generic_chip_ops,
    146					    fic);
    147	if (!fic->domain) {
    148		pr_err("fail to add irq domain\n");
    149		return -ENOMEM;
    150	}
    151
    152	ret = irq_alloc_domain_generic_chips(fic->domain,
    153					     NR_FIC_IRQS,
    154					     1, fic->name,
    155					     handle_level_irq,
    156					     0, 0, IRQ_GC_INIT_MASK_CACHE);
    157	if (ret) {
    158		pr_err("fail to allocate generic chip (%d)\n", ret);
    159		goto err_domain_remove;
    160	}
    161
    162	gc = irq_get_domain_generic_chip(fic->domain, 0);
    163	gc->reg_base = fic->base;
    164	gc->chip_types->regs.mask = AL_FIC_MASK;
    165	gc->chip_types->regs.ack = AL_FIC_CAUSE;
    166	gc->chip_types->chip.irq_mask = irq_gc_mask_set_bit;
    167	gc->chip_types->chip.irq_unmask = irq_gc_mask_clr_bit;
    168	gc->chip_types->chip.irq_ack = irq_gc_ack_clr_bit;
    169	gc->chip_types->chip.irq_set_type = al_fic_irq_set_type;
    170	gc->chip_types->chip.irq_retrigger = al_fic_irq_retrigger;
    171	gc->chip_types->chip.flags = IRQCHIP_SKIP_SET_WAKE;
    172	gc->private = fic;
    173
    174	irq_set_chained_handler_and_data(fic->parent_irq,
    175					 al_fic_irq_handler,
    176					 fic);
    177	return 0;
    178
    179err_domain_remove:
    180	irq_domain_remove(fic->domain);
    181
    182	return ret;
    183}
    184
    185/*
    186 * al_fic_wire_init() - initialize and configure fic in wire mode
    187 * @of_node: optional pointer to interrupt controller's device tree node.
    188 * @base: mmio to fic register
    189 * @name: name of the fic
    190 * @parent_irq: interrupt of parent
    191 *
    192 * This API will configure the fic hardware to to work in wire mode.
    193 * In wire mode, fic hardware is generating a wire ("wired") interrupt.
    194 * Interrupt can be generated based on positive edge or level - configuration is
    195 * to be determined based on connected hardware to this fic.
    196 */
    197static struct al_fic *al_fic_wire_init(struct device_node *node,
    198				       void __iomem *base,
    199				       const char *name,
    200				       unsigned int parent_irq)
    201{
    202	struct al_fic *fic;
    203	int ret;
    204	u32 control = CONTROL_MASK_MSI_X;
    205
    206	fic = kzalloc(sizeof(*fic), GFP_KERNEL);
    207	if (!fic)
    208		return ERR_PTR(-ENOMEM);
    209
    210	fic->base = base;
    211	fic->parent_irq = parent_irq;
    212	fic->name = name;
    213
    214	/* mask out all interrupts */
    215	writel_relaxed(0xFFFFFFFF, fic->base + AL_FIC_MASK);
    216
    217	/* clear any pending interrupt */
    218	writel_relaxed(0, fic->base + AL_FIC_CAUSE);
    219
    220	writel_relaxed(control, fic->base + AL_FIC_CONTROL);
    221
    222	ret = al_fic_register(node, fic);
    223	if (ret) {
    224		pr_err("fail to register irqchip\n");
    225		goto err_free;
    226	}
    227
    228	pr_debug("%s initialized successfully in Legacy mode (parent-irq=%u)\n",
    229		 fic->name, parent_irq);
    230
    231	return fic;
    232
    233err_free:
    234	kfree(fic);
    235	return ERR_PTR(ret);
    236}
    237
    238static int __init al_fic_init_dt(struct device_node *node,
    239				 struct device_node *parent)
    240{
    241	int ret;
    242	void __iomem *base;
    243	unsigned int parent_irq;
    244	struct al_fic *fic;
    245
    246	if (!parent) {
    247		pr_err("%s: unsupported - device require a parent\n",
    248		       node->name);
    249		return -EINVAL;
    250	}
    251
    252	base = of_iomap(node, 0);
    253	if (!base) {
    254		pr_err("%s: fail to map memory\n", node->name);
    255		return -ENOMEM;
    256	}
    257
    258	parent_irq = irq_of_parse_and_map(node, 0);
    259	if (!parent_irq) {
    260		pr_err("%s: fail to map irq\n", node->name);
    261		ret = -EINVAL;
    262		goto err_unmap;
    263	}
    264
    265	fic = al_fic_wire_init(node,
    266			       base,
    267			       node->name,
    268			       parent_irq);
    269	if (IS_ERR(fic)) {
    270		pr_err("%s: fail to initialize irqchip (%lu)\n",
    271		       node->name,
    272		       PTR_ERR(fic));
    273		ret = PTR_ERR(fic);
    274		goto err_irq_dispose;
    275	}
    276
    277	return 0;
    278
    279err_irq_dispose:
    280	irq_dispose_mapping(parent_irq);
    281err_unmap:
    282	iounmap(base);
    283
    284	return ret;
    285}
    286
    287IRQCHIP_DECLARE(al_fic, "amazon,al-fic", al_fic_init_dt);