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-mchp-eic.c (6566B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Microchip External Interrupt Controller driver
      4 *
      5 * Copyright (C) 2021 Microchip Technology Inc. and its subsidiaries
      6 *
      7 * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
      8 */
      9#include <linux/clk.h>
     10#include <linux/delay.h>
     11#include <linux/interrupt.h>
     12#include <linux/irqchip.h>
     13#include <linux/of_address.h>
     14#include <linux/of_irq.h>
     15#include <linux/syscore_ops.h>
     16
     17#include <dt-bindings/interrupt-controller/arm-gic.h>
     18
     19#define MCHP_EIC_GFCS			(0x0)
     20#define MCHP_EIC_SCFG(x)		(0x4 + (x) * 0x4)
     21#define MCHP_EIC_SCFG_EN		BIT(16)
     22#define MCHP_EIC_SCFG_LVL		BIT(9)
     23#define MCHP_EIC_SCFG_POL		BIT(8)
     24
     25#define MCHP_EIC_NIRQ			(2)
     26
     27/*
     28 * struct mchp_eic - EIC private data structure
     29 * @base: base address
     30 * @clk: peripheral clock
     31 * @domain: irq domain
     32 * @irqs: irqs b/w eic and gic
     33 * @scfg: backup for scfg registers (necessary for backup and self-refresh mode)
     34 * @wakeup_source: wakeup source mask
     35 */
     36struct mchp_eic {
     37	void __iomem *base;
     38	struct clk *clk;
     39	struct irq_domain *domain;
     40	u32 irqs[MCHP_EIC_NIRQ];
     41	u32 scfg[MCHP_EIC_NIRQ];
     42	u32 wakeup_source;
     43};
     44
     45static struct mchp_eic *eic;
     46
     47static void mchp_eic_irq_mask(struct irq_data *d)
     48{
     49	unsigned int tmp;
     50
     51	tmp = readl_relaxed(eic->base + MCHP_EIC_SCFG(d->hwirq));
     52	tmp &= ~MCHP_EIC_SCFG_EN;
     53	writel_relaxed(tmp, eic->base + MCHP_EIC_SCFG(d->hwirq));
     54
     55	irq_chip_mask_parent(d);
     56}
     57
     58static void mchp_eic_irq_unmask(struct irq_data *d)
     59{
     60	unsigned int tmp;
     61
     62	tmp = readl_relaxed(eic->base + MCHP_EIC_SCFG(d->hwirq));
     63	tmp |= MCHP_EIC_SCFG_EN;
     64	writel_relaxed(tmp, eic->base + MCHP_EIC_SCFG(d->hwirq));
     65
     66	irq_chip_unmask_parent(d);
     67}
     68
     69static int mchp_eic_irq_set_type(struct irq_data *d, unsigned int type)
     70{
     71	unsigned int parent_irq_type;
     72	unsigned int tmp;
     73
     74	tmp = readl_relaxed(eic->base + MCHP_EIC_SCFG(d->hwirq));
     75	tmp &= ~(MCHP_EIC_SCFG_POL | MCHP_EIC_SCFG_LVL);
     76	switch (type) {
     77	case IRQ_TYPE_LEVEL_HIGH:
     78		tmp |= MCHP_EIC_SCFG_POL | MCHP_EIC_SCFG_LVL;
     79		parent_irq_type = IRQ_TYPE_LEVEL_HIGH;
     80		break;
     81	case IRQ_TYPE_LEVEL_LOW:
     82		tmp |= MCHP_EIC_SCFG_LVL;
     83		parent_irq_type = IRQ_TYPE_LEVEL_HIGH;
     84		break;
     85	case IRQ_TYPE_EDGE_RISING:
     86		parent_irq_type = IRQ_TYPE_EDGE_RISING;
     87		break;
     88	case IRQ_TYPE_EDGE_FALLING:
     89		tmp |= MCHP_EIC_SCFG_POL;
     90		parent_irq_type = IRQ_TYPE_EDGE_RISING;
     91		break;
     92	default:
     93		return -EINVAL;
     94	}
     95
     96	writel_relaxed(tmp, eic->base + MCHP_EIC_SCFG(d->hwirq));
     97
     98	return irq_chip_set_type_parent(d, parent_irq_type);
     99}
    100
    101static int mchp_eic_irq_set_wake(struct irq_data *d, unsigned int on)
    102{
    103	irq_set_irq_wake(eic->irqs[d->hwirq], on);
    104	if (on)
    105		eic->wakeup_source |= BIT(d->hwirq);
    106	else
    107		eic->wakeup_source &= ~BIT(d->hwirq);
    108
    109	return 0;
    110}
    111
    112static int mchp_eic_irq_suspend(void)
    113{
    114	unsigned int hwirq;
    115
    116	for (hwirq = 0; hwirq < MCHP_EIC_NIRQ; hwirq++)
    117		eic->scfg[hwirq] = readl_relaxed(eic->base +
    118						 MCHP_EIC_SCFG(hwirq));
    119
    120	if (!eic->wakeup_source)
    121		clk_disable_unprepare(eic->clk);
    122
    123	return 0;
    124}
    125
    126static void mchp_eic_irq_resume(void)
    127{
    128	unsigned int hwirq;
    129
    130	if (!eic->wakeup_source)
    131		clk_prepare_enable(eic->clk);
    132
    133	for (hwirq = 0; hwirq < MCHP_EIC_NIRQ; hwirq++)
    134		writel_relaxed(eic->scfg[hwirq], eic->base +
    135			       MCHP_EIC_SCFG(hwirq));
    136}
    137
    138static struct syscore_ops mchp_eic_syscore_ops = {
    139	.suspend = mchp_eic_irq_suspend,
    140	.resume = mchp_eic_irq_resume,
    141};
    142
    143static struct irq_chip mchp_eic_chip = {
    144	.name		= "eic",
    145	.flags		= IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SET_TYPE_MASKED,
    146	.irq_mask	= mchp_eic_irq_mask,
    147	.irq_unmask	= mchp_eic_irq_unmask,
    148	.irq_set_type	= mchp_eic_irq_set_type,
    149	.irq_ack	= irq_chip_ack_parent,
    150	.irq_eoi	= irq_chip_eoi_parent,
    151	.irq_retrigger	= irq_chip_retrigger_hierarchy,
    152	.irq_set_wake	= mchp_eic_irq_set_wake,
    153};
    154
    155static int mchp_eic_domain_alloc(struct irq_domain *domain, unsigned int virq,
    156				 unsigned int nr_irqs, void *data)
    157{
    158	struct irq_fwspec *fwspec = data;
    159	struct irq_fwspec parent_fwspec;
    160	irq_hw_number_t hwirq;
    161	unsigned int type;
    162	int ret;
    163
    164	if (WARN_ON(nr_irqs != 1))
    165		return -EINVAL;
    166
    167	ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type);
    168	if (ret || hwirq >= MCHP_EIC_NIRQ)
    169		return ret;
    170
    171	switch (type) {
    172	case IRQ_TYPE_EDGE_RISING:
    173	case IRQ_TYPE_LEVEL_HIGH:
    174		break;
    175	case IRQ_TYPE_EDGE_FALLING:
    176		type = IRQ_TYPE_EDGE_RISING;
    177		break;
    178	case IRQ_TYPE_LEVEL_LOW:
    179		type = IRQ_TYPE_LEVEL_HIGH;
    180		break;
    181	default:
    182		return -EINVAL;
    183	}
    184
    185	irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &mchp_eic_chip, eic);
    186
    187	parent_fwspec.fwnode = domain->parent->fwnode;
    188	parent_fwspec.param_count = 3;
    189	parent_fwspec.param[0] = GIC_SPI;
    190	parent_fwspec.param[1] = eic->irqs[hwirq];
    191	parent_fwspec.param[2] = type;
    192
    193	return irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
    194}
    195
    196static const struct irq_domain_ops mchp_eic_domain_ops = {
    197	.translate	= irq_domain_translate_twocell,
    198	.alloc		= mchp_eic_domain_alloc,
    199	.free		= irq_domain_free_irqs_common,
    200};
    201
    202static int mchp_eic_init(struct device_node *node, struct device_node *parent)
    203{
    204	struct irq_domain *parent_domain = NULL;
    205	int ret, i;
    206
    207	eic = kzalloc(sizeof(*eic), GFP_KERNEL);
    208	if (!eic)
    209		return -ENOMEM;
    210
    211	eic->base = of_iomap(node, 0);
    212	if (!eic->base) {
    213		ret = -ENOMEM;
    214		goto free;
    215	}
    216
    217	parent_domain = irq_find_host(parent);
    218	if (!parent_domain) {
    219		ret = -ENODEV;
    220		goto unmap;
    221	}
    222
    223	eic->clk = of_clk_get_by_name(node, "pclk");
    224	if (IS_ERR(eic->clk)) {
    225		ret = PTR_ERR(eic->clk);
    226		goto unmap;
    227	}
    228
    229	ret = clk_prepare_enable(eic->clk);
    230	if (ret)
    231		goto unmap;
    232
    233	for (i = 0; i < MCHP_EIC_NIRQ; i++) {
    234		struct of_phandle_args irq;
    235
    236		/* Disable it, if any. */
    237		writel_relaxed(0UL, eic->base + MCHP_EIC_SCFG(i));
    238
    239		ret = of_irq_parse_one(node, i, &irq);
    240		if (ret)
    241			goto clk_unprepare;
    242
    243		if (WARN_ON(irq.args_count != 3)) {
    244			ret = -EINVAL;
    245			goto clk_unprepare;
    246		}
    247
    248		eic->irqs[i] = irq.args[1];
    249	}
    250
    251	eic->domain = irq_domain_add_hierarchy(parent_domain, 0, MCHP_EIC_NIRQ,
    252					       node, &mchp_eic_domain_ops, eic);
    253	if (!eic->domain) {
    254		pr_err("%pOF: Failed to add domain\n", node);
    255		ret = -ENODEV;
    256		goto clk_unprepare;
    257	}
    258
    259	register_syscore_ops(&mchp_eic_syscore_ops);
    260
    261	pr_info("%pOF: EIC registered, nr_irqs %u\n", node, MCHP_EIC_NIRQ);
    262
    263	return 0;
    264
    265clk_unprepare:
    266	clk_disable_unprepare(eic->clk);
    267unmap:
    268	iounmap(eic->base);
    269free:
    270	kfree(eic);
    271	return ret;
    272}
    273
    274IRQCHIP_PLATFORM_DRIVER_BEGIN(mchp_eic)
    275IRQCHIP_MATCH("microchip,sama7g5-eic", mchp_eic_init)
    276IRQCHIP_PLATFORM_DRIVER_END(mchp_eic)
    277
    278MODULE_DESCRIPTION("Microchip External Interrupt Controller");
    279MODULE_LICENSE("GPL v2");
    280MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea@microchip.com>");