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-davinci-cp-intc.c (7428B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2//
      3// Author: Steve Chen <schen@mvista.com>
      4// Copyright (C) 2008-2009, MontaVista Software, Inc. <source@mvista.com>
      5// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
      6// Copyright (C) 2019, Texas Instruments
      7//
      8// TI Common Platform Interrupt Controller (cp_intc) driver
      9
     10#include <linux/export.h>
     11#include <linux/init.h>
     12#include <linux/irq.h>
     13#include <linux/irqchip.h>
     14#include <linux/irqchip/irq-davinci-cp-intc.h>
     15#include <linux/irqdomain.h>
     16#include <linux/io.h>
     17#include <linux/of.h>
     18#include <linux/of_address.h>
     19#include <linux/of_irq.h>
     20
     21#include <asm/exception.h>
     22
     23#define DAVINCI_CP_INTC_CTRL			0x04
     24#define DAVINCI_CP_INTC_HOST_CTRL		0x0c
     25#define DAVINCI_CP_INTC_GLOBAL_ENABLE		0x10
     26#define DAVINCI_CP_INTC_SYS_STAT_IDX_CLR	0x24
     27#define DAVINCI_CP_INTC_SYS_ENABLE_IDX_SET	0x28
     28#define DAVINCI_CP_INTC_SYS_ENABLE_IDX_CLR	0x2c
     29#define DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET	0x34
     30#define DAVINCI_CP_INTC_HOST_ENABLE_IDX_CLR	0x38
     31#define DAVINCI_CP_INTC_PRIO_IDX		0x80
     32#define DAVINCI_CP_INTC_SYS_STAT_CLR(n)		(0x0280 + (n << 2))
     33#define DAVINCI_CP_INTC_SYS_ENABLE_CLR(n)	(0x0380 + (n << 2))
     34#define DAVINCI_CP_INTC_CHAN_MAP(n)		(0x0400 + (n << 2))
     35#define DAVINCI_CP_INTC_SYS_POLARITY(n)		(0x0d00 + (n << 2))
     36#define DAVINCI_CP_INTC_SYS_TYPE(n)		(0x0d80 + (n << 2))
     37#define DAVINCI_CP_INTC_HOST_ENABLE(n)		(0x1500 + (n << 2))
     38#define DAVINCI_CP_INTC_PRI_INDX_MASK		GENMASK(9, 0)
     39#define DAVINCI_CP_INTC_GPIR_NONE		BIT(31)
     40
     41static void __iomem *davinci_cp_intc_base;
     42static struct irq_domain *davinci_cp_intc_irq_domain;
     43
     44static inline unsigned int davinci_cp_intc_read(unsigned int offset)
     45{
     46	return readl_relaxed(davinci_cp_intc_base + offset);
     47}
     48
     49static inline void davinci_cp_intc_write(unsigned long value,
     50					 unsigned int offset)
     51{
     52	writel_relaxed(value, davinci_cp_intc_base + offset);
     53}
     54
     55static void davinci_cp_intc_ack_irq(struct irq_data *d)
     56{
     57	davinci_cp_intc_write(d->hwirq, DAVINCI_CP_INTC_SYS_STAT_IDX_CLR);
     58}
     59
     60static void davinci_cp_intc_mask_irq(struct irq_data *d)
     61{
     62	/* XXX don't know why we need to disable nIRQ here... */
     63	davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_CLR);
     64	davinci_cp_intc_write(d->hwirq, DAVINCI_CP_INTC_SYS_ENABLE_IDX_CLR);
     65	davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET);
     66}
     67
     68static void davinci_cp_intc_unmask_irq(struct irq_data *d)
     69{
     70	davinci_cp_intc_write(d->hwirq, DAVINCI_CP_INTC_SYS_ENABLE_IDX_SET);
     71}
     72
     73static int davinci_cp_intc_set_irq_type(struct irq_data *d,
     74					unsigned int flow_type)
     75{
     76	unsigned int reg, mask, polarity, type;
     77
     78	reg = BIT_WORD(d->hwirq);
     79	mask = BIT_MASK(d->hwirq);
     80	polarity = davinci_cp_intc_read(DAVINCI_CP_INTC_SYS_POLARITY(reg));
     81	type = davinci_cp_intc_read(DAVINCI_CP_INTC_SYS_TYPE(reg));
     82
     83	switch (flow_type) {
     84	case IRQ_TYPE_EDGE_RISING:
     85		polarity |= mask;
     86		type |= mask;
     87		break;
     88	case IRQ_TYPE_EDGE_FALLING:
     89		polarity &= ~mask;
     90		type |= mask;
     91		break;
     92	case IRQ_TYPE_LEVEL_HIGH:
     93		polarity |= mask;
     94		type &= ~mask;
     95		break;
     96	case IRQ_TYPE_LEVEL_LOW:
     97		polarity &= ~mask;
     98		type &= ~mask;
     99		break;
    100	default:
    101		return -EINVAL;
    102	}
    103
    104	davinci_cp_intc_write(polarity, DAVINCI_CP_INTC_SYS_POLARITY(reg));
    105	davinci_cp_intc_write(type, DAVINCI_CP_INTC_SYS_TYPE(reg));
    106
    107	return 0;
    108}
    109
    110static struct irq_chip davinci_cp_intc_irq_chip = {
    111	.name		= "cp_intc",
    112	.irq_ack	= davinci_cp_intc_ack_irq,
    113	.irq_mask	= davinci_cp_intc_mask_irq,
    114	.irq_unmask	= davinci_cp_intc_unmask_irq,
    115	.irq_set_type	= davinci_cp_intc_set_irq_type,
    116	.flags		= IRQCHIP_SKIP_SET_WAKE,
    117};
    118
    119static asmlinkage void __exception_irq_entry
    120davinci_cp_intc_handle_irq(struct pt_regs *regs)
    121{
    122	int gpir, irqnr, none;
    123
    124	/*
    125	 * The interrupt number is in first ten bits. The NONE field set to 1
    126	 * indicates a spurious irq.
    127	 */
    128
    129	gpir = davinci_cp_intc_read(DAVINCI_CP_INTC_PRIO_IDX);
    130	irqnr = gpir & DAVINCI_CP_INTC_PRI_INDX_MASK;
    131	none = gpir & DAVINCI_CP_INTC_GPIR_NONE;
    132
    133	if (unlikely(none)) {
    134		pr_err_once("%s: spurious irq!\n", __func__);
    135		return;
    136	}
    137
    138	generic_handle_domain_irq(davinci_cp_intc_irq_domain, irqnr);
    139}
    140
    141static int davinci_cp_intc_host_map(struct irq_domain *h, unsigned int virq,
    142			  irq_hw_number_t hw)
    143{
    144	pr_debug("cp_intc_host_map(%d, 0x%lx)\n", virq, hw);
    145
    146	irq_set_chip(virq, &davinci_cp_intc_irq_chip);
    147	irq_set_probe(virq);
    148	irq_set_handler(virq, handle_edge_irq);
    149
    150	return 0;
    151}
    152
    153static const struct irq_domain_ops davinci_cp_intc_irq_domain_ops = {
    154	.map = davinci_cp_intc_host_map,
    155	.xlate = irq_domain_xlate_onetwocell,
    156};
    157
    158static int __init
    159davinci_cp_intc_do_init(const struct davinci_cp_intc_config *config,
    160			struct device_node *node)
    161{
    162	unsigned int num_regs = BITS_TO_LONGS(config->num_irqs);
    163	int offset, irq_base;
    164	void __iomem *req;
    165
    166	req = request_mem_region(config->reg.start,
    167				 resource_size(&config->reg),
    168				 "davinci-cp-intc");
    169	if (!req) {
    170		pr_err("%s: register range busy\n", __func__);
    171		return -EBUSY;
    172	}
    173
    174	davinci_cp_intc_base = ioremap(config->reg.start,
    175				       resource_size(&config->reg));
    176	if (!davinci_cp_intc_base) {
    177		pr_err("%s: unable to ioremap register range\n", __func__);
    178		return -EINVAL;
    179	}
    180
    181	davinci_cp_intc_write(0, DAVINCI_CP_INTC_GLOBAL_ENABLE);
    182
    183	/* Disable all host interrupts */
    184	davinci_cp_intc_write(0, DAVINCI_CP_INTC_HOST_ENABLE(0));
    185
    186	/* Disable system interrupts */
    187	for (offset = 0; offset < num_regs; offset++)
    188		davinci_cp_intc_write(~0,
    189			DAVINCI_CP_INTC_SYS_ENABLE_CLR(offset));
    190
    191	/* Set to normal mode, no nesting, no priority hold */
    192	davinci_cp_intc_write(0, DAVINCI_CP_INTC_CTRL);
    193	davinci_cp_intc_write(0, DAVINCI_CP_INTC_HOST_CTRL);
    194
    195	/* Clear system interrupt status */
    196	for (offset = 0; offset < num_regs; offset++)
    197		davinci_cp_intc_write(~0,
    198			DAVINCI_CP_INTC_SYS_STAT_CLR(offset));
    199
    200	/* Enable nIRQ (what about nFIQ?) */
    201	davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET);
    202
    203	/* Default all priorities to channel 7. */
    204	num_regs = (config->num_irqs + 3) >> 2;	/* 4 channels per register */
    205	for (offset = 0; offset < num_regs; offset++)
    206		davinci_cp_intc_write(0x07070707,
    207			DAVINCI_CP_INTC_CHAN_MAP(offset));
    208
    209	irq_base = irq_alloc_descs(-1, 0, config->num_irqs, 0);
    210	if (irq_base < 0) {
    211		pr_err("%s: unable to allocate interrupt descriptors: %d\n",
    212		       __func__, irq_base);
    213		return irq_base;
    214	}
    215
    216	davinci_cp_intc_irq_domain = irq_domain_add_legacy(
    217					node, config->num_irqs, irq_base, 0,
    218					&davinci_cp_intc_irq_domain_ops, NULL);
    219
    220	if (!davinci_cp_intc_irq_domain) {
    221		pr_err("%s: unable to create an interrupt domain\n", __func__);
    222		return -EINVAL;
    223	}
    224
    225	set_handle_irq(davinci_cp_intc_handle_irq);
    226
    227	/* Enable global interrupt */
    228	davinci_cp_intc_write(1, DAVINCI_CP_INTC_GLOBAL_ENABLE);
    229
    230	return 0;
    231}
    232
    233int __init davinci_cp_intc_init(const struct davinci_cp_intc_config *config)
    234{
    235	return davinci_cp_intc_do_init(config, NULL);
    236}
    237
    238static int __init davinci_cp_intc_of_init(struct device_node *node,
    239					  struct device_node *parent)
    240{
    241	struct davinci_cp_intc_config config = { };
    242	int ret;
    243
    244	ret = of_address_to_resource(node, 0, &config.reg);
    245	if (ret) {
    246		pr_err("%s: unable to get the register range from device-tree\n",
    247		       __func__);
    248		return ret;
    249	}
    250
    251	ret = of_property_read_u32(node, "ti,intc-size", &config.num_irqs);
    252	if (ret) {
    253		pr_err("%s: unable to read the 'ti,intc-size' property\n",
    254		       __func__);
    255		return ret;
    256	}
    257
    258	return davinci_cp_intc_do_init(&config, node);
    259}
    260IRQCHIP_DECLARE(cp_intc, "ti,cp-intc", davinci_cp_intc_of_init);