18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Annapurna Labs MSIX support services
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2016, Amazon.com, Inc. or its affiliates. All Rights Reserved.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Antoine Tenart <antoine.tenart@free-electrons.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
98c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any
108c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/irqchip.h>
168c2ecf20Sopenharmony_ci#include <linux/irqchip/arm-gic.h>
178c2ecf20Sopenharmony_ci#include <linux/msi.h>
188c2ecf20Sopenharmony_ci#include <linux/of.h>
198c2ecf20Sopenharmony_ci#include <linux/of_address.h>
208c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
218c2ecf20Sopenharmony_ci#include <linux/of_pci.h>
228c2ecf20Sopenharmony_ci#include <linux/pci.h>
238c2ecf20Sopenharmony_ci#include <linux/slab.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <asm/irq.h>
268c2ecf20Sopenharmony_ci#include <asm/msi.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* MSIX message address format: local GIC target */
298c2ecf20Sopenharmony_ci#define ALPINE_MSIX_SPI_TARGET_CLUSTER0		BIT(16)
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct alpine_msix_data {
328c2ecf20Sopenharmony_ci	spinlock_t msi_map_lock;
338c2ecf20Sopenharmony_ci	phys_addr_t addr;
348c2ecf20Sopenharmony_ci	u32 spi_first;		/* The SGI number that MSIs start */
358c2ecf20Sopenharmony_ci	u32 num_spis;		/* The number of SGIs for MSIs */
368c2ecf20Sopenharmony_ci	unsigned long *msi_map;
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic void alpine_msix_mask_msi_irq(struct irq_data *d)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	pci_msi_mask_irq(d);
428c2ecf20Sopenharmony_ci	irq_chip_mask_parent(d);
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic void alpine_msix_unmask_msi_irq(struct irq_data *d)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	pci_msi_unmask_irq(d);
488c2ecf20Sopenharmony_ci	irq_chip_unmask_parent(d);
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic struct irq_chip alpine_msix_irq_chip = {
528c2ecf20Sopenharmony_ci	.name			= "MSIx",
538c2ecf20Sopenharmony_ci	.irq_mask		= alpine_msix_mask_msi_irq,
548c2ecf20Sopenharmony_ci	.irq_unmask		= alpine_msix_unmask_msi_irq,
558c2ecf20Sopenharmony_ci	.irq_eoi		= irq_chip_eoi_parent,
568c2ecf20Sopenharmony_ci	.irq_set_affinity	= irq_chip_set_affinity_parent,
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int alpine_msix_allocate_sgi(struct alpine_msix_data *priv, int num_req)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	int first;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	spin_lock(&priv->msi_map_lock);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	first = bitmap_find_next_zero_area(priv->msi_map, priv->num_spis, 0,
668c2ecf20Sopenharmony_ci					   num_req, 0);
678c2ecf20Sopenharmony_ci	if (first >= priv->num_spis) {
688c2ecf20Sopenharmony_ci		spin_unlock(&priv->msi_map_lock);
698c2ecf20Sopenharmony_ci		return -ENOSPC;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	bitmap_set(priv->msi_map, first, num_req);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	spin_unlock(&priv->msi_map_lock);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return priv->spi_first + first;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void alpine_msix_free_sgi(struct alpine_msix_data *priv, unsigned sgi,
808c2ecf20Sopenharmony_ci				 int num_req)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	int first = sgi - priv->spi_first;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	spin_lock(&priv->msi_map_lock);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	bitmap_clear(priv->msi_map, first, num_req);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	spin_unlock(&priv->msi_map_lock);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void alpine_msix_compose_msi_msg(struct irq_data *data,
928c2ecf20Sopenharmony_ci					struct msi_msg *msg)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct alpine_msix_data *priv = irq_data_get_irq_chip_data(data);
958c2ecf20Sopenharmony_ci	phys_addr_t msg_addr = priv->addr;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	msg_addr |= (data->hwirq << 3);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	msg->address_hi = upper_32_bits(msg_addr);
1008c2ecf20Sopenharmony_ci	msg->address_lo = lower_32_bits(msg_addr);
1018c2ecf20Sopenharmony_ci	msg->data = 0;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic struct msi_domain_info alpine_msix_domain_info = {
1058c2ecf20Sopenharmony_ci	.flags	= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
1068c2ecf20Sopenharmony_ci		  MSI_FLAG_PCI_MSIX,
1078c2ecf20Sopenharmony_ci	.chip	= &alpine_msix_irq_chip,
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic struct irq_chip middle_irq_chip = {
1118c2ecf20Sopenharmony_ci	.name			= "alpine_msix_middle",
1128c2ecf20Sopenharmony_ci	.irq_mask		= irq_chip_mask_parent,
1138c2ecf20Sopenharmony_ci	.irq_unmask		= irq_chip_unmask_parent,
1148c2ecf20Sopenharmony_ci	.irq_eoi		= irq_chip_eoi_parent,
1158c2ecf20Sopenharmony_ci	.irq_set_affinity	= irq_chip_set_affinity_parent,
1168c2ecf20Sopenharmony_ci	.irq_compose_msi_msg	= alpine_msix_compose_msi_msg,
1178c2ecf20Sopenharmony_ci};
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic int alpine_msix_gic_domain_alloc(struct irq_domain *domain,
1208c2ecf20Sopenharmony_ci					unsigned int virq, int sgi)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct irq_fwspec fwspec;
1238c2ecf20Sopenharmony_ci	struct irq_data *d;
1248c2ecf20Sopenharmony_ci	int ret;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (!is_of_node(domain->parent->fwnode))
1278c2ecf20Sopenharmony_ci		return -EINVAL;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	fwspec.fwnode = domain->parent->fwnode;
1308c2ecf20Sopenharmony_ci	fwspec.param_count = 3;
1318c2ecf20Sopenharmony_ci	fwspec.param[0] = 0;
1328c2ecf20Sopenharmony_ci	fwspec.param[1] = sgi;
1338c2ecf20Sopenharmony_ci	fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
1368c2ecf20Sopenharmony_ci	if (ret)
1378c2ecf20Sopenharmony_ci		return ret;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	d = irq_domain_get_irq_data(domain->parent, virq);
1408c2ecf20Sopenharmony_ci	d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return 0;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int alpine_msix_middle_domain_alloc(struct irq_domain *domain,
1468c2ecf20Sopenharmony_ci					   unsigned int virq,
1478c2ecf20Sopenharmony_ci					   unsigned int nr_irqs, void *args)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	struct alpine_msix_data *priv = domain->host_data;
1508c2ecf20Sopenharmony_ci	int sgi, err, i;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	sgi = alpine_msix_allocate_sgi(priv, nr_irqs);
1538c2ecf20Sopenharmony_ci	if (sgi < 0)
1548c2ecf20Sopenharmony_ci		return sgi;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	for (i = 0; i < nr_irqs; i++) {
1578c2ecf20Sopenharmony_ci		err = alpine_msix_gic_domain_alloc(domain, virq + i, sgi + i);
1588c2ecf20Sopenharmony_ci		if (err)
1598c2ecf20Sopenharmony_ci			goto err_sgi;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		irq_domain_set_hwirq_and_chip(domain, virq + i, sgi + i,
1628c2ecf20Sopenharmony_ci					      &middle_irq_chip, priv);
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	return 0;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cierr_sgi:
1688c2ecf20Sopenharmony_ci	irq_domain_free_irqs_parent(domain, virq, i - 1);
1698c2ecf20Sopenharmony_ci	alpine_msix_free_sgi(priv, sgi, nr_irqs);
1708c2ecf20Sopenharmony_ci	return err;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic void alpine_msix_middle_domain_free(struct irq_domain *domain,
1748c2ecf20Sopenharmony_ci					   unsigned int virq,
1758c2ecf20Sopenharmony_ci					   unsigned int nr_irqs)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
1788c2ecf20Sopenharmony_ci	struct alpine_msix_data *priv = irq_data_get_irq_chip_data(d);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
1818c2ecf20Sopenharmony_ci	alpine_msix_free_sgi(priv, d->hwirq, nr_irqs);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic const struct irq_domain_ops alpine_msix_middle_domain_ops = {
1858c2ecf20Sopenharmony_ci	.alloc	= alpine_msix_middle_domain_alloc,
1868c2ecf20Sopenharmony_ci	.free	= alpine_msix_middle_domain_free,
1878c2ecf20Sopenharmony_ci};
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int alpine_msix_init_domains(struct alpine_msix_data *priv,
1908c2ecf20Sopenharmony_ci				    struct device_node *node)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct irq_domain *middle_domain, *msi_domain, *gic_domain;
1938c2ecf20Sopenharmony_ci	struct device_node *gic_node;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	gic_node = of_irq_find_parent(node);
1968c2ecf20Sopenharmony_ci	if (!gic_node) {
1978c2ecf20Sopenharmony_ci		pr_err("Failed to find the GIC node\n");
1988c2ecf20Sopenharmony_ci		return -ENODEV;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	gic_domain = irq_find_host(gic_node);
2028c2ecf20Sopenharmony_ci	of_node_put(gic_node);
2038c2ecf20Sopenharmony_ci	if (!gic_domain) {
2048c2ecf20Sopenharmony_ci		pr_err("Failed to find the GIC domain\n");
2058c2ecf20Sopenharmony_ci		return -ENXIO;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	middle_domain = irq_domain_add_tree(NULL,
2098c2ecf20Sopenharmony_ci					    &alpine_msix_middle_domain_ops,
2108c2ecf20Sopenharmony_ci					    priv);
2118c2ecf20Sopenharmony_ci	if (!middle_domain) {
2128c2ecf20Sopenharmony_ci		pr_err("Failed to create the MSIX middle domain\n");
2138c2ecf20Sopenharmony_ci		return -ENOMEM;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	middle_domain->parent = gic_domain;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
2198c2ecf20Sopenharmony_ci					       &alpine_msix_domain_info,
2208c2ecf20Sopenharmony_ci					       middle_domain);
2218c2ecf20Sopenharmony_ci	if (!msi_domain) {
2228c2ecf20Sopenharmony_ci		pr_err("Failed to create MSI domain\n");
2238c2ecf20Sopenharmony_ci		irq_domain_remove(middle_domain);
2248c2ecf20Sopenharmony_ci		return -ENOMEM;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	return 0;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic int alpine_msix_init(struct device_node *node,
2318c2ecf20Sopenharmony_ci			    struct device_node *parent)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct alpine_msix_data *priv;
2348c2ecf20Sopenharmony_ci	struct resource res;
2358c2ecf20Sopenharmony_ci	int ret;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
2388c2ecf20Sopenharmony_ci	if (!priv)
2398c2ecf20Sopenharmony_ci		return -ENOMEM;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	spin_lock_init(&priv->msi_map_lock);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	ret = of_address_to_resource(node, 0, &res);
2448c2ecf20Sopenharmony_ci	if (ret) {
2458c2ecf20Sopenharmony_ci		pr_err("Failed to allocate resource\n");
2468c2ecf20Sopenharmony_ci		goto err_priv;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	/*
2508c2ecf20Sopenharmony_ci	 * The 20 least significant bits of addr provide direct information
2518c2ecf20Sopenharmony_ci	 * regarding the interrupt destination.
2528c2ecf20Sopenharmony_ci	 *
2538c2ecf20Sopenharmony_ci	 * To select the primary GIC as the target GIC, bits [18:17] must be set
2548c2ecf20Sopenharmony_ci	 * to 0x0. In this case, bit 16 (SPI_TARGET_CLUSTER0) must be set.
2558c2ecf20Sopenharmony_ci	 */
2568c2ecf20Sopenharmony_ci	priv->addr = res.start & GENMASK_ULL(63,20);
2578c2ecf20Sopenharmony_ci	priv->addr |= ALPINE_MSIX_SPI_TARGET_CLUSTER0;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (of_property_read_u32(node, "al,msi-base-spi", &priv->spi_first)) {
2608c2ecf20Sopenharmony_ci		pr_err("Unable to parse MSI base\n");
2618c2ecf20Sopenharmony_ci		ret = -EINVAL;
2628c2ecf20Sopenharmony_ci		goto err_priv;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if (of_property_read_u32(node, "al,msi-num-spis", &priv->num_spis)) {
2668c2ecf20Sopenharmony_ci		pr_err("Unable to parse MSI numbers\n");
2678c2ecf20Sopenharmony_ci		ret = -EINVAL;
2688c2ecf20Sopenharmony_ci		goto err_priv;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	priv->msi_map = kcalloc(BITS_TO_LONGS(priv->num_spis),
2728c2ecf20Sopenharmony_ci				sizeof(*priv->msi_map),
2738c2ecf20Sopenharmony_ci				GFP_KERNEL);
2748c2ecf20Sopenharmony_ci	if (!priv->msi_map) {
2758c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2768c2ecf20Sopenharmony_ci		goto err_priv;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	pr_debug("Registering %d msixs, starting at %d\n",
2808c2ecf20Sopenharmony_ci		 priv->num_spis, priv->spi_first);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	ret = alpine_msix_init_domains(priv, node);
2838c2ecf20Sopenharmony_ci	if (ret)
2848c2ecf20Sopenharmony_ci		goto err_map;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	return 0;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cierr_map:
2898c2ecf20Sopenharmony_ci	kfree(priv->msi_map);
2908c2ecf20Sopenharmony_cierr_priv:
2918c2ecf20Sopenharmony_ci	kfree(priv);
2928c2ecf20Sopenharmony_ci	return ret;
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(alpine_msix, "al,alpine-msix", alpine_msix_init);
295