File Index
Files and directories
Sign in
FILE

irq-alpine-msi.c

File preview with syntax highlighting.

linux-6.19.14/drivers/irqchip/irq-alpine-msi.c C
  1/*
  2 * Annapurna Labs MSIX support services
  3 *
  4 * Copyright (C) 2016, Amazon.com, Inc. or its affiliates. All Rights Reserved.
  5 *
  6 * Antoine Tenart <antoine.tenart@free-electrons.com>
  7 *
  8 * This file is licensed under the terms of the GNU General Public
  9 * License version 2. This program is licensed "as is" without any
 10 * warranty of any kind, whether express or implied.
 11 */
 12
 13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 14
 15#include <linux/irqchip.h>
 16#include <linux/irqchip/arm-gic.h>
 17#include <linux/irqchip/irq-msi-lib.h>
 18#include <linux/msi.h>
 19#include <linux/of.h>
 20#include <linux/of_address.h>
 21#include <linux/of_irq.h>
 22#include <linux/of_pci.h>
 23#include <linux/pci.h>
 24#include <linux/slab.h>
 25
 26#include <asm/irq.h>
 27#include <asm/msi.h>
 28
 29/* MSIX message address format: local GIC target */
 30#define ALPINE_MSIX_SPI_TARGET_CLUSTER0		BIT(16)
 31
 32struct alpine_msix_data {
 33	spinlock_t	msi_map_lock;
 34	phys_addr_t	addr;
 35	u32		spi_first;	/* The SGI number that MSIs start */
 36	u32		num_spis;	/* The number of SGIs for MSIs */
 37	unsigned long	*msi_map;
 38};
 39
 40static int alpine_msix_allocate_sgi(struct alpine_msix_data *priv, int num_req)
 41{
 42	int first;
 43
 44	guard(spinlock)(&priv->msi_map_lock);
 45	first = bitmap_find_next_zero_area(priv->msi_map, priv->num_spis, 0, num_req, 0);
 46	if (first >= priv->num_spis)
 47		return -ENOSPC;
 48
 49	bitmap_set(priv->msi_map, first, num_req);
 50	return priv->spi_first + first;
 51}
 52
 53static void alpine_msix_free_sgi(struct alpine_msix_data *priv, unsigned int sgi, int num_req)
 54{
 55	int first = sgi - priv->spi_first;
 56
 57	guard(spinlock)(&priv->msi_map_lock);
 58	bitmap_clear(priv->msi_map, first, num_req);
 59}
 60
 61static void alpine_msix_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
 62{
 63	struct alpine_msix_data *priv = irq_data_get_irq_chip_data(data);
 64	phys_addr_t msg_addr = priv->addr;
 65
 66	msg_addr |= (data->hwirq << 3);
 67	msg->address_hi = upper_32_bits(msg_addr);
 68	msg->address_lo = lower_32_bits(msg_addr);
 69	msg->data = 0;
 70}
 71
 72static struct irq_chip middle_irq_chip = {
 73	.name			= "alpine_msix_middle",
 74	.irq_mask		= irq_chip_mask_parent,
 75	.irq_unmask		= irq_chip_unmask_parent,
 76	.irq_eoi		= irq_chip_eoi_parent,
 77	.irq_set_affinity	= irq_chip_set_affinity_parent,
 78	.irq_compose_msi_msg	= alpine_msix_compose_msi_msg,
 79};
 80
 81static int alpine_msix_gic_domain_alloc(struct irq_domain *domain, unsigned int virq, int sgi)
 82{
 83	struct irq_fwspec fwspec;
 84	struct irq_data *d;
 85	int ret;
 86
 87	if (!is_of_node(domain->parent->fwnode))
 88		return -EINVAL;
 89
 90	fwspec.fwnode = domain->parent->fwnode;
 91	fwspec.param_count = 3;
 92	fwspec.param[0] = 0;
 93	fwspec.param[1] = sgi;
 94	fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
 95
 96	ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
 97	if (ret)
 98		return ret;
 99
100	d = irq_domain_get_irq_data(domain->parent, virq);
101	d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING);
102	return 0;
103}
104
105static int alpine_msix_middle_domain_alloc(struct irq_domain *domain, unsigned int virq,
106					   unsigned int nr_irqs, void *args)
107{
108	struct alpine_msix_data *priv = domain->host_data;
109	int sgi, err, i;
110
111	sgi = alpine_msix_allocate_sgi(priv, nr_irqs);
112	if (sgi < 0)
113		return sgi;
114
115	for (i = 0; i < nr_irqs; i++) {
116		err = alpine_msix_gic_domain_alloc(domain, virq + i, sgi + i);
117		if (err)
118			goto err_sgi;
119
120		irq_domain_set_hwirq_and_chip(domain, virq + i, sgi + i,
121					      &middle_irq_chip, priv);
122	}
123	return 0;
124
125err_sgi:
126	irq_domain_free_irqs_parent(domain, virq, i);
127	alpine_msix_free_sgi(priv, sgi, nr_irqs);
128	return err;
129}
130
131static void alpine_msix_middle_domain_free(struct irq_domain *domain, unsigned int virq,
132					   unsigned int nr_irqs)
133{
134	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
135	struct alpine_msix_data *priv = irq_data_get_irq_chip_data(d);
136
137	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
138	alpine_msix_free_sgi(priv, d->hwirq, nr_irqs);
139}
140
141static const struct irq_domain_ops alpine_msix_middle_domain_ops = {
142	.select	= msi_lib_irq_domain_select,
143	.alloc	= alpine_msix_middle_domain_alloc,
144	.free	= alpine_msix_middle_domain_free,
145};
146
147#define ALPINE_MSI_FLAGS_REQUIRED  (MSI_FLAG_USE_DEF_DOM_OPS |		\
148				    MSI_FLAG_USE_DEF_CHIP_OPS |		\
149				    MSI_FLAG_PCI_MSI_MASK_PARENT)
150
151#define ALPINE_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK |		\
152				    MSI_FLAG_PCI_MSIX)
153
154static struct msi_parent_ops alpine_msi_parent_ops = {
155	.supported_flags	= ALPINE_MSI_FLAGS_SUPPORTED,
156	.required_flags		= ALPINE_MSI_FLAGS_REQUIRED,
157	.chip_flags		= MSI_CHIP_FLAG_SET_EOI,
158	.bus_select_token	= DOMAIN_BUS_NEXUS,
159	.bus_select_mask	= MATCH_PCI_MSI,
160	.prefix			= "ALPINE-",
161	.init_dev_msi_info	= msi_lib_init_dev_msi_info,
162};
163
164static int alpine_msix_init_domains(struct alpine_msix_data *priv, struct device_node *node)
165{
166	struct irq_domain_info info = {
167		.fwnode		= of_fwnode_handle(node),
168		.ops		= &alpine_msix_middle_domain_ops,
169		.host_data	= priv,
170	};
171	struct device_node *gic_node;
172
173	gic_node = of_irq_find_parent(node);
174	if (!gic_node) {
175		pr_err("Failed to find the GIC node\n");
176		return -ENODEV;
177	}
178
179	info.parent = irq_find_host(gic_node);
180	of_node_put(gic_node);
181	if (!info.parent) {
182		pr_err("Failed to find the GIC domain\n");
183		return -ENXIO;
184	}
185
186	if (!msi_create_parent_irq_domain(&info, &alpine_msi_parent_ops)) {
187		pr_err("Failed to create MSI domain\n");
188		return -ENOMEM;
189	}
190	return 0;
191}
192
193static int alpine_msix_init(struct device_node *node, struct device_node *parent)
194{
195	struct alpine_msix_data *priv __free(kfree) = kzalloc(sizeof(*priv), GFP_KERNEL);
196	struct resource res;
197	int ret;
198
199	if (!priv)
200		return -ENOMEM;
201
202	spin_lock_init(&priv->msi_map_lock);
203
204	ret = of_address_to_resource(node, 0, &res);
205	if (ret) {
206		pr_err("Failed to allocate resource\n");
207		return ret;
208	}
209
210	/*
211	 * The 20 least significant bits of addr provide direct information
212	 * regarding the interrupt destination.
213	 *
214	 * To select the primary GIC as the target GIC, bits [18:17] must be set
215	 * to 0x0. In this case, bit 16 (SPI_TARGET_CLUSTER0) must be set.
216	 */
217	priv->addr = res.start & GENMASK_ULL(63,20);
218	priv->addr |= ALPINE_MSIX_SPI_TARGET_CLUSTER0;
219
220	if (of_property_read_u32(node, "al,msi-base-spi", &priv->spi_first)) {
221		pr_err("Unable to parse MSI base\n");
222		return -EINVAL;
223	}
224
225	if (of_property_read_u32(node, "al,msi-num-spis", &priv->num_spis)) {
226		pr_err("Unable to parse MSI numbers\n");
227		return -EINVAL;
228	}
229
230	unsigned long *msi_map __free(kfree) = bitmap_zalloc(priv->num_spis, GFP_KERNEL);
231
232	if (!msi_map)
233		return -ENOMEM;
234	priv->msi_map = msi_map;
235
236	pr_debug("Registering %d msixs, starting at %d\n", priv->num_spis, priv->spi_first);
237
238	ret = alpine_msix_init_domains(priv, node);
239	if (ret)
240		return ret;
241
242	retain_and_null_ptr(priv);
243	retain_and_null_ptr(msi_map);
244	return 0;
245}
246IRQCHIP_DECLARE(alpine_msix, "al,alpine-msix", alpine_msix_init);
~/ Read-only file preview.