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-loongson-pch-msi.c (5999B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 *  Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
      4 *  Loongson PCH MSI support
      5 */
      6
      7#define pr_fmt(fmt) "pch-msi: " fmt
      8
      9#include <linux/irqchip.h>
     10#include <linux/msi.h>
     11#include <linux/of.h>
     12#include <linux/of_address.h>
     13#include <linux/of_irq.h>
     14#include <linux/of_pci.h>
     15#include <linux/pci.h>
     16#include <linux/slab.h>
     17
     18struct pch_msi_data {
     19	struct mutex	msi_map_lock;
     20	phys_addr_t	doorbell;
     21	u32		irq_first;	/* The vector number that MSIs starts */
     22	u32		num_irqs;	/* The number of vectors for MSIs */
     23	unsigned long	*msi_map;
     24};
     25
     26static void pch_msi_mask_msi_irq(struct irq_data *d)
     27{
     28	pci_msi_mask_irq(d);
     29	irq_chip_mask_parent(d);
     30}
     31
     32static void pch_msi_unmask_msi_irq(struct irq_data *d)
     33{
     34	irq_chip_unmask_parent(d);
     35	pci_msi_unmask_irq(d);
     36}
     37
     38static struct irq_chip pch_msi_irq_chip = {
     39	.name			= "PCH PCI MSI",
     40	.irq_mask		= pch_msi_mask_msi_irq,
     41	.irq_unmask		= pch_msi_unmask_msi_irq,
     42	.irq_ack		= irq_chip_ack_parent,
     43	.irq_set_affinity	= irq_chip_set_affinity_parent,
     44};
     45
     46static int pch_msi_allocate_hwirq(struct pch_msi_data *priv, int num_req)
     47{
     48	int first;
     49
     50	mutex_lock(&priv->msi_map_lock);
     51
     52	first = bitmap_find_free_region(priv->msi_map, priv->num_irqs,
     53					get_count_order(num_req));
     54	if (first < 0) {
     55		mutex_unlock(&priv->msi_map_lock);
     56		return -ENOSPC;
     57	}
     58
     59	mutex_unlock(&priv->msi_map_lock);
     60
     61	return priv->irq_first + first;
     62}
     63
     64static void pch_msi_free_hwirq(struct pch_msi_data *priv,
     65				int hwirq, int num_req)
     66{
     67	int first = hwirq - priv->irq_first;
     68
     69	mutex_lock(&priv->msi_map_lock);
     70	bitmap_release_region(priv->msi_map, first, get_count_order(num_req));
     71	mutex_unlock(&priv->msi_map_lock);
     72}
     73
     74static void pch_msi_compose_msi_msg(struct irq_data *data,
     75					struct msi_msg *msg)
     76{
     77	struct pch_msi_data *priv = irq_data_get_irq_chip_data(data);
     78
     79	msg->address_hi = upper_32_bits(priv->doorbell);
     80	msg->address_lo = lower_32_bits(priv->doorbell);
     81	msg->data = data->hwirq;
     82}
     83
     84static struct msi_domain_info pch_msi_domain_info = {
     85	.flags	= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
     86		  MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
     87	.chip	= &pch_msi_irq_chip,
     88};
     89
     90static struct irq_chip middle_irq_chip = {
     91	.name			= "PCH MSI",
     92	.irq_mask		= irq_chip_mask_parent,
     93	.irq_unmask		= irq_chip_unmask_parent,
     94	.irq_ack		= irq_chip_ack_parent,
     95	.irq_set_affinity	= irq_chip_set_affinity_parent,
     96	.irq_compose_msi_msg	= pch_msi_compose_msi_msg,
     97};
     98
     99static int pch_msi_parent_domain_alloc(struct irq_domain *domain,
    100					unsigned int virq, int hwirq)
    101{
    102	struct irq_fwspec fwspec;
    103
    104	fwspec.fwnode = domain->parent->fwnode;
    105	fwspec.param_count = 1;
    106	fwspec.param[0] = hwirq;
    107
    108	return irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
    109}
    110
    111static int pch_msi_middle_domain_alloc(struct irq_domain *domain,
    112					   unsigned int virq,
    113					   unsigned int nr_irqs, void *args)
    114{
    115	struct pch_msi_data *priv = domain->host_data;
    116	int hwirq, err, i;
    117
    118	hwirq = pch_msi_allocate_hwirq(priv, nr_irqs);
    119	if (hwirq < 0)
    120		return hwirq;
    121
    122	for (i = 0; i < nr_irqs; i++) {
    123		err = pch_msi_parent_domain_alloc(domain, virq + i, hwirq + i);
    124		if (err)
    125			goto err_hwirq;
    126
    127		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
    128					      &middle_irq_chip, priv);
    129	}
    130
    131	return 0;
    132
    133err_hwirq:
    134	pch_msi_free_hwirq(priv, hwirq, nr_irqs);
    135	irq_domain_free_irqs_parent(domain, virq, i - 1);
    136
    137	return err;
    138}
    139
    140static void pch_msi_middle_domain_free(struct irq_domain *domain,
    141					   unsigned int virq,
    142					   unsigned int nr_irqs)
    143{
    144	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
    145	struct pch_msi_data *priv = irq_data_get_irq_chip_data(d);
    146
    147	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
    148	pch_msi_free_hwirq(priv, d->hwirq, nr_irqs);
    149}
    150
    151static const struct irq_domain_ops pch_msi_middle_domain_ops = {
    152	.alloc	= pch_msi_middle_domain_alloc,
    153	.free	= pch_msi_middle_domain_free,
    154};
    155
    156static int pch_msi_init_domains(struct pch_msi_data *priv,
    157				struct device_node *node,
    158				struct irq_domain *parent)
    159{
    160	struct irq_domain *middle_domain, *msi_domain;
    161
    162	middle_domain = irq_domain_create_linear(of_node_to_fwnode(node),
    163						priv->num_irqs,
    164						&pch_msi_middle_domain_ops,
    165						priv);
    166	if (!middle_domain) {
    167		pr_err("Failed to create the MSI middle domain\n");
    168		return -ENOMEM;
    169	}
    170
    171	middle_domain->parent = parent;
    172	irq_domain_update_bus_token(middle_domain, DOMAIN_BUS_NEXUS);
    173
    174	msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
    175					       &pch_msi_domain_info,
    176					       middle_domain);
    177	if (!msi_domain) {
    178		pr_err("Failed to create PCI MSI domain\n");
    179		irq_domain_remove(middle_domain);
    180		return -ENOMEM;
    181	}
    182
    183	return 0;
    184}
    185
    186static int pch_msi_init(struct device_node *node,
    187			    struct device_node *parent)
    188{
    189	struct pch_msi_data *priv;
    190	struct irq_domain *parent_domain;
    191	struct resource res;
    192	int ret;
    193
    194	parent_domain = irq_find_host(parent);
    195	if (!parent_domain) {
    196		pr_err("Failed to find the parent domain\n");
    197		return -ENXIO;
    198	}
    199
    200	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    201	if (!priv)
    202		return -ENOMEM;
    203
    204	mutex_init(&priv->msi_map_lock);
    205
    206	ret = of_address_to_resource(node, 0, &res);
    207	if (ret) {
    208		pr_err("Failed to allocate resource\n");
    209		goto err_priv;
    210	}
    211
    212	priv->doorbell = res.start;
    213
    214	if (of_property_read_u32(node, "loongson,msi-base-vec",
    215				&priv->irq_first)) {
    216		pr_err("Unable to parse MSI vec base\n");
    217		ret = -EINVAL;
    218		goto err_priv;
    219	}
    220
    221	if (of_property_read_u32(node, "loongson,msi-num-vecs",
    222				&priv->num_irqs)) {
    223		pr_err("Unable to parse MSI vec number\n");
    224		ret = -EINVAL;
    225		goto err_priv;
    226	}
    227
    228	priv->msi_map = bitmap_zalloc(priv->num_irqs, GFP_KERNEL);
    229	if (!priv->msi_map) {
    230		ret = -ENOMEM;
    231		goto err_priv;
    232	}
    233
    234	pr_debug("Registering %d MSIs, starting at %d\n",
    235		 priv->num_irqs, priv->irq_first);
    236
    237	ret = pch_msi_init_domains(priv, node, parent_domain);
    238	if (ret)
    239		goto err_map;
    240
    241	return 0;
    242
    243err_map:
    244	bitmap_free(priv->msi_map);
    245err_priv:
    246	kfree(priv);
    247	return ret;
    248}
    249
    250IRQCHIP_DECLARE(pch_msi, "loongson,pch-msi-1.0", pch_msi_init);