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

msi.c (4392B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright 2007, Olof Johansson, PA Semi
      4 *
      5 * Based on arch/powerpc/sysdev/mpic_u3msi.c:
      6 *
      7 * Copyright 2006, Segher Boessenkool, IBM Corporation.
      8 * Copyright 2006-2007, Michael Ellerman, IBM Corporation.
      9 */
     10
     11#include <linux/irq.h>
     12#include <linux/irqdomain.h>
     13#include <linux/msi.h>
     14#include <asm/mpic.h>
     15#include <asm/hw_irq.h>
     16#include <asm/ppc-pci.h>
     17#include <asm/msi_bitmap.h>
     18
     19#include <sysdev/mpic.h>
     20
     21/* Allocate 16 interrupts per device, to give an alignment of 16,
     22 * since that's the size of the grouping w.r.t. affinity. If someone
     23 * needs more than 32 MSI's down the road we'll have to rethink this,
     24 * but it should be OK for now.
     25 */
     26#define ALLOC_CHUNK 16
     27
     28#define PASEMI_MSI_ADDR 0xfc080000
     29
     30/* A bit ugly, can we get this from the pci_dev somehow? */
     31static struct mpic *msi_mpic;
     32
     33
     34static void mpic_pasemi_msi_mask_irq(struct irq_data *data)
     35{
     36	pr_debug("mpic_pasemi_msi_mask_irq %d\n", data->irq);
     37	pci_msi_mask_irq(data);
     38	mpic_mask_irq(data);
     39}
     40
     41static void mpic_pasemi_msi_unmask_irq(struct irq_data *data)
     42{
     43	pr_debug("mpic_pasemi_msi_unmask_irq %d\n", data->irq);
     44	mpic_unmask_irq(data);
     45	pci_msi_unmask_irq(data);
     46}
     47
     48static struct irq_chip mpic_pasemi_msi_chip = {
     49	.irq_shutdown		= mpic_pasemi_msi_mask_irq,
     50	.irq_mask		= mpic_pasemi_msi_mask_irq,
     51	.irq_unmask		= mpic_pasemi_msi_unmask_irq,
     52	.irq_eoi		= mpic_end_irq,
     53	.irq_set_type		= mpic_set_irq_type,
     54	.irq_set_affinity	= mpic_set_affinity,
     55	.name			= "PASEMI-MSI",
     56};
     57
     58static void pasemi_msi_teardown_msi_irqs(struct pci_dev *pdev)
     59{
     60	struct msi_desc *entry;
     61	irq_hw_number_t hwirq;
     62
     63	pr_debug("pasemi_msi_teardown_msi_irqs, pdev %p\n", pdev);
     64
     65	msi_for_each_desc(entry, &pdev->dev, MSI_DESC_ASSOCIATED) {
     66		hwirq = virq_to_hw(entry->irq);
     67		irq_set_msi_desc(entry->irq, NULL);
     68		irq_dispose_mapping(entry->irq);
     69		msi_bitmap_free_hwirqs(&msi_mpic->msi_bitmap, hwirq, ALLOC_CHUNK);
     70	}
     71}
     72
     73static int pasemi_msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
     74{
     75	unsigned int virq;
     76	struct msi_desc *entry;
     77	struct msi_msg msg;
     78	int hwirq;
     79
     80	if (type == PCI_CAP_ID_MSIX)
     81		pr_debug("pasemi_msi: MSI-X untested, trying anyway\n");
     82	pr_debug("pasemi_msi_setup_msi_irqs, pdev %p nvec %d type %d\n",
     83		 pdev, nvec, type);
     84
     85	msg.address_hi = 0;
     86	msg.address_lo = PASEMI_MSI_ADDR;
     87
     88	msi_for_each_desc(entry, &pdev->dev, MSI_DESC_NOTASSOCIATED) {
     89		/* Allocate 16 interrupts for now, since that's the grouping for
     90		 * affinity. This can be changed later if it turns out 32 is too
     91		 * few MSIs for someone, but restrictions will apply to how the
     92		 * sources can be changed independently.
     93		 */
     94		hwirq = msi_bitmap_alloc_hwirqs(&msi_mpic->msi_bitmap,
     95						ALLOC_CHUNK);
     96		if (hwirq < 0) {
     97			pr_debug("pasemi_msi: failed allocating hwirq\n");
     98			return hwirq;
     99		}
    100
    101		virq = irq_create_mapping(msi_mpic->irqhost, hwirq);
    102		if (!virq) {
    103			pr_debug("pasemi_msi: failed mapping hwirq 0x%x\n",
    104				  hwirq);
    105			msi_bitmap_free_hwirqs(&msi_mpic->msi_bitmap, hwirq,
    106					       ALLOC_CHUNK);
    107			return -ENOSPC;
    108		}
    109
    110		/* Vector on MSI is really an offset, the hardware adds
    111		 * it to the value written at the magic address. So set
    112		 * it to 0 to remain sane.
    113		 */
    114		mpic_set_vector(virq, 0);
    115
    116		irq_set_msi_desc(virq, entry);
    117		irq_set_chip(virq, &mpic_pasemi_msi_chip);
    118		irq_set_irq_type(virq, IRQ_TYPE_EDGE_RISING);
    119
    120		pr_debug("pasemi_msi: allocated virq 0x%x (hw 0x%x) " \
    121			 "addr 0x%x\n", virq, hwirq, msg.address_lo);
    122
    123		/* Likewise, the device writes [0...511] into the target
    124		 * register to generate MSI [512...1023]
    125		 */
    126		msg.data = hwirq-0x200;
    127		pci_write_msi_msg(virq, &msg);
    128	}
    129
    130	return 0;
    131}
    132
    133int __init mpic_pasemi_msi_init(struct mpic *mpic)
    134{
    135	int rc;
    136	struct pci_controller *phb;
    137	struct device_node *of_node;
    138
    139	of_node = irq_domain_get_of_node(mpic->irqhost);
    140	if (!of_node ||
    141	    !of_device_is_compatible(of_node,
    142				     "pasemi,pwrficient-openpic"))
    143		return -ENODEV;
    144
    145	rc = mpic_msi_init_allocator(mpic);
    146	if (rc) {
    147		pr_debug("pasemi_msi: Error allocating bitmap!\n");
    148		return rc;
    149	}
    150
    151	pr_debug("pasemi_msi: Registering PA Semi MPIC MSI callbacks\n");
    152
    153	msi_mpic = mpic;
    154	list_for_each_entry(phb, &hose_list, list_node) {
    155		WARN_ON(phb->controller_ops.setup_msi_irqs);
    156		phb->controller_ops.setup_msi_irqs = pasemi_msi_setup_msi_irqs;
    157		phb->controller_ops.teardown_msi_irqs = pasemi_msi_teardown_msi_irqs;
    158	}
    159
    160	return 0;
    161}