Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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/msi.h>
 18#include <linux/of.h>
 19#include <linux/of_address.h>
 20#include <linux/of_irq.h>
 21#include <linux/of_pci.h>
 22#include <linux/pci.h>
 23#include <linux/slab.h>
 24
 25#include <asm/irq.h>
 26#include <asm/msi.h>
 27
 28/* MSIX message address format: local GIC target */
 29#define ALPINE_MSIX_SPI_TARGET_CLUSTER0		BIT(16)
 30
 31struct alpine_msix_data {
 32	spinlock_t msi_map_lock;
 33	phys_addr_t addr;
 34	u32 spi_first;		/* The SGI number that MSIs start */
 35	u32 num_spis;		/* The number of SGIs for MSIs */
 36	unsigned long *msi_map;
 37};
 38
 39static void alpine_msix_mask_msi_irq(struct irq_data *d)
 40{
 41	pci_msi_mask_irq(d);
 42	irq_chip_mask_parent(d);
 43}
 44
 45static void alpine_msix_unmask_msi_irq(struct irq_data *d)
 46{
 47	pci_msi_unmask_irq(d);
 48	irq_chip_unmask_parent(d);
 49}
 50
 51static struct irq_chip alpine_msix_irq_chip = {
 52	.name			= "MSIx",
 53	.irq_mask		= alpine_msix_mask_msi_irq,
 54	.irq_unmask		= alpine_msix_unmask_msi_irq,
 55	.irq_eoi		= irq_chip_eoi_parent,
 56	.irq_set_affinity	= irq_chip_set_affinity_parent,
 57};
 58
 59static int alpine_msix_allocate_sgi(struct alpine_msix_data *priv, int num_req)
 60{
 61	int first;
 62
 63	spin_lock(&priv->msi_map_lock);
 64
 65	first = bitmap_find_next_zero_area(priv->msi_map, priv->num_spis, 0,
 66					   num_req, 0);
 67	if (first >= priv->num_spis) {
 68		spin_unlock(&priv->msi_map_lock);
 69		return -ENOSPC;
 70	}
 71
 72	bitmap_set(priv->msi_map, first, num_req);
 73
 74	spin_unlock(&priv->msi_map_lock);
 75
 76	return priv->spi_first + first;
 77}
 78
 79static void alpine_msix_free_sgi(struct alpine_msix_data *priv, unsigned sgi,
 80				 int num_req)
 81{
 82	int first = sgi - priv->spi_first;
 83
 84	spin_lock(&priv->msi_map_lock);
 85
 86	bitmap_clear(priv->msi_map, first, num_req);
 87
 88	spin_unlock(&priv->msi_map_lock);
 89}
 90
 91static void alpine_msix_compose_msi_msg(struct irq_data *data,
 92					struct msi_msg *msg)
 93{
 94	struct alpine_msix_data *priv = irq_data_get_irq_chip_data(data);
 95	phys_addr_t msg_addr = priv->addr;
 96
 97	msg_addr |= (data->hwirq << 3);
 98
 99	msg->address_hi = upper_32_bits(msg_addr);
100	msg->address_lo = lower_32_bits(msg_addr);
101	msg->data = 0;
102}
103
104static struct msi_domain_info alpine_msix_domain_info = {
105	.flags	= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
106		  MSI_FLAG_PCI_MSIX,
107	.chip	= &alpine_msix_irq_chip,
108};
109
110static struct irq_chip middle_irq_chip = {
111	.name			= "alpine_msix_middle",
112	.irq_mask		= irq_chip_mask_parent,
113	.irq_unmask		= irq_chip_unmask_parent,
114	.irq_eoi		= irq_chip_eoi_parent,
115	.irq_set_affinity	= irq_chip_set_affinity_parent,
116	.irq_compose_msi_msg	= alpine_msix_compose_msi_msg,
117};
118
119static int alpine_msix_gic_domain_alloc(struct irq_domain *domain,
120					unsigned int virq, int sgi)
121{
122	struct irq_fwspec fwspec;
123	struct irq_data *d;
124	int ret;
125
126	if (!is_of_node(domain->parent->fwnode))
127		return -EINVAL;
128
129	fwspec.fwnode = domain->parent->fwnode;
130	fwspec.param_count = 3;
131	fwspec.param[0] = 0;
132	fwspec.param[1] = sgi;
133	fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
134
135	ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
136	if (ret)
137		return ret;
138
139	d = irq_domain_get_irq_data(domain->parent, virq);
140	d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING);
141
142	return 0;
143}
144
145static int alpine_msix_middle_domain_alloc(struct irq_domain *domain,
146					   unsigned int virq,
147					   unsigned int nr_irqs, void *args)
148{
149	struct alpine_msix_data *priv = domain->host_data;
150	int sgi, err, i;
151
152	sgi = alpine_msix_allocate_sgi(priv, nr_irqs);
153	if (sgi < 0)
154		return sgi;
155
156	for (i = 0; i < nr_irqs; i++) {
157		err = alpine_msix_gic_domain_alloc(domain, virq + i, sgi + i);
158		if (err)
159			goto err_sgi;
160
161		irq_domain_set_hwirq_and_chip(domain, virq + i, sgi + i,
162					      &middle_irq_chip, priv);
163	}
164
165	return 0;
166
167err_sgi:
168	while (--i >= 0)
169		irq_domain_free_irqs_parent(domain, virq, i);
170	alpine_msix_free_sgi(priv, sgi, nr_irqs);
171	return err;
172}
173
174static void alpine_msix_middle_domain_free(struct irq_domain *domain,
175					   unsigned int virq,
176					   unsigned int nr_irqs)
177{
178	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
179	struct alpine_msix_data *priv = irq_data_get_irq_chip_data(d);
180
181	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
182	alpine_msix_free_sgi(priv, d->hwirq, nr_irqs);
183}
184
185static const struct irq_domain_ops alpine_msix_middle_domain_ops = {
186	.alloc	= alpine_msix_middle_domain_alloc,
187	.free	= alpine_msix_middle_domain_free,
188};
189
190static int alpine_msix_init_domains(struct alpine_msix_data *priv,
191				    struct device_node *node)
192{
193	struct irq_domain *middle_domain, *msi_domain, *gic_domain;
194	struct device_node *gic_node;
195
196	gic_node = of_irq_find_parent(node);
197	if (!gic_node) {
198		pr_err("Failed to find the GIC node\n");
199		return -ENODEV;
200	}
201
202	gic_domain = irq_find_host(gic_node);
203	if (!gic_domain) {
204		pr_err("Failed to find the GIC domain\n");
205		return -ENXIO;
206	}
207
208	middle_domain = irq_domain_add_tree(NULL,
209					    &alpine_msix_middle_domain_ops,
210					    priv);
211	if (!middle_domain) {
212		pr_err("Failed to create the MSIX middle domain\n");
213		return -ENOMEM;
214	}
215
216	middle_domain->parent = gic_domain;
217
218	msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
219					       &alpine_msix_domain_info,
220					       middle_domain);
221	if (!msi_domain) {
222		pr_err("Failed to create MSI domain\n");
223		irq_domain_remove(middle_domain);
224		return -ENOMEM;
225	}
226
227	return 0;
228}
229
230static int alpine_msix_init(struct device_node *node,
231			    struct device_node *parent)
232{
233	struct alpine_msix_data *priv;
234	struct resource res;
235	int ret;
236
237	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
238	if (!priv)
239		return -ENOMEM;
240
241	spin_lock_init(&priv->msi_map_lock);
242
243	ret = of_address_to_resource(node, 0, &res);
244	if (ret) {
245		pr_err("Failed to allocate resource\n");
246		goto err_priv;
247	}
248
249	/*
250	 * The 20 least significant bits of addr provide direct information
251	 * regarding the interrupt destination.
252	 *
253	 * To select the primary GIC as the target GIC, bits [18:17] must be set
254	 * to 0x0. In this case, bit 16 (SPI_TARGET_CLUSTER0) must be set.
255	 */
256	priv->addr = res.start & GENMASK_ULL(63,20);
257	priv->addr |= ALPINE_MSIX_SPI_TARGET_CLUSTER0;
258
259	if (of_property_read_u32(node, "al,msi-base-spi", &priv->spi_first)) {
260		pr_err("Unable to parse MSI base\n");
261		ret = -EINVAL;
262		goto err_priv;
263	}
264
265	if (of_property_read_u32(node, "al,msi-num-spis", &priv->num_spis)) {
266		pr_err("Unable to parse MSI numbers\n");
267		ret = -EINVAL;
268		goto err_priv;
269	}
270
271	priv->msi_map = kzalloc(sizeof(*priv->msi_map) * BITS_TO_LONGS(priv->num_spis),
272				GFP_KERNEL);
273	if (!priv->msi_map) {
274		ret = -ENOMEM;
275		goto err_priv;
276	}
277
278	pr_debug("Registering %d msixs, starting at %d\n",
279		 priv->num_spis, priv->spi_first);
280
281	ret = alpine_msix_init_domains(priv, node);
282	if (ret)
283		goto err_map;
284
285	return 0;
286
287err_map:
288	kfree(priv->msi_map);
289err_priv:
290	kfree(priv);
291	return ret;
292}
293IRQCHIP_DECLARE(alpine_msix, "al,alpine-msix", alpine_msix_init);