18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PCI Backend Operations - respond to PCI requests from Frontend
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *   Author: Ryan Wilson <hap9@epoch.ncsc.mil>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
98c2ecf20Sopenharmony_ci#define dev_fmt pr_fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
128c2ecf20Sopenharmony_ci#include <linux/wait.h>
138c2ecf20Sopenharmony_ci#include <linux/bitops.h>
148c2ecf20Sopenharmony_ci#include <xen/events.h>
158c2ecf20Sopenharmony_ci#include <linux/sched.h>
168c2ecf20Sopenharmony_ci#include "pciback.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic irqreturn_t xen_pcibk_guest_interrupt(int irq, void *dev_id);
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* Ensure a device is has the fake IRQ handler "turned on/off" and is
218c2ecf20Sopenharmony_ci * ready to be exported. This MUST be run after xen_pcibk_reset_device
228c2ecf20Sopenharmony_ci * which does the actual PCI device enable/disable.
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_cistatic void xen_pcibk_control_isr(struct pci_dev *dev, int reset)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
278c2ecf20Sopenharmony_ci	int rc;
288c2ecf20Sopenharmony_ci	int enable = 0;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	dev_data = pci_get_drvdata(dev);
318c2ecf20Sopenharmony_ci	if (!dev_data)
328c2ecf20Sopenharmony_ci		return;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	/* We don't deal with bridges */
358c2ecf20Sopenharmony_ci	if (dev->hdr_type != PCI_HEADER_TYPE_NORMAL)
368c2ecf20Sopenharmony_ci		return;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	if (reset) {
398c2ecf20Sopenharmony_ci		dev_data->enable_intx = 0;
408c2ecf20Sopenharmony_ci		dev_data->ack_intr = 0;
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci	enable =  dev_data->enable_intx;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	/* Asked to disable, but ISR isn't runnig */
458c2ecf20Sopenharmony_ci	if (!enable && !dev_data->isr_on)
468c2ecf20Sopenharmony_ci		return;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	/* Squirrel away the IRQs in the dev_data. We need this
498c2ecf20Sopenharmony_ci	 * b/c when device transitions to MSI, the dev->irq is
508c2ecf20Sopenharmony_ci	 * overwritten with the MSI vector.
518c2ecf20Sopenharmony_ci	 */
528c2ecf20Sopenharmony_ci	if (enable)
538c2ecf20Sopenharmony_ci		dev_data->irq = dev->irq;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	/*
568c2ecf20Sopenharmony_ci	 * SR-IOV devices in all use MSI-X and have no legacy
578c2ecf20Sopenharmony_ci	 * interrupts, so inhibit creating a fake IRQ handler for them.
588c2ecf20Sopenharmony_ci	 */
598c2ecf20Sopenharmony_ci	if (dev_data->irq == 0)
608c2ecf20Sopenharmony_ci		goto out;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "%s: #%d %s %s%s %s-> %s\n",
638c2ecf20Sopenharmony_ci		dev_data->irq_name,
648c2ecf20Sopenharmony_ci		dev_data->irq,
658c2ecf20Sopenharmony_ci		pci_is_enabled(dev) ? "on" : "off",
668c2ecf20Sopenharmony_ci		dev->msi_enabled ? "MSI" : "",
678c2ecf20Sopenharmony_ci		dev->msix_enabled ? "MSI/X" : "",
688c2ecf20Sopenharmony_ci		dev_data->isr_on ? "enable" : "disable",
698c2ecf20Sopenharmony_ci		enable ? "enable" : "disable");
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (enable) {
728c2ecf20Sopenharmony_ci		/*
738c2ecf20Sopenharmony_ci		 * The MSI or MSI-X should not have an IRQ handler. Otherwise
748c2ecf20Sopenharmony_ci		 * if the guest terminates we BUG_ON in free_msi_irqs.
758c2ecf20Sopenharmony_ci		 */
768c2ecf20Sopenharmony_ci		if (dev->msi_enabled || dev->msix_enabled)
778c2ecf20Sopenharmony_ci			goto out;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		rc = request_irq(dev_data->irq,
808c2ecf20Sopenharmony_ci				xen_pcibk_guest_interrupt, IRQF_SHARED,
818c2ecf20Sopenharmony_ci				dev_data->irq_name, dev);
828c2ecf20Sopenharmony_ci		if (rc) {
838c2ecf20Sopenharmony_ci			dev_err(&dev->dev, "%s: failed to install fake IRQ " \
848c2ecf20Sopenharmony_ci				"handler for IRQ %d! (rc:%d)\n",
858c2ecf20Sopenharmony_ci				dev_data->irq_name, dev_data->irq, rc);
868c2ecf20Sopenharmony_ci			goto out;
878c2ecf20Sopenharmony_ci		}
888c2ecf20Sopenharmony_ci	} else {
898c2ecf20Sopenharmony_ci		free_irq(dev_data->irq, dev);
908c2ecf20Sopenharmony_ci		dev_data->irq = 0;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci	dev_data->isr_on = enable;
938c2ecf20Sopenharmony_ci	dev_data->ack_intr = enable;
948c2ecf20Sopenharmony_ciout:
958c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "%s: #%d %s %s%s %s\n",
968c2ecf20Sopenharmony_ci		dev_data->irq_name,
978c2ecf20Sopenharmony_ci		dev_data->irq,
988c2ecf20Sopenharmony_ci		pci_is_enabled(dev) ? "on" : "off",
998c2ecf20Sopenharmony_ci		dev->msi_enabled ? "MSI" : "",
1008c2ecf20Sopenharmony_ci		dev->msix_enabled ? "MSI/X" : "",
1018c2ecf20Sopenharmony_ci		enable ? (dev_data->isr_on ? "enabled" : "failed to enable") :
1028c2ecf20Sopenharmony_ci			(dev_data->isr_on ? "failed to disable" : "disabled"));
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/* Ensure a device is "turned off" and ready to be exported.
1068c2ecf20Sopenharmony_ci * (Also see xen_pcibk_config_reset to ensure virtual configuration space is
1078c2ecf20Sopenharmony_ci * ready to be re-exported)
1088c2ecf20Sopenharmony_ci */
1098c2ecf20Sopenharmony_civoid xen_pcibk_reset_device(struct pci_dev *dev)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	u16 cmd;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	xen_pcibk_control_isr(dev, 1 /* reset device */);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* Disable devices (but not bridges) */
1168c2ecf20Sopenharmony_ci	if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) {
1178c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
1188c2ecf20Sopenharmony_ci		/* The guest could have been abruptly killed without
1198c2ecf20Sopenharmony_ci		 * disabling MSI/MSI-X interrupts.*/
1208c2ecf20Sopenharmony_ci		if (dev->msix_enabled)
1218c2ecf20Sopenharmony_ci			pci_disable_msix(dev);
1228c2ecf20Sopenharmony_ci		if (dev->msi_enabled)
1238c2ecf20Sopenharmony_ci			pci_disable_msi(dev);
1248c2ecf20Sopenharmony_ci#endif
1258c2ecf20Sopenharmony_ci		if (pci_is_enabled(dev))
1268c2ecf20Sopenharmony_ci			pci_disable_device(dev);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		dev->is_busmaster = 0;
1298c2ecf20Sopenharmony_ci	} else {
1308c2ecf20Sopenharmony_ci		pci_read_config_word(dev, PCI_COMMAND, &cmd);
1318c2ecf20Sopenharmony_ci		if (cmd & (PCI_COMMAND_INVALIDATE)) {
1328c2ecf20Sopenharmony_ci			cmd &= ~(PCI_COMMAND_INVALIDATE);
1338c2ecf20Sopenharmony_ci			pci_write_config_word(dev, PCI_COMMAND, cmd);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci			dev->is_busmaster = 0;
1368c2ecf20Sopenharmony_ci		}
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
1418c2ecf20Sopenharmony_cistatic
1428c2ecf20Sopenharmony_ciint xen_pcibk_enable_msi(struct xen_pcibk_device *pdev,
1438c2ecf20Sopenharmony_ci			 struct pci_dev *dev, struct xen_pci_op *op)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
1468c2ecf20Sopenharmony_ci	int status;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (dev->msi_enabled)
1498c2ecf20Sopenharmony_ci		status = -EALREADY;
1508c2ecf20Sopenharmony_ci	else if (dev->msix_enabled)
1518c2ecf20Sopenharmony_ci		status = -ENXIO;
1528c2ecf20Sopenharmony_ci	else
1538c2ecf20Sopenharmony_ci		status = pci_enable_msi(dev);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (status) {
1568c2ecf20Sopenharmony_ci		dev_warn_ratelimited(&dev->dev, "error enabling MSI for guest %u: err %d\n",
1578c2ecf20Sopenharmony_ci				     pdev->xdev->otherend_id, status);
1588c2ecf20Sopenharmony_ci		op->value = 0;
1598c2ecf20Sopenharmony_ci		return XEN_PCI_ERR_op_failed;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* The value the guest needs is actually the IDT vector, not the
1638c2ecf20Sopenharmony_ci	 * the local domain's IRQ number. */
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "MSI: %d\n", op->value);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	dev_data = pci_get_drvdata(dev);
1708c2ecf20Sopenharmony_ci	if (dev_data)
1718c2ecf20Sopenharmony_ci		dev_data->ack_intr = 0;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return 0;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic
1778c2ecf20Sopenharmony_ciint xen_pcibk_disable_msi(struct xen_pcibk_device *pdev,
1788c2ecf20Sopenharmony_ci			  struct pci_dev *dev, struct xen_pci_op *op)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	if (dev->msi_enabled) {
1818c2ecf20Sopenharmony_ci		struct xen_pcibk_dev_data *dev_data;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci		pci_disable_msi(dev);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		dev_data = pci_get_drvdata(dev);
1868c2ecf20Sopenharmony_ci		if (dev_data)
1878c2ecf20Sopenharmony_ci			dev_data->ack_intr = 1;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci	op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "MSI: %d\n", op->value);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	return 0;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic
1978c2ecf20Sopenharmony_ciint xen_pcibk_enable_msix(struct xen_pcibk_device *pdev,
1988c2ecf20Sopenharmony_ci			  struct pci_dev *dev, struct xen_pci_op *op)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
2018c2ecf20Sopenharmony_ci	int i, result;
2028c2ecf20Sopenharmony_ci	struct msix_entry *entries;
2038c2ecf20Sopenharmony_ci	u16 cmd;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "enable MSI-X\n");
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (op->value > SH_INFO_MAX_VEC)
2088c2ecf20Sopenharmony_ci		return -EINVAL;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (dev->msix_enabled)
2118c2ecf20Sopenharmony_ci		return -EALREADY;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/*
2148c2ecf20Sopenharmony_ci	 * PCI_COMMAND_MEMORY must be enabled, otherwise we may not be able
2158c2ecf20Sopenharmony_ci	 * to access the BARs where the MSI-X entries reside.
2168c2ecf20Sopenharmony_ci	 * But VF devices are unique in which the PF needs to be checked.
2178c2ecf20Sopenharmony_ci	 */
2188c2ecf20Sopenharmony_ci	pci_read_config_word(pci_physfn(dev), PCI_COMMAND, &cmd);
2198c2ecf20Sopenharmony_ci	if (dev->msi_enabled || !(cmd & PCI_COMMAND_MEMORY))
2208c2ecf20Sopenharmony_ci		return -ENXIO;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	entries = kmalloc_array(op->value, sizeof(*entries), GFP_KERNEL);
2238c2ecf20Sopenharmony_ci	if (entries == NULL)
2248c2ecf20Sopenharmony_ci		return -ENOMEM;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	for (i = 0; i < op->value; i++) {
2278c2ecf20Sopenharmony_ci		entries[i].entry = op->msix_entries[i].entry;
2288c2ecf20Sopenharmony_ci		entries[i].vector = op->msix_entries[i].vector;
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	result = pci_enable_msix_exact(dev, entries, op->value);
2328c2ecf20Sopenharmony_ci	if (result == 0) {
2338c2ecf20Sopenharmony_ci		for (i = 0; i < op->value; i++) {
2348c2ecf20Sopenharmony_ci			op->msix_entries[i].entry = entries[i].entry;
2358c2ecf20Sopenharmony_ci			if (entries[i].vector) {
2368c2ecf20Sopenharmony_ci				op->msix_entries[i].vector =
2378c2ecf20Sopenharmony_ci					xen_pirq_from_irq(entries[i].vector);
2388c2ecf20Sopenharmony_ci				dev_dbg(&dev->dev, "MSI-X[%d]: %d\n", i,
2398c2ecf20Sopenharmony_ci					op->msix_entries[i].vector);
2408c2ecf20Sopenharmony_ci			}
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci	} else
2438c2ecf20Sopenharmony_ci		dev_warn_ratelimited(&dev->dev, "error enabling MSI-X for guest %u: err %d!\n",
2448c2ecf20Sopenharmony_ci				     pdev->xdev->otherend_id, result);
2458c2ecf20Sopenharmony_ci	kfree(entries);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	op->value = result;
2488c2ecf20Sopenharmony_ci	dev_data = pci_get_drvdata(dev);
2498c2ecf20Sopenharmony_ci	if (dev_data)
2508c2ecf20Sopenharmony_ci		dev_data->ack_intr = 0;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	return result > 0 ? 0 : result;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic
2568c2ecf20Sopenharmony_ciint xen_pcibk_disable_msix(struct xen_pcibk_device *pdev,
2578c2ecf20Sopenharmony_ci			   struct pci_dev *dev, struct xen_pci_op *op)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	if (dev->msix_enabled) {
2608c2ecf20Sopenharmony_ci		struct xen_pcibk_dev_data *dev_data;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		pci_disable_msix(dev);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci		dev_data = pci_get_drvdata(dev);
2658c2ecf20Sopenharmony_ci		if (dev_data)
2668c2ecf20Sopenharmony_ci			dev_data->ack_intr = 1;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci	/*
2698c2ecf20Sopenharmony_ci	 * SR-IOV devices (which don't have any legacy IRQ) have
2708c2ecf20Sopenharmony_ci	 * an undefined IRQ value of zero.
2718c2ecf20Sopenharmony_ci	 */
2728c2ecf20Sopenharmony_ci	op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "MSI-X: %d\n", op->value);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	return 0;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci#endif
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic inline bool xen_pcibk_test_op_pending(struct xen_pcibk_device *pdev)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	return test_bit(_XEN_PCIF_active,
2838c2ecf20Sopenharmony_ci			(unsigned long *)&pdev->sh_info->flags) &&
2848c2ecf20Sopenharmony_ci	       !test_and_set_bit(_PDEVF_op_active, &pdev->flags);
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci/*
2888c2ecf20Sopenharmony_ci* Now the same evtchn is used for both pcifront conf_read_write request
2898c2ecf20Sopenharmony_ci* as well as pcie aer front end ack. We use a new work_queue to schedule
2908c2ecf20Sopenharmony_ci* xen_pcibk conf_read_write service for avoiding confict with aer_core
2918c2ecf20Sopenharmony_ci* do_recovery job which also use the system default work_queue
2928c2ecf20Sopenharmony_ci*/
2938c2ecf20Sopenharmony_cistatic void xen_pcibk_test_and_schedule_op(struct xen_pcibk_device *pdev)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	bool eoi = true;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	/* Check that frontend is requesting an operation and that we are not
2988c2ecf20Sopenharmony_ci	 * already processing a request */
2998c2ecf20Sopenharmony_ci	if (xen_pcibk_test_op_pending(pdev)) {
3008c2ecf20Sopenharmony_ci		schedule_work(&pdev->op_work);
3018c2ecf20Sopenharmony_ci		eoi = false;
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci	/*_XEN_PCIB_active should have been cleared by pcifront. And also make
3048c2ecf20Sopenharmony_ci	sure xen_pcibk is waiting for ack by checking _PCIB_op_pending*/
3058c2ecf20Sopenharmony_ci	if (!test_bit(_XEN_PCIB_active, (unsigned long *)&pdev->sh_info->flags)
3068c2ecf20Sopenharmony_ci	    && test_bit(_PCIB_op_pending, &pdev->flags)) {
3078c2ecf20Sopenharmony_ci		wake_up(&xen_pcibk_aer_wait_queue);
3088c2ecf20Sopenharmony_ci		eoi = false;
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	/* EOI if there was nothing to do. */
3128c2ecf20Sopenharmony_ci	if (eoi)
3138c2ecf20Sopenharmony_ci		xen_pcibk_lateeoi(pdev, XEN_EOI_FLAG_SPURIOUS);
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci/* Performing the configuration space reads/writes must not be done in atomic
3178c2ecf20Sopenharmony_ci * context because some of the pci_* functions can sleep (mostly due to ACPI
3188c2ecf20Sopenharmony_ci * use of semaphores). This function is intended to be called from a work
3198c2ecf20Sopenharmony_ci * queue in process context taking a struct xen_pcibk_device as a parameter */
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic void xen_pcibk_do_one_op(struct xen_pcibk_device *pdev)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct pci_dev *dev;
3248c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data = NULL;
3258c2ecf20Sopenharmony_ci	struct xen_pci_op *op = &pdev->op;
3268c2ecf20Sopenharmony_ci	int test_intx = 0;
3278c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
3288c2ecf20Sopenharmony_ci	unsigned int nr = 0;
3298c2ecf20Sopenharmony_ci#endif
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	*op = pdev->sh_info->op;
3328c2ecf20Sopenharmony_ci	barrier();
3338c2ecf20Sopenharmony_ci	dev = xen_pcibk_get_pci_dev(pdev, op->domain, op->bus, op->devfn);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (dev == NULL)
3368c2ecf20Sopenharmony_ci		op->err = XEN_PCI_ERR_dev_not_found;
3378c2ecf20Sopenharmony_ci	else {
3388c2ecf20Sopenharmony_ci		dev_data = pci_get_drvdata(dev);
3398c2ecf20Sopenharmony_ci		if (dev_data)
3408c2ecf20Sopenharmony_ci			test_intx = dev_data->enable_intx;
3418c2ecf20Sopenharmony_ci		switch (op->cmd) {
3428c2ecf20Sopenharmony_ci		case XEN_PCI_OP_conf_read:
3438c2ecf20Sopenharmony_ci			op->err = xen_pcibk_config_read(dev,
3448c2ecf20Sopenharmony_ci				  op->offset, op->size, &op->value);
3458c2ecf20Sopenharmony_ci			break;
3468c2ecf20Sopenharmony_ci		case XEN_PCI_OP_conf_write:
3478c2ecf20Sopenharmony_ci			op->err = xen_pcibk_config_write(dev,
3488c2ecf20Sopenharmony_ci				  op->offset, op->size,	op->value);
3498c2ecf20Sopenharmony_ci			break;
3508c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
3518c2ecf20Sopenharmony_ci		case XEN_PCI_OP_enable_msi:
3528c2ecf20Sopenharmony_ci			op->err = xen_pcibk_enable_msi(pdev, dev, op);
3538c2ecf20Sopenharmony_ci			break;
3548c2ecf20Sopenharmony_ci		case XEN_PCI_OP_disable_msi:
3558c2ecf20Sopenharmony_ci			op->err = xen_pcibk_disable_msi(pdev, dev, op);
3568c2ecf20Sopenharmony_ci			break;
3578c2ecf20Sopenharmony_ci		case XEN_PCI_OP_enable_msix:
3588c2ecf20Sopenharmony_ci			nr = op->value;
3598c2ecf20Sopenharmony_ci			op->err = xen_pcibk_enable_msix(pdev, dev, op);
3608c2ecf20Sopenharmony_ci			break;
3618c2ecf20Sopenharmony_ci		case XEN_PCI_OP_disable_msix:
3628c2ecf20Sopenharmony_ci			op->err = xen_pcibk_disable_msix(pdev, dev, op);
3638c2ecf20Sopenharmony_ci			break;
3648c2ecf20Sopenharmony_ci#endif
3658c2ecf20Sopenharmony_ci		default:
3668c2ecf20Sopenharmony_ci			op->err = XEN_PCI_ERR_not_implemented;
3678c2ecf20Sopenharmony_ci			break;
3688c2ecf20Sopenharmony_ci		}
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci	if (!op->err && dev && dev_data) {
3718c2ecf20Sopenharmony_ci		/* Transition detected */
3728c2ecf20Sopenharmony_ci		if ((dev_data->enable_intx != test_intx))
3738c2ecf20Sopenharmony_ci			xen_pcibk_control_isr(dev, 0 /* no reset */);
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci	pdev->sh_info->op.err = op->err;
3768c2ecf20Sopenharmony_ci	pdev->sh_info->op.value = op->value;
3778c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_MSI
3788c2ecf20Sopenharmony_ci	if (op->cmd == XEN_PCI_OP_enable_msix && op->err == 0) {
3798c2ecf20Sopenharmony_ci		unsigned int i;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci		for (i = 0; i < nr; i++)
3828c2ecf20Sopenharmony_ci			pdev->sh_info->op.msix_entries[i].vector =
3838c2ecf20Sopenharmony_ci				op->msix_entries[i].vector;
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci#endif
3868c2ecf20Sopenharmony_ci	/* Tell the driver domain that we're done. */
3878c2ecf20Sopenharmony_ci	wmb();
3888c2ecf20Sopenharmony_ci	clear_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags);
3898c2ecf20Sopenharmony_ci	notify_remote_via_irq(pdev->evtchn_irq);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	/* Mark that we're done. */
3928c2ecf20Sopenharmony_ci	smp_mb__before_atomic(); /* /after/ clearing PCIF_active */
3938c2ecf20Sopenharmony_ci	clear_bit(_PDEVF_op_active, &pdev->flags);
3948c2ecf20Sopenharmony_ci	smp_mb__after_atomic(); /* /before/ final check for work */
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_civoid xen_pcibk_do_op(struct work_struct *data)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	struct xen_pcibk_device *pdev =
4008c2ecf20Sopenharmony_ci		container_of(data, struct xen_pcibk_device, op_work);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	do {
4038c2ecf20Sopenharmony_ci		xen_pcibk_do_one_op(pdev);
4048c2ecf20Sopenharmony_ci	} while (xen_pcibk_test_op_pending(pdev));
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	xen_pcibk_lateeoi(pdev, 0);
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ciirqreturn_t xen_pcibk_handle_event(int irq, void *dev_id)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	struct xen_pcibk_device *pdev = dev_id;
4128c2ecf20Sopenharmony_ci	bool eoi;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/* IRQs might come in before pdev->evtchn_irq is written. */
4158c2ecf20Sopenharmony_ci	if (unlikely(pdev->evtchn_irq != irq))
4168c2ecf20Sopenharmony_ci		pdev->evtchn_irq = irq;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	eoi = test_and_set_bit(_EOI_pending, &pdev->flags);
4198c2ecf20Sopenharmony_ci	WARN(eoi, "IRQ while EOI pending\n");
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	xen_pcibk_test_and_schedule_op(pdev);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_cistatic irqreturn_t xen_pcibk_guest_interrupt(int irq, void *dev_id)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct pci_dev *dev = (struct pci_dev *)dev_id;
4288c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	if (dev_data->isr_on && dev_data->ack_intr) {
4318c2ecf20Sopenharmony_ci		dev_data->handled++;
4328c2ecf20Sopenharmony_ci		if ((dev_data->handled % 1000) == 0) {
4338c2ecf20Sopenharmony_ci			if (xen_test_irq_shared(irq)) {
4348c2ecf20Sopenharmony_ci				dev_info(&dev->dev, "%s IRQ line is not shared "
4358c2ecf20Sopenharmony_ci					"with other domains. Turning ISR off\n",
4368c2ecf20Sopenharmony_ci					 dev_data->irq_name);
4378c2ecf20Sopenharmony_ci				dev_data->ack_intr = 0;
4388c2ecf20Sopenharmony_ci			}
4398c2ecf20Sopenharmony_ci		}
4408c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci	return IRQ_NONE;
4438c2ecf20Sopenharmony_ci}
444