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-mvebu-gicp.c (6564B)


      1/*
      2 * Copyright (C) 2017 Marvell
      3 *
      4 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
      5 *
      6 * This file is licensed under the terms of the GNU General Public
      7 * License version 2. This program is licensed "as is" without any
      8 * warranty of any kind, whether express or implied.
      9 */
     10
     11#include <linux/io.h>
     12#include <linux/irq.h>
     13#include <linux/irqdomain.h>
     14#include <linux/msi.h>
     15#include <linux/of.h>
     16#include <linux/of_irq.h>
     17#include <linux/of_platform.h>
     18#include <linux/platform_device.h>
     19
     20#include <dt-bindings/interrupt-controller/arm-gic.h>
     21
     22#define GICP_SETSPI_NSR_OFFSET	0x0
     23#define GICP_CLRSPI_NSR_OFFSET	0x8
     24
     25struct mvebu_gicp_spi_range {
     26	unsigned int start;
     27	unsigned int count;
     28};
     29
     30struct mvebu_gicp {
     31	struct mvebu_gicp_spi_range *spi_ranges;
     32	unsigned int spi_ranges_cnt;
     33	unsigned int spi_cnt;
     34	unsigned long *spi_bitmap;
     35	spinlock_t spi_lock;
     36	struct resource *res;
     37	struct device *dev;
     38};
     39
     40static int gicp_idx_to_spi(struct mvebu_gicp *gicp, int idx)
     41{
     42	int i;
     43
     44	for (i = 0; i < gicp->spi_ranges_cnt; i++) {
     45		struct mvebu_gicp_spi_range *r = &gicp->spi_ranges[i];
     46
     47		if (idx < r->count)
     48			return r->start + idx;
     49
     50		idx -= r->count;
     51	}
     52
     53	return -EINVAL;
     54}
     55
     56static void gicp_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
     57{
     58	struct mvebu_gicp *gicp = data->chip_data;
     59	phys_addr_t setspi = gicp->res->start + GICP_SETSPI_NSR_OFFSET;
     60	phys_addr_t clrspi = gicp->res->start + GICP_CLRSPI_NSR_OFFSET;
     61
     62	msg[0].data = data->hwirq;
     63	msg[0].address_lo = lower_32_bits(setspi);
     64	msg[0].address_hi = upper_32_bits(setspi);
     65	msg[1].data = data->hwirq;
     66	msg[1].address_lo = lower_32_bits(clrspi);
     67	msg[1].address_hi = upper_32_bits(clrspi);
     68}
     69
     70static struct irq_chip gicp_irq_chip = {
     71	.name			= "GICP",
     72	.irq_mask		= irq_chip_mask_parent,
     73	.irq_unmask		= irq_chip_unmask_parent,
     74	.irq_eoi		= irq_chip_eoi_parent,
     75	.irq_set_affinity	= irq_chip_set_affinity_parent,
     76	.irq_set_type		= irq_chip_set_type_parent,
     77	.irq_compose_msi_msg	= gicp_compose_msi_msg,
     78};
     79
     80static int gicp_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
     81				 unsigned int nr_irqs, void *args)
     82{
     83	struct mvebu_gicp *gicp = domain->host_data;
     84	struct irq_fwspec fwspec;
     85	unsigned int hwirq;
     86	int ret;
     87
     88	spin_lock(&gicp->spi_lock);
     89	hwirq = find_first_zero_bit(gicp->spi_bitmap, gicp->spi_cnt);
     90	if (hwirq == gicp->spi_cnt) {
     91		spin_unlock(&gicp->spi_lock);
     92		return -ENOSPC;
     93	}
     94	__set_bit(hwirq, gicp->spi_bitmap);
     95	spin_unlock(&gicp->spi_lock);
     96
     97	fwspec.fwnode = domain->parent->fwnode;
     98	fwspec.param_count = 3;
     99	fwspec.param[0] = GIC_SPI;
    100	fwspec.param[1] = gicp_idx_to_spi(gicp, hwirq) - 32;
    101	/*
    102	 * Assume edge rising for now, it will be properly set when
    103	 * ->set_type() is called
    104	 */
    105	fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
    106
    107	ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
    108	if (ret) {
    109		dev_err(gicp->dev, "Cannot allocate parent IRQ\n");
    110		goto free_hwirq;
    111	}
    112
    113	ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
    114					    &gicp_irq_chip, gicp);
    115	if (ret)
    116		goto free_irqs_parent;
    117
    118	return 0;
    119
    120free_irqs_parent:
    121	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
    122free_hwirq:
    123	spin_lock(&gicp->spi_lock);
    124	__clear_bit(hwirq, gicp->spi_bitmap);
    125	spin_unlock(&gicp->spi_lock);
    126	return ret;
    127}
    128
    129static void gicp_irq_domain_free(struct irq_domain *domain,
    130				 unsigned int virq, unsigned int nr_irqs)
    131{
    132	struct mvebu_gicp *gicp = domain->host_data;
    133	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
    134
    135	if (d->hwirq >= gicp->spi_cnt) {
    136		dev_err(gicp->dev, "Invalid hwirq %lu\n", d->hwirq);
    137		return;
    138	}
    139
    140	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
    141
    142	spin_lock(&gicp->spi_lock);
    143	__clear_bit(d->hwirq, gicp->spi_bitmap);
    144	spin_unlock(&gicp->spi_lock);
    145}
    146
    147static const struct irq_domain_ops gicp_domain_ops = {
    148	.alloc	= gicp_irq_domain_alloc,
    149	.free	= gicp_irq_domain_free,
    150};
    151
    152static struct irq_chip gicp_msi_irq_chip = {
    153	.name		= "GICP",
    154	.irq_set_type	= irq_chip_set_type_parent,
    155	.flags		= IRQCHIP_SUPPORTS_LEVEL_MSI,
    156};
    157
    158static struct msi_domain_ops gicp_msi_ops = {
    159};
    160
    161static struct msi_domain_info gicp_msi_domain_info = {
    162	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
    163		   MSI_FLAG_LEVEL_CAPABLE),
    164	.ops	= &gicp_msi_ops,
    165	.chip	= &gicp_msi_irq_chip,
    166};
    167
    168static int mvebu_gicp_probe(struct platform_device *pdev)
    169{
    170	struct mvebu_gicp *gicp;
    171	struct irq_domain *inner_domain, *plat_domain, *parent_domain;
    172	struct device_node *node = pdev->dev.of_node;
    173	struct device_node *irq_parent_dn;
    174	int ret, i;
    175
    176	gicp = devm_kzalloc(&pdev->dev, sizeof(*gicp), GFP_KERNEL);
    177	if (!gicp)
    178		return -ENOMEM;
    179
    180	gicp->dev = &pdev->dev;
    181	spin_lock_init(&gicp->spi_lock);
    182
    183	gicp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    184	if (!gicp->res)
    185		return -ENODEV;
    186
    187	ret = of_property_count_u32_elems(node, "marvell,spi-ranges");
    188	if (ret < 0)
    189		return ret;
    190
    191	gicp->spi_ranges_cnt = ret / 2;
    192
    193	gicp->spi_ranges =
    194		devm_kcalloc(&pdev->dev,
    195			     gicp->spi_ranges_cnt,
    196			     sizeof(struct mvebu_gicp_spi_range),
    197			     GFP_KERNEL);
    198	if (!gicp->spi_ranges)
    199		return -ENOMEM;
    200
    201	for (i = 0; i < gicp->spi_ranges_cnt; i++) {
    202		of_property_read_u32_index(node, "marvell,spi-ranges",
    203					   i * 2,
    204					   &gicp->spi_ranges[i].start);
    205
    206		of_property_read_u32_index(node, "marvell,spi-ranges",
    207					   i * 2 + 1,
    208					   &gicp->spi_ranges[i].count);
    209
    210		gicp->spi_cnt += gicp->spi_ranges[i].count;
    211	}
    212
    213	gicp->spi_bitmap = devm_bitmap_zalloc(&pdev->dev, gicp->spi_cnt, GFP_KERNEL);
    214	if (!gicp->spi_bitmap)
    215		return -ENOMEM;
    216
    217	irq_parent_dn = of_irq_find_parent(node);
    218	if (!irq_parent_dn) {
    219		dev_err(&pdev->dev, "failed to find parent IRQ node\n");
    220		return -ENODEV;
    221	}
    222
    223	parent_domain = irq_find_host(irq_parent_dn);
    224	if (!parent_domain) {
    225		dev_err(&pdev->dev, "failed to find parent IRQ domain\n");
    226		return -ENODEV;
    227	}
    228
    229	inner_domain = irq_domain_create_hierarchy(parent_domain, 0,
    230						   gicp->spi_cnt,
    231						   of_node_to_fwnode(node),
    232						   &gicp_domain_ops, gicp);
    233	if (!inner_domain)
    234		return -ENOMEM;
    235
    236
    237	plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node),
    238						     &gicp_msi_domain_info,
    239						     inner_domain);
    240	if (!plat_domain) {
    241		irq_domain_remove(inner_domain);
    242		return -ENOMEM;
    243	}
    244
    245	platform_set_drvdata(pdev, gicp);
    246
    247	return 0;
    248}
    249
    250static const struct of_device_id mvebu_gicp_of_match[] = {
    251	{ .compatible = "marvell,ap806-gicp", },
    252	{},
    253};
    254
    255static struct platform_driver mvebu_gicp_driver = {
    256	.probe  = mvebu_gicp_probe,
    257	.driver = {
    258		.name = "mvebu-gicp",
    259		.of_match_table = mvebu_gicp_of_match,
    260	},
    261};
    262builtin_platform_driver(mvebu_gicp_driver);