xen/pci: support multi-segment systems
[pandora-kernel.git] / drivers / xen / pci.c
1 /*
2  * Copyright (c) 2009, Intel Corporation.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15  * Place - Suite 330, Boston, MA 02111-1307 USA.
16  *
17  * Author: Weidong Han <weidong.han@intel.com>
18  */
19
20 #include <linux/pci.h>
21 #include <linux/acpi.h>
22 #include <xen/xen.h>
23 #include <xen/interface/physdev.h>
24 #include <xen/interface/xen.h>
25
26 #include <asm/xen/hypervisor.h>
27 #include <asm/xen/hypercall.h>
28 #include "../pci/pci.h"
29
30 static bool __read_mostly pci_seg_supported = true;
31
32 static int xen_add_device(struct device *dev)
33 {
34         int r;
35         struct pci_dev *pci_dev = to_pci_dev(dev);
36 #ifdef CONFIG_PCI_IOV
37         struct pci_dev *physfn = pci_dev->physfn;
38 #endif
39
40         if (pci_seg_supported) {
41                 struct physdev_pci_device_add add = {
42                         .seg = pci_domain_nr(pci_dev->bus),
43                         .bus = pci_dev->bus->number,
44                         .devfn = pci_dev->devfn
45                 };
46 #ifdef CONFIG_ACPI
47                 acpi_handle handle;
48 #endif
49
50 #ifdef CONFIG_PCI_IOV
51                 if (pci_dev->is_virtfn) {
52                         add.flags = XEN_PCI_DEV_VIRTFN;
53                         add.physfn.bus = physfn->bus->number;
54                         add.physfn.devfn = physfn->devfn;
55                 } else
56 #endif
57                 if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn))
58                         add.flags = XEN_PCI_DEV_EXTFN;
59
60 #ifdef CONFIG_ACPI
61                 handle = DEVICE_ACPI_HANDLE(&pci_dev->dev);
62                 if (!handle)
63                         handle = DEVICE_ACPI_HANDLE(pci_dev->bus->bridge);
64 #ifdef CONFIG_PCI_IOV
65                 if (!handle && pci_dev->is_virtfn)
66                         handle = DEVICE_ACPI_HANDLE(physfn->bus->bridge);
67 #endif
68                 if (handle) {
69                         acpi_status status;
70
71                         do {
72                                 unsigned long long pxm;
73
74                                 status = acpi_evaluate_integer(handle, "_PXM",
75                                                                NULL, &pxm);
76                                 if (ACPI_SUCCESS(status)) {
77                                         add.optarr[0] = pxm;
78                                         add.flags |= XEN_PCI_DEV_PXM;
79                                         break;
80                                 }
81                                 status = acpi_get_parent(handle, &handle);
82                         } while (ACPI_SUCCESS(status));
83                 }
84 #endif /* CONFIG_ACPI */
85
86                 r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_add, &add);
87                 if (r != -ENOSYS)
88                         return r;
89                 pci_seg_supported = false;
90         }
91
92         if (pci_domain_nr(pci_dev->bus))
93                 r = -ENOSYS;
94 #ifdef CONFIG_PCI_IOV
95         else if (pci_dev->is_virtfn) {
96                 struct physdev_manage_pci_ext manage_pci_ext = {
97                         .bus            = pci_dev->bus->number,
98                         .devfn          = pci_dev->devfn,
99                         .is_virtfn      = 1,
100                         .physfn.bus     = physfn->bus->number,
101                         .physfn.devfn   = physfn->devfn,
102                 };
103
104                 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
105                         &manage_pci_ext);
106         }
107 #endif
108         else if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) {
109                 struct physdev_manage_pci_ext manage_pci_ext = {
110                         .bus            = pci_dev->bus->number,
111                         .devfn          = pci_dev->devfn,
112                         .is_extfn       = 1,
113                 };
114
115                 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
116                         &manage_pci_ext);
117         } else {
118                 struct physdev_manage_pci manage_pci = {
119                         .bus    = pci_dev->bus->number,
120                         .devfn  = pci_dev->devfn,
121                 };
122
123                 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add,
124                         &manage_pci);
125         }
126
127         return r;
128 }
129
130 static int xen_remove_device(struct device *dev)
131 {
132         int r;
133         struct pci_dev *pci_dev = to_pci_dev(dev);
134
135         if (pci_seg_supported) {
136                 struct physdev_pci_device device = {
137                         .seg = pci_domain_nr(pci_dev->bus),
138                         .bus = pci_dev->bus->number,
139                         .devfn = pci_dev->devfn
140                 };
141
142                 r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_remove,
143                                           &device);
144         } else if (pci_domain_nr(pci_dev->bus))
145                 r = -ENOSYS;
146         else {
147                 struct physdev_manage_pci manage_pci = {
148                         .bus = pci_dev->bus->number,
149                         .devfn = pci_dev->devfn
150                 };
151
152                 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove,
153                                           &manage_pci);
154         }
155
156         return r;
157 }
158
159 static int xen_pci_notifier(struct notifier_block *nb,
160                             unsigned long action, void *data)
161 {
162         struct device *dev = data;
163         int r = 0;
164
165         switch (action) {
166         case BUS_NOTIFY_ADD_DEVICE:
167                 r = xen_add_device(dev);
168                 break;
169         case BUS_NOTIFY_DEL_DEVICE:
170                 r = xen_remove_device(dev);
171                 break;
172         default:
173                 return NOTIFY_DONE;
174         }
175         if (r)
176                 dev_err(dev, "Failed to %s - passthrough or MSI/MSI-X might fail!\n",
177                         action == BUS_NOTIFY_ADD_DEVICE ? "add" :
178                         (action == BUS_NOTIFY_DEL_DEVICE ? "delete" : "?"));
179         return NOTIFY_OK;
180 }
181
182 static struct notifier_block device_nb = {
183         .notifier_call = xen_pci_notifier,
184 };
185
186 static int __init register_xen_pci_notifier(void)
187 {
188         if (!xen_initial_domain())
189                 return 0;
190
191         return bus_register_notifier(&pci_bus_type, &device_nb);
192 }
193
194 arch_initcall(register_xen_pci_notifier);