18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Volume Management Device driver
48c2ecf20Sopenharmony_ci * Copyright (c) 2015, Intel Corporation.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/device.h>
88c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
98c2ecf20Sopenharmony_ci#include <linux/irq.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/msi.h>
138c2ecf20Sopenharmony_ci#include <linux/pci.h>
148c2ecf20Sopenharmony_ci#include <linux/srcu.h>
158c2ecf20Sopenharmony_ci#include <linux/rculist.h>
168c2ecf20Sopenharmony_ci#include <linux/rcupdate.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <asm/irqdomain.h>
198c2ecf20Sopenharmony_ci#include <asm/device.h>
208c2ecf20Sopenharmony_ci#include <asm/msi.h>
218c2ecf20Sopenharmony_ci#include <asm/msidef.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define VMD_CFGBAR	0
248c2ecf20Sopenharmony_ci#define VMD_MEMBAR1	2
258c2ecf20Sopenharmony_ci#define VMD_MEMBAR2	4
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define PCI_REG_VMCAP		0x40
288c2ecf20Sopenharmony_ci#define BUS_RESTRICT_CAP(vmcap)	(vmcap & 0x1)
298c2ecf20Sopenharmony_ci#define PCI_REG_VMCONFIG	0x44
308c2ecf20Sopenharmony_ci#define BUS_RESTRICT_CFG(vmcfg)	((vmcfg >> 8) & 0x3)
318c2ecf20Sopenharmony_ci#define PCI_REG_VMLOCK		0x70
328c2ecf20Sopenharmony_ci#define MB2_SHADOW_EN(vmlock)	(vmlock & 0x2)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define MB2_SHADOW_OFFSET	0x2000
358c2ecf20Sopenharmony_ci#define MB2_SHADOW_SIZE		16
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cienum vmd_features {
388c2ecf20Sopenharmony_ci	/*
398c2ecf20Sopenharmony_ci	 * Device may contain registers which hint the physical location of the
408c2ecf20Sopenharmony_ci	 * membars, in order to allow proper address translation during
418c2ecf20Sopenharmony_ci	 * resource assignment to enable guest virtualization
428c2ecf20Sopenharmony_ci	 */
438c2ecf20Sopenharmony_ci	VMD_FEAT_HAS_MEMBAR_SHADOW		= (1 << 0),
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	/*
468c2ecf20Sopenharmony_ci	 * Device may provide root port configuration information which limits
478c2ecf20Sopenharmony_ci	 * bus numbering
488c2ecf20Sopenharmony_ci	 */
498c2ecf20Sopenharmony_ci	VMD_FEAT_HAS_BUS_RESTRICTIONS		= (1 << 1),
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/*
528c2ecf20Sopenharmony_ci	 * Device contains physical location shadow registers in
538c2ecf20Sopenharmony_ci	 * vendor-specific capability space
548c2ecf20Sopenharmony_ci	 */
558c2ecf20Sopenharmony_ci	VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP	= (1 << 2),
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/*
598c2ecf20Sopenharmony_ci * Lock for manipulating VMD IRQ lists.
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(list_lock);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/**
648c2ecf20Sopenharmony_ci * struct vmd_irq - private data to map driver IRQ to the VMD shared vector
658c2ecf20Sopenharmony_ci * @node:	list item for parent traversal.
668c2ecf20Sopenharmony_ci * @irq:	back pointer to parent.
678c2ecf20Sopenharmony_ci * @enabled:	true if driver enabled IRQ
688c2ecf20Sopenharmony_ci * @virq:	the virtual IRQ value provided to the requesting driver.
698c2ecf20Sopenharmony_ci *
708c2ecf20Sopenharmony_ci * Every MSI/MSI-X IRQ requested for a device in a VMD domain will be mapped to
718c2ecf20Sopenharmony_ci * a VMD IRQ using this structure.
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_cistruct vmd_irq {
748c2ecf20Sopenharmony_ci	struct list_head	node;
758c2ecf20Sopenharmony_ci	struct vmd_irq_list	*irq;
768c2ecf20Sopenharmony_ci	bool			enabled;
778c2ecf20Sopenharmony_ci	unsigned int		virq;
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/**
818c2ecf20Sopenharmony_ci * struct vmd_irq_list - list of driver requested IRQs mapping to a VMD vector
828c2ecf20Sopenharmony_ci * @irq_list:	the list of irq's the VMD one demuxes to.
838c2ecf20Sopenharmony_ci * @srcu:	SRCU struct for local synchronization.
848c2ecf20Sopenharmony_ci * @count:	number of child IRQs assigned to this vector; used to track
858c2ecf20Sopenharmony_ci *		sharing.
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_cistruct vmd_irq_list {
888c2ecf20Sopenharmony_ci	struct list_head	irq_list;
898c2ecf20Sopenharmony_ci	struct srcu_struct	srcu;
908c2ecf20Sopenharmony_ci	unsigned int		count;
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistruct vmd_dev {
948c2ecf20Sopenharmony_ci	struct pci_dev		*dev;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	spinlock_t		cfg_lock;
978c2ecf20Sopenharmony_ci	char __iomem		*cfgbar;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	int msix_count;
1008c2ecf20Sopenharmony_ci	struct vmd_irq_list	*irqs;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	struct pci_sysdata	sysdata;
1038c2ecf20Sopenharmony_ci	struct resource		resources[3];
1048c2ecf20Sopenharmony_ci	struct irq_domain	*irq_domain;
1058c2ecf20Sopenharmony_ci	struct pci_bus		*bus;
1068c2ecf20Sopenharmony_ci	u8			busn_start;
1078c2ecf20Sopenharmony_ci};
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	return container_of(bus->sysdata, struct vmd_dev, sysdata);
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic inline unsigned int index_from_irqs(struct vmd_dev *vmd,
1158c2ecf20Sopenharmony_ci					   struct vmd_irq_list *irqs)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	return irqs - vmd->irqs;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/*
1218c2ecf20Sopenharmony_ci * Drivers managing a device in a VMD domain allocate their own IRQs as before,
1228c2ecf20Sopenharmony_ci * but the MSI entry for the hardware it's driving will be programmed with a
1238c2ecf20Sopenharmony_ci * destination ID for the VMD MSI-X table.  The VMD muxes interrupts in its
1248c2ecf20Sopenharmony_ci * domain into one of its own, and the VMD driver de-muxes these for the
1258c2ecf20Sopenharmony_ci * handlers sharing that VMD IRQ.  The vmd irq_domain provides the operations
1268c2ecf20Sopenharmony_ci * and irq_chip to set this up.
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_cistatic void vmd_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct vmd_irq *vmdirq = data->chip_data;
1318c2ecf20Sopenharmony_ci	struct vmd_irq_list *irq = vmdirq->irq;
1328c2ecf20Sopenharmony_ci	struct vmd_dev *vmd = irq_data_get_irq_handler_data(data);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	msg->address_hi = MSI_ADDR_BASE_HI;
1358c2ecf20Sopenharmony_ci	msg->address_lo = MSI_ADDR_BASE_LO |
1368c2ecf20Sopenharmony_ci			  MSI_ADDR_DEST_ID(index_from_irqs(vmd, irq));
1378c2ecf20Sopenharmony_ci	msg->data = 0;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/*
1418c2ecf20Sopenharmony_ci * We rely on MSI_FLAG_USE_DEF_CHIP_OPS to set the IRQ mask/unmask ops.
1428c2ecf20Sopenharmony_ci */
1438c2ecf20Sopenharmony_cistatic void vmd_irq_enable(struct irq_data *data)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct vmd_irq *vmdirq = data->chip_data;
1468c2ecf20Sopenharmony_ci	unsigned long flags;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&list_lock, flags);
1498c2ecf20Sopenharmony_ci	WARN_ON(vmdirq->enabled);
1508c2ecf20Sopenharmony_ci	list_add_tail_rcu(&vmdirq->node, &vmdirq->irq->irq_list);
1518c2ecf20Sopenharmony_ci	vmdirq->enabled = true;
1528c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&list_lock, flags);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	data->chip->irq_unmask(data);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic void vmd_irq_disable(struct irq_data *data)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct vmd_irq *vmdirq = data->chip_data;
1608c2ecf20Sopenharmony_ci	unsigned long flags;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	data->chip->irq_mask(data);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&list_lock, flags);
1658c2ecf20Sopenharmony_ci	if (vmdirq->enabled) {
1668c2ecf20Sopenharmony_ci		list_del_rcu(&vmdirq->node);
1678c2ecf20Sopenharmony_ci		vmdirq->enabled = false;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&list_lock, flags);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/*
1738c2ecf20Sopenharmony_ci * XXX: Stubbed until we develop acceptable way to not create conflicts with
1748c2ecf20Sopenharmony_ci * other devices sharing the same vector.
1758c2ecf20Sopenharmony_ci */
1768c2ecf20Sopenharmony_cistatic int vmd_irq_set_affinity(struct irq_data *data,
1778c2ecf20Sopenharmony_ci				const struct cpumask *dest, bool force)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	return -EINVAL;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic struct irq_chip vmd_msi_controller = {
1838c2ecf20Sopenharmony_ci	.name			= "VMD-MSI",
1848c2ecf20Sopenharmony_ci	.irq_enable		= vmd_irq_enable,
1858c2ecf20Sopenharmony_ci	.irq_disable		= vmd_irq_disable,
1868c2ecf20Sopenharmony_ci	.irq_compose_msi_msg	= vmd_compose_msi_msg,
1878c2ecf20Sopenharmony_ci	.irq_set_affinity	= vmd_irq_set_affinity,
1888c2ecf20Sopenharmony_ci};
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic irq_hw_number_t vmd_get_hwirq(struct msi_domain_info *info,
1918c2ecf20Sopenharmony_ci				     msi_alloc_info_t *arg)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	return 0;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci/*
1978c2ecf20Sopenharmony_ci * XXX: We can be even smarter selecting the best IRQ once we solve the
1988c2ecf20Sopenharmony_ci * affinity problem.
1998c2ecf20Sopenharmony_ci */
2008c2ecf20Sopenharmony_cistatic struct vmd_irq_list *vmd_next_irq(struct vmd_dev *vmd, struct msi_desc *desc)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	int i, best = 1;
2038c2ecf20Sopenharmony_ci	unsigned long flags;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (vmd->msix_count == 1)
2068c2ecf20Sopenharmony_ci		return &vmd->irqs[0];
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/*
2098c2ecf20Sopenharmony_ci	 * White list for fast-interrupt handlers. All others will share the
2108c2ecf20Sopenharmony_ci	 * "slow" interrupt vector.
2118c2ecf20Sopenharmony_ci	 */
2128c2ecf20Sopenharmony_ci	switch (msi_desc_to_pci_dev(desc)->class) {
2138c2ecf20Sopenharmony_ci	case PCI_CLASS_STORAGE_EXPRESS:
2148c2ecf20Sopenharmony_ci		break;
2158c2ecf20Sopenharmony_ci	default:
2168c2ecf20Sopenharmony_ci		return &vmd->irqs[0];
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&list_lock, flags);
2208c2ecf20Sopenharmony_ci	for (i = 1; i < vmd->msix_count; i++)
2218c2ecf20Sopenharmony_ci		if (vmd->irqs[i].count < vmd->irqs[best].count)
2228c2ecf20Sopenharmony_ci			best = i;
2238c2ecf20Sopenharmony_ci	vmd->irqs[best].count++;
2248c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&list_lock, flags);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	return &vmd->irqs[best];
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic int vmd_msi_init(struct irq_domain *domain, struct msi_domain_info *info,
2308c2ecf20Sopenharmony_ci			unsigned int virq, irq_hw_number_t hwirq,
2318c2ecf20Sopenharmony_ci			msi_alloc_info_t *arg)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct msi_desc *desc = arg->desc;
2348c2ecf20Sopenharmony_ci	struct vmd_dev *vmd = vmd_from_bus(msi_desc_to_pci_dev(desc)->bus);
2358c2ecf20Sopenharmony_ci	struct vmd_irq *vmdirq = kzalloc(sizeof(*vmdirq), GFP_KERNEL);
2368c2ecf20Sopenharmony_ci	unsigned int index, vector;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (!vmdirq)
2398c2ecf20Sopenharmony_ci		return -ENOMEM;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&vmdirq->node);
2428c2ecf20Sopenharmony_ci	vmdirq->irq = vmd_next_irq(vmd, desc);
2438c2ecf20Sopenharmony_ci	vmdirq->virq = virq;
2448c2ecf20Sopenharmony_ci	index = index_from_irqs(vmd, vmdirq->irq);
2458c2ecf20Sopenharmony_ci	vector = pci_irq_vector(vmd->dev, index);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	irq_domain_set_info(domain, virq, vector, info->chip, vmdirq,
2488c2ecf20Sopenharmony_ci			    handle_untracked_irq, vmd, NULL);
2498c2ecf20Sopenharmony_ci	return 0;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic void vmd_msi_free(struct irq_domain *domain,
2538c2ecf20Sopenharmony_ci			struct msi_domain_info *info, unsigned int virq)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	struct vmd_irq *vmdirq = irq_get_chip_data(virq);
2568c2ecf20Sopenharmony_ci	unsigned long flags;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	synchronize_srcu(&vmdirq->irq->srcu);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/* XXX: Potential optimization to rebalance */
2618c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&list_lock, flags);
2628c2ecf20Sopenharmony_ci	vmdirq->irq->count--;
2638c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&list_lock, flags);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	kfree(vmdirq);
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic int vmd_msi_prepare(struct irq_domain *domain, struct device *dev,
2698c2ecf20Sopenharmony_ci			   int nvec, msi_alloc_info_t *arg)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
2728c2ecf20Sopenharmony_ci	struct vmd_dev *vmd = vmd_from_bus(pdev->bus);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (nvec > vmd->msix_count)
2758c2ecf20Sopenharmony_ci		return vmd->msix_count;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	memset(arg, 0, sizeof(*arg));
2788c2ecf20Sopenharmony_ci	return 0;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic void vmd_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	arg->desc = desc;
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic struct msi_domain_ops vmd_msi_domain_ops = {
2878c2ecf20Sopenharmony_ci	.get_hwirq	= vmd_get_hwirq,
2888c2ecf20Sopenharmony_ci	.msi_init	= vmd_msi_init,
2898c2ecf20Sopenharmony_ci	.msi_free	= vmd_msi_free,
2908c2ecf20Sopenharmony_ci	.msi_prepare	= vmd_msi_prepare,
2918c2ecf20Sopenharmony_ci	.set_desc	= vmd_set_desc,
2928c2ecf20Sopenharmony_ci};
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic struct msi_domain_info vmd_msi_domain_info = {
2958c2ecf20Sopenharmony_ci	.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
2968c2ecf20Sopenharmony_ci			  MSI_FLAG_PCI_MSIX,
2978c2ecf20Sopenharmony_ci	.ops		= &vmd_msi_domain_ops,
2988c2ecf20Sopenharmony_ci	.chip		= &vmd_msi_controller,
2998c2ecf20Sopenharmony_ci};
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic int vmd_create_irq_domain(struct vmd_dev *vmd)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct fwnode_handle *fn;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	fn = irq_domain_alloc_named_id_fwnode("VMD-MSI", vmd->sysdata.domain);
3068c2ecf20Sopenharmony_ci	if (!fn)
3078c2ecf20Sopenharmony_ci		return -ENODEV;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	vmd->irq_domain = pci_msi_create_irq_domain(fn, &vmd_msi_domain_info, NULL);
3108c2ecf20Sopenharmony_ci	if (!vmd->irq_domain) {
3118c2ecf20Sopenharmony_ci		irq_domain_free_fwnode(fn);
3128c2ecf20Sopenharmony_ci		return -ENODEV;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	return 0;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic void vmd_remove_irq_domain(struct vmd_dev *vmd)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	if (vmd->irq_domain) {
3218c2ecf20Sopenharmony_ci		struct fwnode_handle *fn = vmd->irq_domain->fwnode;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		irq_domain_remove(vmd->irq_domain);
3248c2ecf20Sopenharmony_ci		irq_domain_free_fwnode(fn);
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic char __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus,
3298c2ecf20Sopenharmony_ci				  unsigned int devfn, int reg, int len)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	char __iomem *addr = vmd->cfgbar +
3328c2ecf20Sopenharmony_ci			     ((bus->number - vmd->busn_start) << 20) +
3338c2ecf20Sopenharmony_ci			     (devfn << 12) + reg;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if ((addr - vmd->cfgbar) + len >=
3368c2ecf20Sopenharmony_ci	    resource_size(&vmd->dev->resource[VMD_CFGBAR]))
3378c2ecf20Sopenharmony_ci		return NULL;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	return addr;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci/*
3438c2ecf20Sopenharmony_ci * CPU may deadlock if config space is not serialized on some versions of this
3448c2ecf20Sopenharmony_ci * hardware, so all config space access is done under a spinlock.
3458c2ecf20Sopenharmony_ci */
3468c2ecf20Sopenharmony_cistatic int vmd_pci_read(struct pci_bus *bus, unsigned int devfn, int reg,
3478c2ecf20Sopenharmony_ci			int len, u32 *value)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct vmd_dev *vmd = vmd_from_bus(bus);
3508c2ecf20Sopenharmony_ci	char __iomem *addr = vmd_cfg_addr(vmd, bus, devfn, reg, len);
3518c2ecf20Sopenharmony_ci	unsigned long flags;
3528c2ecf20Sopenharmony_ci	int ret = 0;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	if (!addr)
3558c2ecf20Sopenharmony_ci		return -EFAULT;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vmd->cfg_lock, flags);
3588c2ecf20Sopenharmony_ci	switch (len) {
3598c2ecf20Sopenharmony_ci	case 1:
3608c2ecf20Sopenharmony_ci		*value = readb(addr);
3618c2ecf20Sopenharmony_ci		break;
3628c2ecf20Sopenharmony_ci	case 2:
3638c2ecf20Sopenharmony_ci		*value = readw(addr);
3648c2ecf20Sopenharmony_ci		break;
3658c2ecf20Sopenharmony_ci	case 4:
3668c2ecf20Sopenharmony_ci		*value = readl(addr);
3678c2ecf20Sopenharmony_ci		break;
3688c2ecf20Sopenharmony_ci	default:
3698c2ecf20Sopenharmony_ci		ret = -EINVAL;
3708c2ecf20Sopenharmony_ci		break;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vmd->cfg_lock, flags);
3738c2ecf20Sopenharmony_ci	return ret;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci/*
3778c2ecf20Sopenharmony_ci * VMD h/w converts non-posted config writes to posted memory writes. The
3788c2ecf20Sopenharmony_ci * read-back in this function forces the completion so it returns only after
3798c2ecf20Sopenharmony_ci * the config space was written, as expected.
3808c2ecf20Sopenharmony_ci */
3818c2ecf20Sopenharmony_cistatic int vmd_pci_write(struct pci_bus *bus, unsigned int devfn, int reg,
3828c2ecf20Sopenharmony_ci			 int len, u32 value)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	struct vmd_dev *vmd = vmd_from_bus(bus);
3858c2ecf20Sopenharmony_ci	char __iomem *addr = vmd_cfg_addr(vmd, bus, devfn, reg, len);
3868c2ecf20Sopenharmony_ci	unsigned long flags;
3878c2ecf20Sopenharmony_ci	int ret = 0;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	if (!addr)
3908c2ecf20Sopenharmony_ci		return -EFAULT;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vmd->cfg_lock, flags);
3938c2ecf20Sopenharmony_ci	switch (len) {
3948c2ecf20Sopenharmony_ci	case 1:
3958c2ecf20Sopenharmony_ci		writeb(value, addr);
3968c2ecf20Sopenharmony_ci		readb(addr);
3978c2ecf20Sopenharmony_ci		break;
3988c2ecf20Sopenharmony_ci	case 2:
3998c2ecf20Sopenharmony_ci		writew(value, addr);
4008c2ecf20Sopenharmony_ci		readw(addr);
4018c2ecf20Sopenharmony_ci		break;
4028c2ecf20Sopenharmony_ci	case 4:
4038c2ecf20Sopenharmony_ci		writel(value, addr);
4048c2ecf20Sopenharmony_ci		readl(addr);
4058c2ecf20Sopenharmony_ci		break;
4068c2ecf20Sopenharmony_ci	default:
4078c2ecf20Sopenharmony_ci		ret = -EINVAL;
4088c2ecf20Sopenharmony_ci		break;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vmd->cfg_lock, flags);
4118c2ecf20Sopenharmony_ci	return ret;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic struct pci_ops vmd_ops = {
4158c2ecf20Sopenharmony_ci	.read		= vmd_pci_read,
4168c2ecf20Sopenharmony_ci	.write		= vmd_pci_write,
4178c2ecf20Sopenharmony_ci};
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic void vmd_attach_resources(struct vmd_dev *vmd)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	vmd->dev->resource[VMD_MEMBAR1].child = &vmd->resources[1];
4228c2ecf20Sopenharmony_ci	vmd->dev->resource[VMD_MEMBAR2].child = &vmd->resources[2];
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic void vmd_detach_resources(struct vmd_dev *vmd)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	vmd->dev->resource[VMD_MEMBAR1].child = NULL;
4288c2ecf20Sopenharmony_ci	vmd->dev->resource[VMD_MEMBAR2].child = NULL;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci/*
4328c2ecf20Sopenharmony_ci * VMD domains start at 0x10000 to not clash with ACPI _SEG domains.
4338c2ecf20Sopenharmony_ci * Per ACPI r6.0, sec 6.5.6,  _SEG returns an integer, of which the lower
4348c2ecf20Sopenharmony_ci * 16 bits are the PCI Segment Group (domain) number.  Other bits are
4358c2ecf20Sopenharmony_ci * currently reserved.
4368c2ecf20Sopenharmony_ci */
4378c2ecf20Sopenharmony_cistatic int vmd_find_free_domain(void)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	int domain = 0xffff;
4408c2ecf20Sopenharmony_ci	struct pci_bus *bus = NULL;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	while ((bus = pci_find_next_bus(bus)) != NULL)
4438c2ecf20Sopenharmony_ci		domain = max_t(int, domain, pci_domain_nr(bus));
4448c2ecf20Sopenharmony_ci	return domain + 1;
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic int vmd_get_phys_offsets(struct vmd_dev *vmd, bool native_hint,
4488c2ecf20Sopenharmony_ci				resource_size_t *offset1,
4498c2ecf20Sopenharmony_ci				resource_size_t *offset2)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	struct pci_dev *dev = vmd->dev;
4528c2ecf20Sopenharmony_ci	u64 phys1, phys2;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	if (native_hint) {
4558c2ecf20Sopenharmony_ci		u32 vmlock;
4568c2ecf20Sopenharmony_ci		int ret;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci		ret = pci_read_config_dword(dev, PCI_REG_VMLOCK, &vmlock);
4598c2ecf20Sopenharmony_ci		if (ret || vmlock == ~0)
4608c2ecf20Sopenharmony_ci			return -ENODEV;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci		if (MB2_SHADOW_EN(vmlock)) {
4638c2ecf20Sopenharmony_ci			void __iomem *membar2;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci			membar2 = pci_iomap(dev, VMD_MEMBAR2, 0);
4668c2ecf20Sopenharmony_ci			if (!membar2)
4678c2ecf20Sopenharmony_ci				return -ENOMEM;
4688c2ecf20Sopenharmony_ci			phys1 = readq(membar2 + MB2_SHADOW_OFFSET);
4698c2ecf20Sopenharmony_ci			phys2 = readq(membar2 + MB2_SHADOW_OFFSET + 8);
4708c2ecf20Sopenharmony_ci			pci_iounmap(dev, membar2);
4718c2ecf20Sopenharmony_ci		} else
4728c2ecf20Sopenharmony_ci			return 0;
4738c2ecf20Sopenharmony_ci	} else {
4748c2ecf20Sopenharmony_ci		/* Hypervisor-Emulated Vendor-Specific Capability */
4758c2ecf20Sopenharmony_ci		int pos = pci_find_capability(dev, PCI_CAP_ID_VNDR);
4768c2ecf20Sopenharmony_ci		u32 reg, regu;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci		pci_read_config_dword(dev, pos + 4, &reg);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		/* "SHDW" */
4818c2ecf20Sopenharmony_ci		if (pos && reg == 0x53484457) {
4828c2ecf20Sopenharmony_ci			pci_read_config_dword(dev, pos + 8, &reg);
4838c2ecf20Sopenharmony_ci			pci_read_config_dword(dev, pos + 12, &regu);
4848c2ecf20Sopenharmony_ci			phys1 = (u64) regu << 32 | reg;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci			pci_read_config_dword(dev, pos + 16, &reg);
4878c2ecf20Sopenharmony_ci			pci_read_config_dword(dev, pos + 20, &regu);
4888c2ecf20Sopenharmony_ci			phys2 = (u64) regu << 32 | reg;
4898c2ecf20Sopenharmony_ci		} else
4908c2ecf20Sopenharmony_ci			return 0;
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	*offset1 = dev->resource[VMD_MEMBAR1].start -
4948c2ecf20Sopenharmony_ci			(phys1 & PCI_BASE_ADDRESS_MEM_MASK);
4958c2ecf20Sopenharmony_ci	*offset2 = dev->resource[VMD_MEMBAR2].start -
4968c2ecf20Sopenharmony_ci			(phys2 & PCI_BASE_ADDRESS_MEM_MASK);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	return 0;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic int vmd_get_bus_number_start(struct vmd_dev *vmd)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	struct pci_dev *dev = vmd->dev;
5048c2ecf20Sopenharmony_ci	u16 reg;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	pci_read_config_word(dev, PCI_REG_VMCAP, &reg);
5078c2ecf20Sopenharmony_ci	if (BUS_RESTRICT_CAP(reg)) {
5088c2ecf20Sopenharmony_ci		pci_read_config_word(dev, PCI_REG_VMCONFIG, &reg);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci		switch (BUS_RESTRICT_CFG(reg)) {
5118c2ecf20Sopenharmony_ci		case 0:
5128c2ecf20Sopenharmony_ci			vmd->busn_start = 0;
5138c2ecf20Sopenharmony_ci			break;
5148c2ecf20Sopenharmony_ci		case 1:
5158c2ecf20Sopenharmony_ci			vmd->busn_start = 128;
5168c2ecf20Sopenharmony_ci			break;
5178c2ecf20Sopenharmony_ci		case 2:
5188c2ecf20Sopenharmony_ci			vmd->busn_start = 224;
5198c2ecf20Sopenharmony_ci			break;
5208c2ecf20Sopenharmony_ci		default:
5218c2ecf20Sopenharmony_ci			pci_err(dev, "Unknown Bus Offset Setting (%d)\n",
5228c2ecf20Sopenharmony_ci				BUS_RESTRICT_CFG(reg));
5238c2ecf20Sopenharmony_ci			return -ENODEV;
5248c2ecf20Sopenharmony_ci		}
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	return 0;
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistatic irqreturn_t vmd_irq(int irq, void *data)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	struct vmd_irq_list *irqs = data;
5338c2ecf20Sopenharmony_ci	struct vmd_irq *vmdirq;
5348c2ecf20Sopenharmony_ci	int idx;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	idx = srcu_read_lock(&irqs->srcu);
5378c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node)
5388c2ecf20Sopenharmony_ci		generic_handle_irq(vmdirq->virq);
5398c2ecf20Sopenharmony_ci	srcu_read_unlock(&irqs->srcu, idx);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_cistatic int vmd_alloc_irqs(struct vmd_dev *vmd)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	struct pci_dev *dev = vmd->dev;
5478c2ecf20Sopenharmony_ci	int i, err;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	vmd->msix_count = pci_msix_vec_count(dev);
5508c2ecf20Sopenharmony_ci	if (vmd->msix_count < 0)
5518c2ecf20Sopenharmony_ci		return -ENODEV;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	vmd->msix_count = pci_alloc_irq_vectors(dev, 1, vmd->msix_count,
5548c2ecf20Sopenharmony_ci						PCI_IRQ_MSIX);
5558c2ecf20Sopenharmony_ci	if (vmd->msix_count < 0)
5568c2ecf20Sopenharmony_ci		return vmd->msix_count;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	vmd->irqs = devm_kcalloc(&dev->dev, vmd->msix_count, sizeof(*vmd->irqs),
5598c2ecf20Sopenharmony_ci				 GFP_KERNEL);
5608c2ecf20Sopenharmony_ci	if (!vmd->irqs)
5618c2ecf20Sopenharmony_ci		return -ENOMEM;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	for (i = 0; i < vmd->msix_count; i++) {
5648c2ecf20Sopenharmony_ci		err = init_srcu_struct(&vmd->irqs[i].srcu);
5658c2ecf20Sopenharmony_ci		if (err)
5668c2ecf20Sopenharmony_ci			return err;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&vmd->irqs[i].irq_list);
5698c2ecf20Sopenharmony_ci		err = devm_request_irq(&dev->dev, pci_irq_vector(dev, i),
5708c2ecf20Sopenharmony_ci				       vmd_irq, IRQF_NO_THREAD,
5718c2ecf20Sopenharmony_ci				       "vmd", &vmd->irqs[i]);
5728c2ecf20Sopenharmony_ci		if (err)
5738c2ecf20Sopenharmony_ci			return err;
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	return 0;
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	struct pci_sysdata *sd = &vmd->sysdata;
5828c2ecf20Sopenharmony_ci	struct resource *res;
5838c2ecf20Sopenharmony_ci	u32 upper_bits;
5848c2ecf20Sopenharmony_ci	unsigned long flags;
5858c2ecf20Sopenharmony_ci	LIST_HEAD(resources);
5868c2ecf20Sopenharmony_ci	resource_size_t offset[2] = {0};
5878c2ecf20Sopenharmony_ci	resource_size_t membar2_offset = 0x2000;
5888c2ecf20Sopenharmony_ci	struct pci_bus *child;
5898c2ecf20Sopenharmony_ci	int ret;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	/*
5928c2ecf20Sopenharmony_ci	 * Shadow registers may exist in certain VMD device ids which allow
5938c2ecf20Sopenharmony_ci	 * guests to correctly assign host physical addresses to the root ports
5948c2ecf20Sopenharmony_ci	 * and child devices. These registers will either return the host value
5958c2ecf20Sopenharmony_ci	 * or 0, depending on an enable bit in the VMD device.
5968c2ecf20Sopenharmony_ci	 */
5978c2ecf20Sopenharmony_ci	if (features & VMD_FEAT_HAS_MEMBAR_SHADOW) {
5988c2ecf20Sopenharmony_ci		membar2_offset = MB2_SHADOW_OFFSET + MB2_SHADOW_SIZE;
5998c2ecf20Sopenharmony_ci		ret = vmd_get_phys_offsets(vmd, true, &offset[0], &offset[1]);
6008c2ecf20Sopenharmony_ci		if (ret)
6018c2ecf20Sopenharmony_ci			return ret;
6028c2ecf20Sopenharmony_ci	} else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP) {
6038c2ecf20Sopenharmony_ci		ret = vmd_get_phys_offsets(vmd, false, &offset[0], &offset[1]);
6048c2ecf20Sopenharmony_ci		if (ret)
6058c2ecf20Sopenharmony_ci			return ret;
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	/*
6098c2ecf20Sopenharmony_ci	 * Certain VMD devices may have a root port configuration option which
6108c2ecf20Sopenharmony_ci	 * limits the bus range to between 0-127, 128-255, or 224-255
6118c2ecf20Sopenharmony_ci	 */
6128c2ecf20Sopenharmony_ci	if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) {
6138c2ecf20Sopenharmony_ci		ret = vmd_get_bus_number_start(vmd);
6148c2ecf20Sopenharmony_ci		if (ret)
6158c2ecf20Sopenharmony_ci			return ret;
6168c2ecf20Sopenharmony_ci	}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	res = &vmd->dev->resource[VMD_CFGBAR];
6198c2ecf20Sopenharmony_ci	vmd->resources[0] = (struct resource) {
6208c2ecf20Sopenharmony_ci		.name  = "VMD CFGBAR",
6218c2ecf20Sopenharmony_ci		.start = vmd->busn_start,
6228c2ecf20Sopenharmony_ci		.end   = vmd->busn_start + (resource_size(res) >> 20) - 1,
6238c2ecf20Sopenharmony_ci		.flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
6248c2ecf20Sopenharmony_ci	};
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	/*
6278c2ecf20Sopenharmony_ci	 * If the window is below 4GB, clear IORESOURCE_MEM_64 so we can
6288c2ecf20Sopenharmony_ci	 * put 32-bit resources in the window.
6298c2ecf20Sopenharmony_ci	 *
6308c2ecf20Sopenharmony_ci	 * There's no hardware reason why a 64-bit window *couldn't*
6318c2ecf20Sopenharmony_ci	 * contain a 32-bit resource, but pbus_size_mem() computes the
6328c2ecf20Sopenharmony_ci	 * bridge window size assuming a 64-bit window will contain no
6338c2ecf20Sopenharmony_ci	 * 32-bit resources.  __pci_assign_resource() enforces that
6348c2ecf20Sopenharmony_ci	 * artificial restriction to make sure everything will fit.
6358c2ecf20Sopenharmony_ci	 *
6368c2ecf20Sopenharmony_ci	 * The only way we could use a 64-bit non-prefetchable MEMBAR is
6378c2ecf20Sopenharmony_ci	 * if its address is <4GB so that we can convert it to a 32-bit
6388c2ecf20Sopenharmony_ci	 * resource.  To be visible to the host OS, all VMD endpoints must
6398c2ecf20Sopenharmony_ci	 * be initially configured by platform BIOS, which includes setting
6408c2ecf20Sopenharmony_ci	 * up these resources.  We can assume the device is configured
6418c2ecf20Sopenharmony_ci	 * according to the platform needs.
6428c2ecf20Sopenharmony_ci	 */
6438c2ecf20Sopenharmony_ci	res = &vmd->dev->resource[VMD_MEMBAR1];
6448c2ecf20Sopenharmony_ci	upper_bits = upper_32_bits(res->end);
6458c2ecf20Sopenharmony_ci	flags = res->flags & ~IORESOURCE_SIZEALIGN;
6468c2ecf20Sopenharmony_ci	if (!upper_bits)
6478c2ecf20Sopenharmony_ci		flags &= ~IORESOURCE_MEM_64;
6488c2ecf20Sopenharmony_ci	vmd->resources[1] = (struct resource) {
6498c2ecf20Sopenharmony_ci		.name  = "VMD MEMBAR1",
6508c2ecf20Sopenharmony_ci		.start = res->start,
6518c2ecf20Sopenharmony_ci		.end   = res->end,
6528c2ecf20Sopenharmony_ci		.flags = flags,
6538c2ecf20Sopenharmony_ci		.parent = res,
6548c2ecf20Sopenharmony_ci	};
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	res = &vmd->dev->resource[VMD_MEMBAR2];
6578c2ecf20Sopenharmony_ci	upper_bits = upper_32_bits(res->end);
6588c2ecf20Sopenharmony_ci	flags = res->flags & ~IORESOURCE_SIZEALIGN;
6598c2ecf20Sopenharmony_ci	if (!upper_bits)
6608c2ecf20Sopenharmony_ci		flags &= ~IORESOURCE_MEM_64;
6618c2ecf20Sopenharmony_ci	vmd->resources[2] = (struct resource) {
6628c2ecf20Sopenharmony_ci		.name  = "VMD MEMBAR2",
6638c2ecf20Sopenharmony_ci		.start = res->start + membar2_offset,
6648c2ecf20Sopenharmony_ci		.end   = res->end,
6658c2ecf20Sopenharmony_ci		.flags = flags,
6668c2ecf20Sopenharmony_ci		.parent = res,
6678c2ecf20Sopenharmony_ci	};
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	sd->vmd_dev = vmd->dev;
6708c2ecf20Sopenharmony_ci	sd->domain = vmd_find_free_domain();
6718c2ecf20Sopenharmony_ci	if (sd->domain < 0)
6728c2ecf20Sopenharmony_ci		return sd->domain;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	sd->node = pcibus_to_node(vmd->dev->bus);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	ret = vmd_create_irq_domain(vmd);
6778c2ecf20Sopenharmony_ci	if (ret)
6788c2ecf20Sopenharmony_ci		return ret;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	/*
6818c2ecf20Sopenharmony_ci	 * Override the irq domain bus token so the domain can be distinguished
6828c2ecf20Sopenharmony_ci	 * from a regular PCI/MSI domain.
6838c2ecf20Sopenharmony_ci	 */
6848c2ecf20Sopenharmony_ci	irq_domain_update_bus_token(vmd->irq_domain, DOMAIN_BUS_VMD_MSI);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	pci_add_resource(&resources, &vmd->resources[0]);
6878c2ecf20Sopenharmony_ci	pci_add_resource_offset(&resources, &vmd->resources[1], offset[0]);
6888c2ecf20Sopenharmony_ci	pci_add_resource_offset(&resources, &vmd->resources[2], offset[1]);
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	vmd->bus = pci_create_root_bus(&vmd->dev->dev, vmd->busn_start,
6918c2ecf20Sopenharmony_ci				       &vmd_ops, sd, &resources);
6928c2ecf20Sopenharmony_ci	if (!vmd->bus) {
6938c2ecf20Sopenharmony_ci		pci_free_resource_list(&resources);
6948c2ecf20Sopenharmony_ci		vmd_remove_irq_domain(vmd);
6958c2ecf20Sopenharmony_ci		return -ENODEV;
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	vmd_attach_resources(vmd);
6998c2ecf20Sopenharmony_ci	if (vmd->irq_domain)
7008c2ecf20Sopenharmony_ci		dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	pci_scan_child_bus(vmd->bus);
7038c2ecf20Sopenharmony_ci	pci_assign_unassigned_bus_resources(vmd->bus);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	/*
7068c2ecf20Sopenharmony_ci	 * VMD root buses are virtual and don't return true on pci_is_pcie()
7078c2ecf20Sopenharmony_ci	 * and will fail pcie_bus_configure_settings() early. It can instead be
7088c2ecf20Sopenharmony_ci	 * run on each of the real root ports.
7098c2ecf20Sopenharmony_ci	 */
7108c2ecf20Sopenharmony_ci	list_for_each_entry(child, &vmd->bus->children, node)
7118c2ecf20Sopenharmony_ci		pcie_bus_configure_settings(child);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	pci_bus_add_devices(vmd->bus);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	WARN(sysfs_create_link(&vmd->dev->dev.kobj, &vmd->bus->dev.kobj,
7168c2ecf20Sopenharmony_ci			       "domain"), "Can't create symlink to domain\n");
7178c2ecf20Sopenharmony_ci	return 0;
7188c2ecf20Sopenharmony_ci}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_cistatic int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	struct vmd_dev *vmd;
7238c2ecf20Sopenharmony_ci	int err;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	if (resource_size(&dev->resource[VMD_CFGBAR]) < (1 << 20))
7268c2ecf20Sopenharmony_ci		return -ENOMEM;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	vmd = devm_kzalloc(&dev->dev, sizeof(*vmd), GFP_KERNEL);
7298c2ecf20Sopenharmony_ci	if (!vmd)
7308c2ecf20Sopenharmony_ci		return -ENOMEM;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	vmd->dev = dev;
7338c2ecf20Sopenharmony_ci	err = pcim_enable_device(dev);
7348c2ecf20Sopenharmony_ci	if (err < 0)
7358c2ecf20Sopenharmony_ci		return err;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	vmd->cfgbar = pcim_iomap(dev, VMD_CFGBAR, 0);
7388c2ecf20Sopenharmony_ci	if (!vmd->cfgbar)
7398c2ecf20Sopenharmony_ci		return -ENOMEM;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	pci_set_master(dev);
7428c2ecf20Sopenharmony_ci	if (dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(64)) &&
7438c2ecf20Sopenharmony_ci	    dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)))
7448c2ecf20Sopenharmony_ci		return -ENODEV;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	err = vmd_alloc_irqs(vmd);
7478c2ecf20Sopenharmony_ci	if (err)
7488c2ecf20Sopenharmony_ci		return err;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	spin_lock_init(&vmd->cfg_lock);
7518c2ecf20Sopenharmony_ci	pci_set_drvdata(dev, vmd);
7528c2ecf20Sopenharmony_ci	err = vmd_enable_domain(vmd, (unsigned long) id->driver_data);
7538c2ecf20Sopenharmony_ci	if (err)
7548c2ecf20Sopenharmony_ci		return err;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	dev_info(&vmd->dev->dev, "Bound to PCI domain %04x\n",
7578c2ecf20Sopenharmony_ci		 vmd->sysdata.domain);
7588c2ecf20Sopenharmony_ci	return 0;
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cistatic void vmd_cleanup_srcu(struct vmd_dev *vmd)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	int i;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	for (i = 0; i < vmd->msix_count; i++)
7668c2ecf20Sopenharmony_ci		cleanup_srcu_struct(&vmd->irqs[i].srcu);
7678c2ecf20Sopenharmony_ci}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_cistatic void vmd_remove(struct pci_dev *dev)
7708c2ecf20Sopenharmony_ci{
7718c2ecf20Sopenharmony_ci	struct vmd_dev *vmd = pci_get_drvdata(dev);
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	sysfs_remove_link(&vmd->dev->dev.kobj, "domain");
7748c2ecf20Sopenharmony_ci	pci_stop_root_bus(vmd->bus);
7758c2ecf20Sopenharmony_ci	pci_remove_root_bus(vmd->bus);
7768c2ecf20Sopenharmony_ci	vmd_cleanup_srcu(vmd);
7778c2ecf20Sopenharmony_ci	vmd_detach_resources(vmd);
7788c2ecf20Sopenharmony_ci	vmd_remove_irq_domain(vmd);
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
7828c2ecf20Sopenharmony_cistatic int vmd_suspend(struct device *dev)
7838c2ecf20Sopenharmony_ci{
7848c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
7858c2ecf20Sopenharmony_ci	struct vmd_dev *vmd = pci_get_drvdata(pdev);
7868c2ecf20Sopenharmony_ci	int i;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	for (i = 0; i < vmd->msix_count; i++)
7898c2ecf20Sopenharmony_ci		devm_free_irq(dev, pci_irq_vector(pdev, i), &vmd->irqs[i]);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	return 0;
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_cistatic int vmd_resume(struct device *dev)
7958c2ecf20Sopenharmony_ci{
7968c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
7978c2ecf20Sopenharmony_ci	struct vmd_dev *vmd = pci_get_drvdata(pdev);
7988c2ecf20Sopenharmony_ci	int err, i;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	for (i = 0; i < vmd->msix_count; i++) {
8018c2ecf20Sopenharmony_ci		err = devm_request_irq(dev, pci_irq_vector(pdev, i),
8028c2ecf20Sopenharmony_ci				       vmd_irq, IRQF_NO_THREAD,
8038c2ecf20Sopenharmony_ci				       "vmd", &vmd->irqs[i]);
8048c2ecf20Sopenharmony_ci		if (err)
8058c2ecf20Sopenharmony_ci			return err;
8068c2ecf20Sopenharmony_ci	}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	return 0;
8098c2ecf20Sopenharmony_ci}
8108c2ecf20Sopenharmony_ci#endif
8118c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(vmd_dev_pm_ops, vmd_suspend, vmd_resume);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_cistatic const struct pci_device_id vmd_ids[] = {
8148c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_201D),
8158c2ecf20Sopenharmony_ci		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP,},
8168c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_28C0),
8178c2ecf20Sopenharmony_ci		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW |
8188c2ecf20Sopenharmony_ci				VMD_FEAT_HAS_BUS_RESTRICTIONS,},
8198c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x467f),
8208c2ecf20Sopenharmony_ci		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP |
8218c2ecf20Sopenharmony_ci				VMD_FEAT_HAS_BUS_RESTRICTIONS,},
8228c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4c3d),
8238c2ecf20Sopenharmony_ci		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP |
8248c2ecf20Sopenharmony_ci				VMD_FEAT_HAS_BUS_RESTRICTIONS,},
8258c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B),
8268c2ecf20Sopenharmony_ci		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP |
8278c2ecf20Sopenharmony_ci				VMD_FEAT_HAS_BUS_RESTRICTIONS,},
8288c2ecf20Sopenharmony_ci	{0,}
8298c2ecf20Sopenharmony_ci};
8308c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, vmd_ids);
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_cistatic struct pci_driver vmd_drv = {
8338c2ecf20Sopenharmony_ci	.name		= "vmd",
8348c2ecf20Sopenharmony_ci	.id_table	= vmd_ids,
8358c2ecf20Sopenharmony_ci	.probe		= vmd_probe,
8368c2ecf20Sopenharmony_ci	.remove		= vmd_remove,
8378c2ecf20Sopenharmony_ci	.driver		= {
8388c2ecf20Sopenharmony_ci		.pm	= &vmd_dev_pm_ops,
8398c2ecf20Sopenharmony_ci	},
8408c2ecf20Sopenharmony_ci};
8418c2ecf20Sopenharmony_cimodule_pci_driver(vmd_drv);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Intel Corporation");
8448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
8458c2ecf20Sopenharmony_ciMODULE_VERSION("0.6");
846