162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * PCI Message Signaled Interrupt (MSI).
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Legacy architecture specific setup and teardown mechanism.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include "msi.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/* Arch hooks */
1062306a36Sopenharmony_ciint __weak arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
1162306a36Sopenharmony_ci{
1262306a36Sopenharmony_ci	return -EINVAL;
1362306a36Sopenharmony_ci}
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_civoid __weak arch_teardown_msi_irq(unsigned int irq)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci}
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciint __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	struct msi_desc *desc;
2262306a36Sopenharmony_ci	int ret;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	/*
2562306a36Sopenharmony_ci	 * If an architecture wants to support multiple MSI, it needs to
2662306a36Sopenharmony_ci	 * override arch_setup_msi_irqs()
2762306a36Sopenharmony_ci	 */
2862306a36Sopenharmony_ci	if (type == PCI_CAP_ID_MSI && nvec > 1)
2962306a36Sopenharmony_ci		return 1;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	msi_for_each_desc(desc, &dev->dev, MSI_DESC_NOTASSOCIATED) {
3262306a36Sopenharmony_ci		ret = arch_setup_msi_irq(dev, desc);
3362306a36Sopenharmony_ci		if (ret)
3462306a36Sopenharmony_ci			return ret < 0 ? ret : -ENOSPC;
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return 0;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_civoid __weak arch_teardown_msi_irqs(struct pci_dev *dev)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct msi_desc *desc;
4362306a36Sopenharmony_ci	int i;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	msi_for_each_desc(desc, &dev->dev, MSI_DESC_ASSOCIATED) {
4662306a36Sopenharmony_ci		for (i = 0; i < desc->nvec_used; i++)
4762306a36Sopenharmony_ci			arch_teardown_msi_irq(desc->irq + i);
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int pci_msi_setup_check_result(struct pci_dev *dev, int type, int ret)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct msi_desc *desc;
5462306a36Sopenharmony_ci	int avail = 0;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (type != PCI_CAP_ID_MSIX || ret >= 0)
5762306a36Sopenharmony_ci		return ret;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* Scan the MSI descriptors for successfully allocated ones. */
6062306a36Sopenharmony_ci	msi_for_each_desc(desc, &dev->dev, MSI_DESC_ASSOCIATED)
6162306a36Sopenharmony_ci		avail++;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	return avail ? avail : ret;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ciint pci_msi_legacy_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	int ret = arch_setup_msi_irqs(dev, nvec, type);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	ret = pci_msi_setup_check_result(dev, type, ret);
7162306a36Sopenharmony_ci	if (!ret)
7262306a36Sopenharmony_ci		ret = msi_device_populate_sysfs(&dev->dev);
7362306a36Sopenharmony_ci	return ret;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_civoid pci_msi_legacy_teardown_msi_irqs(struct pci_dev *dev)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	msi_device_destroy_sysfs(&dev->dev);
7962306a36Sopenharmony_ci	arch_teardown_msi_irqs(dev);
8062306a36Sopenharmony_ci}
81