xref: /openbmc/linux/drivers/pci/msi/msi.c (revision ed4543328f7108e1047b83b96ca7f7208747d930)
1288c81ceSThomas Gleixner // SPDX-License-Identifier: GPL-2.0
2288c81ceSThomas Gleixner /*
3288c81ceSThomas Gleixner  * PCI Message Signaled Interrupt (MSI)
4288c81ceSThomas Gleixner  *
5288c81ceSThomas Gleixner  * Copyright (C) 2003-2004 Intel
6288c81ceSThomas Gleixner  * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
7288c81ceSThomas Gleixner  * Copyright (C) 2016 Christoph Hellwig.
8288c81ceSThomas Gleixner  */
9288c81ceSThomas Gleixner #include <linux/err.h>
10288c81ceSThomas Gleixner #include <linux/export.h>
11288c81ceSThomas Gleixner #include <linux/irq.h>
12288c81ceSThomas Gleixner 
13288c81ceSThomas Gleixner #include "../pci.h"
14aa423ac4SThomas Gleixner #include "msi.h"
15288c81ceSThomas Gleixner 
16897a0b6aSAhmed S. Darwish int pci_msi_enable = 1;
17288c81ceSThomas Gleixner int pci_msi_ignore_mask;
18288c81ceSThomas Gleixner 
1912910ffdSAhmed S. Darwish /**
2012910ffdSAhmed S. Darwish  * pci_msi_supported - check whether MSI may be enabled on a device
2112910ffdSAhmed S. Darwish  * @dev: pointer to the pci_dev data structure of MSI device function
2212910ffdSAhmed S. Darwish  * @nvec: how many MSIs have been requested?
2312910ffdSAhmed S. Darwish  *
2412910ffdSAhmed S. Darwish  * Look at global flags, the device itself, and its parent buses
2512910ffdSAhmed S. Darwish  * to determine if MSI/-X are supported for the device. If MSI/-X is
2612910ffdSAhmed S. Darwish  * supported return 1, else return 0.
2712910ffdSAhmed S. Darwish  **/
pci_msi_supported(struct pci_dev * dev,int nvec)2812910ffdSAhmed S. Darwish static int pci_msi_supported(struct pci_dev *dev, int nvec)
2912910ffdSAhmed S. Darwish {
3012910ffdSAhmed S. Darwish 	struct pci_bus *bus;
3112910ffdSAhmed S. Darwish 
3212910ffdSAhmed S. Darwish 	/* MSI must be globally enabled and supported by the device */
3312910ffdSAhmed S. Darwish 	if (!pci_msi_enable)
3412910ffdSAhmed S. Darwish 		return 0;
3512910ffdSAhmed S. Darwish 
3612910ffdSAhmed S. Darwish 	if (!dev || dev->no_msi)
3712910ffdSAhmed S. Darwish 		return 0;
3812910ffdSAhmed S. Darwish 
3912910ffdSAhmed S. Darwish 	/*
4012910ffdSAhmed S. Darwish 	 * You can't ask to have 0 or less MSIs configured.
4112910ffdSAhmed S. Darwish 	 *  a) it's stupid ..
4212910ffdSAhmed S. Darwish 	 *  b) the list manipulation code assumes nvec >= 1.
4312910ffdSAhmed S. Darwish 	 */
4412910ffdSAhmed S. Darwish 	if (nvec < 1)
4512910ffdSAhmed S. Darwish 		return 0;
4612910ffdSAhmed S. Darwish 
4712910ffdSAhmed S. Darwish 	/*
4812910ffdSAhmed S. Darwish 	 * Any bridge which does NOT route MSI transactions from its
4912910ffdSAhmed S. Darwish 	 * secondary bus to its primary bus must set NO_MSI flag on
5012910ffdSAhmed S. Darwish 	 * the secondary pci_bus.
5112910ffdSAhmed S. Darwish 	 *
5212910ffdSAhmed S. Darwish 	 * The NO_MSI flag can either be set directly by:
5312910ffdSAhmed S. Darwish 	 * - arch-specific PCI host bus controller drivers (deprecated)
5412910ffdSAhmed S. Darwish 	 * - quirks for specific PCI bridges
5512910ffdSAhmed S. Darwish 	 *
5612910ffdSAhmed S. Darwish 	 * or indirectly by platform-specific PCI host bridge drivers by
5712910ffdSAhmed S. Darwish 	 * advertising the 'msi_domain' property, which results in
5812910ffdSAhmed S. Darwish 	 * the NO_MSI flag when no MSI domain is found for this bridge
5912910ffdSAhmed S. Darwish 	 * at probe time.
6012910ffdSAhmed S. Darwish 	 */
6112910ffdSAhmed S. Darwish 	for (bus = dev->bus; bus; bus = bus->parent)
6212910ffdSAhmed S. Darwish 		if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
6312910ffdSAhmed S. Darwish 			return 0;
6412910ffdSAhmed S. Darwish 
6512910ffdSAhmed S. Darwish 	return 1;
6612910ffdSAhmed S. Darwish }
6712910ffdSAhmed S. Darwish 
pcim_msi_release(void * pcidev)6812910ffdSAhmed S. Darwish static void pcim_msi_release(void *pcidev)
6912910ffdSAhmed S. Darwish {
7012910ffdSAhmed S. Darwish 	struct pci_dev *dev = pcidev;
7112910ffdSAhmed S. Darwish 
7212910ffdSAhmed S. Darwish 	dev->is_msi_managed = false;
7312910ffdSAhmed S. Darwish 	pci_free_irq_vectors(dev);
7412910ffdSAhmed S. Darwish }
7512910ffdSAhmed S. Darwish 
7612910ffdSAhmed S. Darwish /*
7712910ffdSAhmed S. Darwish  * Needs to be separate from pcim_release to prevent an ordering problem
7812910ffdSAhmed S. Darwish  * vs. msi_device_data_release() in the MSI core code.
7912910ffdSAhmed S. Darwish  */
pcim_setup_msi_release(struct pci_dev * dev)8012910ffdSAhmed S. Darwish static int pcim_setup_msi_release(struct pci_dev *dev)
8112910ffdSAhmed S. Darwish {
8212910ffdSAhmed S. Darwish 	int ret;
8312910ffdSAhmed S. Darwish 
8412910ffdSAhmed S. Darwish 	if (!pci_is_managed(dev) || dev->is_msi_managed)
8512910ffdSAhmed S. Darwish 		return 0;
8612910ffdSAhmed S. Darwish 
8712910ffdSAhmed S. Darwish 	ret = devm_add_action(&dev->dev, pcim_msi_release, dev);
8812910ffdSAhmed S. Darwish 	if (!ret)
8912910ffdSAhmed S. Darwish 		dev->is_msi_managed = true;
9012910ffdSAhmed S. Darwish 	return ret;
9112910ffdSAhmed S. Darwish }
9212910ffdSAhmed S. Darwish 
9312910ffdSAhmed S. Darwish /*
9412910ffdSAhmed S. Darwish  * Ordering vs. devres: msi device data has to be installed first so that
9512910ffdSAhmed S. Darwish  * pcim_msi_release() is invoked before it on device release.
9612910ffdSAhmed S. Darwish  */
pci_setup_msi_context(struct pci_dev * dev)9712910ffdSAhmed S. Darwish static int pci_setup_msi_context(struct pci_dev *dev)
9812910ffdSAhmed S. Darwish {
9912910ffdSAhmed S. Darwish 	int ret = msi_setup_device_data(&dev->dev);
10012910ffdSAhmed S. Darwish 
10112910ffdSAhmed S. Darwish 	if (!ret)
10212910ffdSAhmed S. Darwish 		ret = pcim_setup_msi_release(dev);
10312910ffdSAhmed S. Darwish 	return ret;
10412910ffdSAhmed S. Darwish }
10512910ffdSAhmed S. Darwish 
10612910ffdSAhmed S. Darwish /*
10712910ffdSAhmed S. Darwish  * Helper functions for mask/unmask and MSI message handling
10812910ffdSAhmed S. Darwish  */
10912910ffdSAhmed S. Darwish 
pci_msi_update_mask(struct msi_desc * desc,u32 clear,u32 set)110c93fd526SAhmed S. Darwish void pci_msi_update_mask(struct msi_desc *desc, u32 clear, u32 set)
111288c81ceSThomas Gleixner {
112cd119b09SThomas Gleixner 	raw_spinlock_t *lock = &to_pci_dev(desc->dev)->msi_lock;
113288c81ceSThomas Gleixner 	unsigned long flags;
114288c81ceSThomas Gleixner 
115288c81ceSThomas Gleixner 	if (!desc->pci.msi_attrib.can_mask)
116288c81ceSThomas Gleixner 		return;
117288c81ceSThomas Gleixner 
118288c81ceSThomas Gleixner 	raw_spin_lock_irqsave(lock, flags);
119288c81ceSThomas Gleixner 	desc->pci.msi_mask &= ~clear;
120288c81ceSThomas Gleixner 	desc->pci.msi_mask |= set;
121288c81ceSThomas Gleixner 	pci_write_config_dword(msi_desc_to_pci_dev(desc), desc->pci.mask_pos,
122288c81ceSThomas Gleixner 			       desc->pci.msi_mask);
123288c81ceSThomas Gleixner 	raw_spin_unlock_irqrestore(lock, flags);
124288c81ceSThomas Gleixner }
125288c81ceSThomas Gleixner 
126288c81ceSThomas Gleixner /**
127288c81ceSThomas Gleixner  * pci_msi_mask_irq - Generic IRQ chip callback to mask PCI/MSI interrupts
128288c81ceSThomas Gleixner  * @data:	pointer to irqdata associated to that interrupt
129288c81ceSThomas Gleixner  */
pci_msi_mask_irq(struct irq_data * data)130288c81ceSThomas Gleixner void pci_msi_mask_irq(struct irq_data *data)
131288c81ceSThomas Gleixner {
132288c81ceSThomas Gleixner 	struct msi_desc *desc = irq_data_get_msi_desc(data);
133288c81ceSThomas Gleixner 
134288c81ceSThomas Gleixner 	__pci_msi_mask_desc(desc, BIT(data->irq - desc->irq));
135288c81ceSThomas Gleixner }
136288c81ceSThomas Gleixner EXPORT_SYMBOL_GPL(pci_msi_mask_irq);
137288c81ceSThomas Gleixner 
138288c81ceSThomas Gleixner /**
139288c81ceSThomas Gleixner  * pci_msi_unmask_irq - Generic IRQ chip callback to unmask PCI/MSI interrupts
140288c81ceSThomas Gleixner  * @data:	pointer to irqdata associated to that interrupt
141288c81ceSThomas Gleixner  */
pci_msi_unmask_irq(struct irq_data * data)142288c81ceSThomas Gleixner void pci_msi_unmask_irq(struct irq_data *data)
143288c81ceSThomas Gleixner {
144288c81ceSThomas Gleixner 	struct msi_desc *desc = irq_data_get_msi_desc(data);
145288c81ceSThomas Gleixner 
146288c81ceSThomas Gleixner 	__pci_msi_unmask_desc(desc, BIT(data->irq - desc->irq));
147288c81ceSThomas Gleixner }
148288c81ceSThomas Gleixner EXPORT_SYMBOL_GPL(pci_msi_unmask_irq);
149288c81ceSThomas Gleixner 
__pci_read_msi_msg(struct msi_desc * entry,struct msi_msg * msg)150288c81ceSThomas Gleixner void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
151288c81ceSThomas Gleixner {
152288c81ceSThomas Gleixner 	struct pci_dev *dev = msi_desc_to_pci_dev(entry);
153288c81ceSThomas Gleixner 
154288c81ceSThomas Gleixner 	BUG_ON(dev->current_state != PCI_D0);
155288c81ceSThomas Gleixner 
156288c81ceSThomas Gleixner 	if (entry->pci.msi_attrib.is_msix) {
157288c81ceSThomas Gleixner 		void __iomem *base = pci_msix_desc_addr(entry);
158288c81ceSThomas Gleixner 
159288c81ceSThomas Gleixner 		if (WARN_ON_ONCE(entry->pci.msi_attrib.is_virtual))
160288c81ceSThomas Gleixner 			return;
161288c81ceSThomas Gleixner 
162288c81ceSThomas Gleixner 		msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR);
163288c81ceSThomas Gleixner 		msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR);
164288c81ceSThomas Gleixner 		msg->data = readl(base + PCI_MSIX_ENTRY_DATA);
165288c81ceSThomas Gleixner 	} else {
166288c81ceSThomas Gleixner 		int pos = dev->msi_cap;
167288c81ceSThomas Gleixner 		u16 data;
168288c81ceSThomas Gleixner 
169288c81ceSThomas Gleixner 		pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_LO,
170288c81ceSThomas Gleixner 				      &msg->address_lo);
171288c81ceSThomas Gleixner 		if (entry->pci.msi_attrib.is_64) {
172288c81ceSThomas Gleixner 			pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_HI,
173288c81ceSThomas Gleixner 					      &msg->address_hi);
174288c81ceSThomas Gleixner 			pci_read_config_word(dev, pos + PCI_MSI_DATA_64, &data);
175288c81ceSThomas Gleixner 		} else {
176288c81ceSThomas Gleixner 			msg->address_hi = 0;
177288c81ceSThomas Gleixner 			pci_read_config_word(dev, pos + PCI_MSI_DATA_32, &data);
178288c81ceSThomas Gleixner 		}
179288c81ceSThomas Gleixner 		msg->data = data;
180288c81ceSThomas Gleixner 	}
181288c81ceSThomas Gleixner }
182288c81ceSThomas Gleixner 
pci_write_msg_msi(struct pci_dev * dev,struct msi_desc * desc,struct msi_msg * msg)183877d6c4eSThomas Gleixner static inline void pci_write_msg_msi(struct pci_dev *dev, struct msi_desc *desc,
184877d6c4eSThomas Gleixner 				     struct msi_msg *msg)
185288c81ceSThomas Gleixner {
186877d6c4eSThomas Gleixner 	int pos = dev->msi_cap;
187877d6c4eSThomas Gleixner 	u16 msgctl;
188288c81ceSThomas Gleixner 
189877d6c4eSThomas Gleixner 	pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl);
190877d6c4eSThomas Gleixner 	msgctl &= ~PCI_MSI_FLAGS_QSIZE;
191877d6c4eSThomas Gleixner 	msgctl |= desc->pci.msi_attrib.multiple << 4;
192877d6c4eSThomas Gleixner 	pci_write_config_word(dev, pos + PCI_MSI_FLAGS, msgctl);
193877d6c4eSThomas Gleixner 
194877d6c4eSThomas Gleixner 	pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, msg->address_lo);
195877d6c4eSThomas Gleixner 	if (desc->pci.msi_attrib.is_64) {
196877d6c4eSThomas Gleixner 		pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI,  msg->address_hi);
197877d6c4eSThomas Gleixner 		pci_write_config_word(dev, pos + PCI_MSI_DATA_64, msg->data);
198877d6c4eSThomas Gleixner 	} else {
199877d6c4eSThomas Gleixner 		pci_write_config_word(dev, pos + PCI_MSI_DATA_32, msg->data);
200877d6c4eSThomas Gleixner 	}
201877d6c4eSThomas Gleixner 	/* Ensure that the writes are visible in the device */
202877d6c4eSThomas Gleixner 	pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl);
203877d6c4eSThomas Gleixner }
204877d6c4eSThomas Gleixner 
pci_write_msg_msix(struct msi_desc * desc,struct msi_msg * msg)205877d6c4eSThomas Gleixner static inline void pci_write_msg_msix(struct msi_desc *desc, struct msi_msg *msg)
206877d6c4eSThomas Gleixner {
207877d6c4eSThomas Gleixner 	void __iomem *base = pci_msix_desc_addr(desc);
208877d6c4eSThomas Gleixner 	u32 ctrl = desc->pci.msix_ctrl;
209288c81ceSThomas Gleixner 	bool unmasked = !(ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT);
210288c81ceSThomas Gleixner 
211877d6c4eSThomas Gleixner 	if (desc->pci.msi_attrib.is_virtual)
212877d6c4eSThomas Gleixner 		return;
213288c81ceSThomas Gleixner 	/*
214288c81ceSThomas Gleixner 	 * The specification mandates that the entry is masked
215288c81ceSThomas Gleixner 	 * when the message is modified:
216288c81ceSThomas Gleixner 	 *
217288c81ceSThomas Gleixner 	 * "If software changes the Address or Data value of an
218288c81ceSThomas Gleixner 	 * entry while the entry is unmasked, the result is
219288c81ceSThomas Gleixner 	 * undefined."
220288c81ceSThomas Gleixner 	 */
221288c81ceSThomas Gleixner 	if (unmasked)
222877d6c4eSThomas Gleixner 		pci_msix_write_vector_ctrl(desc, ctrl | PCI_MSIX_ENTRY_CTRL_MASKBIT);
223288c81ceSThomas Gleixner 
224288c81ceSThomas Gleixner 	writel(msg->address_lo, base + PCI_MSIX_ENTRY_LOWER_ADDR);
225288c81ceSThomas Gleixner 	writel(msg->address_hi, base + PCI_MSIX_ENTRY_UPPER_ADDR);
226288c81ceSThomas Gleixner 	writel(msg->data, base + PCI_MSIX_ENTRY_DATA);
227288c81ceSThomas Gleixner 
228288c81ceSThomas Gleixner 	if (unmasked)
229877d6c4eSThomas Gleixner 		pci_msix_write_vector_ctrl(desc, ctrl);
230288c81ceSThomas Gleixner 
231288c81ceSThomas Gleixner 	/* Ensure that the writes are visible in the device */
232288c81ceSThomas Gleixner 	readl(base + PCI_MSIX_ENTRY_DATA);
233288c81ceSThomas Gleixner }
234288c81ceSThomas Gleixner 
__pci_write_msi_msg(struct msi_desc * entry,struct msi_msg * msg)235877d6c4eSThomas Gleixner void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
236877d6c4eSThomas Gleixner {
237877d6c4eSThomas Gleixner 	struct pci_dev *dev = msi_desc_to_pci_dev(entry);
238877d6c4eSThomas Gleixner 
239877d6c4eSThomas Gleixner 	if (dev->current_state != PCI_D0 || pci_dev_is_disconnected(dev)) {
240877d6c4eSThomas Gleixner 		/* Don't touch the hardware now */
241877d6c4eSThomas Gleixner 	} else if (entry->pci.msi_attrib.is_msix) {
242877d6c4eSThomas Gleixner 		pci_write_msg_msix(entry, msg);
243877d6c4eSThomas Gleixner 	} else {
244877d6c4eSThomas Gleixner 		pci_write_msg_msi(dev, entry, msg);
245877d6c4eSThomas Gleixner 	}
246877d6c4eSThomas Gleixner 
247288c81ceSThomas Gleixner 	entry->msg = *msg;
248288c81ceSThomas Gleixner 
249288c81ceSThomas Gleixner 	if (entry->write_msi_msg)
250288c81ceSThomas Gleixner 		entry->write_msi_msg(entry, entry->write_msi_msg_data);
251288c81ceSThomas Gleixner }
252288c81ceSThomas Gleixner 
pci_write_msi_msg(unsigned int irq,struct msi_msg * msg)253288c81ceSThomas Gleixner void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg)
254288c81ceSThomas Gleixner {
255288c81ceSThomas Gleixner 	struct msi_desc *entry = irq_get_msi_desc(irq);
256288c81ceSThomas Gleixner 
257288c81ceSThomas Gleixner 	__pci_write_msi_msg(entry, msg);
258288c81ceSThomas Gleixner }
259288c81ceSThomas Gleixner EXPORT_SYMBOL_GPL(pci_write_msi_msg);
260288c81ceSThomas Gleixner 
261288c81ceSThomas Gleixner 
26212910ffdSAhmed S. Darwish /* PCI/MSI specific functionality */
263288c81ceSThomas Gleixner 
pci_intx_for_msi(struct pci_dev * dev,int enable)264288c81ceSThomas Gleixner static void pci_intx_for_msi(struct pci_dev *dev, int enable)
265288c81ceSThomas Gleixner {
266288c81ceSThomas Gleixner 	if (!(dev->dev_flags & PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG))
267288c81ceSThomas Gleixner 		pci_intx(dev, enable);
268288c81ceSThomas Gleixner }
269288c81ceSThomas Gleixner 
pci_msi_set_enable(struct pci_dev * dev,int enable)270288c81ceSThomas Gleixner static void pci_msi_set_enable(struct pci_dev *dev, int enable)
271288c81ceSThomas Gleixner {
272288c81ceSThomas Gleixner 	u16 control;
273288c81ceSThomas Gleixner 
274288c81ceSThomas Gleixner 	pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
275288c81ceSThomas Gleixner 	control &= ~PCI_MSI_FLAGS_ENABLE;
276288c81ceSThomas Gleixner 	if (enable)
277288c81ceSThomas Gleixner 		control |= PCI_MSI_FLAGS_ENABLE;
278288c81ceSThomas Gleixner 	pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control);
279288c81ceSThomas Gleixner }
280288c81ceSThomas Gleixner 
msi_setup_msi_desc(struct pci_dev * dev,int nvec,struct irq_affinity_desc * masks)28171020a3cSThomas Gleixner static int msi_setup_msi_desc(struct pci_dev *dev, int nvec,
28271020a3cSThomas Gleixner 			      struct irq_affinity_desc *masks)
283288c81ceSThomas Gleixner {
28471020a3cSThomas Gleixner 	struct msi_desc desc;
285288c81ceSThomas Gleixner 	u16 control;
286288c81ceSThomas Gleixner 
287288c81ceSThomas Gleixner 	/* MSI Entry Initialization */
28871020a3cSThomas Gleixner 	memset(&desc, 0, sizeof(desc));
289288c81ceSThomas Gleixner 
290288c81ceSThomas Gleixner 	pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
291288c81ceSThomas Gleixner 	/* Lies, damned lies, and MSIs */
292288c81ceSThomas Gleixner 	if (dev->dev_flags & PCI_DEV_FLAGS_HAS_MSI_MASKING)
293288c81ceSThomas Gleixner 		control |= PCI_MSI_FLAGS_MASKBIT;
29471020a3cSThomas Gleixner 	/* Respect XEN's mask disabling */
29571020a3cSThomas Gleixner 	if (pci_msi_ignore_mask)
29671020a3cSThomas Gleixner 		control &= ~PCI_MSI_FLAGS_MASKBIT;
297288c81ceSThomas Gleixner 
29871020a3cSThomas Gleixner 	desc.nvec_used			= nvec;
29971020a3cSThomas Gleixner 	desc.pci.msi_attrib.is_64	= !!(control & PCI_MSI_FLAGS_64BIT);
30071020a3cSThomas Gleixner 	desc.pci.msi_attrib.can_mask	= !!(control & PCI_MSI_FLAGS_MASKBIT);
30171020a3cSThomas Gleixner 	desc.pci.msi_attrib.default_irq	= dev->irq;
30271020a3cSThomas Gleixner 	desc.pci.msi_attrib.multi_cap	= (control & PCI_MSI_FLAGS_QMASK) >> 1;
30371020a3cSThomas Gleixner 	desc.pci.msi_attrib.multiple	= ilog2(__roundup_pow_of_two(nvec));
30471020a3cSThomas Gleixner 	desc.affinity			= masks;
305288c81ceSThomas Gleixner 
306288c81ceSThomas Gleixner 	if (control & PCI_MSI_FLAGS_64BIT)
30771020a3cSThomas Gleixner 		desc.pci.mask_pos = dev->msi_cap + PCI_MSI_MASK_64;
308288c81ceSThomas Gleixner 	else
30971020a3cSThomas Gleixner 		desc.pci.mask_pos = dev->msi_cap + PCI_MSI_MASK_32;
310288c81ceSThomas Gleixner 
311288c81ceSThomas Gleixner 	/* Save the initial mask status */
31271020a3cSThomas Gleixner 	if (desc.pci.msi_attrib.can_mask)
31371020a3cSThomas Gleixner 		pci_read_config_dword(dev, desc.pci.mask_pos, &desc.pci.msi_mask);
314288c81ceSThomas Gleixner 
3151c893963SThomas Gleixner 	return msi_insert_msi_desc(&dev->dev, &desc);
316288c81ceSThomas Gleixner }
317288c81ceSThomas Gleixner 
msi_verify_entries(struct pci_dev * dev)318288c81ceSThomas Gleixner static int msi_verify_entries(struct pci_dev *dev)
319288c81ceSThomas Gleixner {
320288c81ceSThomas Gleixner 	struct msi_desc *entry;
321288c81ceSThomas Gleixner 
322288c81ceSThomas Gleixner 	if (!dev->no_64bit_msi)
323288c81ceSThomas Gleixner 		return 0;
324288c81ceSThomas Gleixner 
325ae24e28fSThomas Gleixner 	msi_for_each_desc(entry, &dev->dev, MSI_DESC_ALL) {
326288c81ceSThomas Gleixner 		if (entry->msg.address_hi) {
327288c81ceSThomas Gleixner 			pci_err(dev, "arch assigned 64-bit MSI address %#x%08x but device only supports 32 bits\n",
328288c81ceSThomas Gleixner 				entry->msg.address_hi, entry->msg.address_lo);
329ae24e28fSThomas Gleixner 			break;
330288c81ceSThomas Gleixner 		}
331288c81ceSThomas Gleixner 	}
332ae24e28fSThomas Gleixner 	return !entry ? 0 : -EIO;
333288c81ceSThomas Gleixner }
334288c81ceSThomas Gleixner 
335288c81ceSThomas Gleixner /**
336288c81ceSThomas Gleixner  * msi_capability_init - configure device's MSI capability structure
337288c81ceSThomas Gleixner  * @dev: pointer to the pci_dev data structure of MSI device function
338288c81ceSThomas Gleixner  * @nvec: number of interrupts to allocate
339288c81ceSThomas Gleixner  * @affd: description of automatic IRQ affinity assignments (may be %NULL)
340288c81ceSThomas Gleixner  *
341288c81ceSThomas Gleixner  * Setup the MSI capability structure of the device with the requested
342288c81ceSThomas Gleixner  * number of interrupts.  A return value of zero indicates the successful
343288c81ceSThomas Gleixner  * setup of an entry with the new MSI IRQ.  A negative return value indicates
344288c81ceSThomas Gleixner  * an error, and a positive return value indicates the number of interrupts
345288c81ceSThomas Gleixner  * which could have been allocated.
346288c81ceSThomas Gleixner  */
msi_capability_init(struct pci_dev * dev,int nvec,struct irq_affinity * affd)347288c81ceSThomas Gleixner static int msi_capability_init(struct pci_dev *dev, int nvec,
348288c81ceSThomas Gleixner 			       struct irq_affinity *affd)
349288c81ceSThomas Gleixner {
3505512c5eaSThomas Gleixner 	struct irq_affinity_desc *masks = NULL;
351ff1121d2SMostafa Saleh 	struct msi_desc *entry, desc;
352288c81ceSThomas Gleixner 	int ret;
353288c81ceSThomas Gleixner 
354d2a463b2SThomas Gleixner 	/* Reject multi-MSI early on irq domain enabled architectures */
355d2a463b2SThomas Gleixner 	if (nvec > 1 && !pci_msi_domain_supports(dev, MSI_FLAG_MULTI_PCI_MSI, ALLOW_LEGACY))
356d2a463b2SThomas Gleixner 		return 1;
357d2a463b2SThomas Gleixner 
358c7ecb95cSThomas Gleixner 	/*
359c7ecb95cSThomas Gleixner 	 * Disable MSI during setup in the hardware, but mark it enabled
360c7ecb95cSThomas Gleixner 	 * so that setup code can evaluate it.
361c7ecb95cSThomas Gleixner 	 */
362c7ecb95cSThomas Gleixner 	pci_msi_set_enable(dev, 0);
363c7ecb95cSThomas Gleixner 	dev->msi_enabled = 1;
364288c81ceSThomas Gleixner 
3655512c5eaSThomas Gleixner 	if (affd)
3665512c5eaSThomas Gleixner 		masks = irq_create_affinity_masks(nvec, affd);
3675512c5eaSThomas Gleixner 
3685512c5eaSThomas Gleixner 	msi_lock_descs(&dev->dev);
36971020a3cSThomas Gleixner 	ret = msi_setup_msi_desc(dev, nvec, masks);
37071020a3cSThomas Gleixner 	if (ret)
371c7ecb95cSThomas Gleixner 		goto fail;
372288c81ceSThomas Gleixner 
373288c81ceSThomas Gleixner 	/* All MSIs are unmasked by default; mask them all */
374ae24e28fSThomas Gleixner 	entry = msi_first_desc(&dev->dev, MSI_DESC_ALL);
375288c81ceSThomas Gleixner 	pci_msi_mask(entry, msi_multi_mask(entry));
376ff1121d2SMostafa Saleh 	/*
377ff1121d2SMostafa Saleh 	 * Copy the MSI descriptor for the error path because
378ff1121d2SMostafa Saleh 	 * pci_msi_setup_msi_irqs() will free it for the hierarchical
379ff1121d2SMostafa Saleh 	 * interrupt domain case.
380ff1121d2SMostafa Saleh 	 */
381ff1121d2SMostafa Saleh 	memcpy(&desc, entry, sizeof(desc));
382288c81ceSThomas Gleixner 
383288c81ceSThomas Gleixner 	/* Configure MSI capability structure */
384288c81ceSThomas Gleixner 	ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI);
385288c81ceSThomas Gleixner 	if (ret)
386288c81ceSThomas Gleixner 		goto err;
387288c81ceSThomas Gleixner 
388288c81ceSThomas Gleixner 	ret = msi_verify_entries(dev);
389288c81ceSThomas Gleixner 	if (ret)
390288c81ceSThomas Gleixner 		goto err;
391288c81ceSThomas Gleixner 
392288c81ceSThomas Gleixner 	/* Set MSI enabled bits	*/
393288c81ceSThomas Gleixner 	pci_intx_for_msi(dev, 0);
394288c81ceSThomas Gleixner 	pci_msi_set_enable(dev, 1);
395288c81ceSThomas Gleixner 
396288c81ceSThomas Gleixner 	pcibios_free_irq(dev);
397288c81ceSThomas Gleixner 	dev->irq = entry->irq;
3985512c5eaSThomas Gleixner 	goto unlock;
399288c81ceSThomas Gleixner 
400288c81ceSThomas Gleixner err:
401ff1121d2SMostafa Saleh 	pci_msi_unmask(&desc, msi_multi_mask(&desc));
402b12d0becSAhmed S. Darwish 	pci_free_msi_irqs(dev);
403c7ecb95cSThomas Gleixner fail:
404c7ecb95cSThomas Gleixner 	dev->msi_enabled = 0;
4055512c5eaSThomas Gleixner unlock:
4065512c5eaSThomas Gleixner 	msi_unlock_descs(&dev->dev);
4075512c5eaSThomas Gleixner 	kfree(masks);
408288c81ceSThomas Gleixner 	return ret;
409288c81ceSThomas Gleixner }
410288c81ceSThomas Gleixner 
__pci_enable_msi_range(struct pci_dev * dev,int minvec,int maxvec,struct irq_affinity * affd)41112910ffdSAhmed S. Darwish int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,
41212910ffdSAhmed S. Darwish 			   struct irq_affinity *affd)
41312910ffdSAhmed S. Darwish {
41412910ffdSAhmed S. Darwish 	int nvec;
41512910ffdSAhmed S. Darwish 	int rc;
41612910ffdSAhmed S. Darwish 
41712910ffdSAhmed S. Darwish 	if (!pci_msi_supported(dev, minvec) || dev->current_state != PCI_D0)
41812910ffdSAhmed S. Darwish 		return -EINVAL;
41912910ffdSAhmed S. Darwish 
42012910ffdSAhmed S. Darwish 	/* Check whether driver already requested MSI-X IRQs */
42112910ffdSAhmed S. Darwish 	if (dev->msix_enabled) {
42212910ffdSAhmed S. Darwish 		pci_info(dev, "can't enable MSI (MSI-X already enabled)\n");
42312910ffdSAhmed S. Darwish 		return -EINVAL;
42412910ffdSAhmed S. Darwish 	}
42512910ffdSAhmed S. Darwish 
42612910ffdSAhmed S. Darwish 	if (maxvec < minvec)
42712910ffdSAhmed S. Darwish 		return -ERANGE;
42812910ffdSAhmed S. Darwish 
42912910ffdSAhmed S. Darwish 	if (WARN_ON_ONCE(dev->msi_enabled))
43012910ffdSAhmed S. Darwish 		return -EINVAL;
43112910ffdSAhmed S. Darwish 
432*b1f7476eSThomas Gleixner 	/* Test for the availability of MSI support */
433*b1f7476eSThomas Gleixner 	if (!pci_msi_domain_supports(dev, 0, ALLOW_LEGACY))
434*b1f7476eSThomas Gleixner 		return -ENOTSUPP;
435*b1f7476eSThomas Gleixner 
43612910ffdSAhmed S. Darwish 	nvec = pci_msi_vec_count(dev);
43712910ffdSAhmed S. Darwish 	if (nvec < 0)
43812910ffdSAhmed S. Darwish 		return nvec;
43912910ffdSAhmed S. Darwish 	if (nvec < minvec)
44012910ffdSAhmed S. Darwish 		return -ENOSPC;
44112910ffdSAhmed S. Darwish 
44212910ffdSAhmed S. Darwish 	if (nvec > maxvec)
44312910ffdSAhmed S. Darwish 		nvec = maxvec;
44412910ffdSAhmed S. Darwish 
44512910ffdSAhmed S. Darwish 	rc = pci_setup_msi_context(dev);
44612910ffdSAhmed S. Darwish 	if (rc)
44712910ffdSAhmed S. Darwish 		return rc;
44812910ffdSAhmed S. Darwish 
44915c72f82SThomas Gleixner 	if (!pci_setup_msi_device_domain(dev))
45015c72f82SThomas Gleixner 		return -ENODEV;
45115c72f82SThomas Gleixner 
45212910ffdSAhmed S. Darwish 	for (;;) {
45312910ffdSAhmed S. Darwish 		if (affd) {
45412910ffdSAhmed S. Darwish 			nvec = irq_calc_affinity_vectors(minvec, nvec, affd);
45512910ffdSAhmed S. Darwish 			if (nvec < minvec)
45612910ffdSAhmed S. Darwish 				return -ENOSPC;
45712910ffdSAhmed S. Darwish 		}
45812910ffdSAhmed S. Darwish 
45912910ffdSAhmed S. Darwish 		rc = msi_capability_init(dev, nvec, affd);
46012910ffdSAhmed S. Darwish 		if (rc == 0)
46112910ffdSAhmed S. Darwish 			return nvec;
46212910ffdSAhmed S. Darwish 
46312910ffdSAhmed S. Darwish 		if (rc < 0)
46412910ffdSAhmed S. Darwish 			return rc;
46512910ffdSAhmed S. Darwish 		if (rc < minvec)
46612910ffdSAhmed S. Darwish 			return -ENOSPC;
46712910ffdSAhmed S. Darwish 
46812910ffdSAhmed S. Darwish 		nvec = rc;
46912910ffdSAhmed S. Darwish 	}
47012910ffdSAhmed S. Darwish }
47112910ffdSAhmed S. Darwish 
47212910ffdSAhmed S. Darwish /**
47312910ffdSAhmed S. Darwish  * pci_msi_vec_count - Return the number of MSI vectors a device can send
47412910ffdSAhmed S. Darwish  * @dev: device to report about
47512910ffdSAhmed S. Darwish  *
47612910ffdSAhmed S. Darwish  * This function returns the number of MSI vectors a device requested via
47712910ffdSAhmed S. Darwish  * Multiple Message Capable register. It returns a negative errno if the
47812910ffdSAhmed S. Darwish  * device is not capable sending MSI interrupts. Otherwise, the call succeeds
47912910ffdSAhmed S. Darwish  * and returns a power of two, up to a maximum of 2^5 (32), according to the
48012910ffdSAhmed S. Darwish  * MSI specification.
48112910ffdSAhmed S. Darwish  **/
pci_msi_vec_count(struct pci_dev * dev)48212910ffdSAhmed S. Darwish int pci_msi_vec_count(struct pci_dev *dev)
48312910ffdSAhmed S. Darwish {
48412910ffdSAhmed S. Darwish 	int ret;
48512910ffdSAhmed S. Darwish 	u16 msgctl;
48612910ffdSAhmed S. Darwish 
48712910ffdSAhmed S. Darwish 	if (!dev->msi_cap)
48812910ffdSAhmed S. Darwish 		return -EINVAL;
48912910ffdSAhmed S. Darwish 
49012910ffdSAhmed S. Darwish 	pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl);
49112910ffdSAhmed S. Darwish 	ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1);
49212910ffdSAhmed S. Darwish 
49312910ffdSAhmed S. Darwish 	return ret;
49412910ffdSAhmed S. Darwish }
49512910ffdSAhmed S. Darwish EXPORT_SYMBOL(pci_msi_vec_count);
49612910ffdSAhmed S. Darwish 
49712910ffdSAhmed S. Darwish /*
49812910ffdSAhmed S. Darwish  * Architecture override returns true when the PCI MSI message should be
49912910ffdSAhmed S. Darwish  * written by the generic restore function.
50012910ffdSAhmed S. Darwish  */
arch_restore_msi_irqs(struct pci_dev * dev)50112910ffdSAhmed S. Darwish bool __weak arch_restore_msi_irqs(struct pci_dev *dev)
50212910ffdSAhmed S. Darwish {
50312910ffdSAhmed S. Darwish 	return true;
50412910ffdSAhmed S. Darwish }
50512910ffdSAhmed S. Darwish 
__pci_restore_msi_state(struct pci_dev * dev)50612910ffdSAhmed S. Darwish void __pci_restore_msi_state(struct pci_dev *dev)
50712910ffdSAhmed S. Darwish {
50812910ffdSAhmed S. Darwish 	struct msi_desc *entry;
50912910ffdSAhmed S. Darwish 	u16 control;
51012910ffdSAhmed S. Darwish 
51112910ffdSAhmed S. Darwish 	if (!dev->msi_enabled)
51212910ffdSAhmed S. Darwish 		return;
51312910ffdSAhmed S. Darwish 
51412910ffdSAhmed S. Darwish 	entry = irq_get_msi_desc(dev->irq);
51512910ffdSAhmed S. Darwish 
51612910ffdSAhmed S. Darwish 	pci_intx_for_msi(dev, 0);
51712910ffdSAhmed S. Darwish 	pci_msi_set_enable(dev, 0);
51812910ffdSAhmed S. Darwish 	if (arch_restore_msi_irqs(dev))
51912910ffdSAhmed S. Darwish 		__pci_write_msi_msg(entry, &entry->msg);
52012910ffdSAhmed S. Darwish 
52112910ffdSAhmed S. Darwish 	pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
52212910ffdSAhmed S. Darwish 	pci_msi_update_mask(entry, 0, 0);
52312910ffdSAhmed S. Darwish 	control &= ~PCI_MSI_FLAGS_QSIZE;
52412910ffdSAhmed S. Darwish 	control |= (entry->pci.msi_attrib.multiple << 4) | PCI_MSI_FLAGS_ENABLE;
52512910ffdSAhmed S. Darwish 	pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control);
52612910ffdSAhmed S. Darwish }
52712910ffdSAhmed S. Darwish 
pci_msi_shutdown(struct pci_dev * dev)52812910ffdSAhmed S. Darwish void pci_msi_shutdown(struct pci_dev *dev)
52912910ffdSAhmed S. Darwish {
53012910ffdSAhmed S. Darwish 	struct msi_desc *desc;
53112910ffdSAhmed S. Darwish 
53212910ffdSAhmed S. Darwish 	if (!pci_msi_enable || !dev || !dev->msi_enabled)
53312910ffdSAhmed S. Darwish 		return;
53412910ffdSAhmed S. Darwish 
53512910ffdSAhmed S. Darwish 	pci_msi_set_enable(dev, 0);
53612910ffdSAhmed S. Darwish 	pci_intx_for_msi(dev, 1);
53712910ffdSAhmed S. Darwish 	dev->msi_enabled = 0;
53812910ffdSAhmed S. Darwish 
53912910ffdSAhmed S. Darwish 	/* Return the device with MSI unmasked as initial states */
54012910ffdSAhmed S. Darwish 	desc = msi_first_desc(&dev->dev, MSI_DESC_ALL);
54112910ffdSAhmed S. Darwish 	if (!WARN_ON_ONCE(!desc))
54212910ffdSAhmed S. Darwish 		pci_msi_unmask(desc, msi_multi_mask(desc));
54312910ffdSAhmed S. Darwish 
54412910ffdSAhmed S. Darwish 	/* Restore dev->irq to its default pin-assertion IRQ */
54512910ffdSAhmed S. Darwish 	dev->irq = desc->pci.msi_attrib.default_irq;
54612910ffdSAhmed S. Darwish 	pcibios_alloc_irq(dev);
54712910ffdSAhmed S. Darwish }
54812910ffdSAhmed S. Darwish 
54912910ffdSAhmed S. Darwish /* PCI/MSI-X specific functionality */
55012910ffdSAhmed S. Darwish 
pci_msix_clear_and_set_ctrl(struct pci_dev * dev,u16 clear,u16 set)55112910ffdSAhmed S. Darwish static void pci_msix_clear_and_set_ctrl(struct pci_dev *dev, u16 clear, u16 set)
55212910ffdSAhmed S. Darwish {
55312910ffdSAhmed S. Darwish 	u16 ctrl;
55412910ffdSAhmed S. Darwish 
55512910ffdSAhmed S. Darwish 	pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &ctrl);
55612910ffdSAhmed S. Darwish 	ctrl &= ~clear;
55712910ffdSAhmed S. Darwish 	ctrl |= set;
55812910ffdSAhmed S. Darwish 	pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, ctrl);
55912910ffdSAhmed S. Darwish }
56012910ffdSAhmed S. Darwish 
msix_map_region(struct pci_dev * dev,unsigned int nr_entries)561288c81ceSThomas Gleixner static void __iomem *msix_map_region(struct pci_dev *dev,
562288c81ceSThomas Gleixner 				     unsigned int nr_entries)
563288c81ceSThomas Gleixner {
564288c81ceSThomas Gleixner 	resource_size_t phys_addr;
565288c81ceSThomas Gleixner 	u32 table_offset;
566288c81ceSThomas Gleixner 	unsigned long flags;
567288c81ceSThomas Gleixner 	u8 bir;
568288c81ceSThomas Gleixner 
569288c81ceSThomas Gleixner 	pci_read_config_dword(dev, dev->msix_cap + PCI_MSIX_TABLE,
570288c81ceSThomas Gleixner 			      &table_offset);
571288c81ceSThomas Gleixner 	bir = (u8)(table_offset & PCI_MSIX_TABLE_BIR);
572288c81ceSThomas Gleixner 	flags = pci_resource_flags(dev, bir);
573288c81ceSThomas Gleixner 	if (!flags || (flags & IORESOURCE_UNSET))
574288c81ceSThomas Gleixner 		return NULL;
575288c81ceSThomas Gleixner 
576288c81ceSThomas Gleixner 	table_offset &= PCI_MSIX_TABLE_OFFSET;
577288c81ceSThomas Gleixner 	phys_addr = pci_resource_start(dev, bir) + table_offset;
578288c81ceSThomas Gleixner 
579288c81ceSThomas Gleixner 	return ioremap(phys_addr, nr_entries * PCI_MSIX_ENTRY_SIZE);
580288c81ceSThomas Gleixner }
581288c81ceSThomas Gleixner 
582612ad433SThomas Gleixner /**
583612ad433SThomas Gleixner  * msix_prepare_msi_desc - Prepare a half initialized MSI descriptor for operation
584612ad433SThomas Gleixner  * @dev:	The PCI device for which the descriptor is prepared
585612ad433SThomas Gleixner  * @desc:	The MSI descriptor for preparation
586612ad433SThomas Gleixner  *
587612ad433SThomas Gleixner  * This is separate from msix_setup_msi_descs() below to handle dynamic
588612ad433SThomas Gleixner  * allocations for MSI-X after initial enablement.
589612ad433SThomas Gleixner  *
590612ad433SThomas Gleixner  * Ideally the whole MSI-X setup would work that way, but there is no way to
591612ad433SThomas Gleixner  * support this for the legacy arch_setup_msi_irqs() mechanism and for the
592612ad433SThomas Gleixner  * fake irq domains like the x86 XEN one. Sigh...
593612ad433SThomas Gleixner  *
594612ad433SThomas Gleixner  * The descriptor is zeroed and only @desc::msi_index and @desc::affinity
595612ad433SThomas Gleixner  * are set. When called from msix_setup_msi_descs() then the is_virtual
596612ad433SThomas Gleixner  * attribute is initialized as well.
597612ad433SThomas Gleixner  *
598612ad433SThomas Gleixner  * Fill in the rest.
599612ad433SThomas Gleixner  */
msix_prepare_msi_desc(struct pci_dev * dev,struct msi_desc * desc)600612ad433SThomas Gleixner void msix_prepare_msi_desc(struct pci_dev *dev, struct msi_desc *desc)
601612ad433SThomas Gleixner {
602612ad433SThomas Gleixner 	desc->nvec_used				= 1;
603612ad433SThomas Gleixner 	desc->pci.msi_attrib.is_msix		= 1;
604612ad433SThomas Gleixner 	desc->pci.msi_attrib.is_64		= 1;
605612ad433SThomas Gleixner 	desc->pci.msi_attrib.default_irq	= dev->irq;
606612ad433SThomas Gleixner 	desc->pci.mask_base			= dev->msix_base;
607612ad433SThomas Gleixner 	desc->pci.msi_attrib.can_mask		= !pci_msi_ignore_mask &&
608612ad433SThomas Gleixner 						  !desc->pci.msi_attrib.is_virtual;
609612ad433SThomas Gleixner 
610612ad433SThomas Gleixner 	if (desc->pci.msi_attrib.can_mask) {
611612ad433SThomas Gleixner 		void __iomem *addr = pci_msix_desc_addr(desc);
612612ad433SThomas Gleixner 
613612ad433SThomas Gleixner 		desc->pci.msix_ctrl = readl(addr + PCI_MSIX_ENTRY_VECTOR_CTRL);
614612ad433SThomas Gleixner 	}
615612ad433SThomas Gleixner }
616612ad433SThomas Gleixner 
msix_setup_msi_descs(struct pci_dev * dev,struct msix_entry * entries,int nvec,struct irq_affinity_desc * masks)617612ad433SThomas Gleixner static int msix_setup_msi_descs(struct pci_dev *dev, struct msix_entry *entries,
618612ad433SThomas Gleixner 				int nvec, struct irq_affinity_desc *masks)
619288c81ceSThomas Gleixner {
62071020a3cSThomas Gleixner 	int ret = 0, i, vec_count = pci_msix_vec_count(dev);
6215512c5eaSThomas Gleixner 	struct irq_affinity_desc *curmsk;
62271020a3cSThomas Gleixner 	struct msi_desc desc;
623288c81ceSThomas Gleixner 
62471020a3cSThomas Gleixner 	memset(&desc, 0, sizeof(desc));
62571020a3cSThomas Gleixner 
62671020a3cSThomas Gleixner 	for (i = 0, curmsk = masks; i < nvec; i++, curmsk++) {
62771020a3cSThomas Gleixner 		desc.msi_index = entries ? entries[i].entry : i;
62871020a3cSThomas Gleixner 		desc.affinity = masks ? curmsk : NULL;
62971020a3cSThomas Gleixner 		desc.pci.msi_attrib.is_virtual = desc.msi_index >= vec_count;
63071020a3cSThomas Gleixner 
631612ad433SThomas Gleixner 		msix_prepare_msi_desc(dev, &desc);
632288c81ceSThomas Gleixner 
6331c893963SThomas Gleixner 		ret = msi_insert_msi_desc(&dev->dev, &desc);
63471020a3cSThomas Gleixner 		if (ret)
63571020a3cSThomas Gleixner 			break;
636288c81ceSThomas Gleixner 	}
63771020a3cSThomas Gleixner 	return ret;
638288c81ceSThomas Gleixner }
639288c81ceSThomas Gleixner 
msix_update_entries(struct pci_dev * dev,struct msix_entry * entries)640288c81ceSThomas Gleixner static void msix_update_entries(struct pci_dev *dev, struct msix_entry *entries)
641288c81ceSThomas Gleixner {
642ae24e28fSThomas Gleixner 	struct msi_desc *desc;
643288c81ceSThomas Gleixner 
644288c81ceSThomas Gleixner 	if (entries) {
645ae24e28fSThomas Gleixner 		msi_for_each_desc(desc, &dev->dev, MSI_DESC_ALL) {
646ae24e28fSThomas Gleixner 			entries->vector = desc->irq;
647288c81ceSThomas Gleixner 			entries++;
648288c81ceSThomas Gleixner 		}
649288c81ceSThomas Gleixner 	}
650288c81ceSThomas Gleixner }
651288c81ceSThomas Gleixner 
msix_mask_all(void __iomem * base,int tsize)652288c81ceSThomas Gleixner static void msix_mask_all(void __iomem *base, int tsize)
653288c81ceSThomas Gleixner {
654288c81ceSThomas Gleixner 	u32 ctrl = PCI_MSIX_ENTRY_CTRL_MASKBIT;
655288c81ceSThomas Gleixner 	int i;
656288c81ceSThomas Gleixner 
657288c81ceSThomas Gleixner 	if (pci_msi_ignore_mask)
658288c81ceSThomas Gleixner 		return;
659288c81ceSThomas Gleixner 
660288c81ceSThomas Gleixner 	for (i = 0; i < tsize; i++, base += PCI_MSIX_ENTRY_SIZE)
661288c81ceSThomas Gleixner 		writel(ctrl, base + PCI_MSIX_ENTRY_VECTOR_CTRL);
662288c81ceSThomas Gleixner }
663288c81ceSThomas Gleixner 
msix_setup_interrupts(struct pci_dev * dev,struct msix_entry * entries,int nvec,struct irq_affinity * affd)664612ad433SThomas Gleixner static int msix_setup_interrupts(struct pci_dev *dev, struct msix_entry *entries,
665612ad433SThomas Gleixner 				 int nvec, struct irq_affinity *affd)
6665512c5eaSThomas Gleixner {
6675512c5eaSThomas Gleixner 	struct irq_affinity_desc *masks = NULL;
6685512c5eaSThomas Gleixner 	int ret;
6695512c5eaSThomas Gleixner 
6705512c5eaSThomas Gleixner 	if (affd)
6715512c5eaSThomas Gleixner 		masks = irq_create_affinity_masks(nvec, affd);
6725512c5eaSThomas Gleixner 
6735512c5eaSThomas Gleixner 	msi_lock_descs(&dev->dev);
674612ad433SThomas Gleixner 	ret = msix_setup_msi_descs(dev, entries, nvec, masks);
6755512c5eaSThomas Gleixner 	if (ret)
6765512c5eaSThomas Gleixner 		goto out_free;
6775512c5eaSThomas Gleixner 
6785512c5eaSThomas Gleixner 	ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
6795512c5eaSThomas Gleixner 	if (ret)
6805512c5eaSThomas Gleixner 		goto out_free;
6815512c5eaSThomas Gleixner 
6825512c5eaSThomas Gleixner 	/* Check if all MSI entries honor device restrictions */
6835512c5eaSThomas Gleixner 	ret = msi_verify_entries(dev);
6845512c5eaSThomas Gleixner 	if (ret)
6855512c5eaSThomas Gleixner 		goto out_free;
6865512c5eaSThomas Gleixner 
6875512c5eaSThomas Gleixner 	msix_update_entries(dev, entries);
6885512c5eaSThomas Gleixner 	goto out_unlock;
6895512c5eaSThomas Gleixner 
6905512c5eaSThomas Gleixner out_free:
691b12d0becSAhmed S. Darwish 	pci_free_msi_irqs(dev);
6925512c5eaSThomas Gleixner out_unlock:
6935512c5eaSThomas Gleixner 	msi_unlock_descs(&dev->dev);
6945512c5eaSThomas Gleixner 	kfree(masks);
6955512c5eaSThomas Gleixner 	return ret;
6965512c5eaSThomas Gleixner }
6975512c5eaSThomas Gleixner 
698288c81ceSThomas Gleixner /**
699288c81ceSThomas Gleixner  * msix_capability_init - configure device's MSI-X capability
700288c81ceSThomas Gleixner  * @dev: pointer to the pci_dev data structure of MSI-X device function
701288c81ceSThomas Gleixner  * @entries: pointer to an array of struct msix_entry entries
702288c81ceSThomas Gleixner  * @nvec: number of @entries
703288c81ceSThomas Gleixner  * @affd: Optional pointer to enable automatic affinity assignment
704288c81ceSThomas Gleixner  *
705288c81ceSThomas Gleixner  * Setup the MSI-X capability structure of device function with a
706288c81ceSThomas Gleixner  * single MSI-X IRQ. A return of zero indicates the successful setup of
707288c81ceSThomas Gleixner  * requested MSI-X entries with allocated IRQs or non-zero for otherwise.
708288c81ceSThomas Gleixner  **/
msix_capability_init(struct pci_dev * dev,struct msix_entry * entries,int nvec,struct irq_affinity * affd)709288c81ceSThomas Gleixner static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries,
710288c81ceSThomas Gleixner 				int nvec, struct irq_affinity *affd)
711288c81ceSThomas Gleixner {
712288c81ceSThomas Gleixner 	int ret, tsize;
713288c81ceSThomas Gleixner 	u16 control;
714288c81ceSThomas Gleixner 
715288c81ceSThomas Gleixner 	/*
716288c81ceSThomas Gleixner 	 * Some devices require MSI-X to be enabled before the MSI-X
717288c81ceSThomas Gleixner 	 * registers can be accessed.  Mask all the vectors to prevent
718288c81ceSThomas Gleixner 	 * interrupts coming in before they're fully set up.
719288c81ceSThomas Gleixner 	 */
720288c81ceSThomas Gleixner 	pci_msix_clear_and_set_ctrl(dev, 0, PCI_MSIX_FLAGS_MASKALL |
721288c81ceSThomas Gleixner 				    PCI_MSIX_FLAGS_ENABLE);
722288c81ceSThomas Gleixner 
723c7ecb95cSThomas Gleixner 	/* Mark it enabled so setup functions can query it */
724c7ecb95cSThomas Gleixner 	dev->msix_enabled = 1;
725c7ecb95cSThomas Gleixner 
726288c81ceSThomas Gleixner 	pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control);
727288c81ceSThomas Gleixner 	/* Request & Map MSI-X table region */
728288c81ceSThomas Gleixner 	tsize = msix_table_size(control);
729612ad433SThomas Gleixner 	dev->msix_base = msix_map_region(dev, tsize);
730612ad433SThomas Gleixner 	if (!dev->msix_base) {
731288c81ceSThomas Gleixner 		ret = -ENOMEM;
732288c81ceSThomas Gleixner 		goto out_disable;
733288c81ceSThomas Gleixner 	}
734288c81ceSThomas Gleixner 
735612ad433SThomas Gleixner 	ret = msix_setup_interrupts(dev, entries, nvec, affd);
736288c81ceSThomas Gleixner 	if (ret)
7375512c5eaSThomas Gleixner 		goto out_disable;
738288c81ceSThomas Gleixner 
739c7ecb95cSThomas Gleixner 	/* Disable INTX */
740288c81ceSThomas Gleixner 	pci_intx_for_msi(dev, 0);
74109eb3ad5SThomas Gleixner 
74209eb3ad5SThomas Gleixner 	/*
74309eb3ad5SThomas Gleixner 	 * Ensure that all table entries are masked to prevent
74409eb3ad5SThomas Gleixner 	 * stale entries from firing in a crash kernel.
74509eb3ad5SThomas Gleixner 	 *
74609eb3ad5SThomas Gleixner 	 * Done late to deal with a broken Marvell NVME device
74709eb3ad5SThomas Gleixner 	 * which takes the MSI-X mask bits into account even
74809eb3ad5SThomas Gleixner 	 * when MSI-X is disabled, which prevents MSI delivery.
74909eb3ad5SThomas Gleixner 	 */
750612ad433SThomas Gleixner 	msix_mask_all(dev->msix_base, tsize);
751288c81ceSThomas Gleixner 	pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0);
752288c81ceSThomas Gleixner 
753288c81ceSThomas Gleixner 	pcibios_free_irq(dev);
754288c81ceSThomas Gleixner 	return 0;
755288c81ceSThomas Gleixner 
756288c81ceSThomas Gleixner out_disable:
757c7ecb95cSThomas Gleixner 	dev->msix_enabled = 0;
75809eb3ad5SThomas Gleixner 	pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE, 0);
759288c81ceSThomas Gleixner 
760288c81ceSThomas Gleixner 	return ret;
761288c81ceSThomas Gleixner }
762288c81ceSThomas Gleixner 
pci_msix_validate_entries(struct pci_dev * dev,struct msix_entry * entries,int nvec)763e3c026beSThomas Gleixner static bool pci_msix_validate_entries(struct pci_dev *dev, struct msix_entry *entries, int nvec)
764288c81ceSThomas Gleixner {
7654644d22eSThomas Gleixner 	bool nogap;
766288c81ceSThomas Gleixner 	int i, j;
767288c81ceSThomas Gleixner 
768bab65e48SThomas Gleixner 	if (!entries)
769bab65e48SThomas Gleixner 		return true;
770288c81ceSThomas Gleixner 
7714644d22eSThomas Gleixner 	nogap = pci_msi_domain_supports(dev, MSI_FLAG_MSIX_CONTIGUOUS, DENY_LEGACY);
7724644d22eSThomas Gleixner 
773288c81ceSThomas Gleixner 	for (i = 0; i < nvec; i++) {
774bab65e48SThomas Gleixner 		/* Check for duplicate entries */
775288c81ceSThomas Gleixner 		for (j = i + 1; j < nvec; j++) {
776288c81ceSThomas Gleixner 			if (entries[i].entry == entries[j].entry)
777bab65e48SThomas Gleixner 				return false;
778288c81ceSThomas Gleixner 		}
7794644d22eSThomas Gleixner 		/* Check for unsupported gaps */
7804644d22eSThomas Gleixner 		if (nogap && entries[i].entry != i)
7814644d22eSThomas Gleixner 			return false;
782288c81ceSThomas Gleixner 	}
783bab65e48SThomas Gleixner 	return true;
784288c81ceSThomas Gleixner }
785288c81ceSThomas Gleixner 
__pci_enable_msix_range(struct pci_dev * dev,struct msix_entry * entries,int minvec,int maxvec,struct irq_affinity * affd,int flags)786bab65e48SThomas Gleixner int __pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, int minvec,
787bab65e48SThomas Gleixner 			    int maxvec, struct irq_affinity *affd, int flags)
788288c81ceSThomas Gleixner {
789bab65e48SThomas Gleixner 	int hwsize, rc, nvec = maxvec;
790288c81ceSThomas Gleixner 
791288c81ceSThomas Gleixner 	if (maxvec < minvec)
792288c81ceSThomas Gleixner 		return -ERANGE;
793288c81ceSThomas Gleixner 
794fe97f59aSThomas Gleixner 	if (dev->msi_enabled) {
795fe97f59aSThomas Gleixner 		pci_info(dev, "can't enable MSI-X (MSI already enabled)\n");
796fe97f59aSThomas Gleixner 		return -EINVAL;
797fe97f59aSThomas Gleixner 	}
798fe97f59aSThomas Gleixner 
799288c81ceSThomas Gleixner 	if (WARN_ON_ONCE(dev->msix_enabled))
800288c81ceSThomas Gleixner 		return -EINVAL;
801288c81ceSThomas Gleixner 
80299f3d279SThomas Gleixner 	/* Check MSI-X early on irq domain enabled architectures */
80399f3d279SThomas Gleixner 	if (!pci_msi_domain_supports(dev, MSI_FLAG_PCI_MSIX, ALLOW_LEGACY))
80499f3d279SThomas Gleixner 		return -ENOTSUPP;
80599f3d279SThomas Gleixner 
806bab65e48SThomas Gleixner 	if (!pci_msi_supported(dev, nvec) || dev->current_state != PCI_D0)
807bab65e48SThomas Gleixner 		return -EINVAL;
808bab65e48SThomas Gleixner 
809bab65e48SThomas Gleixner 	hwsize = pci_msix_vec_count(dev);
810bab65e48SThomas Gleixner 	if (hwsize < 0)
811bab65e48SThomas Gleixner 		return hwsize;
812bab65e48SThomas Gleixner 
813e3c026beSThomas Gleixner 	if (!pci_msix_validate_entries(dev, entries, nvec))
814bab65e48SThomas Gleixner 		return -EINVAL;
815bab65e48SThomas Gleixner 
81615c72f82SThomas Gleixner 	if (hwsize < nvec) {
81715c72f82SThomas Gleixner 		/* Keep the IRQ virtual hackery working */
81815c72f82SThomas Gleixner 		if (flags & PCI_IRQ_VIRTUAL)
81915c72f82SThomas Gleixner 			hwsize = nvec;
82015c72f82SThomas Gleixner 		else
821bab65e48SThomas Gleixner 			nvec = hwsize;
82215c72f82SThomas Gleixner 	}
823bab65e48SThomas Gleixner 
824bab65e48SThomas Gleixner 	if (nvec < minvec)
825bab65e48SThomas Gleixner 		return -ENOSPC;
826bab65e48SThomas Gleixner 
82793296cd1SThomas Gleixner 	rc = pci_setup_msi_context(dev);
8283f35d2cfSThomas Gleixner 	if (rc)
8293f35d2cfSThomas Gleixner 		return rc;
8303f35d2cfSThomas Gleixner 
83115c72f82SThomas Gleixner 	if (!pci_setup_msix_device_domain(dev, hwsize))
83215c72f82SThomas Gleixner 		return -ENODEV;
83315c72f82SThomas Gleixner 
834288c81ceSThomas Gleixner 	for (;;) {
835288c81ceSThomas Gleixner 		if (affd) {
836288c81ceSThomas Gleixner 			nvec = irq_calc_affinity_vectors(minvec, nvec, affd);
837288c81ceSThomas Gleixner 			if (nvec < minvec)
838288c81ceSThomas Gleixner 				return -ENOSPC;
839288c81ceSThomas Gleixner 		}
840288c81ceSThomas Gleixner 
841bab65e48SThomas Gleixner 		rc = msix_capability_init(dev, entries, nvec, affd);
842288c81ceSThomas Gleixner 		if (rc == 0)
843288c81ceSThomas Gleixner 			return nvec;
844288c81ceSThomas Gleixner 
845288c81ceSThomas Gleixner 		if (rc < 0)
846288c81ceSThomas Gleixner 			return rc;
847288c81ceSThomas Gleixner 		if (rc < minvec)
848288c81ceSThomas Gleixner 			return -ENOSPC;
849288c81ceSThomas Gleixner 
850288c81ceSThomas Gleixner 		nvec = rc;
851288c81ceSThomas Gleixner 	}
852288c81ceSThomas Gleixner }
853288c81ceSThomas Gleixner 
__pci_restore_msix_state(struct pci_dev * dev)85412910ffdSAhmed S. Darwish void __pci_restore_msix_state(struct pci_dev *dev)
85512910ffdSAhmed S. Darwish {
85612910ffdSAhmed S. Darwish 	struct msi_desc *entry;
85712910ffdSAhmed S. Darwish 	bool write_msg;
85812910ffdSAhmed S. Darwish 
85912910ffdSAhmed S. Darwish 	if (!dev->msix_enabled)
86012910ffdSAhmed S. Darwish 		return;
86112910ffdSAhmed S. Darwish 
86212910ffdSAhmed S. Darwish 	/* route the table */
86312910ffdSAhmed S. Darwish 	pci_intx_for_msi(dev, 0);
86412910ffdSAhmed S. Darwish 	pci_msix_clear_and_set_ctrl(dev, 0,
86512910ffdSAhmed S. Darwish 				PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL);
86612910ffdSAhmed S. Darwish 
86712910ffdSAhmed S. Darwish 	write_msg = arch_restore_msi_irqs(dev);
86812910ffdSAhmed S. Darwish 
86912910ffdSAhmed S. Darwish 	msi_lock_descs(&dev->dev);
87012910ffdSAhmed S. Darwish 	msi_for_each_desc(entry, &dev->dev, MSI_DESC_ALL) {
87112910ffdSAhmed S. Darwish 		if (write_msg)
87212910ffdSAhmed S. Darwish 			__pci_write_msi_msg(entry, &entry->msg);
87312910ffdSAhmed S. Darwish 		pci_msix_write_vector_ctrl(entry, entry->pci.msix_ctrl);
87412910ffdSAhmed S. Darwish 	}
87512910ffdSAhmed S. Darwish 	msi_unlock_descs(&dev->dev);
87612910ffdSAhmed S. Darwish 
87712910ffdSAhmed S. Darwish 	pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0);
87812910ffdSAhmed S. Darwish }
87912910ffdSAhmed S. Darwish 
pci_msix_shutdown(struct pci_dev * dev)88012910ffdSAhmed S. Darwish void pci_msix_shutdown(struct pci_dev *dev)
88112910ffdSAhmed S. Darwish {
88212910ffdSAhmed S. Darwish 	struct msi_desc *desc;
88312910ffdSAhmed S. Darwish 
88412910ffdSAhmed S. Darwish 	if (!pci_msi_enable || !dev || !dev->msix_enabled)
88512910ffdSAhmed S. Darwish 		return;
88612910ffdSAhmed S. Darwish 
88712910ffdSAhmed S. Darwish 	if (pci_dev_is_disconnected(dev)) {
88812910ffdSAhmed S. Darwish 		dev->msix_enabled = 0;
88912910ffdSAhmed S. Darwish 		return;
89012910ffdSAhmed S. Darwish 	}
89112910ffdSAhmed S. Darwish 
89212910ffdSAhmed S. Darwish 	/* Return the device with MSI-X masked as initial states */
89312910ffdSAhmed S. Darwish 	msi_for_each_desc(desc, &dev->dev, MSI_DESC_ALL)
89412910ffdSAhmed S. Darwish 		pci_msix_mask(desc);
89512910ffdSAhmed S. Darwish 
89612910ffdSAhmed S. Darwish 	pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0);
89712910ffdSAhmed S. Darwish 	pci_intx_for_msi(dev, 1);
89812910ffdSAhmed S. Darwish 	dev->msix_enabled = 0;
89912910ffdSAhmed S. Darwish 	pcibios_alloc_irq(dev);
90012910ffdSAhmed S. Darwish }
90112910ffdSAhmed S. Darwish 
90212910ffdSAhmed S. Darwish /* Common interfaces */
90312910ffdSAhmed S. Darwish 
pci_free_msi_irqs(struct pci_dev * dev)90412910ffdSAhmed S. Darwish void pci_free_msi_irqs(struct pci_dev *dev)
90512910ffdSAhmed S. Darwish {
90612910ffdSAhmed S. Darwish 	pci_msi_teardown_msi_irqs(dev);
90712910ffdSAhmed S. Darwish 
90812910ffdSAhmed S. Darwish 	if (dev->msix_base) {
90912910ffdSAhmed S. Darwish 		iounmap(dev->msix_base);
91012910ffdSAhmed S. Darwish 		dev->msix_base = NULL;
91112910ffdSAhmed S. Darwish 	}
91212910ffdSAhmed S. Darwish }
91312910ffdSAhmed S. Darwish 
91412910ffdSAhmed S. Darwish /* Misc. infrastructure */
91512910ffdSAhmed S. Darwish 
msi_desc_to_pci_dev(struct msi_desc * desc)916288c81ceSThomas Gleixner struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc)
917288c81ceSThomas Gleixner {
918288c81ceSThomas Gleixner 	return to_pci_dev(desc->dev);
919288c81ceSThomas Gleixner }
920288c81ceSThomas Gleixner EXPORT_SYMBOL(msi_desc_to_pci_dev);
921288c81ceSThomas Gleixner 
pci_no_msi(void)922aa423ac4SThomas Gleixner void pci_no_msi(void)
923288c81ceSThomas Gleixner {
924aa423ac4SThomas Gleixner 	pci_msi_enable = 0;
925288c81ceSThomas Gleixner }
926