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