ia64/linux-2.6.18-xen.hg

annotate drivers/pci/msi-altix.c @ 882:8dec4aa9b8b9

PCI pass through: PCIe IO space multiplexing

This is required for more than 16 HVM domain to boot from
PCIe pass through device.

Linux as dom0 exclusively assigns IO space to downstream PCI bridges
and the assignment unit of PCI bridge IO space is 4K. So the only up
to 16 PCIe device can be accessed via IO space within 64K IO ports.
PCI expansion ROM BIOS often uses IO port access to boot from the
device, so on virtualized environment, it means only up to 16 guest
domain can boot from pass-through device.

This patch allows PCIe IO space sharing of pass-through device.
- reassign IO space of PCIe devices specified by
"guestiomuldev=[<segment>:]<bus>:<dev>[,[<segment:><bus>:dev]][,...]"
to be shared.
This is implemented as Linux PCI quirk fixup.

The sharing unit is PCIe switch. Ie IO space of the end point
devices under the same switch will be shared. If there are more than
one switches, two areas of IO space will be used.

- And the driver which arbitrates the accesses to the multiplexed PCIe
IO space. Later qemu-dm will use this.

Limitation:
IO port of IO shared devices can't be accessed from dom0 Linux device
driver. But this wouldn't be a big issue because PCIe specification
discourages the use of IO space and recommends that IO space should be
used only for bootable device with ROM code. OS device driver should
work without IO space access.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
author Keir Fraser <keir.fraser@citrix.com>
date Thu May 28 09:57:49 2009 +0100 (2009-05-28)
parents 831230e53067
children
rev   line source
ian@0 1 /*
ian@0 2 * This file is subject to the terms and conditions of the GNU General Public
ian@0 3 * License. See the file "COPYING" in the main directory of this archive
ian@0 4 * for more details.
ian@0 5 *
ian@0 6 * Copyright (C) 2006 Silicon Graphics, Inc. All Rights Reserved.
ian@0 7 */
ian@0 8
ian@0 9 #include <linux/types.h>
ian@0 10 #include <linux/pci.h>
ian@0 11 #include <linux/cpumask.h>
ian@0 12
ian@0 13 #include <asm/sn/addrs.h>
ian@0 14 #include <asm/sn/intr.h>
ian@0 15 #include <asm/sn/pcibus_provider_defs.h>
ian@0 16 #include <asm/sn/pcidev.h>
ian@0 17 #include <asm/sn/nodepda.h>
ian@0 18
ian@0 19 #include "msi.h"
ian@0 20
ian@0 21 struct sn_msi_info {
ian@0 22 u64 pci_addr;
ian@0 23 struct sn_irq_info *sn_irq_info;
ian@0 24 };
ian@0 25
ian@0 26 static struct sn_msi_info *sn_msi_info;
ian@0 27
ian@0 28 static void
ian@0 29 sn_msi_teardown(unsigned int vector)
ian@0 30 {
ian@0 31 nasid_t nasid;
ian@0 32 int widget;
ian@0 33 struct pci_dev *pdev;
ian@0 34 struct pcidev_info *sn_pdev;
ian@0 35 struct sn_irq_info *sn_irq_info;
ian@0 36 struct pcibus_bussoft *bussoft;
ian@0 37 struct sn_pcibus_provider *provider;
ian@0 38
ian@0 39 sn_irq_info = sn_msi_info[vector].sn_irq_info;
ian@0 40 if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0)
ian@0 41 return;
ian@0 42
ian@0 43 sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
ian@0 44 pdev = sn_pdev->pdi_linux_pcidev;
ian@0 45 provider = SN_PCIDEV_BUSPROVIDER(pdev);
ian@0 46
ian@0 47 (*provider->dma_unmap)(pdev,
ian@0 48 sn_msi_info[vector].pci_addr,
ian@0 49 PCI_DMA_FROMDEVICE);
ian@0 50 sn_msi_info[vector].pci_addr = 0;
ian@0 51
ian@0 52 bussoft = SN_PCIDEV_BUSSOFT(pdev);
ian@0 53 nasid = NASID_GET(bussoft->bs_base);
ian@0 54 widget = (nasid & 1) ?
ian@0 55 TIO_SWIN_WIDGETNUM(bussoft->bs_base) :
ian@0 56 SWIN_WIDGETNUM(bussoft->bs_base);
ian@0 57
ian@0 58 sn_intr_free(nasid, widget, sn_irq_info);
ian@0 59 sn_msi_info[vector].sn_irq_info = NULL;
ian@0 60
ian@0 61 return;
ian@0 62 }
ian@0 63
ian@0 64 int
ian@0 65 sn_msi_setup(struct pci_dev *pdev, unsigned int vector,
ian@0 66 u32 *addr_hi, u32 *addr_lo, u32 *data)
ian@0 67 {
ian@0 68 int widget;
ian@0 69 int status;
ian@0 70 nasid_t nasid;
ian@0 71 u64 bus_addr;
ian@0 72 struct sn_irq_info *sn_irq_info;
ian@0 73 struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(pdev);
ian@0 74 struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev);
ian@0 75
ian@0 76 if (bussoft == NULL)
ian@0 77 return -EINVAL;
ian@0 78
ian@0 79 if (provider == NULL || provider->dma_map_consistent == NULL)
ian@0 80 return -EINVAL;
ian@0 81
ian@0 82 /*
ian@0 83 * Set up the vector plumbing. Let the prom (via sn_intr_alloc)
ian@0 84 * decide which cpu to direct this msi at by default.
ian@0 85 */
ian@0 86
ian@0 87 nasid = NASID_GET(bussoft->bs_base);
ian@0 88 widget = (nasid & 1) ?
ian@0 89 TIO_SWIN_WIDGETNUM(bussoft->bs_base) :
ian@0 90 SWIN_WIDGETNUM(bussoft->bs_base);
ian@0 91
ian@0 92 sn_irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL);
ian@0 93 if (! sn_irq_info)
ian@0 94 return -ENOMEM;
ian@0 95
ian@0 96 status = sn_intr_alloc(nasid, widget, sn_irq_info, vector, -1, -1);
ian@0 97 if (status) {
ian@0 98 kfree(sn_irq_info);
ian@0 99 return -ENOMEM;
ian@0 100 }
ian@0 101
ian@0 102 sn_irq_info->irq_int_bit = -1; /* mark this as an MSI irq */
ian@0 103 sn_irq_fixup(pdev, sn_irq_info);
ian@0 104
ian@0 105 /* Prom probably should fill these in, but doesn't ... */
ian@0 106 sn_irq_info->irq_bridge_type = bussoft->bs_asic_type;
ian@0 107 sn_irq_info->irq_bridge = (void *)bussoft->bs_base;
ian@0 108
ian@0 109 /*
ian@0 110 * Map the xio address into bus space
ian@0 111 */
ian@0 112 bus_addr = (*provider->dma_map_consistent)(pdev,
ian@0 113 sn_irq_info->irq_xtalkaddr,
ian@0 114 sizeof(sn_irq_info->irq_xtalkaddr),
ian@0 115 SN_DMA_MSI|SN_DMA_ADDR_XIO);
ian@0 116 if (! bus_addr) {
ian@0 117 sn_intr_free(nasid, widget, sn_irq_info);
ian@0 118 kfree(sn_irq_info);
ian@0 119 return -ENOMEM;
ian@0 120 }
ian@0 121
ian@0 122 sn_msi_info[vector].sn_irq_info = sn_irq_info;
ian@0 123 sn_msi_info[vector].pci_addr = bus_addr;
ian@0 124
ian@0 125 *addr_hi = (u32)(bus_addr >> 32);
ian@0 126 *addr_lo = (u32)(bus_addr & 0x00000000ffffffff);
ian@0 127
ian@0 128 /*
ian@0 129 * In the SN platform, bit 16 is a "send vector" bit which
ian@0 130 * must be present in order to move the vector through the system.
ian@0 131 */
ian@0 132 *data = 0x100 + (unsigned int)vector;
ian@0 133
ian@0 134 #ifdef CONFIG_SMP
ian@0 135 set_irq_affinity_info((vector & 0xff), sn_irq_info->irq_cpuid, 0);
ian@0 136 #endif
ian@0 137
ian@0 138 return 0;
ian@0 139 }
ian@0 140
ian@0 141 static void
ian@0 142 sn_msi_target(unsigned int vector, unsigned int cpu,
ian@0 143 u32 *addr_hi, u32 *addr_lo)
ian@0 144 {
ian@0 145 int slice;
ian@0 146 nasid_t nasid;
ian@0 147 u64 bus_addr;
ian@0 148 struct pci_dev *pdev;
ian@0 149 struct pcidev_info *sn_pdev;
ian@0 150 struct sn_irq_info *sn_irq_info;
ian@0 151 struct sn_irq_info *new_irq_info;
ian@0 152 struct sn_pcibus_provider *provider;
ian@0 153
ian@0 154 sn_irq_info = sn_msi_info[vector].sn_irq_info;
ian@0 155 if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0)
ian@0 156 return;
ian@0 157
ian@0 158 /*
ian@0 159 * Release XIO resources for the old MSI PCI address
ian@0 160 */
ian@0 161
ian@0 162 sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
ian@0 163 pdev = sn_pdev->pdi_linux_pcidev;
ian@0 164 provider = SN_PCIDEV_BUSPROVIDER(pdev);
ian@0 165
ian@0 166 bus_addr = (u64)(*addr_hi) << 32 | (u64)(*addr_lo);
ian@0 167 (*provider->dma_unmap)(pdev, bus_addr, PCI_DMA_FROMDEVICE);
ian@0 168 sn_msi_info[vector].pci_addr = 0;
ian@0 169
ian@0 170 nasid = cpuid_to_nasid(cpu);
ian@0 171 slice = cpuid_to_slice(cpu);
ian@0 172
ian@0 173 new_irq_info = sn_retarget_vector(sn_irq_info, nasid, slice);
ian@0 174 sn_msi_info[vector].sn_irq_info = new_irq_info;
ian@0 175 if (new_irq_info == NULL)
ian@0 176 return;
ian@0 177
ian@0 178 /*
ian@0 179 * Map the xio address into bus space
ian@0 180 */
ian@0 181
ian@0 182 bus_addr = (*provider->dma_map_consistent)(pdev,
ian@0 183 new_irq_info->irq_xtalkaddr,
ian@0 184 sizeof(new_irq_info->irq_xtalkaddr),
ian@0 185 SN_DMA_MSI|SN_DMA_ADDR_XIO);
ian@0 186
ian@0 187 sn_msi_info[vector].pci_addr = bus_addr;
ian@0 188 *addr_hi = (u32)(bus_addr >> 32);
ian@0 189 *addr_lo = (u32)(bus_addr & 0x00000000ffffffff);
ian@0 190 }
ian@0 191
ian@0 192 struct msi_ops sn_msi_ops = {
ian@0 193 .setup = sn_msi_setup,
ian@0 194 .teardown = sn_msi_teardown,
ian@0 195 #ifdef CONFIG_SMP
ian@0 196 .target = sn_msi_target,
ian@0 197 #endif
ian@0 198 };
ian@0 199
ian@0 200 int
ian@0 201 sn_msi_init(void)
ian@0 202 {
ian@0 203 sn_msi_info =
ian@0 204 kzalloc(sizeof(struct sn_msi_info) * NR_VECTORS, GFP_KERNEL);
ian@0 205 if (! sn_msi_info)
ian@0 206 return -ENOMEM;
ian@0 207
ian@0 208 msi_register(&sn_msi_ops);
ian@0 209 return 0;
ian@0 210 }